/* * SPDX-License-Identifier: MIT * * Copyright (C) 2013-2024 OpenMV, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * MT9M114 driver. */ #include "omv_boardconfig.h" #if (OMV_MT9M114_ENABLE == 1) #include #include #include #include "omv_i2c.h" #include "omv_csi.h" #include "py/mphal.h" #include "mt9m114.h" #include "sensor_config.h" #define DUMMY_LINES 8 #define DUMMY_COLUMNS 8 #define SENSOR_WIDTH 1296 #define SENSOR_HEIGHT 976 #define ACTIVE_SENSOR_WIDTH (SENSOR_WIDTH - (2 * DUMMY_COLUMNS)) #define ACTIVE_SENSOR_HEIGHT (SENSOR_HEIGHT - (2 * DUMMY_LINES)) #define DUMMY_WIDTH_BUFFER 8 #define DUMMY_HEIGHT_BUFFER 8 static int16_t readout_x = 0; static int16_t readout_y = 0; static uint16_t readout_w = ACTIVE_SENSOR_WIDTH; static uint16_t readout_h = ACTIVE_SENSOR_HEIGHT; static const uint16_t default_regs[][2] = { // Sensor Optimization {0x316A, 0x8270}, {0x316C, 0x8270}, {0x3ED0, 0x2305}, {0x3ED2, 0x77CF}, {0x316E, 0x8202}, {0x3180, 0x87FF}, {0x30D4, 0x6080}, {0xA802, 0x0008}, // Errata 1 (Column Bands) {0x3E14, 0xFF39} }; static const uint8_t patch_0202[] = { 0xd0, 0x00, 0x70, 0xcf, 0xff, 0xff, 0xc5, 0xd4, 0x90, 0x3a, 0x21, 0x44, 0x0c, 0x00, 0x21, 0x86, 0x0f, 0xf3, 0xb8, 0x44, 0xb9, 0x48, 0xe0, 0x82, 0x20, 0xcc, 0x80, 0xe2, 0x21, 0xcc, 0x80, 0xa2, 0x21, 0xcc, 0x80, 0xe2, 0xf4, 0x04, 0xd8, 0x01, 0xf0, 0x03, 0xd8, 0x00, 0x7e, 0xe0, 0xc0, 0xf1, 0x08, 0xba, 0x06, 0x00, 0xc1, 0xa1, 0x76, 0xcf, 0xff, 0xff, 0xc1, 0x30, 0x6e, 0x04, 0xc0, 0x40, 0x71, 0xcf, 0xff, 0xff, 0xc7, 0x90, 0x81, 0x03, 0x77, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xe0, 0x01, 0xa1, 0x03, 0xd8, 0x00, 0x0c, 0x6a, 0x04, 0xe0, 0xb8, 0x9e, 0x75, 0x08, 0x8e, 0x1c, 0x08, 0x09, 0x01, 0x91, 0xd8, 0x01, 0xae, 0x1d, 0xe5, 0x80, 0x20, 0xca, 0x00, 0x22, 0x20, 0xcf, 0x05, 0x22, 0x0c, 0x5c, 0x04, 0xe2, 0x21, 0xca, 0x00, 0x62, 0xe5, 0x80, 0xd9, 0x01, 0x79, 0xc0, 0xd8, 0x00, 0x0b, 0xe6, 0x04, 0xe0, 0xb8, 0x9e, 0x70, 0xcf, 0xff, 0xff, 0xc8, 0xd4, 0x90, 0x02, 0x08, 0x57, 0x02, 0x5e, 0xff, 0xdc, 0xe0, 0x80, 0x25, 0xcc, 0x90, 0x22, 0xf2, 0x25, 0x17, 0x00, 0x10, 0x8a, 0x73, 0xcf, 0xff, 0x00, 0x31, 0x74, 0x93, 0x07, 0x2a, 0x04, 0x10, 0x3e, 0x93, 0x28, 0x29, 0x42, 0x71, 0x40, 0x2a, 0x04, 0x10, 0x7e, 0x93, 0x49, 0x29, 0x42, 0x71, 0x41, 0x2a, 0x04, 0x10, 0xbe, 0x93, 0x4a, 0x29, 0x42, 0x71, 0x4b, 0x2a, 0x04, 0x10, 0xbe, 0x13, 0x0c, 0x01, 0x0a, 0x29, 0x42, 0x71, 0x42, 0x22, 0x50, 0x13, 0xca, 0x1b, 0x0c, 0x02, 0x84, 0xb3, 0x07, 0xb3, 0x28, 0x1b, 0x12, 0x02, 0xc4, 0xb3, 0x4a, 0xed, 0x88, 0x71, 0xcf, 0xff, 0x00, 0x31, 0x74, 0x91, 0x06, 0xb8, 0x8f, 0xb1, 0x06, 0x21, 0x0a, 0x83, 0x40, 0xc0, 0x00, 0x21, 0xca, 0x00, 0x62, 0x20, 0xf0, 0x00, 0x40, 0x0b, 0x02, 0x03, 0x20, 0xd9, 0x01, 0x07, 0xf1, 0x05, 0xe0, 0xc0, 0xa1, 0x78, 0xe0, 0xc0, 0xf1, 0x71, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xd8, 0x40, 0xa9, 0x00, 0x71, 0xcf, 0xff, 0xff, 0xd0, 0x2c, 0xd8, 0x1e, 0x0a, 0x5a, 0x04, 0xe0, 0xda, 0x00, 0xd8, 0x00, 0xc0, 0xd1, 0x7e, 0xe0 }; static const uint8_t patch_0302[] = { 0xd1, 0x2c, 0x70, 0xcf, 0xff, 0xff, 0xc5, 0xd4, 0x90, 0x3a, 0x21, 0x44, 0x0c, 0x00, 0x21, 0x86, 0x0f, 0xf3, 0xb8, 0x44, 0x26, 0x2f, 0xf0, 0x08, 0xb9, 0x48, 0x21, 0xcc, 0x80, 0x21, 0xd8, 0x01, 0xf2, 0x03, 0xd8, 0x00, 0x7e, 0xe0, 0xc0, 0xf1, 0x71, 0xcf, 0xff, 0xff, 0xc6, 0x10, 0x91, 0x0e, 0x20, 0x8c, 0x80, 0x14, 0xf4, 0x18, 0x91, 0x0f, 0x20, 0x8c, 0x80, 0x0f, 0xf4, 0x14, 0x91, 0x16, 0x20, 0x8c, 0x80, 0x0a, 0xf4, 0x10, 0x91, 0x17, 0x20, 0x8c, 0x88, 0x07, 0xf4, 0x0c, 0x91, 0x18, 0x20, 0x86, 0x0f, 0xf3, 0xb8, 0x48, 0x08, 0x0d, 0x00, 0x90, 0xff, 0xea, 0xe0, 0x81, 0xd8, 0x01, 0xf2, 0x03, 0xd8, 0x00, 0xc0, 0xd1, 0x7e, 0xe0, 0x78, 0xe0, 0xc0, 0xf1, 0x71, 0xcf, 0xff, 0xff, 0xc6, 0x10, 0x91, 0x0e, 0x20, 0x8c, 0x80, 0x0a, 0xf4, 0x18, 0x91, 0x0f, 0x20, 0x8c, 0x88, 0x07, 0xf4, 0x14, 0x91, 0x16, 0x20, 0x8c, 0x80, 0x0a, 0xf4, 0x10, 0x91, 0x17, 0x20, 0x8c, 0x88, 0x07, 0xf4, 0x0c, 0x91, 0x18, 0x20, 0x86, 0x0f, 0xf3, 0xb8, 0x48, 0x08, 0x0d, 0x00, 0x90, 0xff, 0xd9, 0xe0, 0x80, 0xd8, 0x01, 0xf2, 0x03, 0xd8, 0x00, 0xf1, 0xdf, 0x90, 0x40, 0x71, 0xcf, 0xff, 0xff, 0xc5, 0xd4, 0xb1, 0x5a, 0x90, 0x41, 0x73, 0xcf, 0xff, 0xff, 0xc7, 0xd0, 0xb1, 0x40, 0x90, 0x42, 0xb1, 0x41, 0x90, 0x43, 0xb1, 0x42, 0x90, 0x44, 0xb1, 0x43, 0x90, 0x45, 0xb1, 0x47, 0x90, 0x46, 0xb1, 0x48, 0x90, 0x47, 0xb1, 0x4b, 0x90, 0x48, 0xb1, 0x4c, 0x90, 0x49, 0x19, 0x58, 0x00, 0x84, 0x90, 0x4a, 0x19, 0x5a, 0x00, 0x84, 0x88, 0x56, 0x1b, 0x36, 0x80, 0x82, 0x88, 0x57, 0x1b, 0x37, 0x80, 0x82, 0x90, 0x4c, 0x19, 0xa7, 0x00, 0x9c, 0x88, 0x1a, 0x7f, 0xe0, 0x1b, 0x54, 0x80, 0x02, 0x78, 0xe0, 0x71, 0xcf, 0xff, 0xff, 0xc3, 0x50, 0xd8, 0x28, 0xa9, 0x0b, 0x81, 0x00, 0x01, 0xc5, 0x03, 0x20, 0xd9, 0x00, 0x78, 0xe0, 0x22, 0x0a, 0x1f, 0x80, 0xff, 0xff, 0xd4, 0xe0, 0xc0, 0xf1, 0x08, 0x11, 0x00, 0x51, 0x22, 0x40, 0x12, 0x00, 0xff, 0xe1, 0xd8, 0x01, 0xf0, 0x06, 0x22, 0x40, 0x19, 0x00, 0xff, 0xde, 0xd8, 0x02, 0x1a, 0x05, 0x10, 0x02, 0xff, 0xf2, 0xf1, 0x95, 0xc0, 0xf1, 0x0e, 0x7e, 0x05, 0xc0, 0x75, 0xcf, 0xff, 0xff, 0xc8, 0x4c, 0x95, 0x02, 0x77, 0xcf, 0xff, 0xff, 0xc3, 0x44, 0x20, 0x44, 0x00, 0x8e, 0xb8, 0xa1, 0x09, 0x26, 0x03, 0xe0, 0xb5, 0x02, 0x95, 0x02, 0x95, 0x2e, 0x7e, 0x05, 0xb5, 0xc2, 0x70, 0xcf, 0xff, 0xff, 0xc6, 0x10, 0x09, 0x9a, 0x04, 0xa0, 0xb0, 0x26, 0x0e, 0x02, 0x05, 0x60, 0xde, 0x00, 0x0a, 0x12, 0x03, 0x20, 0xb7, 0xc4, 0x0b, 0x36, 0x03, 0xa0, 0x70, 0xc9, 0x95, 0x02, 0x76, 0x08, 0xb8, 0xa8, 0xb5, 0x02, 0x70, 0xcf, 0x00, 0x00, 0x55, 0x36, 0x78, 0x60, 0x26, 0x86, 0x1f, 0xfb, 0x95, 0x02, 0x78, 0xc5, 0x06, 0x31, 0x05, 0xe0, 0xb5, 0x02, 0x72, 0xcf, 0xff, 0xff, 0xc5, 0xd4, 0x92, 0x3a, 0x73, 0xcf, 0xff, 0xff, 0xc7, 0xd0, 0xb0, 0x20, 0x92, 0x20, 0xb0, 0x21, 0x92, 0x21, 0xb0, 0x22, 0x92, 0x22, 0xb0, 0x23, 0x92, 0x23, 0xb0, 0x24, 0x92, 0x27, 0xb0, 0x25, 0x92, 0x28, 0xb0, 0x26, 0x92, 0x2b, 0xb0, 0x27, 0x92, 0x2c, 0xb0, 0x28, 0x12, 0x58, 0x01, 0x01, 0xb0, 0x29, 0x12, 0x5a, 0x01, 0x01, 0xb0, 0x2a, 0x13, 0x36, 0x80, 0x81, 0xa8, 0x36, 0x13, 0x37, 0x80, 0x81, 0xa8, 0x37, 0x12, 0xa7, 0x07, 0x01, 0xb0, 0x2c, 0x13, 0x54, 0x80, 0x81, 0x7f, 0xe0, 0xa8, 0x3a, 0x78, 0xe0, 0xc0, 0xf1, 0x0d, 0xc2, 0x05, 0xc0, 0x76, 0x08, 0x09, 0xbb, 0x00, 0x10, 0x75, 0xcf, 0xff, 0xff, 0xd4, 0xe0, 0x8d, 0x21, 0x8d, 0x00, 0x21, 0x53, 0x00, 0x03, 0xb8, 0xc0, 0x8d, 0x45, 0x0b, 0x23, 0x00, 0x00, 0xea, 0x8f, 0x09, 0x15, 0x00, 0x1e, 0xff, 0x81, 0xe8, 0x08, 0x25, 0x40, 0x19, 0x00, 0xff, 0xde, 0x8d, 0x00, 0xb8, 0x80, 0xf0, 0x04, 0x8d, 0x00, 0xb8, 0xa0, 0xad, 0x00, 0x8d, 0x05, 0xe0, 0x81, 0x20, 0xcc, 0x80, 0xa2, 0xdf, 0x00, 0xf4, 0x0a, 0x71, 0xcf, 0xff, 0xff, 0xc8, 0x4c, 0x91, 0x02, 0x77, 0x08, 0xb8, 0xa6, 0x27, 0x86, 0x1f, 0xfe, 0xb1, 0x02, 0x0b, 0x42, 0x01, 0x80, 0x0e, 0x3e, 0x01, 0x80, 0x0f, 0x4a, 0x01, 0x60, 0x70, 0xc9, 0x8d, 0x05, 0xe0, 0x81, 0x20, 0xcc, 0x80, 0xa2, 0xf4, 0x29, 0x76, 0xcf, 0xff, 0xff, 0xc8, 0x4c, 0x08, 0x2d, 0x00, 0x51, 0x70, 0xcf, 0xff, 0xff, 0xc9, 0x0c, 0x88, 0x05, 0x09, 0xb6, 0x03, 0x60, 0xd9, 0x08, 0x20, 0x99, 0x08, 0x02, 0x96, 0x34, 0xb5, 0x03, 0x79, 0x02, 0x15, 0x23, 0x10, 0x80, 0xb6, 0x34, 0xe0, 0x01, 0x1d, 0x23, 0x10, 0x02, 0xf0, 0x0b, 0x96, 0x34, 0x95, 0x03, 0x60, 0x38, 0xb6, 0x14, 0x15, 0x3f, 0x10, 0x80, 0xe0, 0x01, 0x1d, 0x3f, 0x10, 0x02, 0xff, 0xa4, 0x96, 0x02, 0x7f, 0x05, 0xd8, 0x00, 0xb6, 0xe2, 0xad, 0x05, 0x05, 0x11, 0x05, 0xe0, 0xd8, 0x00, 0xc0, 0xf1, 0x0c, 0xfe, 0x05, 0xc0, 0x0a, 0x96, 0x05, 0xa0, 0x76, 0x08, 0x0c, 0x22, 0x02, 0x40, 0xe0, 0x80, 0x20, 0xca, 0x0f, 0x82, 0x00, 0x00, 0x19, 0x0b, 0x0c, 0x60, 0x05, 0xa2, 0x21, 0xca, 0x00, 0x22, 0x0c, 0x56, 0x02, 0x40, 0xe8, 0x06, 0x0e, 0x0e, 0x02, 0x20, 0x70, 0xc9, 0xf0, 0x48, 0x08, 0x96, 0x04, 0x40, 0x0e, 0x96, 0x04, 0x00, 0x09, 0x66, 0x03, 0x80, 0x75, 0xcf, 0xff, 0xff, 0xd4, 0xe0, 0x8d, 0x00, 0x08, 0x4d, 0x00, 0x1e, 0xff, 0x47, 0x08, 0x0d, 0x00, 0x50, 0xff, 0x57, 0x08, 0x41, 0x00, 0x51, 0x8d, 0x04, 0x95, 0x21, 0xe0, 0x64, 0x79, 0x0c, 0x70, 0x2f, 0x0c, 0xe2, 0x05, 0xe0, 0xd9, 0x64, 0x72, 0xcf, 0xff, 0xff, 0xc7, 0x00, 0x92, 0x35, 0x08, 0x11, 0x00, 0x43, 0xff, 0x3d, 0x08, 0x0d, 0x00, 0x51, 0xd8, 0x01, 0xff, 0x77, 0xf0, 0x25, 0x95, 0x01, 0x92, 0x35, 0x09, 0x11, 0x00, 0x03, 0xff, 0x49, 0x08, 0x0d, 0x00, 0x51, 0xd8, 0x00, 0xff, 0x72, 0xf0, 0x1b, 0x08, 0x86, 0x03, 0xe0, 0xd8, 0x01, 0x0e, 0xf6, 0x03, 0xc0, 0x0f, 0x52, 0x03, 0x40, 0x0d, 0xba, 0x02, 0x00, 0x0a, 0xf6, 0x04, 0x40, 0x0c, 0x22, 0x04, 0x00, 0x0d, 0x72, 0x04, 0x40, 0x0d, 0xc2, 0x02, 0x00, 0x09, 0x72, 0x04, 0x40, 0x0d, 0x3a, 0x02, 0x20, 0xd8, 0x20, 0x0b, 0xfa, 0x02, 0x60, 0x70, 0xc9, 0x04, 0x51, 0x05, 0xc0, 0x78, 0xe0, 0xd9, 0x00, 0xf0, 0x0a, 0x70, 0xcf, 0xff, 0xff, 0xd5, 0x20, 0x78, 0x35, 0x80, 0x41, 0x80, 0x00, 0xe1, 0x02, 0xa0, 0x40, 0x09, 0xf1, 0x81, 0x14, 0x71, 0xcf, 0xff, 0xff, 0xd4, 0xe0, 0x70, 0xcf, 0xff, 0xff, 0xc5, 0x94, 0xb0, 0x3a, 0x7f, 0xe0, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x02, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcd, 0x05, 0x0d, 0x01, 0xc5, 0x03, 0xb3, 0x00, 0xe0, 0x01, 0xe3, 0x02, 0x80, 0x01, 0xe0, 0x01, 0x09, 0x00, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc9, 0xb4, 0xff, 0xff, 0xd3, 0x24, 0xff, 0xff, 0xca, 0x34, 0xff, 0xff, 0xd3, 0xec }; static const uint8_t patch_0402[] = { 0xd5, 0x30, 0xc0, 0xf1, 0x0b, 0xba, 0x05, 0xc0, 0xc1, 0xa2, 0x75, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xde, 0x00, 0x1c, 0x05, 0x33, 0x82, 0xc6, 0x61, 0x08, 0xc3, 0x00, 0x40, 0xe1, 0xd1, 0xf2, 0x17, 0xe1, 0xd4, 0xf4, 0x5b, 0x8d, 0x01, 0x08, 0xb3, 0x00, 0x1e, 0x1c, 0x00, 0x3f, 0x80, 0x00, 0x0c, 0x35, 0x00, 0x71, 0x8b, 0x0f, 0xbe, 0x04, 0xe0, 0x70, 0xc9, 0x08, 0x06, 0x05, 0x20, 0x70, 0xc9, 0x95, 0x25, 0x70, 0xcf, 0xff, 0x00, 0x00, 0x00, 0xb0, 0x39, 0xf0, 0x47, 0x8d, 0x01, 0x08, 0x27, 0x00, 0xdf, 0x8d, 0x02, 0x08, 0x1d, 0x00, 0x1e, 0x8d, 0x02, 0xad, 0x01, 0x8d, 0x01, 0xb8, 0xa3, 0xad, 0x01, 0x70, 0xcf, 0xff, 0x00, 0x00, 0x00, 0x90, 0x19, 0xb5, 0x05, 0xf0, 0x05, 0x8d, 0x01, 0xb8, 0xa0, 0xad, 0x01, 0x8d, 0x01, 0x08, 0x11, 0x00, 0xde, 0x8d, 0x01, 0xb8, 0xa3, 0xad, 0x01, 0x0d, 0x02, 0x04, 0xa0, 0xd8, 0x01, 0x8d, 0x01, 0x08, 0x53, 0x00, 0x1e, 0x85, 0x01, 0x0b, 0x5e, 0x05, 0xe0, 0x21, 0x8a, 0x04, 0x1f, 0x95, 0x24, 0x29, 0x05, 0x00, 0x3e, 0x1c, 0x00, 0x3e, 0x40, 0x71, 0x8b, 0x0f, 0x4e, 0x04, 0xe0, 0xd8, 0x00, 0x0f, 0x9a, 0x04, 0xe0, 0xd8, 0x00, 0xd8, 0x00, 0x0f, 0xb6, 0x04, 0xe0, 0xd9, 0x01, 0x8d, 0x01, 0x08, 0x23, 0x00, 0x5f, 0x20, 0x8a, 0x00, 0x1c, 0x0b, 0xe2, 0x04, 0xe0, 0xd9, 0x00, 0xd8, 0x0d, 0xb8, 0x0a, 0x0b, 0xd6, 0x04, 0xe0, 0xd9, 0x00, 0xd8, 0x90, 0x0b, 0xea, 0x04, 0xe0, 0xd9, 0x01, 0xd8, 0x00, 0x02, 0xf5, 0x05, 0xe0, 0xc0, 0xa2, 0x78, 0xe0, 0xc0, 0xf1, 0x73, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0x8b, 0x41, 0x0a, 0x0d, 0x00, 0xde, 0x8b, 0x41, 0x0a, 0x0f, 0x00, 0x5e, 0x72, 0xcf, 0x00, 0x00, 0x40, 0x3e, 0x7a, 0x40, 0xf0, 0x02, 0xd8, 0x00, 0xc0, 0xd1, 0x7e, 0xe0, 0xc0, 0xf1, 0xc5, 0xe1, 0x75, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xd9, 0x00, 0xf0, 0x09, 0x70, 0xcf, 0xff, 0xff, 0xd7, 0x40, 0x78, 0x35, 0x80, 0x41, 0x80, 0x00, 0xe1, 0x02, 0xa0, 0x40, 0x09, 0xf3, 0x80, 0x94, 0x71, 0xcf, 0xff, 0xff, 0xd6, 0xd0, 0xd8, 0x03, 0x0d, 0x1e, 0x04, 0xa0, 0xda, 0x00, 0x70, 0xcf, 0xff, 0xff, 0xd5, 0x30, 0x0c, 0x22, 0x03, 0x00, 0xe8, 0x87, 0x70, 0xcf, 0xff, 0xff, 0xd6, 0xe8, 0x09, 0x4a, 0x02, 0x40, 0x21, 0x8a, 0x0a, 0x0f, 0xb5, 0x24, 0xd9, 0x04, 0xad, 0x23, 0x1d, 0x04, 0x1f, 0x80, 0x01, 0x6e, 0x36, 0x00, 0x02, 0x79, 0x05, 0xc0, 0xc0, 0xf1, 0x71, 0xcf, 0xff, 0xff, 0xc3, 0x50, 0x11, 0x09, 0x00, 0xc0, 0x73, 0xcf, 0xff, 0xff, 0xc1, 0x64, 0xe0, 0xd2, 0x72, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xf4, 0x12, 0x8b, 0x08, 0x08, 0x21, 0x03, 0xd1, 0x8a, 0x01, 0x08, 0x19, 0x00, 0xdf, 0xd8, 0x54, 0xa9, 0x0b, 0xd8, 0x00, 0xa9, 0x08, 0x8a, 0x21, 0xb9, 0x83, 0xaa, 0x21, 0xaa, 0x0c, 0x0b, 0xe6, 0x04, 0x80, 0xf1, 0xb2, 0x78, 0xe0, 0xc0, 0xf1, 0x70, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0x88, 0x01, 0xb8, 0xe0, 0x0f, 0xb4, 0xff, 0xc2, 0x0e, 0x9a, 0x02, 0x40, 0xf1, 0xa6, 0x78, 0xe0, 0xc0, 0xf1, 0x71, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0x89, 0x61, 0x72, 0xcf, 0xff, 0xff, 0xc3, 0x50, 0x0b, 0x45, 0x00, 0xde, 0x08, 0x41, 0x08, 0x11, 0x89, 0x0c, 0xe0, 0x01, 0xa9, 0x0c, 0x89, 0x6c, 0x89, 0x03, 0x0b, 0x0d, 0x00, 0x03, 0x89, 0x02, 0x08, 0x2d, 0x00, 0x9e, 0x89, 0x02, 0x08, 0x13, 0x00, 0x9e, 0x89, 0x02, 0xb8, 0xa2, 0xa9, 0x02, 0x89, 0x01, 0xb8, 0x82, 0xf0, 0x03, 0x89, 0x01, 0xb8, 0xa2, 0xa9, 0x01, 0xd8, 0x50, 0xaa, 0x0b, 0xd9, 0x00, 0xaa, 0x2a, 0xaa, 0x28, 0x0c, 0xca, 0x02, 0xe0, 0x82, 0x00, 0xd8, 0x00, 0xf1, 0x7a, 0xff, 0xff, 0xca, 0xd0, 0xff, 0xff, 0xd6, 0x10 }; static const uint8_t patch_0502[] = { 0xd7, 0x48, 0x72, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0x08, 0x27, 0x00, 0x40, 0xe1, 0xd1, 0xf4, 0x0f, 0x8a, 0x01, 0x08, 0x1b, 0x00, 0xdf, 0x70, 0xcf, 0xff, 0xff, 0xc6, 0x44, 0x88, 0x08, 0xb8, 0xe4, 0x8a, 0x0e, 0x20, 0xcf, 0x00, 0x62, 0x20, 0xd0, 0x00, 0x61, 0xaa, 0x0e, 0x7f, 0xe0, 0xd8, 0x00, 0x78, 0xe0, 0xc0, 0xf1, 0x09, 0x6e, 0x05, 0xc0, 0x75, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0x70, 0xcf, 0x00, 0x00, 0x55, 0x36, 0x78, 0x60, 0xc1, 0xa1, 0x95, 0xca, 0x77, 0xcf, 0xff, 0xff, 0xc8, 0xd4, 0x9f, 0x1c, 0x08, 0x27, 0x00, 0x12, 0xd9, 0x08, 0x0d, 0x56, 0x03, 0x20, 0xda, 0x05, 0x9f, 0x3c, 0x09, 0x13, 0x0f, 0x82, 0x00, 0x00, 0x0b, 0x00, 0xb8, 0x2a, 0x7e, 0x0c, 0x76, 0x2f, 0xf0, 0x0f, 0x7e, 0x0c, 0x29, 0x41, 0x72, 0x8e, 0xf0, 0x0b, 0x78, 0x13, 0xd9, 0x08, 0x0d, 0x32, 0x03, 0x20, 0xda, 0x05, 0x71, 0x08, 0x09, 0x52, 0x05, 0xe0, 0x70, 0xc9, 0x76, 0x08, 0x8d, 0x0f, 0x08, 0x25, 0x00, 0x1e, 0x70, 0xcf, 0xff, 0xff, 0xc8, 0x2f, 0x0d, 0x1e, 0x04, 0x00, 0x28, 0x05, 0x03, 0xbe, 0x70, 0xcf, 0xff, 0xff, 0xc9, 0x14, 0x90, 0x33, 0xb9, 0x24, 0x09, 0x2a, 0x05, 0xe0, 0x70, 0x2f, 0x76, 0x08, 0x8d, 0x0e, 0x08, 0x83, 0x00, 0x5e, 0x8d, 0x01, 0x08, 0x7b, 0x00, 0xde, 0x8d, 0x0e, 0x77, 0xcf, 0xff, 0xff, 0xc8, 0x4c, 0xb8, 0xa0, 0xad, 0x0e, 0x8f, 0x27, 0x8f, 0x49, 0x22, 0x02, 0x80, 0x40, 0x00, 0x08, 0x00, 0x03, 0x21, 0x02, 0x00, 0x80, 0x78, 0x0e, 0xc0, 0x40, 0x8f, 0x08, 0x78, 0x2c, 0x70, 0x2f, 0xe0, 0x63, 0x08, 0xfe, 0x05, 0xe0, 0xd9, 0x64, 0x97, 0x20, 0x78, 0x0d, 0xb9, 0xc1, 0x21, 0x42, 0x80, 0x02, 0x22, 0xca, 0x00, 0x62, 0xc3, 0x00, 0x72, 0x59, 0x08, 0x17, 0x00, 0xe3, 0xd9, 0x00, 0x70, 0xcf, 0xff, 0xff, 0xd8, 0xba, 0x88, 0x00, 0xe0, 0x80, 0x22, 0xcc, 0x90, 0x22, 0xf2, 0x02, 0xd9, 0x01, 0x26, 0x2f, 0xf0, 0x47, 0xf2, 0x08, 0x8d, 0x0e, 0xb8, 0x80, 0xad, 0x0e, 0xa5, 0xc4, 0x8d, 0x02, 0xb8, 0x82, 0xad, 0x02, 0x70, 0xcf, 0xff, 0xff, 0xd8, 0xba, 0xa8, 0x40, 0xf0, 0x02, 0xa5, 0xc4, 0x00, 0x79, 0x05, 0xe0, 0xc0, 0xa1, 0x78, 0xe0, 0xc0, 0xf1, 0xc5, 0xe1, 0x75, 0xcf, 0xff, 0xff, 0xc7, 0xc0, 0xd9, 0x00, 0xf0, 0x09, 0x70, 0xcf, 0xff, 0xff, 0xd8, 0xbc, 0x78, 0x35, 0x80, 0x41, 0x80, 0x00, 0xe1, 0x02, 0xa0, 0x40, 0x09, 0xf3, 0x80, 0x94, 0x70, 0xcf, 0xff, 0xff, 0xd7, 0x48, 0x09, 0xde, 0x03, 0x00, 0x21, 0x8a, 0x00, 0x14, 0xb5, 0x2a, 0x00, 0x51, 0x05, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x54, 0xff, 0xff, 0xd7, 0x78 }; static const uint8_t awb_ccm[] = { 0xc8, 0x92, 0x02, 0x67, 0xff, 0x1a, 0xff, 0xb3, 0xff, 0x80, 0x01, 0x66, 0x00, 0x03, 0xff, 0x9a, 0xfe, 0xb4, 0x02, 0x4d, 0x01, 0xbf, 0xff, 0x01, 0xff, 0xf3, 0xff, 0x75, 0x01, 0x98, 0xff, 0xfd, 0xff, 0x9a, 0xfe, 0xe7, 0x02, 0xa8, 0x01, 0xd9, 0xff, 0x26, 0xff, 0xf3, 0xff, 0xb3, 0x01, 0x32, 0xff, 0xe8, 0xff, 0xda, 0xfe, 0xcd, 0x02, 0xc2 }; static const uint8_t awb_weights[] = { 0xc8, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x24, 0x15, 0x83, 0x20, 0x45, 0x03, 0xff, 0x00, 0x7c }; static const uint16_t cpipe_regs_8_bit_a[] = { 0xC92A, // CAM_LL_START_SATURATION 0xC92B, // CAM_LL_END_SATURATION 0xC92C, // CAM_LL_START_DESATURATION 0xC92D, // CAM_LL_END_DESATURATION 0xC92E, // CAM_LL_START_DEMOSAIC 0xC92F, // CAM_LL_START_AP_GAIN 0xC930, // CAM_LL_START_AP_THRESH 0xC931, // CAM_LL_STOP_DEMOSAIC 0xC932, // CAM_LL_STOP_AP_GAIN 0xC933, // CAM_LL_STOP_AP_THRESH 0xC934, // CAM_LL_START_NR_RED 0xC935, // CAM_LL_START_NR_GREEN 0xC936, // CAM_LL_START_NR_BLUE 0xC937, // CAM_LL_START_NR_THRESH 0xC938, // CAM_LL_STOP_NR_RED 0xC939, // CAM_LL_STOP_NR_GREEN 0xC93A, // CAM_LL_STOP_NR_BLUE 0xC93B, // CAM_LL_STOP_NR_THRESH 0xC942, // CAM_LL_START_CONTRAST_GRADIENT 0xC943, // CAM_LL_STOP_CONTRAST_GRADIENT 0xC944, // CAM_LL_START_CONTRAST_LUMA_PERCENTAGE 0xC945, // CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE 0xC950, // CAM_LL_CLUSTER_DC_GATE_PERCENTAGE 0xC951, // CAM_LL_SUMMING_SENSITIVITY_FACTOR 0xC87B, // CAM_AET_TARGET_AVERAGE_LUMA_DARK 0xC878, // CAM_AET_AEMODE 0xB42A, // CCM_DELTA_GAIN 0xA80A, // AE_TRACK_AE_TRACKING_DAMPENING_SPEED }; static const uint16_t cpipe_regs_8_bit_d[] = { 0x80, // CAM_LL_START_SATURATION 0x4B, // CAM_LL_END_SATURATION 0x00, // CAM_LL_START_DESATURATION 0xFF, // CAM_LL_END_DESATURATION 0x3C, // CAM_LL_START_DEMOSAIC 0x02, // CAM_LL_START_AP_GAIN 0x06, // CAM_LL_START_AP_THRESH 0x64, // CAM_LL_STOP_DEMOSAIC 0x01, // CAM_LL_STOP_AP_GAIN 0x0C, // CAM_LL_STOP_AP_THRESH 0x3C, // CAM_LL_START_NR_RED 0x3C, // CAM_LL_START_NR_GREEN 0x3C, // CAM_LL_START_NR_BLUE 0x0F, // CAM_LL_START_NR_THRESH 0x64, // CAM_LL_STOP_NR_RED 0x64, // CAM_LL_STOP_NR_GREEN 0x64, // CAM_LL_STOP_NR_BLUE 0x32, // CAM_LL_STOP_NR_THRESH 0x38, // CAM_LL_START_CONTRAST_GRADIENT 0x30, // CAM_LL_STOP_CONTRAST_GRADIENT 0x50, // CAM_LL_START_CONTRAST_LUMA_PERCENTAGE 0x19, // CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE 0x05, // CAM_LL_CLUSTER_DC_GATE_PERCENTAGE 0x40, // CAM_LL_SUMMING_SENSITIVITY_FACTOR 0x1B, // CAM_AET_TARGET_AVERAGE_LUMA_DARK 0x00, // CAM_AET_AEMODE (was 0x0E) 0x05, // CCM_DELTA_GAIN 0x20 // AE_TRACK_AE_TRACKING_DAMPENING_SPEED }; static const uint16_t cpipe_regs_16_bit[][2] = { {0xC926, 0x0020}, // CAM_LL_START_BRIGHTNESS {0xC928, 0x009A}, // CAM_LL_STOP_BRIGHTNESS {0xC946, 0x0070}, // CAM_LL_START_GAIN_METRIC {0xC948, 0x00F3}, // CAM_LL_STOP_GAIN_METRIC {0xC952, 0x0020}, // CAM_LL_START_TARGET_LUMA_BM {0xC954, 0x009A}, // CAM_LL_STOP_TARGET_LUMA_BM {0xC93C, 0x0020}, // CAM_LL_START_CONTRAST_BM {0xC93E, 0x009A}, // CAM_LL_STOP_CONTRAST_BM {0xC940, 0x00DC}, // CAM_LL_GAMMA {0xC94A, 0x0230}, // CAM_LL_START_FADE_TO_BLACK_LUMA {0xC94C, 0x0010}, // CAM_LL_STOP_FADE_TO_BLACK_LUMA {0xC94E, 0x01CD}, // CAM_LL_CLUSTER_DC_TH_BM {0xC890, 0x0080}, // CAM_AET_TARGET_GAIN {0xC886, 0x0100}, // CAM_AET_AE_MAX_VIRT_AGAIN {0xC87C, 0x005A} // CAM_AET_BLACK_CLIPPING_TARGET }; static int host_command(omv_csi_t *csi, uint16_t command) { if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_HOST_COMMAND, (command | MT9M114_HC_OK)) != 0) { return -1; } for (mp_uint_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(MT9M114_HC_DELAY)) { uint16_t reg_data; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_HOST_COMMAND, ®_data) != 0) { return -1; } if ((reg_data & command) == 0) { return (reg_data & MT9M114_HC_OK) ? 0 : -1; } if ((mp_hal_ticks_ms() - start) >= MT9M114_HC_TIMEOUT) { return -1; } } return 0; } static int load_patch(omv_csi_t *csi, const uint8_t *patch, size_t patch_len, uint16_t patch_loader_address, uint16_t patch_id, uint32_t patch_firmware_id) { int ret = 0; // Patch address is stashed in the first two bytes. uint16_t patch_address = (((uint16_t) patch[0]) << 8) | patch[1]; ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_XMDA_ACCESS_CTL_STAT, patch_address >> 15); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_XMDA_PHYSICAL_ADDRESS_ACCESS, patch_address & 0x7FFF); ret |= omv_i2c_write_bytes(csi->i2c, csi->slv_addr, (uint8_t *) patch, patch_len, OMV_I2C_XFER_NO_FLAGS); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_XMDA_LOGIC_ADDRESS_ACCESS, 0x0000); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_PATCHLDR_LOADER_ADDRESS, patch_loader_address); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_PATCHLDR_PATCH_ID, patch_id); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_PATCHLDR_FIRMWARE_ID_HI, patch_firmware_id >> 16); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_PATCHLDR_FIRMWARE_ID_LO, patch_firmware_id); ret |= host_command(csi, MT9M114_HC_APPLY_PATCH); uint8_t reg_data; ret |= omv_i2c_readb2(csi->i2c, csi->slv_addr, MT9M114_REG_PATCHLDR_APPLY_STATUS, ®_data); ret |= (reg_data == 0) ? 0: -1; return ret; } static int load_awb_cmm(omv_csi_t *csi) { return omv_i2c_write_bytes(csi->i2c, csi->slv_addr, (uint8_t *) awb_ccm, sizeof(awb_ccm), OMV_I2C_XFER_NO_FLAGS); } static int load_awb(omv_csi_t *csi) { int ret = 0; ret |= omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AWB_XSCALE, 0x03); ret |= omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AWB_YSCALE, 0x02); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AWB_Y_SHIFT_PRE_ADJ, 0x003C); ret |= omv_i2c_write_bytes(csi->i2c, csi->slv_addr, (uint8_t *) awb_weights, sizeof(awb_weights), OMV_I2C_XFER_NO_FLAGS); for (int i = 0xC90C; i <= 0xC911; i++) { ret |= omv_i2c_writeb2(csi->i2c, csi->slv_addr, i, 0x80); } return ret; } static int load_cpipe(omv_csi_t *csi) { int ret = 0; int a_size = sizeof(cpipe_regs_8_bit_a) / sizeof(cpipe_regs_8_bit_a[0]); int d_size = sizeof(cpipe_regs_8_bit_d) / sizeof(cpipe_regs_8_bit_d[0]); for (int i = 0, ii = IM_MIN(a_size, d_size); i < ii; i++) { ret |= omv_i2c_writeb2(csi->i2c, csi->slv_addr, cpipe_regs_8_bit_a[i], cpipe_regs_8_bit_d[i]); } for (int i = 0; i < (sizeof(cpipe_regs_16_bit) / sizeof(cpipe_regs_16_bit[0])); i++) { ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, cpipe_regs_16_bit[i][0], cpipe_regs_16_bit[i][1]); } return ret; } static int set_system_state(omv_csi_t *csi, uint8_t state) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_SYSMGR_NEXT_STATE, state) != 0) { return -1; } return (host_command(csi, MT9M114_HC_SET_STATE) == 0) ? 0 : -1; } static int change_config(omv_csi_t *csi) { return set_system_state(csi, MT9M114_SS_ENTER_CONFIG_CHANGE); } static int refresh(omv_csi_t *csi) { int ret = host_command(csi, MT9M114_HC_REFRESH); if (ret == 0) { return ret; } uint8_t reg_data; if (omv_i2c_readb2(csi->i2c, csi->slv_addr, MT9M114_REG_SEQ_ERROR_CODE, ®_data) != 0) { return -1; } return -reg_data; } static int set_pixformat(omv_csi_t *csi, pixformat_t pixformat); static int set_framesize(omv_csi_t *csi, omv_csi_framesize_t framesize); static int reset(omv_csi_t *csi) { int ret = 0; readout_x = 0; readout_y = 0; readout_w = ACTIVE_SENSOR_WIDTH; readout_h = ACTIVE_SENSOR_HEIGHT; ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SYSCTL, MT9M114_SYSCTL_SOFT_RESET); mp_hal_delay_ms(1); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SYSCTL, 0); mp_hal_delay_ms(45); for (mp_uint_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(MT9M114_HC_DELAY)) { uint16_t reg_data; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_HOST_COMMAND, ®_data) != 0) { return -1; } if ((reg_data & MT9M114_HC_SET_STATE) != 0) { break; } if ((mp_hal_ticks_ms() - start) >= MT9M114_HC_TIMEOUT) { return -1; } } for (mp_uint_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(MT9M114_HC_DELAY)) { uint8_t reg_data; if (omv_i2c_readb2(csi->i2c, csi->slv_addr, MT9M114_REG_SYSMGR_CURRENT_STATE, ®_data) != 0) { return -1; } if (reg_data != MT9M114_SS_STANDBY) { break; } if ((mp_hal_ticks_ms() - start) >= MT9M114_HC_TIMEOUT) { return -1; } } // Errata 2 (Black Frame Output) uint16_t reg; ret |= omv_i2c_readw2(csi->i2c, csi->slv_addr, 0x301A, ®); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, 0x301A, reg | (1 << 9)); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_SYSCTL_PLL_DIVIDER_M_N, (omv_csi_get_clk_frequency(csi, true) == OMV_MT9M114_CLK_FREQ) ? 0x120 // xclk=24MHz, m=32, n=1, csi=48MHz, bus=76.8MHz : 0x448); // xclk=25MHz, m=72, n=4, csi=45MHz, bus=72MHz for (int i = 0; i < (sizeof(default_regs) / sizeof(default_regs[0])); i++) { ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, default_regs[i][0], default_regs[i][1]); } ret |= set_framesize(csi, OMV_CSI_FRAMESIZE_SXGAM); // Black level correction fix ret |= load_patch(csi, patch_0202, sizeof(patch_0202), 0x010c, 0x0202, 0x41030202); // Adaptive Sensitivity ret |= load_patch(csi, patch_0302, sizeof(patch_0302), 0x04b4, 0x0302, 0x41030202); // System Idle Control (auto wakeup from standby) ret |= load_patch(csi, patch_0402, sizeof(patch_0402), 0x0634, 0x0402, 0x41030202); // Ambient light csi ret |= load_patch(csi, patch_0502, sizeof(patch_0502), 0x0884, 0x0502, 0x41030202); ret |= load_awb_cmm(csi); ret |= load_awb(csi); ret |= load_cpipe(csi); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_PORT_OUTPUT_CONTROL, 0x8008); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_PAD_SLEW, 0x0777); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, MT9M114_SENSOR_CONTROL_READ_MODE_HMIRROR | MT9M114_SENSOR_CONTROL_READ_MODE_VFLIP); ret |= change_config(csi); return ret; } static int sleep(omv_csi_t *csi, int enable) { uint8_t state = MT9M114_SS_LEAVE_STANDBY; uint8_t new_state = MT9M114_SS_STREAMING; if (enable) { state = MT9M114_SS_ENTER_STANDBY; new_state = MT9M114_SS_STANDBY; } if (set_system_state(csi, state) != 0) { return -1; } for (mp_uint_t start = mp_hal_ticks_ms();; mp_hal_delay_ms(MT9M114_HC_DELAY)) { uint8_t reg_data; if (omv_i2c_readb2(csi->i2c, csi->slv_addr, MT9M114_REG_SYSMGR_CURRENT_STATE, ®_data) != 0) { return -1; } if (reg_data == new_state) { return 0; } if ((mp_hal_ticks_ms() - start) >= MT9M114_HC_TIMEOUT) { return -1; } } } static int read_reg(omv_csi_t *csi, uint16_t reg_addr) { uint16_t reg_data; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, reg_addr, ®_data) != 0) { return -1; } return reg_data; } static int write_reg(omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data) { return omv_i2c_writew2(csi->i2c, csi->slv_addr, reg_addr, reg_data); } static int set_pixformat(omv_csi_t *csi, pixformat_t pixformat) { uint16_t reg = 0; switch (pixformat) { case PIXFORMAT_GRAYSCALE: case PIXFORMAT_YUV422: reg = MT9M114_OUTPUT_FORMAT_YUV | MT9M114_OUTPUT_FORMAT_SWAP_BYTES; break; case PIXFORMAT_RGB565: reg = MT9M114_OUTPUT_FORMAT_RGB | MT9M114_OUTPUT_FORMAT_RGB565 | MT9M114_OUTPUT_FORMAT_SWAP_BYTES; break; case PIXFORMAT_BAYER: if ((csi->framesize != OMV_CSI_FRAMESIZE_INVALID) && (csi->framesize != OMV_CSI_FRAMESIZE_VGA) && (csi->framesize != OMV_CSI_FRAMESIZE_SXGAM)) { return -1; } reg = MT9M114_OUTPUT_FORMAT_BAYER | MT9M114_OUTPUT_FORMAT_RAW_BAYER_10; break; default: return -1; } if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_OUTPUT_FORMAT, reg) != 0) { return -1; } return change_config(csi); } static int set_framesize(omv_csi_t *csi, omv_csi_framesize_t framesize) { int ret = 0; uint16_t w = csi->resolution[framesize][0]; uint16_t h = csi->resolution[framesize][1]; if ((csi->pixformat == PIXFORMAT_BAYER) && ((framesize != OMV_CSI_FRAMESIZE_VGA) && (framesize != OMV_CSI_FRAMESIZE_SXGAM))) { return -1; } if ((w > ACTIVE_SENSOR_WIDTH) || (h > ACTIVE_SENSOR_HEIGHT)) { return -1; } // Step 0: Clamp readout settings. readout_w = IM_MAX(readout_w, w); readout_h = IM_MAX(readout_h, h); int readout_x_max = (ACTIVE_SENSOR_WIDTH - readout_w) / 2; int readout_y_max = (ACTIVE_SENSOR_HEIGHT - readout_h) / 2; readout_x = IM_CLAMP(readout_x, -readout_x_max, readout_x_max); readout_y = IM_CLAMP(readout_y, -readout_y_max, readout_y_max); // Step 1: Determine readout area and subsampling amount. uint16_t read_mode; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, &read_mode) != 0) { return -1; } int read_mode_div = 1; read_mode &= ~(MT9M114_SENSOR_CONTROL_READ_MODE_HBIN_MASK | MT9M114_SENSOR_CONTROL_READ_MODE_VBIN_MASK); if ((w <= (readout_w / 2)) && (h <= (readout_h / 2))) { read_mode_div = 2; read_mode |= MT9M114_SENSOR_CONTROL_READ_MODE_HBIN | MT9M114_SENSOR_CONTROL_READ_MODE_VBIN; } // Step 2: Determine horizontal and vertical start and end points. uint16_t sensor_w = readout_w + DUMMY_WIDTH_BUFFER; // camera hardware needs dummy pixels to sync uint16_t sensor_h = readout_h + DUMMY_HEIGHT_BUFFER; // camera hardware needs dummy lines to sync uint16_t sensor_ws = IM_CLAMP((((ACTIVE_SENSOR_WIDTH - sensor_w) / 4) + (readout_x / 2)) * 2, -(DUMMY_WIDTH_BUFFER / 2), ACTIVE_SENSOR_WIDTH - sensor_w) + DUMMY_COLUMNS; // must be multiple of 2 uint16_t sensor_we = sensor_ws + sensor_w - 1; int sensor_ws_mod = sensor_ws % (read_mode_div * 4); // multiple 4/8 if (sensor_ws_mod) { sensor_ws -= sensor_ws_mod; sensor_we += read_mode_div; } uint16_t sensor_hs = IM_CLAMP((((ACTIVE_SENSOR_HEIGHT - sensor_h) / 4) - (readout_y / 2)) * 2, -(DUMMY_HEIGHT_BUFFER / 2), ACTIVE_SENSOR_HEIGHT - sensor_h) + DUMMY_LINES; // must be multiple of 2 uint16_t sensor_he = sensor_hs + sensor_h - 1; int sensor_hs_mod = sensor_hs % (read_mode_div * 4); // multiple 4/8 if (sensor_hs_mod) { sensor_hs -= sensor_hs_mod; sensor_he += read_mode_div; } // Step 3: Determine scaling window offset. float ratio = IM_MIN((readout_w / read_mode_div) / ((float) w), (readout_h / read_mode_div) / ((float) h)); uint16_t w_mul = w * ratio; uint16_t h_mul = h * ratio; uint16_t x_off = ((readout_w / read_mode_div) - w_mul) / 2; uint16_t y_off = ((readout_h / read_mode_div) - h_mul) / 2; // Step 4: Write regs. uint16_t frame_length_lines = (readout_h / read_mode_div) + ((read_mode_div == 2) ? 40 : 39) + DUMMY_HEIGHT_BUFFER; uint16_t line_length_pck = (readout_w / read_mode_div) + ((read_mode_div == 2) ? 534 : 323) + DUMMY_WIDTH_BUFFER; // Errata 4 (Cam_port_clock_slowdown issues/limitations) if (!(line_length_pck % 5)) { line_length_pck += 1; } ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_Y_ADDR_START, sensor_hs); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_X_ADDR_START, sensor_ws); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_Y_ADDR_END, sensor_he); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_X_ADDR_END, sensor_we); int pixclk = (omv_csi_get_clk_frequency(csi, true) == OMV_MT9M114_CLK_FREQ) ? 48000000 : 45000000; ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_PIXCLK, pixclk >> 16); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_PIXCLK + 2, pixclk); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_FINE_INTEG_TIME_MIN, (read_mode_div == 2) ? 451 : 219); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_FINE_INTEG_TIME_MAX, (read_mode_div == 2) ? 947 : 1480); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_FRAME_LENGTH_LINES, frame_length_lines); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_LINE_LENGTH_PCK, line_length_pck); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_FINE_CORRECTION, (read_mode_div == 2) ? 224 : 96); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CFG_CPIPE_LAST_ROW, (readout_h / read_mode_div) + 3); // figured this out by checking register wizard against datasheet ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, read_mode); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CROP_WINDOW_X_OFFSET, x_off); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CROP_WINDOW_Y_OFFSET, y_off); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CROP_WINDOW_WIDTH, w_mul); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CROP_WINDOW_HEIGHT, h_mul); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_OUTPUT_WIDTH, w); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_OUTPUT_HEIGHT, h); float rate = (((float) pixclk) / (frame_length_lines * line_length_pck)) * 256; ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AET_MAX_FRAME_RATE, fast_ceilf(rate)); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AET_MIN_FRAME_RATE, fast_floorf(rate / 2.f)); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AWB_CLIP_WINDOW_X_START, 0); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AWB_CLIP_WINDOW_Y_START, 0); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AWB_CLIP_WINDOW_X_END, w - 1); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AWB_CLIP_WINDOW_Y_END, h - 1); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AE_INITIAL_WINDOW_X_START, 0); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AE_INITIAL_WINDOW_Y_START, 0); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AE_INITIAL_WINDOW_X_END, (w / 5) - 1); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_AE_INITIAL_WINDOW_Y_END, (h / 5) - 1); ret |= omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_AUTO_BINNING_MODE, 0); ret |= omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_LL_ALGO, 0); return change_config(csi); } static int set_framerate(omv_csi_t *csi, int framerate) { if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AET_MAX_FRAME_RATE, framerate * 256) != 0) { return -1; } if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_AET_MIN_FRAME_RATE, framerate * 128) != 0) { return -1; } return change_config(csi); } static int set_contrast(omv_csi_t *csi, int level) { // -16 to +16 int new_level = (level > 0) ? (level * 2) : level; if ((new_level < -16) || (32 < new_level)) { return -1; } if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_CONTRAST_CONTROL, new_level + 32) != 0) { return -1; } return refresh(csi); } static int set_brightness(omv_csi_t *csi, int level) { // -16 to +16 int new_level = level * 2; if ((new_level < -32) || (32 < new_level)) { return -1; } if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_BRIGHTNESS_CONTROL, new_level + 55) != 0) { return -1; } return refresh(csi); } static int set_saturation(omv_csi_t *csi, int level) { // -16 to +16 int new_level = level * 8; if ((new_level < -128) || (128 < new_level)) { return -1; } new_level = IM_MIN(new_level, 127); if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_SATURATION_CONTROL, new_level + 128) != 0) { return -1; } return refresh(csi); } static int set_gainceiling(omv_csi_t *csi, omv_csi_gainceiling_t gainceiling) { return 0; } static int set_colorbar(omv_csi_t *csi, int enable) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_MODE_SELECT, enable ? 2 : 0) != 0) { return -1; } return change_config(csi); } static int set_auto_gain(omv_csi_t *csi, int enable, float gain_db, float gain_db_ceiling) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_AE_MODE_CONTROL, enable ? 0x2 : 0x1) != 0) { return -1; } if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { int gain = __USAT(expf((gain_db / 20.0f) * M_LN10) * 32.0f, 16); if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_GAIN_CONTROL, gain) != 0) { return -1; } } return refresh(csi); } static int get_gain_db(omv_csi_t *csi, float *gain_db) { uint16_t gain; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_GAIN_CONTROL, &gain) != 0) { return -1; } *gain_db = 20.0f * log10f(gain / 32.0f); return 0; } static int set_auto_exposure(omv_csi_t *csi, int enable, int exposure_us) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_AE_MODE_CONTROL, enable ? 0x2 : 0x1) != 0) { return -1; } if ((enable == 0) && (exposure_us >= 0)) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_MANUAL_EXPOSURE_CONFIG, 0x1) != 0) { return -1; } int exposure_100_us = exposure_us / 100; if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_EXPOSURE_TIME_ABSOLUTE_CTRL, exposure_100_us >> 16) != 0) { return -1; } if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_EXPOSURE_TIME_ABSOLUTE_CTRL + 2, exposure_100_us) != 0) { return -1; } } return refresh(csi); } static int get_exposure_us(omv_csi_t *csi, int *exposure_us) { uint16_t reg_h, reg_l; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_EXPOSURE_TIME_ABSOLUTE_CTRL, ®_h) != 0) { return -1; } if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_EXPOSURE_TIME_ABSOLUTE_CTRL + 2, ®_l) != 0) { return -1; } *exposure_us = ((reg_h << 16) | reg_l) * 100; return 0; } static int set_auto_whitebal(omv_csi_t *csi, int enable, float r_gain_db, float g_gain_db, float b_gain_db) { if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_UVC_WHITE_BALANCE_AUTO_CONTROL, enable ? 0x1 : 0x0) != 0) { return -1; } return 0; } static int get_rgb_gain_db(omv_csi_t *csi, float *r_gain_db, float *g_gain_db, float *b_gain_db) { *r_gain_db = 0; *g_gain_db = 0; *b_gain_db = 0; return 0; } static int set_hmirror(omv_csi_t *csi, int enable) { uint16_t reg_data; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, ®_data) != 0) { return -1; } reg_data = (reg_data & (~MT9M114_SENSOR_CONTROL_READ_MODE_HMIRROR)) | (enable ? 0x0 : MT9M114_SENSOR_CONTROL_READ_MODE_HMIRROR); // inverted if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, reg_data) != 0) { return -1; } return change_config(csi); } static int set_vflip(omv_csi_t *csi, int enable) { uint16_t reg_data; if (omv_i2c_readw2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, ®_data) != 0) { return -1; } reg_data = (reg_data & (~MT9M114_SENSOR_CONTROL_READ_MODE_VFLIP)) | (enable ? 0x0 : MT9M114_SENSOR_CONTROL_READ_MODE_VFLIP); // inverted if (omv_i2c_writew2(csi->i2c, csi->slv_addr, MT9M114_REG_SENSOR_CONTROL_READ_MODE, reg_data) != 0) { return -1; } return change_config(csi); } static int set_special_effect(omv_csi_t *csi, omv_csi_sde_t sde) { switch (sde) { case OMV_CSI_SDE_NEGATIVE: if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_SFX_CONTROL, 0x3) != 0) { return -1; } break; case OMV_CSI_SDE_NORMAL: if (omv_i2c_writeb2(csi->i2c, csi->slv_addr, MT9M114_REG_CAM_SFX_CONTROL, 0x0) != 0) { return -1; } break; default: return -1; } return refresh(csi); } static int ioctl(omv_csi_t *csi, int request, va_list ap) { int ret = 0; switch (request) { case OMV_CSI_IOCTL_SET_READOUT_WINDOW: { int tmp_readout_x = va_arg(ap, int); int tmp_readout_y = va_arg(ap, int); int tmp_readout_w = IM_CLAMP(va_arg(ap, int), csi->resolution[csi->framesize][0], ACTIVE_SENSOR_WIDTH); int tmp_readout_h = IM_CLAMP(va_arg(ap, int), csi->resolution[csi->framesize][1], ACTIVE_SENSOR_HEIGHT); int readout_x_max = (ACTIVE_SENSOR_WIDTH - tmp_readout_w) / 2; int readout_y_max = (ACTIVE_SENSOR_HEIGHT - tmp_readout_h) / 2; tmp_readout_x = IM_CLAMP(tmp_readout_x, -readout_x_max, readout_x_max); tmp_readout_y = IM_CLAMP(tmp_readout_y, -readout_y_max, readout_y_max); bool changed = (tmp_readout_x != readout_x) || (tmp_readout_y != readout_y) || (tmp_readout_w != readout_w) || (tmp_readout_h != readout_h); readout_x = tmp_readout_x; readout_y = tmp_readout_y; readout_w = tmp_readout_w; readout_h = tmp_readout_h; if (changed && (csi->framesize != OMV_CSI_FRAMESIZE_INVALID)) { ret |= set_framesize(csi, csi->framesize); } break; } case OMV_CSI_IOCTL_GET_READOUT_WINDOW: { *va_arg(ap, int *) = readout_x; *va_arg(ap, int *) = readout_y; *va_arg(ap, int *) = readout_w; *va_arg(ap, int *) = readout_h; break; } default: { ret = -1; break; } } return ret; } int mt9m114_init(omv_csi_t *csi) { // Initialize csi structure. csi->reset = reset; csi->sleep = sleep; csi->read_reg = read_reg; csi->write_reg = write_reg; csi->set_pixformat = set_pixformat; csi->set_framesize = set_framesize; csi->set_framerate = set_framerate; csi->set_contrast = set_contrast; csi->set_brightness = set_brightness; csi->set_saturation = set_saturation; csi->set_gainceiling = set_gainceiling; csi->set_colorbar = set_colorbar; csi->set_auto_gain = set_auto_gain; csi->get_gain_db = get_gain_db; csi->set_auto_exposure = set_auto_exposure; csi->get_exposure_us = get_exposure_us; csi->set_auto_whitebal = set_auto_whitebal; csi->get_rgb_gain_db = get_rgb_gain_db; csi->set_hmirror = set_hmirror; csi->set_vflip = set_vflip; csi->set_special_effect = set_special_effect; csi->ioctl = ioctl; // Set csi flags csi->vsync_pol = 0; csi->hsync_pol = 0; csi->pixck_pol = 0; csi->frame_sync = 0; csi->mono_bpp = 2; csi->rgb_swap = 0; csi->yuv_format = SUBFORMAT_ID_YVU422; csi->cfa_format = SUBFORMAT_ID_GBRG; return 0; } #endif // (OMV_MT9M114_ENABLE == 1)