|  |  |  | #include <cassert>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common/swaglog.h"
 | 
					
						
							|  |  |  | #include "system/camerad/cameras/camera_common.h"
 | 
					
						
							|  |  |  | #include "system/camerad/cameras/camera_qcom2.h"
 | 
					
						
							|  |  |  | #include "system/camerad/sensors/sensor.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const size_t AR0231_REGISTERS_HEIGHT = 2;
 | 
					
						
							|  |  |  | // TODO: this extra height is universal and doesn't apply per camera
 | 
					
						
							|  |  |  | const size_t AR0231_STATS_HEIGHT = 2 + 8;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const float sensor_analog_gains_AR0231[] = {
 | 
					
						
							|  |  |  |     1.0 / 8.0, 2.0 / 8.0, 2.0 / 7.0, 3.0 / 7.0,  // 0, 1, 2, 3
 | 
					
						
							|  |  |  |     3.0 / 6.0, 4.0 / 6.0, 4.0 / 5.0, 5.0 / 5.0,  // 4, 5, 6, 7
 | 
					
						
							|  |  |  |     5.0 / 4.0, 6.0 / 4.0, 6.0 / 3.0, 7.0 / 3.0,  // 8, 9, 10, 11
 | 
					
						
							|  |  |  |     7.0 / 2.0, 8.0 / 2.0, 8.0 / 1.0};            // 12, 13, 14, 15 = bypass
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::map<uint16_t, std::pair<int, int>> ar0231_build_register_lut(CameraState *c, uint8_t *data) {
 | 
					
						
							|  |  |  |   // This function builds a lookup table from register address, to a pair of indices in the
 | 
					
						
							|  |  |  |   // buffer where to read this address. The buffer contains padding bytes,
 | 
					
						
							|  |  |  |   // as well as markers to indicate the type of the next byte.
 | 
					
						
							|  |  |  |   //
 | 
					
						
							|  |  |  |   // 0xAA is used to indicate the MSB of the address, 0xA5 for the LSB of the address.
 | 
					
						
							|  |  |  |   // Every byte of data (MSB and LSB) is preceded by 0x5A. Specifying an address is optional
 | 
					
						
							|  |  |  |   // for contiguous ranges. See page 27-29 of the AR0231 Developer guide for more information.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int max_i[] = {1828 / 2 * 3, 1500 / 2 * 3};
 | 
					
						
							|  |  |  |   auto get_next_idx = [](int cur_idx) {
 | 
					
						
							|  |  |  |     return (cur_idx % 3 == 1) ? cur_idx + 2 : cur_idx + 1;  // Every third byte is padding
 | 
					
						
							|  |  |  |   };
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::map<uint16_t, std::pair<int, int>> registers;
 | 
					
						
							|  |  |  |   for (int register_row = 0; register_row < 2; register_row++) {
 | 
					
						
							|  |  |  |     uint8_t *registers_raw = data + c->ci->frame_stride * register_row;
 | 
					
						
							|  |  |  |     assert(registers_raw[0] == 0x0a);  // Start of line
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int value_tag_count = 0;
 | 
					
						
							|  |  |  |     int first_val_idx = 0;
 | 
					
						
							|  |  |  |     uint16_t cur_addr = 0;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 1; i <= max_i[register_row]; i = get_next_idx(get_next_idx(i))) {
 | 
					
						
							|  |  |  |       int val_idx = get_next_idx(i);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       uint8_t tag = registers_raw[i];
 | 
					
						
							|  |  |  |       uint16_t val = registers_raw[val_idx];
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (tag == 0xAA) {  // Register MSB tag
 | 
					
						
							|  |  |  |         cur_addr = val << 8;
 | 
					
						
							|  |  |  |       } else if (tag == 0xA5) {  // Register LSB tag
 | 
					
						
							|  |  |  |         cur_addr |= val;
 | 
					
						
							|  |  |  |         cur_addr -= 2;           // Next value tag will increment address again
 | 
					
						
							|  |  |  |       } else if (tag == 0x5A) {  // Value tag
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // First tag
 | 
					
						
							|  |  |  |         if (value_tag_count % 2 == 0) {
 | 
					
						
							|  |  |  |           cur_addr += 2;
 | 
					
						
							|  |  |  |           first_val_idx = val_idx;
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |           registers[cur_addr] = std::make_pair(first_val_idx + c->ci->frame_stride * register_row, val_idx + c->ci->frame_stride * register_row);
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         value_tag_count++;
 | 
					
						
							|  |  |  |       }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   return registers;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float ar0231_parse_temp_sensor(uint16_t calib1, uint16_t calib2, uint16_t data_reg) {
 | 
					
						
							|  |  |  |   // See AR0231 Developer Guide - page 36
 | 
					
						
							|  |  |  |   float slope = (125.0 - 55.0) / ((float)calib1 - (float)calib2);
 | 
					
						
							|  |  |  |   float t0 = 55.0 - slope * (float)calib2;
 | 
					
						
							|  |  |  |   return t0 + slope * (float)data_reg;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AR0231::AR0231() {
 | 
					
						
							|  |  |  |   image_sensor = cereal::FrameData::ImageSensor::AR0231;
 | 
					
						
							|  |  |  |   data_word = true;
 | 
					
						
							|  |  |  |   frame_width = 1928;
 | 
					
						
							|  |  |  |   frame_height = 1208;
 | 
					
						
							|  |  |  |   frame_stride = (frame_width * 12 / 8) + 4;
 | 
					
						
							|  |  |  |   extra_height = AR0231_REGISTERS_HEIGHT + AR0231_STATS_HEIGHT;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   registers_offset = 0;
 | 
					
						
							|  |  |  |   frame_offset = AR0231_REGISTERS_HEIGHT;
 | 
					
						
							|  |  |  |   stats_offset = AR0231_REGISTERS_HEIGHT + frame_height;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   start_reg_array.assign(std::begin(start_reg_array_ar0231), std::end(start_reg_array_ar0231));
 | 
					
						
							|  |  |  |   init_reg_array.assign(std::begin(init_array_ar0231), std::end(init_array_ar0231));
 | 
					
						
							|  |  |  |   probe_reg_addr = 0x3000;
 | 
					
						
							|  |  |  |   probe_expected_data = 0x354;
 | 
					
						
							|  |  |  |   mipi_format = CAM_FORMAT_MIPI_RAW_12;
 | 
					
						
							|  |  |  |   frame_data_type = 0x12;  // Changing stats to 0x2C doesn't work, so change pixels to 0x12 instead
 | 
					
						
							|  |  |  |   mclk_frequency = 19200000; //Hz
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   dc_gain_factor = 2.5;
 | 
					
						
							|  |  |  |   dc_gain_min_weight = 0;
 | 
					
						
							|  |  |  |   dc_gain_max_weight = 1;
 | 
					
						
							|  |  |  |   dc_gain_on_grey = 0.2;
 | 
					
						
							|  |  |  |   dc_gain_off_grey = 0.3;
 | 
					
						
							|  |  |  |   exposure_time_min = 2;       // with HDR, fastest ss
 | 
					
						
							|  |  |  |   exposure_time_max = 0x0855;  // with HDR, slowest ss, 40ms
 | 
					
						
							|  |  |  |   analog_gain_min_idx = 0x1;   // 0.25x
 | 
					
						
							|  |  |  |   analog_gain_rec_idx = 0x6;   // 0.8x
 | 
					
						
							|  |  |  |   analog_gain_max_idx = 0xD;   // 4.0x
 | 
					
						
							|  |  |  |   analog_gain_cost_delta = 0;
 | 
					
						
							|  |  |  |   analog_gain_cost_low = 0.1;
 | 
					
						
							|  |  |  |   analog_gain_cost_high = 5.0;
 | 
					
						
							|  |  |  |   for (int i = 0; i <= analog_gain_max_idx; i++) {
 | 
					
						
							|  |  |  |     sensor_analog_gains[i] = sensor_analog_gains_AR0231[i];
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   min_ev = exposure_time_min * sensor_analog_gains[analog_gain_min_idx];
 | 
					
						
							|  |  |  |   max_ev = exposure_time_max * dc_gain_factor * sensor_analog_gains[analog_gain_max_idx];
 | 
					
						
							|  |  |  |   target_grey_factor = 1.0;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AR0231::processRegisters(CameraState *c, cereal::FrameData::Builder &framed) const {
 | 
					
						
							|  |  |  |   const uint8_t expected_preamble[] = {0x0a, 0xaa, 0x55, 0x20, 0xa5, 0x55};
 | 
					
						
							|  |  |  |   uint8_t *data = (uint8_t *)c->buf.cur_camera_buf->addr + c->ci->registers_offset;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (memcmp(data, expected_preamble, std::size(expected_preamble)) != 0) {
 | 
					
						
							|  |  |  |     LOGE("unexpected register data found");
 | 
					
						
							|  |  |  |     return;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (ar0231_register_lut.empty()) {
 | 
					
						
							|  |  |  |     ar0231_register_lut = ar0231_build_register_lut(c, data);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   std::map<uint16_t, uint16_t> registers;
 | 
					
						
							|  |  |  |   for (uint16_t addr : {0x2000, 0x2002, 0x20b0, 0x20b2, 0x30c6, 0x30c8, 0x30ca, 0x30cc}) {
 | 
					
						
							|  |  |  |     auto offset = ar0231_register_lut[addr];
 | 
					
						
							|  |  |  |     registers[addr] = ((uint16_t)data[offset.first] << 8) | data[offset.second];
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   uint32_t frame_id = ((uint32_t)registers[0x2000] << 16) | registers[0x2002];
 | 
					
						
							|  |  |  |   framed.setFrameIdSensor(frame_id);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float temp_0 = ar0231_parse_temp_sensor(registers[0x30c6], registers[0x30c8], registers[0x20b0]);
 | 
					
						
							|  |  |  |   float temp_1 = ar0231_parse_temp_sensor(registers[0x30ca], registers[0x30cc], registers[0x20b2]);
 | 
					
						
							|  |  |  |   framed.setTemperaturesC({temp_0, temp_1});
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<i2c_random_wr_payload> AR0231::getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const {
 | 
					
						
							|  |  |  |   uint16_t analog_gain_reg = 0xFF00 | (new_exp_g << 4) | new_exp_g;
 | 
					
						
							|  |  |  |   return {
 | 
					
						
							|  |  |  |     {0x3366, analog_gain_reg},
 | 
					
						
							|  |  |  |     {0x3362, (uint16_t)(dc_gain_enabled ? 0x1 : 0x0)},
 | 
					
						
							|  |  |  |     {0x3012, (uint16_t)exposure_time},
 | 
					
						
							|  |  |  |   };
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int AR0231::getSlaveAddress(int port) const {
 | 
					
						
							|  |  |  |   assert(port >= 0 && port <= 2);
 | 
					
						
							|  |  |  |   return (int[]){0x20, 0x30, 0x20}[port];
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float AR0231::getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const {
 | 
					
						
							|  |  |  |   // Cost of ev diff
 | 
					
						
							|  |  |  |   float score = std::abs(desired_ev - (exp_t * exp_gain)) * 10;
 | 
					
						
							|  |  |  |   // Cost of absolute gain
 | 
					
						
							|  |  |  |   float m = exp_g_idx > analog_gain_rec_idx ? analog_gain_cost_high : analog_gain_cost_low;
 | 
					
						
							|  |  |  |   score += std::abs(exp_g_idx - (int)analog_gain_rec_idx) * m;
 | 
					
						
							|  |  |  |   // Cost of changing gain
 | 
					
						
							|  |  |  |   score += std::abs(exp_g_idx - gain_idx) * (score + 1.0) / 10.0;
 | 
					
						
							|  |  |  |   return score;
 | 
					
						
							|  |  |  | }
 |