/* * ar0521.c - AR0521 sensor driver * Copyright (c) 2020-2021, e-con Systems. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "ar0521.h" #include "mcu_firmware.h" //#define AR0521_DEBUG 1 /*! * Maintains the information on the current state of the sensor. */ static struct ar0521 ar0521_data; static int pwdn_gpio, reset_gpio; int gpios_available(void) { return (pwdn_gpio >= 0) && (reset_gpio >= 0); } /********************************************************************** * * START of AR0521 related code * ********************************************************************** */ static int ar0521_write(struct i2c_client *client, u8 * val, u32 count) { int ret; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .len = count, .buf = val, }; ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) { dev_err(&client->dev, "Failed writing register.\n"); dev_err(&client->dev, "addr: %x; val = %hhu, ret = %d!\n", client->addr, *val, ret); return ret; } return 0; } static int ar0521_read(struct i2c_client *client, u8 * val, u32 count) { int ret; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .buf = val, }; msg.flags = I2C_M_RD; msg.len = count; ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) goto err; return 0; err: dev_err(&client->dev, "Failed reading register ret = %d!\n", ret); return ret; } /* * The MCU specific functions below depend on the sensor-specific * functions above. */ /* * --------------------------------------------------------- * START of MCU realed functions * --------------------------------------------------------- */ /* * NOTE about modularizing this MCU related function. * * - The functions: * * x mcu_get_fw_version * x mcu_bload_get_version * x mcu_bload_erase_flash * x mcu_bload_parse_send_cmd * x mcu_bload_go * x mcu_bload_read * x mcu_count_or_list_ctrls * x mcu_count_or_list_fmts * x mcu_get_sensor_id * x mcu_get_ctrl_ui * x mcu_stream_config * x mcu_isp_power_down * x mcu_isp_power_wakeup * x mcu_set_ctrl * x mcu_get_ctrl * * seem to directly use platform specific functions: * * x ar0521_write * x ar0521_read * * This could be passed in as a function pointer. */ static unsigned short int mcu_bload_calc_crc16(unsigned char *buf, int len) { unsigned short int crc = 0; int i = 0; if (!buf || !(buf + len)) return 0; for (i = 0; i < len; i++) { crc ^= buf[i]; } return crc; } static int mcu_bload_ascii2hex(unsigned char ascii) { if (ascii <= '9') { return (ascii - '0'); } else if ((ascii >= 'a') && (ascii <= 'f')) { return (0xA + (ascii - 'a')); } else if ((ascii >= 'A') && (ascii <= 'F')) { return (0xA + (ascii - 'A')); } return -1; } static unsigned char errorcheck(char *data, unsigned int len) { unsigned int i = 0; unsigned char crc = 0x00; for (i = 0; i < len; i++) { crc ^= data[i]; } return crc; } /* * mcu_get_fw_version: * * Read the firmware version from the MCU. * * A success value (0) is returned when the MCU version could be successfully read. * else a negative value indicating error is returned. */ static int mcu_get_fw_version(struct i2c_client *client, unsigned char *fw_version) { uint32_t payload_len = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0; int ret = 0, err = 0, loop; /* Query firmware version from MCU */ /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_VERSION; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); err = ar0521_write(client, mc_data, TX_LEN_PKT); if (err != 0) { dev_err(&client->dev, "MCU CMD ID version Error- %d\n", err); ret = -EIO; goto exit; } mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_VERSION; err = ar0521_write(client, mc_data, 2); if (err != 0) { dev_err(&client->dev," %s(%d) MCU CMD ID Write PKT fw Version Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) MCU CMD ID Read PKT fw Version Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) MCU CMD ID fw Version Error CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) MCU CMD ID fw Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } /* Read the actual version from MCU*/ payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) MCU fw CMD ID Read Version Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], 32); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) MCU fw CMD ID Version CRC ERROR 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) MCU fw CMD ID Read Payload Error - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } for (loop = 0 ; loop < VERSION_SIZE ; loop++ ) *(fw_version+loop) = mc_ret_data[2+loop]; ret = ERRCODE_SUCCESS; exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } /** * mcu_verify_fw_version: * * Verify the firmware version obtained from the MCU and that found in the * firmware text file present in our driver. * * The return value after verification is as follows: * * - If the version number matches a success value (0) is returned. * * - In case the version number mismatches, a negative value indicating error * is returned. * * - In case the force update bit is set in firmware version in the text * file, a positive value is returned. */ static int mcu_verify_fw_version(const unsigned char *const fw_version) { int loop, i = 0, ret; char txt_fw_version[32] = {0}; unsigned long txt_fw_pos = ARRAY_SIZE(g_mcu_fw_buf)-VERSION_FILE_OFFSET; /* Get Text Firmware version*/ for(loop = txt_fw_pos; loop < (txt_fw_pos+64); loop=loop+2) { *(txt_fw_version+i) = (mcu_bload_ascii2hex(g_mcu_fw_buf[loop]) << 4 | mcu_bload_ascii2hex(g_mcu_fw_buf[loop+1])); i++; } /* Check for forced/always update field in the text firmware version*/ if(txt_fw_version[17] == '1') { #ifdef AR0521_DEBUG pr_info("Forced Update Enabled - Firmware Version - (%.32s) \n", fw_version); #endif ret = 2; } else { for(i = 0; i < VERSION_SIZE; i++) { if(txt_fw_version[i] != fw_version[i]) { pr_info("Previous Firmware Version - (%.32s)\n", fw_version); ret = -1; break; } } if (i == VERSION_SIZE) ret = ERRCODE_SUCCESS; } return ret; } static int mcu_bload_get_version(struct i2c_client *client) { int ret = 0; /*----------------------------- GET VERSION -------------------- */ /* Write Get Version CMD */ g_bload_buf[0] = BL_GET_VERSION; g_bload_buf[1] = ~(BL_GET_VERSION); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != 'y') { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed\n"); return -1; } /* ---------------- GET VERSION END ------------------- */ return 0; } static int mcu_bload_erase_flash(struct i2c_client *client) { unsigned short int pagenum = 0x0000; int ret = 0, i = 0, checksum = 0; /* --------------- ERASE FLASH --------------------- */ for (i = 0; i < NUM_ERASE_CYCLES; i++) { checksum = 0x00; /* Write Erase Pages CMD */ g_bload_buf[0] = BL_ERASE_MEM_NS; g_bload_buf[1] = ~(BL_ERASE_MEM_NS); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } g_bload_buf[0] = (MAX_PAGES - 1) >> 8; g_bload_buf[1] = (MAX_PAGES - 1) & 0xFF; g_bload_buf[2] = g_bload_buf[0] ^ g_bload_buf[1]; ret = ar0521_write(client, g_bload_buf, 3); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } for (pagenum = 0; pagenum < MAX_PAGES; pagenum++) { g_bload_buf[(2 * pagenum)] = (pagenum + (i * MAX_PAGES)) >> 8; g_bload_buf[(2 * pagenum) + 1] = (pagenum + (i * MAX_PAGES)) & 0xFF; checksum = checksum ^ g_bload_buf[(2 * pagenum)] ^ g_bload_buf[(2 * pagenum) + 1]; } g_bload_buf[2 * MAX_PAGES] = checksum; ret = ar0521_write(client, g_bload_buf, (2 * MAX_PAGES) + 1); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } poll_busy: /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] == RESP_BUSY) goto poll_busy; if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } #ifdef AR0521_DEBUG pr_info(" ERASE Sector %d success !! \n", i + 1); #endif } /* ------------ ERASE FLASH END ----------------------- */ return 0; } static unsigned char mcu_bload_inv_checksum(unsigned char *buf, int len) { unsigned int checksum = 0x00; int i = 0; if (!buf || !(buf + len)) return 0; for (i = 0; i < len; i++) { checksum = (checksum + buf[i]); } checksum &= (0xFF); return (~(checksum) + 1); } static int mcu_bload_parse_send_cmd(struct i2c_client *client, unsigned char *bytearray, int rec_len) { IHEX_RECORD *ihex_rec = NULL; unsigned char checksum = 0, calc_checksum = 0; int i = 0, ret = 0; if (!bytearray) return -1; ihex_rec = (IHEX_RECORD *) bytearray; ihex_rec->addr = htons(ihex_rec->addr); checksum = bytearray[rec_len - 1]; calc_checksum = mcu_bload_inv_checksum(bytearray, rec_len - 1); if (checksum != calc_checksum) { dev_err(&client->dev," Invalid Checksum 0x%02x != 0x%02x !! \n", checksum, calc_checksum); return -1; } if ((ihex_rec->rectype == REC_TYPE_ELA) && (ihex_rec->addr == 0x0000) && (ihex_rec->datasize = 0x02)) { /* Upper 32-bit configuration */ g_bload_flashaddr = (ihex_rec->recdata[0] << 24) | (ihex_rec->recdata[1] << 16); #ifdef AR0521_DEBUG pr_info("Updated Flash Addr = 0x%08x \n", g_bload_flashaddr); #endif } else if (ihex_rec->rectype == REC_TYPE_DATA) { /* Flash Data into Flashaddr */ g_bload_flashaddr = (g_bload_flashaddr & 0xFFFF0000) | (ihex_rec->addr); g_bload_crc16 ^= mcu_bload_calc_crc16(ihex_rec->recdata, ihex_rec->datasize); /* Write Erase Pages CMD */ g_bload_buf[0] = BL_WRITE_MEM_NS; g_bload_buf[1] = ~(BL_WRITE_MEM_NS); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } g_bload_buf[0] = (g_bload_flashaddr & 0xFF000000) >> 24; g_bload_buf[1] = (g_bload_flashaddr & 0x00FF0000) >> 16; g_bload_buf[2] = (g_bload_flashaddr & 0x0000FF00) >> 8; g_bload_buf[3] = (g_bload_flashaddr & 0x000000FF); g_bload_buf[4] = g_bload_buf[0] ^ g_bload_buf[1] ^ g_bload_buf[2] ^ g_bload_buf[3]; ret = ar0521_write(client, g_bload_buf, 5); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } g_bload_buf[0] = ihex_rec->datasize - 1; checksum = g_bload_buf[0]; for (i = 0; i < ihex_rec->datasize; i++) { g_bload_buf[i + 1] = ihex_rec->recdata[i]; checksum ^= g_bload_buf[i + 1]; } g_bload_buf[i + 1] = checksum; ret = ar0521_write(client, g_bload_buf, i + 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } poll_busy: /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] == RESP_BUSY) goto poll_busy; if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } } else if (ihex_rec->rectype == REC_TYPE_SLA) { /* Update Instruction pointer to this address */ } else if (ihex_rec->rectype == REC_TYPE_EOF) { /* End of File - Issue I2C Go Command */ return 0; } else { /* Unhandled Type */ dev_err(&client->dev,"Unhandled Command Type \n"); return -1; } return 0; } static int mcu_bload_update_fw(struct i2c_client *client) { /* exclude NULL character at end of string */ unsigned long hex_file_size = ARRAY_SIZE(g_mcu_fw_buf) - 1; unsigned char wbuf[MAX_BUF_LEN]; int i = 0, recindex = 0, ret = 0; for (i = 0; i < hex_file_size; i++) { if ((recindex == 0) && (g_mcu_fw_buf[i] == ':')) { /* pr_info("Start of a Record \n"); */ } else if (g_mcu_fw_buf[i] == CR) { /* No Implementation */ } else if (g_mcu_fw_buf[i] == LF) { if (recindex == 0) { /* Parsing Complete */ break; } /* Analyze Packet and Send Commands */ ret = mcu_bload_parse_send_cmd(client, wbuf, recindex); if (ret < 0) { dev_err(&client->dev,"Error in Processing Commands \n"); break; } recindex = 0; } else { /* Parse Rec Data */ if ((ret = mcu_bload_ascii2hex(g_mcu_fw_buf[i])) < 0) { dev_err(&client->dev, "Invalid Character - 0x%02x !! \n", g_mcu_fw_buf[i]); break; } wbuf[recindex] = (0xF0 & (ret << 4)); i++; if ((ret = mcu_bload_ascii2hex(g_mcu_fw_buf[i])) < 0) { dev_err(&client->dev, "Invalid Character - 0x%02x !!!! \n", g_mcu_fw_buf[i]); break; } wbuf[recindex] |= (0x0F & ret); recindex++; } } #ifdef AR0521_DEBUG pr_info("Program FLASH Success !! - CRC = 0x%04x \n", g_bload_crc16); #endif /* ------------ PROGRAM FLASH END ----------------------- */ return ret; } static int mcu_bload_read(struct i2c_client *client, unsigned int g_bload_flashaddr, char *bytearray, unsigned int len) { int ret = 0; g_bload_buf[0] = BL_READ_MEM; g_bload_buf[1] = ~(BL_READ_MEM); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } g_bload_buf[0] = (g_bload_flashaddr & 0xFF000000) >> 24; g_bload_buf[1] = (g_bload_flashaddr & 0x00FF0000) >> 16; g_bload_buf[2] = (g_bload_flashaddr & 0x0000FF00) >> 8; g_bload_buf[3] = (g_bload_flashaddr & 0x000000FF); g_bload_buf[4] = g_bload_buf[0] ^ g_bload_buf[1] ^ g_bload_buf[2] ^ g_bload_buf[3]; ret = ar0521_write(client, g_bload_buf, 5); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } g_bload_buf[0] = len - 1; g_bload_buf[1] = ~(len - 1); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } /* Wait for ACK or NACK */ ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } ret = ar0521_read(client, bytearray, len); if (ret < 0) { dev_err(&client->dev,"Read Failed \n"); return -1; } return 0; } static int mcu_bload_verify_flash(struct i2c_client *client, unsigned short int orig_crc) { char bytearray[FLASH_READ_LEN]; unsigned short int calc_crc = 0; unsigned int flash_addr = FLASH_START_ADDRESS, i = 0; while ((i + FLASH_READ_LEN) <= FLASH_SIZE) { memset(bytearray, 0x0, FLASH_READ_LEN); if (mcu_bload_read( client, flash_addr + i, bytearray, FLASH_READ_LEN) < 0) { dev_err(&client->dev," i2c_bload_read FAIL !! \n"); return -1; } calc_crc ^= mcu_bload_calc_crc16(bytearray, FLASH_READ_LEN); i += FLASH_READ_LEN; } if ((FLASH_SIZE - i) > 0) { memset(bytearray, 0x0, FLASH_READ_LEN); if (mcu_bload_read( client, flash_addr + i, bytearray, (FLASH_SIZE - i) ) < 0) { dev_err(&client->dev," i2c_bload_read FAIL !! \n"); return -1; } calc_crc ^= mcu_bload_calc_crc16(bytearray, FLASH_READ_LEN); } if (orig_crc != calc_crc) { dev_err(&client->dev," CRC verification fail !! 0x%04x != 0x%04x \n", orig_crc, calc_crc); } #ifdef AR0521_DEBUG pr_info(" CRC Verification Success 0x%04x == 0x%04x \n", orig_crc, calc_crc); #endif return 0; } static int mcu_bload_go(struct i2c_client *client) { int ret = 0; g_bload_buf[0] = BL_GO; g_bload_buf[1] = ~(BL_GO); ret = ar0521_write(client, g_bload_buf, 2); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Failed Read 1 \n"); return -1; } /* Start Address */ g_bload_buf[0] = (FLASH_START_ADDRESS & 0xFF000000) >> 24; g_bload_buf[1] = (FLASH_START_ADDRESS & 0x00FF0000) >> 16; g_bload_buf[2] = (FLASH_START_ADDRESS & 0x0000FF00) >> 8; g_bload_buf[3] = (FLASH_START_ADDRESS & 0x000000FF); g_bload_buf[4] = g_bload_buf[0] ^ g_bload_buf[1] ^ g_bload_buf[2] ^ g_bload_buf[3]; ret = ar0521_write(client, g_bload_buf, 5); if (ret < 0) { dev_err(&client->dev,"Write Failed \n"); return -1; } ret = ar0521_read(client, g_bload_buf, 1); if (ret < 0) { dev_err(&client->dev,"Failed Read 1 \n"); return -1; } if (g_bload_buf[0] != RESP_ACK) { /* NACK Received */ dev_err(&client->dev," NACK Received... exiting.. \n"); return -1; } return 0; } static int mcu_fw_update(struct i2c_client *client, unsigned char *mcu_fw_version) { int ret = 0; g_bload_crc16 = 0; /* * TODO: Is this necessary? It seems redundant as it's already called before * calling this function. */ /* Read Firmware version from bootloader MCU */ ret = mcu_bload_get_version(client); if (ret < 0) { dev_err(&client->dev," Error in Get Version \n"); goto exit; } #ifdef AR0521_DEBUG pr_info(" Get Version SUCCESS !! \n"); #endif /* Erase firmware present in the MCU and flash new firmware*/ ret = mcu_bload_erase_flash(client); if (ret < 0) { dev_err(&client->dev," Error in Erase Flash \n"); goto exit; } #ifdef AR0521_DEBUG pr_info("Erase Flash Success !! \n"); #endif /* Read the firmware present in the text file */ if ((ret = mcu_bload_update_fw(client)) < 0) { dev_err(&client->dev," Write Flash FAIL !! \n"); goto exit; } /* Verify the checksum for the update firmware */ if ((ret = mcu_bload_verify_flash(client, g_bload_crc16)) < 0) { dev_err(&client->dev," verify_flash FAIL !! \n"); goto exit; } /* Reverting from bootloader mode */ /* I2C GO Command */ if ((ret = mcu_bload_go(client)) < 0) { dev_err(&client->dev," i2c_bload_go FAIL !! \n"); goto exit; } if(mcu_fw_version) { #ifdef AR0521_DEBUG pr_info("(%s) - Firmware Updated - (%.32s)\n", __func__, mcu_fw_version); #endif } exit: return ret; } static int mcu_count_or_list_ctrls(struct i2c_client *client, ISP_CTRL_INFO * mcu_cam_ctrl, int *numctrls) { uint32_t payload_len = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0; uint16_t index = 0; int ret = 0, err = 0; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* Array of Ctrl Info */ while (1) { /* First Txn Payload length = 0 */ payload_len = 2; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL_INFO; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL_INFO; mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); err = ar0521_write(client, mc_data, 5); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev, " %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } if (((mc_ret_data[2] << 8) | mc_ret_data[3]) == 0) { *numctrls = index; break; } payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev, " %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -1; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], payload_len - HEADER_FOOTER_SIZE); if (orig_crc != calc_crc) { dev_err(&client->dev, " %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev, " %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EINVAL; goto exit; } if(mcu_cam_ctrl != NULL) { int sorted_elem = index - 1, elem = index; /* append ctrl info in array */ mcu_cam_ctrl[index].ctrl_id = mc_ret_data[2] << 24 | mc_ret_data[3] << 16 | mc_ret_data[4] << 8 | mc_ret_data[5]; mcu_cam_ctrl[index].ctrl_type = mc_ret_data[6]; switch (mcu_cam_ctrl[index].ctrl_type) { case CTRL_STANDARD: mcu_cam_ctrl[index].ctrl_data.std.ctrl_min = mc_ret_data[7] << 24 | mc_ret_data[8] << 16 | mc_ret_data[9] << 8 | mc_ret_data[10]; mcu_cam_ctrl[index].ctrl_data.std.ctrl_max = mc_ret_data[11] << 24 | mc_ret_data[12] << 16 | mc_ret_data[13] << 8 | mc_ret_data[14]; mcu_cam_ctrl[index].ctrl_data.std.ctrl_def = mc_ret_data[15] << 24 | mc_ret_data[16] << 16 | mc_ret_data[17] << 8 | mc_ret_data[18]; mcu_cam_ctrl[index].ctrl_data.std.ctrl_step = mc_ret_data[19] << 24 | mc_ret_data[20] << 16 | mc_ret_data[21] << 8 | mc_ret_data[22]; mcu_cam_ctrl[index].mcu_ctrl_index = index; break; case CTRL_EXTENDED: /* Not Implemented */ break; } #ifdef AR0521_DEBUG pr_info("Control: ID: 0x%x; Type: %u; min: %d; Max: %d; Def: %d; Step: %u\n", mcu_cam_ctrl[index].ctrl_id, mcu_cam_ctrl[index].ctrl_type, mcu_cam_ctrl[index].ctrl_data.std.ctrl_min, mcu_cam_ctrl[index].ctrl_data.std.ctrl_max, mcu_cam_ctrl[index].ctrl_data.std.ctrl_def, mcu_cam_ctrl[index].ctrl_data.std.ctrl_step ); #endif ctrldb[index] = mcu_cam_ctrl[index].ctrl_id; /* * Keep the control list and control db sorted. */ while( sorted_elem >= 0 && ( mcu_cam_ctrl[sorted_elem].ctrl_id > mcu_cam_ctrl[elem].ctrl_id ) ) { ISP_CTRL_INFO swap_ctrl_elem; uint32_t swap_ctrldb_elem; /* * Swap the elements in the mcu_cam_ctrl list */ memcpy(&swap_ctrl_elem, (mcu_cam_ctrl + sorted_elem), sizeof(ISP_CTRL_INFO)); memcpy((mcu_cam_ctrl + sorted_elem), (mcu_cam_ctrl + elem), sizeof(ISP_CTRL_INFO)); memcpy((mcu_cam_ctrl + elem), &swap_ctrl_elem, sizeof(ISP_CTRL_INFO)); /* * Swap the elements in ctrldb */ swap_ctrldb_elem = ctrldb[sorted_elem]; ctrldb[sorted_elem] = ctrldb[elem]; ctrldb[elem] = swap_ctrldb_elem; elem = sorted_elem; sorted_elem = elem - 1; } } index++; } exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } static int mcu_count_or_list_fmts(struct i2c_client *client, ISP_STREAM_INFO *stream_info, int *frm_fmt_size) { uint32_t payload_len = 0, err = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0, skip = 0; uint16_t index = 0, mode = 0; int loop = 0, num_frates = 0, ret = 0; /* Stream Info Variables */ /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* List all formats from MCU and append to mcu_ar0521_frmfmt array */ for (index = 0;; index++) { /* First Txn Payload length = 0 */ payload_len = 2; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_STREAM_INFO; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_STREAM_INFO; mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); err = ar0521_write(client, mc_data, 5); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev, " %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } if (((mc_ret_data[2] << 8) | mc_ret_data[3]) == 0) { if(stream_info == NULL) { *frm_fmt_size = index; } else { *frm_fmt_size = mode; } break; } payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev, " %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -1; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], payload_len - HEADER_FOOTER_SIZE); if (orig_crc != calc_crc) { dev_err(&client->dev, " %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev, " %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } if(stream_info != NULL) { stream_info->fmt_fourcc = mc_ret_data[2] << 24 | mc_ret_data[3] << 16 | mc_ret_data[4] << 8 | mc_ret_data[5]; stream_info->width = mc_ret_data[6] << 8 | mc_ret_data[7]; stream_info->height = mc_ret_data[8] << 8 | mc_ret_data[9]; stream_info->frame_rate_type = mc_ret_data[10]; switch (stream_info->frame_rate_type) { case FRAME_RATE_DISCRETE: stream_info->frame_rate.disc.frame_rate_num = mc_ret_data[11] << 8 | mc_ret_data[12]; stream_info->frame_rate.disc.frame_rate_denom = mc_ret_data[13] << 8 | mc_ret_data[14]; break; case FRAME_RATE_CONTINOUS: dev_err(&client->dev, " The Stream format at index 0x%04x has FRAME_RATE_CONTINOUS," "which is unsupported !! \n", index); continue; } switch (stream_info->fmt_fourcc) { /* * We check for UYVY here instead of YUYV as the output from the sensor * is UYVY. We swap it to YUYV only making changes in the platform driver. */ case V4L2_PIX_FMT_UYVY: /* ar0521_codes is already populated with V4L2_PIX_FMT_YUYV */ /* check if width and height are already in array - update frame rate only */ for (loop = 0; loop < (mode); loop++) { if ((ar0521_data.mcu_cam_frmfmt[loop].size.width == stream_info->width) && (ar0521_data.mcu_cam_frmfmt[loop].size.height == stream_info->height)) { num_frates = ar0521_data.mcu_cam_frmfmt[loop].num_framerates; *((int *)(ar0521_data.mcu_cam_frmfmt[loop].framerates) + num_frates) = (int)(stream_info->frame_rate. disc.frame_rate_num / stream_info->frame_rate. disc.frame_rate_denom); ar0521_data.mcu_cam_frmfmt[loop].num_framerates++; streamdb[index] = loop; skip = 1; break; } } if (skip) { skip = 0; continue; } /* Add Width, Height, Frame Rate array, Mode into mcu_ar0521_frmfmt array */ ar0521_data.mcu_cam_frmfmt[mode].size.width = stream_info->width; ar0521_data.mcu_cam_frmfmt[mode].size.height = stream_info->height; num_frates = ar0521_data.mcu_cam_frmfmt[mode].num_framerates; *(ar0521_data.mcu_cam_frmfmt[mode].framerates + num_frates) = (int)(stream_info->frame_rate.disc.frame_rate_num / stream_info->frame_rate.disc.frame_rate_denom); ar0521_data.mcu_cam_frmfmt[mode].num_framerates++; ar0521_data.mcu_cam_frmfmt[mode].mode = mode; ar0521_data.mcu_cam_frmfmt[mode].mode = mode; streamdb[index] = mode; mode++; break; default: dev_err(&client->dev, " The Stream format at index 0x%04x has format 0x%08x ," "which is unsupported !! \n", index, stream_info->fmt_fourcc); } } } exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } /* * Function to initialise the data related to MCU. Needs to be called * before trying to use them. */ static int mcu_data_init(struct device *dev, int frm_fmt_size) { int loop = 0; if (dev == NULL) { dev_err(dev, "%s: Invalid device parameter\n", __func__); return -EINVAL; } mcu_ctrl_info = devm_kzalloc(dev, sizeof(ISP_CTRL_INFO) * num_ctrls, GFP_KERNEL); if(!mcu_ctrl_info) { dev_err(dev, "Unable to allocate memory \n"); return -ENOMEM; } ctrldb = devm_kzalloc(dev, sizeof(uint32_t) * num_ctrls, GFP_KERNEL); if(!ctrldb) { dev_err(dev, "Unable to allocate memory \n"); return -ENOMEM; } stream_info = devm_kzalloc(dev, sizeof(ISP_STREAM_INFO) * (frm_fmt_size + 1), GFP_KERNEL); streamdb = devm_kzalloc(dev, sizeof(int) * (frm_fmt_size + 1), GFP_KERNEL); if(!streamdb) { dev_err(dev,"Unable to allocate memory \n"); return -ENOMEM; } ar0521_data.mcu_cam_frmfmt = devm_kzalloc(dev, sizeof(struct mcu_frmfmt) * (frm_fmt_size), GFP_KERNEL); if(!ar0521_data.mcu_cam_frmfmt) { dev_err(dev, "Unable to allocate memory \n"); return -ENOMEM; } for(; loop < frm_fmt_size; loop++) { ar0521_data.mcu_cam_frmfmt[loop].framerates = devm_kzalloc(dev, sizeof(int) * MAX_NUM_FRATES, GFP_KERNEL); if(!ar0521_data.mcu_cam_frmfmt[loop].framerates) { dev_err(dev, "Unable to allocate memory \n"); return -ENOMEM; } } return 0; } static int mcu_get_sensor_id(struct i2c_client *client, uint16_t * sensor_id) { uint32_t payload_len = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0; int ret = 0, err = 0; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* Read the version info. from Micro controller */ /* First Txn Payload length = 0 */ payload_len = 0; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_SENSOR_ID; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_SENSOR_ID; err = ar0521_write(client, mc_data, 2); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } *sensor_id = mc_ret_data[2] << 8 | mc_ret_data[3]; exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } static int mcu_get_cmd_status(struct i2c_client *client, uint8_t * cmd_id, uint16_t * cmd_status, uint8_t * ret_code) { uint32_t payload_len = 0; uint8_t orig_crc = 0, calc_crc = 0; int err = 0; /* No Semaphore in Get command Status */ /* First Txn Payload length = 0 */ payload_len = 1; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_STATUS; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_STATUS; mc_data[2] = *cmd_id; err = ar0521_write(client, mc_data, 3); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); return -EIO; } payload_len = CMD_STATUS_MSG_LEN; memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); return -EIO; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], 3); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); return -EINVAL; } *cmd_id = mc_ret_data[2]; *cmd_status = mc_ret_data[3] << 8 | mc_ret_data[4]; *ret_code = mc_ret_data[payload_len - 1]; return 0; } static int mcu_isp_init(struct i2c_client *client) { uint32_t payload_len = 0; uint16_t cmd_status = 0; uint8_t retcode = 0, cmd_id = 0; int retry = 1000, err = 0; /* check current status - if initialized, no need for Init */ cmd_id = CMD_ID_INIT_CAM; if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev," %s(%d) Error \n", __func__, __LINE__); return -EIO; } if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && (retcode == ERRCODE_SUCCESS)) { #ifdef AR0521_DEBUG pr_info(" Already Initialized !! \n"); #endif return 0; } /* call ISP init command */ /* First Txn Payload length = 0 */ payload_len = 0; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_INIT_CAM; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_INIT_CAM; err = ar0521_write(client, mc_data, 2); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); return -EIO; } while (--retry > 0) { /* Some Sleep for init to process */ mdelay(5); cmd_id = CMD_ID_INIT_CAM; if (mcu_get_cmd_status( client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev," %s(%d) Error \n", __func__, __LINE__); return -EIO; } if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && ((retcode == ERRCODE_SUCCESS) || (retcode == ERRCODE_ALREADY))) { #ifdef AR0521_DEBUG pr_info(" ISP Already Initialized !! \n"); #endif return 0; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) { dev_err(&client->dev, "(%s) %d Init Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); return -EIO; } } return -ETIMEDOUT; } static int mcu_get_ctrl_ui(struct i2c_client *client, ISP_CTRL_INFO * mcu_ui_info, int index) { uint32_t payload_len = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0; int ret = 0, i = 0, err = 0; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* First Txn Payload length = 0 */ payload_len = 2; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL_UI_INFO; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL_UI_INFO; mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); err = ar0521_write(client, mc_data, 5); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EINVAL; goto exit; } memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], payload_len - HEADER_FOOTER_SIZE); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } strncpy((char *)mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_name, &mc_ret_data[2],MAX_CTRL_UI_STRING_LEN); mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_ui_type = mc_ret_data[34]; mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_ui_flags = mc_ret_data[35] << 8 | mc_ret_data[36]; if (mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_ui_type == V4L2_CTRL_TYPE_MENU) { mcu_ui_info->ctrl_ui_data.ctrl_menu_info.num_menu_elem = mc_ret_data[37]; mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu = devm_kzalloc(&client->dev,((mcu_ui_info->ctrl_ui_data.ctrl_menu_info.num_menu_elem +1) * sizeof(char *)), GFP_KERNEL); for (i = 0; i < mcu_ui_info->ctrl_ui_data.ctrl_menu_info.num_menu_elem; i++) { mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu[i] = devm_kzalloc(&client->dev,MAX_CTRL_UI_STRING_LEN, GFP_KERNEL); strncpy((char *)mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu[i], &mc_ret_data[38 +(i *MAX_CTRL_UI_STRING_LEN)], MAX_CTRL_UI_STRING_LEN); #ifdef AR0521_DEBUG pr_info(" Menu Element %d : %s \n", i, mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu[i]); #endif } mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu[i] = NULL; } exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } static int mcu_isp_configuration(uint8_t cmd_id,struct i2c_client *client) { unsigned char mc_data[100]; uint32_t payload_len = 0; uint16_t payload_data; uint16_t cmd_status = 0; uint8_t retcode = 0; int retry = 1000, err = 0; /*lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* First Txn Payload length = 0 */ payload_len = 2; mc_data[0] = CMD_SIGNATURE; mc_data[1] = cmd_id; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = cmd_id; switch(cmd_id) { case CMD_ID_LANE_CONFIG: /*Lane configuration */ payload_data = ar0521_data.mipi_lane_config == 4 ? NUM_LANES_4: NUM_LANES_2; mc_data[2] = payload_data >> 8; mc_data[3] = payload_data & 0xFF; break; case CMD_ID_MIPI_CLK_CONFIG: /* MIPI CLK Configuration */ payload_data = ar0521_data.mipi_clk_config; mc_data[2] = payload_data >> 8; mc_data[3] = payload_data & 0xFF; break; default: dev_err(&client->dev, "MCU ISP CONF Error\n"); err = -1; goto exit; } /* CRC*/ mc_data[4] = errorcheck(&mc_data[2], payload_len); err = ar0521_write(client, mc_data, payload_len+3); if (err != 0) { dev_err(&client->dev, " %s(%d) Error - %d \n", __func__, __LINE__, err); goto exit; } while (--retry > 0) { msleep(20); if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev, " %s(%d) Error \n", __func__, __LINE__); err = -EIO; goto exit; } if ((cmd_status == MCU_CMD_STATUS_ISP_UNINIT) && ((retcode == ERRCODE_SUCCESS) || retcode == ERRCODE_ALREADY)) { err = 0; goto exit; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_ISP_UNINIT))) { dev_err(&client->dev, "(%s) %d Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); err = -EIO; goto exit; } } err = -ETIMEDOUT; exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return err; } static int mcu_stream_config(struct i2c_client *client, uint32_t format, int mode, int frate_index) { uint32_t payload_len = 0; uint16_t cmd_status = 0, index = 0xFFFF; uint8_t retcode = 0, cmd_id = 0; int loop = 0, ret = 0, err = 0, retry = 1000; static uint16_t prev_index = 0xFFFE; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); cmd_id = CMD_ID_STREAM_CONFIG; if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev," %s(%d) Error \n", __func__, __LINE__); ret = -EIO; goto exit; } if ((cmd_status != MCU_CMD_STATUS_SUCCESS) || (retcode != ERRCODE_SUCCESS)) { dev_err(&client->dev, " ISP is Unintialized or Busy STATUS = 0x%04x Errcode = 0x%02x !! \n", cmd_status, retcode); ret = -EBUSY; goto exit; } for (loop = 0;(&streamdb[loop]) != NULL; loop++) { pr_info("streamdb[%d]=%d, mode=%d",loop,streamdb[loop],mode); if (streamdb[loop] == mode) { index = loop + frate_index; break; } } #ifdef AR0521_DEBUG pr_info(" Index = 0x%04x , format = 0x%08x, width = %hu," " height = %hu, frate num = %hu \n", index, format, ar0521_data.mcu_cam_frmfmt[mode].size.width, ar0521_data.mcu_cam_frmfmt[mode].size.height, ar0521_data.mcu_cam_frmfmt[mode].framerates[frate_index]); #endif if (index == 0xFFFF) { ret = -EINVAL; goto exit; } if(prev_index == index) { #ifdef AR0521_DEBUG pr_info("Skipping Previous mode set ... \n"); #endif ret = 0; goto exit; } issue_cmd: /* First Txn Payload length = 0 */ payload_len = 14; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_STREAM_CONFIG; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_STREAM_CONFIG; mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; /* Format Fourcc - currently only YUYV */ mc_data[4] = format >> 24; mc_data[5] = format >> 16; mc_data[6] = format >> 8; mc_data[7] = format & 0xFF; /* width */ mc_data[8] = ar0521_data.mcu_cam_frmfmt[mode].size.width >> 8; mc_data[9] = ar0521_data.mcu_cam_frmfmt[mode].size.width & 0xFF; /* height */ mc_data[10] = ar0521_data.mcu_cam_frmfmt[mode].size.height >> 8; mc_data[11] = ar0521_data.mcu_cam_frmfmt[mode].size.height & 0xFF; /* frame rate num */ mc_data[12] = ar0521_data.mcu_cam_frmfmt[mode].framerates[frate_index] >> 8; mc_data[13] = ar0521_data.mcu_cam_frmfmt[mode].framerates[frate_index] & 0xFF; /* frame rate denom */ mc_data[14] = 0x00; mc_data[15] = 0x01; mc_data[16] = errorcheck(&mc_data[2], 14); err = ar0521_write(client, mc_data, 17); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } while (--retry > 0) { cmd_id = CMD_ID_STREAM_CONFIG; if (mcu_get_cmd_status( client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev, " %s(%d) MCU GET CMD Status Error : loop : %d \n", __func__, __LINE__, loop); ret = -EIO; goto exit; } if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && (retcode == ERRCODE_SUCCESS)) { ret = 0; goto exit; } if(retcode == ERRCODE_AGAIN) { /* Issue Command Again if Set */ retry = 1000; goto issue_cmd; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) { dev_err(&client->dev, "(%s) %d Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); ret = -EIO; goto exit; } /* Delay after retry */ mdelay(10); } dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -ETIMEDOUT; exit: if(!ret) prev_index = index; /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } static int mcu_isp_power_down(struct i2c_client *client) { uint32_t payload_len = 0; uint16_t cmd_status = 0; uint8_t retcode = 0, cmd_id = 0; int retry = 1000, err = 0; /*lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* First Txn Payload length = 0 */ payload_len = 0; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_ISP_PDOWN; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_ISP_PDOWN; err = ar0521_write(client, mc_data, 2); if (err != 0) { dev_err(&client->dev, " %s(%d) Error - %d \n", __func__, __LINE__, err); goto exit; } while (--retry > 0) { msleep(20); cmd_id = CMD_ID_ISP_PDOWN; if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev, " %s(%d) Get Status Error \n", __func__, __LINE__); err = -EINVAL; goto exit; } if ((cmd_status == MCU_CMD_STATUS_ISP_PWDN) && ((retcode == ERRCODE_SUCCESS) || retcode == ERRCODE_ALREADY)) { err = 0; goto exit; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) { dev_err(&client->dev, "(%s) %d Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); err = -EIO; goto exit; } } err = -ETIMEDOUT; exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return err; } static int mcu_isp_power_wakeup(struct i2c_client *client) { uint32_t payload_len = 0; uint16_t cmd_status = 0; uint8_t retcode = 0, cmd_id = 0; int retry = 1000, err = 0; /*lock semaphore */ mutex_lock(&mcu_i2c_mutex); /* First Txn Payload length = 0 */ payload_len = 0; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_ISP_PUP; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_ISP_PUP; err = ar0521_write(client, mc_data, 2); if (err != 0) { dev_err(&client->dev, " %s(%d) Error - %d \n", __func__, __LINE__, err); goto exit; } while (--retry > 0) { msleep(20); cmd_id = CMD_ID_ISP_PUP; if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev, " %s(%d) Error \n", __func__, __LINE__); err = -EIO; goto exit; } if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && ((retcode == ERRCODE_SUCCESS) || retcode == ERRCODE_ALREADY)) { err = 0; goto exit; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) { dev_err(&client->dev, "(%s) %d Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); err = -EIO; goto exit; } } err = -ETIMEDOUT; exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return err; } static int mcu_set_ctrl(struct i2c_client *client, uint32_t arg_ctrl_id, uint8_t ctrl_type, int32_t curr_val) { uint32_t payload_len = 0; uint16_t cmd_status = 0, index = 0xFFFF; uint8_t retcode = 0, cmd_id = 0; int loop = 0, ret = 0, err = 0; uint32_t ctrl_id = 0; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); ctrl_id = arg_ctrl_id; /* call ISP Ctrl config command */ for (loop = 0; loop < num_ctrls; loop++) { if (ctrldb[loop] == ctrl_id) { index = mcu_ctrl_info[loop].mcu_ctrl_index; break; } } if (index == 0xFFFF) { ret = -EINVAL; goto exit; } /* First Txn Payload length = 0 */ payload_len = 11; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_SET_CTRL; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); /* Second Txn */ mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_SET_CTRL; /* Index */ mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; /* Control ID */ mc_data[4] = ctrl_id >> 24; mc_data[5] = ctrl_id >> 16; mc_data[6] = ctrl_id >> 8; mc_data[7] = ctrl_id & 0xFF; /* Ctrl Type */ mc_data[8] = ctrl_type; /* Ctrl Value */ mc_data[9] = curr_val >> 24; mc_data[10] = curr_val >> 16; mc_data[11] = curr_val >> 8; mc_data[12] = curr_val & 0xFF; /* CRC */ mc_data[13] = errorcheck(&mc_data[2], 11); err = ar0521_write(client, mc_data, 14); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } while (1) { cmd_id = CMD_ID_SET_CTRL; if (mcu_get_cmd_status( client, &cmd_id, &cmd_status, &retcode) < 0) { dev_err(&client->dev," %s(%d) Error \n", __func__, __LINE__); ret = -EINVAL; goto exit; } if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && (retcode == ERRCODE_SUCCESS)) { ret = 0; goto exit; } if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) { dev_err(&client->dev, "(%s) %d ISP Error STATUS = 0x%04x RET = 0x%02x\n", __func__, __LINE__, cmd_status, retcode); ret = -EIO; goto exit; } } exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } static int mcu_get_ctrl(struct i2c_client *client, uint32_t arg_ctrl_id, uint8_t * ctrl_type, int32_t * curr_val) { uint32_t payload_len = 0; uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0; uint16_t index = 0xFFFF; int loop = 0, ret = 0, err = 0; uint32_t ctrl_id = 0; /* lock semaphore */ mutex_lock(&mcu_i2c_mutex); ctrl_id = arg_ctrl_id; /* Read the Ctrl Value from Micro controller */ for (loop = 0; loop < num_ctrls; loop++) { if (ctrldb[loop] == ctrl_id) { index = mcu_ctrl_info[loop].mcu_ctrl_index; break; } } if (index == 0xFFFF) { ret = -EINVAL; goto exit; } if ( mcu_ctrl_info[loop].ctrl_ui_data.ctrl_ui_info.ctrl_ui_flags & V4L2_CTRL_FLAG_WRITE_ONLY ) { ret = -EACCES; goto exit; } /* First Txn Payload length = 2 */ payload_len = 2; mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL; mc_data[2] = payload_len >> 8; mc_data[3] = payload_len & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); ar0521_write(client, mc_data, TX_LEN_PKT); mc_data[0] = CMD_SIGNATURE; mc_data[1] = CMD_ID_GET_CTRL; mc_data[2] = index >> 8; mc_data[3] = index & 0xFF; mc_data[4] = errorcheck(&mc_data[2], 2); err = ar0521_write(client, mc_data, 5); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } err = ar0521_read(client, mc_ret_data, RX_LEN_PKT); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[4]; calc_crc = errorcheck(&mc_ret_data[2], 2); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -1; goto exit; } if (((mc_ret_data[2] << 8) | mc_ret_data[3]) == 0) { ret = -EIO; goto exit; } errcode = mc_ret_data[5]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EIO; goto exit; } payload_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE; memset(mc_ret_data, 0x00, payload_len); err = ar0521_read(client, mc_ret_data, payload_len); if (err != 0) { dev_err(&client->dev," %s(%d) Error - %d \n", __func__, __LINE__, err); ret = -EIO; goto exit; } /* Verify CRC */ orig_crc = mc_ret_data[payload_len - 2]; calc_crc = errorcheck(&mc_ret_data[2], payload_len - HEADER_FOOTER_SIZE); if (orig_crc != calc_crc) { dev_err(&client->dev," %s(%d) CRC 0x%02x != 0x%02x \n", __func__, __LINE__, orig_crc, calc_crc); ret = -EINVAL; goto exit; } /* Verify Errcode */ errcode = mc_ret_data[payload_len - 1]; if (errcode != ERRCODE_SUCCESS) { dev_err(&client->dev," %s(%d) Errcode - 0x%02x \n", __func__, __LINE__, errcode); ret = -EINVAL; goto exit; } /* Ctrl type starts from index 6 */ *ctrl_type = mc_ret_data[6]; switch (*ctrl_type) { case CTRL_STANDARD: *curr_val = mc_ret_data[7] << 24 | mc_ret_data[8] << 16 | mc_ret_data[9] << 8 | mc_ret_data[10]; break; case CTRL_EXTENDED: /* Not Implemented */ break; } exit: /* unlock semaphore */ mutex_unlock(&mcu_i2c_mutex); return ret; } /* * --------------------------------------------------------- * END of MCU realed functions * --------------------------------------------------------- */ static void toggle_gpio(unsigned int gpio, int val) { if (gpio_cansleep(gpio)){ gpio_direction_output(gpio,val); gpio_set_value_cansleep(gpio, val); } else{ gpio_direction_output(gpio,val); gpio_set_value(gpio, val); } } static int ar0521_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm) { uint32_t index = 0; int loop; if (sd == NULL || qm == NULL) return -EINVAL; for (loop = 0; loop < num_ctrls; loop++) { if (ctrldb[loop] == qm->id) { index = loop; break; } } if (loop == num_ctrls) { return -EINVAL; } if ( !( 0 <= qm->index && qm->index < mcu_ctrl_info[index].ctrl_ui_data.ctrl_menu_info.num_menu_elem ) ) { return -EINVAL; } /* * Copy the name of the menu. * * We deal only with V4L2_CTRL_TYPE_MENU and not * V4L2_CTRL_TYPE_INTEGER_MENU. So, this should be * enough. */ strcpy(qm->name, mcu_ctrl_info[index].ctrl_ui_data.ctrl_menu_info.menu[qm->index]); /* * Set the reserved to zero as mentioned in spec */ qm->reserved = 0; return 0; } static int ar0521_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { int index, ctrl_index = -1, ctrl_id; bool next_ctrl = (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL); if (sd == NULL || qc == NULL) return -EINVAL; if (next_ctrl) { ctrl_id = qc->id & (~V4L2_CTRL_FLAG_NEXT_CTRL); /* * Ignore the V4L2_CTRL_FLAG_NEXT_COMPOUND for now */ ctrl_id = ctrl_id & (~V4L2_CTRL_FLAG_NEXT_COMPOUND); } else { /* * Assume we've just got the control ID itself * directly. */ ctrl_id = qc->id; } if (ctrl_id) { for (index = 0; index < num_ctrls; index++) { if (ctrldb[index] == ctrl_id) { ctrl_index = (next_ctrl) ? index + 1 : index; break; } } if (index == num_ctrls) { /* * We do not know about this control */ return -EINVAL; } else if ( next_ctrl && index == num_ctrls - 1 ) { /* * We've got a request for the control * after the last one. */ return -EINVAL; } } else if (next_ctrl) { ctrl_index = 0; } else { return -EINVAL; } if ( mcu_ctrl_info[ctrl_index].ctrl_type == CTRL_STANDARD ) { /* * We cannot use `v4l2_ctrl_query_fill` instead of manually filling * the details even for standard controls as we sometimes implement our * own version for some controls. * * e.g., V4L2_CID_FOCUS_AUTO has a max value of 1 according to standard * but our version of it has a max value of 5. */ qc->id = mcu_ctrl_info[ctrl_index].ctrl_id; strcpy(qc->name, mcu_ctrl_info[ctrl_index].ctrl_ui_data.ctrl_ui_info.ctrl_name); qc->type = mcu_ctrl_info[ctrl_index].ctrl_ui_data.ctrl_ui_info.ctrl_ui_type; qc->flags = mcu_ctrl_info[ctrl_index].ctrl_ui_data.ctrl_ui_info.ctrl_ui_flags; qc->minimum = mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_min; qc->maximum = mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_max; qc->step = mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_step; qc->default_value = mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_def; } else { return -EINVAL; } return 0; } static int ar0521_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct i2c_client *client = ar0521_data.i2c_client; int err = 0; uint8_t ctrl_type = 0; int ctrl_val = 0; if (sd == NULL || ctrl == NULL) return -EINVAL; if ((err = mcu_get_ctrl(client, ctrl->id, &ctrl_type, &ctrl_val)) < 0) { return err; } if (ctrl_type == CTRL_STANDARD) { ctrl->value = ctrl_val; } else { /* Not Implemented */ return -EINVAL; } return err; } static int ar0521_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct i2c_client *client = ar0521_data.i2c_client; int err = 0, index, ctrl_index = 0; if (sd == NULL || ctrl == NULL) return -EINVAL; for (index = 0; index < num_ctrls; index++) { if (ctrldb[index] == ctrl->id) { ctrl_index = index; break; } } if (index == num_ctrls) { return -EINVAL; } if ( ctrl->value < mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_min || ctrl->value > mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_max ) return -ERANGE; if ((err = mcu_set_ctrl(client, ctrl->id, CTRL_STANDARD, ctrl->value)) < 0) { dev_err(&client->dev," %s (%d ) \n", __func__, __LINE__); return -EINVAL; } return err; } static int ar0521_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) { int i, err = 0; if (sd == NULL || ctrls == NULL) return -EINVAL; for (i = 0; i < ctrls->count; i++) { struct v4l2_ext_control *ext_ctrl = ctrls->controls + i; struct v4l2_control ctrl = { .id = ext_ctrl->id, }; err = ar0521_g_ctrl(sd, &ctrl); if (err) { ctrls->error_idx = ctrls->count; break; } else { ext_ctrl->value = ctrl.value; } } return err; } static int ar0521_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) { int i; if (sd == NULL || ctrls == NULL) return -EINVAL; for (i = 0; i < ctrls->count; i++) { struct v4l2_ext_control *ext_ctrl = ctrls->controls + i; int ctrl_index = 0, index; for (index = 0; index < num_ctrls; index++) { if (ctrldb[index] == ext_ctrl->id) { ctrl_index = index; break; } } if (index == num_ctrls) { ctrls->error_idx = ext_ctrl->id; return -EINVAL; } if ( ext_ctrl->value < mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_min || ext_ctrl->value > mcu_ctrl_info[ctrl_index].ctrl_data.std.ctrl_max ) { ctrls->error_idx = ext_ctrl->id; return -ERANGE; } } return 0; } static int ar0521_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) { int i, err = 0; if (sd == NULL || ctrls == NULL) return -EINVAL; for (i = 0; i < ctrls->count; i++) { struct v4l2_ext_control *ext_ctrl = ctrls->controls + i; struct v4l2_control ctrl = { .id = ext_ctrl->id, .value = ext_ctrl->value }; err = ar0521_s_ctrl(sd, &ctrl); if (err) { /* * TODO: We would have to indicate whether there * is an issue in validation or in the * hardware by correctly setting the error_idx * to count only when the validation failed * and setting it to index when there is an * issue in communication with the hardware. * * For now, just return the count for all cases. */ ctrls->error_idx = ctrls->count; break; } } return err; } static int ar0521_enum_frameintervals(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_interval_enum *fival) { struct i2c_client *client = v4l2_get_subdevdata(sd); int j; if (fival->width == 0 || fival->height == 0) { dev_err(&client->dev, "Please assign width and height.\n"); return -EINVAL; } if (fival->code != ar0521_data.fmt.code) { return -EINVAL; } for (j = 0; j < ar0521_data.num_frm_fmts; j++) { if ( fival->width == ar0521_data.mcu_cam_frmfmt[j].size.width && fival->height == ar0521_data.mcu_cam_frmfmt[j].size.height ) { if (fival->index >= ar0521_data.mcu_cam_frmfmt[j].num_framerates) { return -EINVAL; } fival->interval.numerator = 1; fival->interval.denominator = ar0521_data.mcu_cam_frmfmt[j].framerates[fival->index]; return 0; } } return -EINVAL; } static int ar0521_enum_framesizes(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ar0521_data.num_frm_fmts) { return -EINVAL; } if (fse->code != ar0521_data.fmt.code) { return -EINVAL; } fse->max_width = ar0521_data.mcu_cam_frmfmt[fse->index].size.width; fse->min_width = fse->max_width; fse->max_height = ar0521_data.mcu_cam_frmfmt[fse->index].size.height; fse->min_height = fse->max_height; return 0; } static int ar0521_enum_mbus_code(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || code->index >= AR0521_MAX_FORMAT_SUPPORTED) return -EINVAL; code->code = ar0521_data.fmt.code; return 0; } static int ar0521_s_stream(struct v4l2_subdev *sd, int enable) { if (!enable) { /* Perform Stream Off Sequence - if any */ } /* Perform Stream On Sequence - if any */ mdelay(10); return 0; } static int ar0521_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param) { int mode = ar0521_data.streamcap.capturemode; param->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; param->parm.capture.timeperframe.denominator = ar0521_data.mcu_cam_frmfmt[mode].framerates[ar0521_data.frate_index]; param->parm.capture.timeperframe.numerator = 1; return 0; } static int ar0521_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param) { struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0, err = 0; int mode = ar0521_data.streamcap.capturemode; int fourcc = ar0521_data.pix.pixelformat; param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; memset(param->parm.capture.reserved, 0, 4*sizeof(u32)); if ( param->parm.capture.timeperframe.denominator == 0 && param->parm.capture.timeperframe.numerator == 0 && ar0521_data.mcu_cam_frmfmt[mode].num_framerates == 1 ) { param->parm.capture.timeperframe.denominator = ar0521_data.mcu_cam_frmfmt[mode].framerates[ar0521_data.frate_index]; param->parm.capture.timeperframe.numerator = 1; /* * We would have to reset the frame interval to a * nominal value in this case but as we just have one * frame interval we just return success. */ return 0; } if (param->parm.capture.timeperframe.numerator != 1) { dev_err(&client->dev, "Invalid numerator for timeperframe\n"); return -EINVAL; } for (ret = 0; ret < ar0521_data.mcu_cam_frmfmt[mode].num_framerates; ret++) { if ((ar0521_data.mcu_cam_frmfmt[mode].framerates[ret] == param->parm.capture.timeperframe.denominator)) { ar0521_data.frate_index = ret; /* call stream config with width, height, frame rate */ err = mcu_stream_config(client, fourcc, mode, ar0521_data.frate_index); if (err < 0) { dev_err(&client->dev, "%s: Failed stream_config \n", __func__); return err; } mdelay(10); return 0; } } return -EINVAL; } static int ar0521_s_power(struct v4l2_subdev *sd, int on) { /* Perform Power On/Off Sequence - if any */ return 0; } static int ar0521_get_fmt(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_format *format) { int ret = 0; if (format->pad) return -EINVAL; format->format.code = ar0521_data.fmt.code; format->format.colorspace = ar0521_data.fmt.colorspace; format->format.field = V4L2_FIELD_NONE; format->format.width = ar0521_data.pix.width; format->format.height = ar0521_data.pix.height; return ret; } static int ar0521_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { int ret = 0, i; struct i2c_client *client = v4l2_get_subdevdata(sd); int flag = 0, err = 0; format->format.code = ar0521_data.fmt.code; format->format.colorspace = ar0521_data.fmt.colorspace; format->format.field = V4L2_FIELD_NONE; for (i = 0; i < ar0521_data.num_frm_fmts ; i++) { if ( ar0521_data.mcu_cam_frmfmt[i].size.width == format->format.width && ar0521_data.mcu_cam_frmfmt[i].size.height == format->format.height ) { flag = 1; break; } } if(flag == 0) { format->format.width = ar0521_data.pix.width; format->format.height = ar0521_data.pix.height; } if (format->which == V4L2_SUBDEV_FORMAT_TRY) { return 0; } /* call stream config with width, height, frame rate */ err = mcu_stream_config(client, ar0521_data.pix.pixelformat, ar0521_data.mcu_cam_frmfmt[i].mode, ar0521_data.frate_index); if (err < 0) { dev_err(&client->dev, "%s: Failed stream_config \n", __func__); return err; } ar0521_data.pix.width = format->format.width; ar0521_data.pix.height = format->format.height; ar0521_data.streamcap.capturemode = ar0521_data.mcu_cam_frmfmt[i].mode; mdelay(10); return ret; } static int ar0521_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { return 0; } static struct v4l2_subdev_video_ops ar0521_subdev_video_ops = { .g_parm = ar0521_g_parm, .s_parm = ar0521_s_parm, .s_stream = ar0521_s_stream, }; static const struct v4l2_subdev_pad_ops ar0521_subdev_pad_ops = { .enum_frame_size = ar0521_enum_framesizes, .enum_frame_interval = ar0521_enum_frameintervals, .enum_mbus_code = ar0521_enum_mbus_code, .set_fmt = ar0521_set_fmt, .get_fmt = ar0521_get_fmt, }; static struct v4l2_subdev_core_ops ar0521_subdev_core_ops = { .s_power = ar0521_s_power, .queryctrl = ar0521_queryctrl, .g_ctrl = ar0521_g_ctrl, .s_ctrl = ar0521_s_ctrl, .g_ext_ctrls = ar0521_g_ext_ctrls, .s_ext_ctrls = ar0521_s_ext_ctrls, .try_ext_ctrls = ar0521_try_ext_ctrls, .querymenu = ar0521_querymenu, }; static struct v4l2_subdev_ops ar0521_subdev_ops = { .core = &ar0521_subdev_core_ops, .video = &ar0521_subdev_video_ops, .pad = &ar0521_subdev_pad_ops, }; static const struct media_entity_operations ar0521_sd_media_ops = { .link_setup = ar0521_link_setup, }; static int ar0521_init(struct i2c_client *client) { u32 tgt_xclk; /* target xclk */ int ret = 0; ar0521_data.on = true; /* mclk */ tgt_xclk = ar0521_data.mclk; tgt_xclk = min(tgt_xclk, (u32)AR0521_XCLK_MAX); tgt_xclk = max(tgt_xclk, (u32)AR0521_XCLK_MIN); ar0521_data.mclk = tgt_xclk; #ifdef AR0521_DEBUG pr_info("mclk: %d MHz\n", tgt_xclk / 1000000); #endif ret = mcu_stream_config(client, ar0521_data.pix.pixelformat, ar0521_data.streamcap.capturemode, ar0521_data.frate_index); return ret; } static int ar0521_ctrls_init(ISP_CTRL_INFO *mcu_cam_ctrls) { struct i2c_client *client = NULL; int numctrls = 0; int err = 0, i = 0; client = ar0521_data.i2c_client; if (mcu_cam_ctrls == NULL) { dev_err( &client->dev, "%s: MCU control data hasn't been allocated\n", __func__ ); return -EINVAL; } /* * Enumerate the controls from the MCU */ err = mcu_count_or_list_ctrls(client, mcu_cam_ctrls, &numctrls); if (err < 0) { dev_err(&client->dev, "Unable to enumerate the controls in the sensor\n"); return err; } for (i = 0; i < numctrls; i++) { if (mcu_cam_ctrls[i].ctrl_type == CTRL_STANDARD) { err = mcu_get_ctrl_ui(client, &mcu_ctrl_info[i], mcu_ctrl_info[i].mcu_ctrl_index); if (err != ERRCODE_SUCCESS) { dev_err(&client->dev, "Error Enumerating Control 0x%08x !! \n", mcu_ctrl_info[i].ctrl_id); return err; } else if ( mcu_ctrl_info[i].ctrl_ui_data.ctrl_ui_info.ctrl_ui_type == V4L2_CTRL_TYPE_MENU ) { mcu_ctrl_info[i].ctrl_data.std.ctrl_step = 1; } } } return 0; } static int ar0521_verify_mcu(struct i2c_client *client) { int ret = 0, try = 0; unsigned char fw_version[32] = {0}; if (client == NULL) { dev_err(&client->dev, "%s: Invalid I2C client parameter\n", __func__); return -EINVAL; } /* * Try to boot the MCU into firmware mode. * * We do this only when the reset_gpio and pwdn_gpio are * available. */ if (gpios_available()) { toggle_gpio(pwdn_gpio, 0); msleep(10); toggle_gpio(reset_gpio, 0); msleep(10); toggle_gpio(reset_gpio, 1); msleep(500); for(try = 0; try < 10; try++) { ret = mcu_get_fw_version(client, fw_version); if(ret < 0) { msleep(100); continue; } else { break; } } if (ret == 0) { ret = mcu_verify_fw_version(fw_version); } else { dev_dbg( &client->dev, "Could not read the firmware version from the MCU\n" ); pr_info(" Could not read the firmware version from the MCU, tries=%d\n", try+1); } /* * Try booting and flashing in bootloader mode when an error is detected * or the force update bit is set in the firmware version */ if (ret != 0) { int loop = 0; /* * Verification of the MCU in firmware mode failed so * try to boot the MCU in bootloader mode. */ pr_info(" Trying to Detect Bootloader mode\n"); if (gpios_available()) { toggle_gpio(reset_gpio, 0); msleep(10); toggle_gpio(pwdn_gpio, 1); msleep(100); toggle_gpio(reset_gpio, 1); msleep(100); } for(loop = 0; loop < 10; loop++) { ret = mcu_bload_get_version(client); if (ret < 0) { /* Trial and Error for 1 second (100ms * 10) */ msleep(100); continue; } else { #ifdef AR0521_DEBUG pr_info(" Get Bload Version Success\n"); #endif break; } } if(loop == 10) { dev_err(&client->dev, "Error getting firmware version in bootloader mode\n"); return -EINVAL; } if (mcu_fw_update(client, NULL) < 0) { dev_err(&client->dev, "Error when trying to update the firmware\n"); return -EFAULT; } if (gpios_available()) { toggle_gpio(pwdn_gpio, 0); } /* Allow FW Updated MCU to reboot */ msleep(500); /* * Ensure the firmware has been flashed correctly by getting the version * of the firmware (in firmware mode). */ for(loop = 0; loop < 100; loop++) { ret = mcu_get_fw_version(client, fw_version); if (ret == 0) { ret = mcu_verify_fw_version(fw_version); } if (ret < 0) { /* Trial and Error for 10 seconds (100ms * 100) */ msleep(100); continue; } else { #ifdef AR0521_DEBUG pr_info(" Get FW Version Success\n"); #endif break; } } if(loop == 100) { dev_err( &client->dev, "Couldn't get firmware version correctly after update (did the update fail?\n" ); return -EINVAL; } } } else { static const char *const flash_error_message = "Please connect only one camera and fix the MCU.\n" "Hint: Connected only one camera? Is someone else using the " "power down/reset GPIOs?\n"; /* * When we do not have the reset GPIO, we cannot toggle it * to make the MCU switch to firmware mode. So, we try getting * the MCU into firmware assuming it is currently in * bootloader mode. * * The following sequence is required for switching it to * firmware mode. */ int loop, get_firmware_version = 0; /* * We would have to verify that the MCU is in bootloader mode * before sending the command to make it switch to firmware mode. * * So, we try to get the version of the bootloader. */ for(loop = 0; loop < 10; loop++) { ret = mcu_bload_get_version(client); if (ret < 0) { /* Trial and Error for 1 second (100ms * 10) */ msleep(100); } else { pr_info(" Get Bload Version Success\n"); break; } } if(loop == 10) { dev_err(&client->dev, "couldn't get bootloader version\n"); get_firmware_version = 1; msleep(1000); } else { /* * We retry if we could boot into firmware mode as we're * currently in bootloader mode (as mcu_bload_get_version * succeeded). */ ret = mcu_bload_go(client); if (ret < 0) { dev_err(&client->dev,"couldn't switch to firmware mode.\n"); dev_err(&client->dev, flash_error_message); return -EINVAL; } else { msleep(10); get_firmware_version = 1; } } /* * We should verify if we have the version of the MCU we * need. If not, we let the user know about it and exit * the probe. */ if (get_firmware_version) { ret = mcu_get_fw_version(client, fw_version); if (ret == 0) { ret = mcu_verify_fw_version(fw_version); } else { dev_err(&client->dev, "couldn't get the MCU firmware version"); dev_err(&client->dev, flash_error_message); return -EINVAL; } if (ret != 0) { dev_err(&client->dev, "wrong MCU firmware version"); dev_err(&client->dev, flash_error_message); return -EINVAL; } } } dev_info(&client->dev, "Current Firmware Version - (%.32s)\n", fw_version); return 0; } static int ar0521_parse_and_get_clocks(struct device *dev) { int retval = 0; if (dev == NULL) { dev_err(dev, "%s: Invalid device parameter\n", __func__); return -EINVAL; } ar0521_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); if (IS_ERR(ar0521_data.sensor_clk)) { /* assuming clock enabled by default */ ar0521_data.sensor_clk = NULL; dev_err(dev, "clock-frequency missing or invalid\n"); return PTR_ERR(ar0521_data.sensor_clk); } /*mclk reserved for future use*/ retval = of_property_read_u32(dev->of_node, "mclk", &(ar0521_data.mclk)); if (retval) { dev_err(dev, "mclk missing or invalid\n"); return retval; } /*mclk_source reserved for future use*/ retval = of_property_read_u32(dev->of_node, "mclk_source", (u32 *) &(ar0521_data.mclk_source)); if (retval) { dev_err(dev, "mclk_source missing or invalid\n"); return retval; } retval = of_property_read_u32(dev->of_node, "csi_id", &(ar0521_data.csi)); if (retval) { dev_err(dev, "csi id missing or invalid\n"); return retval; } return 0; } static int ar0521_parse_and_get_gpios(struct device *dev) { int err; struct device_node *node = NULL; if (dev == NULL) { dev_err(dev, "%s: Invalid device parameter\n", __func__); return -EINVAL; } node = dev->of_node; pwdn_gpio = of_get_named_gpio(node, "pwn-gpios", 0); if (!gpio_is_valid(pwdn_gpio)) { dev_err(dev, "no sensor pwdn pin available"); return -EINVAL; } else { #ifdef AR0521_DEBUG printk("BOOT = %x \n", pwdn_gpio); #endif } reset_gpio = of_get_named_gpio(node, "rst-gpios", 0); if (!gpio_is_valid(reset_gpio)) { dev_err(dev, "no sensor reset pin available"); return -EINVAL; } else { #ifdef AR0521_DEBUG printk("RESET = %x \n", reset_gpio); #endif } err = devm_gpio_request_one(dev, pwdn_gpio, GPIOF_OUT_INIT_HIGH, "ar0521_mipi_pwdn"); if (err < 0) { dev_warn(dev, "Failed to set power pin\n"); dev_warn(dev, "err = %d\n", err); return err; } err = devm_gpio_request_one(dev, reset_gpio, GPIOF_OUT_INIT_HIGH, "ar0521_mipi_reset"); if (err < 0) { dev_warn(dev, "Failed to set reset pin\n"); dev_warn(dev, "err = %d\n", err); return err; } return 0; } /*! * ar0521 I2C probe function * * @param adapter struct i2c_adapter * * @return Error code indicating success or failure */ static int ar0521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pinctrl *pinctrl; struct device_node *node = client->dev.of_node; struct device *dev = &client->dev; int ret, frm_fmt_size = 0, i; uint16_t sensor_id = 0; if (!IS_ENABLED(CONFIG_OF) || !node) return -EINVAL; pinctrl = devm_pinctrl_get_select_default(dev); if (IS_ERR(pinctrl)) dev_warn(dev, "no pin available\n"); /* Set initial values for the sensor struct. */ memset(&ar0521_data, 0, sizeof(ar0521_data)); ret = ar0521_parse_and_get_gpios(dev); if (ret) { pr_info("Warning: couldn't get GPIOs\n"); } ret = ar0521_parse_and_get_clocks(dev); if (ret) { dev_err(dev, "Error occurred when getting clock\n"); return ret; } clk_prepare_enable(ar0521_data.sensor_clk); /* * We usually get and set/enable regulators here. But it doesn't * seem to be needed here as the Variscite EVK seems to be supplying * the required voltage directly without us needing to set it. */ toggle_gpio(reset_gpio, 1); msleep(500); ret = ar0521_verify_mcu(client); if (ret) { dev_err(dev, "Error occurred when verifying MCU\n"); return ret; } ar0521_data.mipi_lane_config = 4; ret = mcu_isp_configuration(CMD_ID_LANE_CONFIG, client); if(ret) { dev_err(dev, "Error occurred in configuring mipi lanes\n"); return ret; } /* * Query the number of controls from MCU */ if (mcu_count_or_list_ctrls(client, NULL, &num_ctrls) < 0) { dev_err(dev, "%s, Failed to get number of controls for sensor\n", __func__); return -EFAULT; } /* * Query the number for Formats available from MCU */ if (mcu_count_or_list_fmts(client, NULL, &frm_fmt_size) < 0) { dev_err(dev, "%s, Failed to get number of formats for sensor\n", __func__); return -EFAULT; } /* * Initialise the MCU related data as we're about to use them. */ ret = mcu_data_init(dev, frm_fmt_size); if (ret) { dev_err(dev, "%s: failed to initialize MCU related data\n", __func__); return -EFAULT; } if (mcu_get_sensor_id(client, &sensor_id) < 0) { dev_err(dev, "Unable to get MCU Sensor ID \n"); return -EFAULT; } if (mcu_isp_init(client) < 0) { dev_err(dev, "Unable to INIT ISP \n"); return -EFAULT; } /* * Enumerate the Formats in the sensor */ if (mcu_count_or_list_fmts(client, stream_info, &frm_fmt_size) < 0) { dev_err(dev, "Unable to enumerate the formats in the sensor\n"); return -EFAULT; } /* * Fill some state information as required. */ ar0521_data.i2c_client = client; ar0521_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; ar0521_data.fmt.code = AR0521_DEFAULT_DATAFMT; ar0521_data.fmt.colorspace = AR0521_DEFAULT_COLORSPACE; ar0521_data.pix.width = AR0521_DEFAULT_WIDTH; ar0521_data.pix.height = AR0521_DEFAULT_HEIGHT; ar0521_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | V4L2_CAP_TIMEPERFRAME; ar0521_data.streamcap.capturemode = AR0521_DEFAULT_MODE; ar0521_data.num_frm_fmts = frm_fmt_size; ar0521_data.power_on = 0; /* * Configure the stream with default configuration */ ret = ar0521_init(client); if (ret) { dev_err(dev, "Failed to initialise the device with default configuration\n"); return ret; } v4l2_i2c_subdev_init(&ar0521_data.subdev, client, &ar0521_subdev_ops); /* * Initialize Controls by getting details about the controls from the MCU */ ret = ar0521_ctrls_init(mcu_ctrl_info); if (ret) { dev_warn(dev, "Failed to initialise the controls. Controls might not work\n"); } /* * Write default values for all controls */ for (i = 0; i < num_ctrls; i++) { if (mcu_ctrl_info[i].ctrl_type == CTRL_STANDARD) { int ret; struct v4l2_control ctrl = { .id = mcu_ctrl_info[i].ctrl_id, .value = mcu_ctrl_info[i].ctrl_data.std.ctrl_def }; if ( mcu_ctrl_info[i].ctrl_id == 0x9a0926 ) { /* * We know that the MCU would fail when we * try to write the V4L2_CID_ROI_EXPOSURE * control as we are not in the correct auto * exposure mode by default. So, skip it. */ continue; } ret = ar0521_s_ctrl(&ar0521_data.subdev, &ctrl); if (ret < 0) { dev_err(dev, "Failed to write default value for a control: %d; Control ID: %x\n", i, mcu_ctrl_info[i].ctrl_id); } } } ar0521_data.subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ar0521_data.subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; ar0521_data.pads[0].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ar0521_data.subdev.entity, 1, ar0521_data.pads); ar0521_data.subdev.entity.ops = &ar0521_sd_media_ops; if (ret < 0) { dev_err(dev, "Failed to init media entity pads\n"); return ret; } ret = v4l2_async_register_subdev(&ar0521_data.subdev); if (ret) { dev_err(dev, "Failed to register the I2C subdev for the sensor\n"); return ret; } pr_info("AR0521 detected.\n"); return 0; } /*! * ar0521 I2C detach function * * @param client struct i2c_client * * @return Error code indicating success or failure */ static int ar0521_remove(struct i2c_client *client) { v4l2_async_unregister_subdev(&ar0521_data.subdev); clk_disable_unprepare(ar0521_data.sensor_clk); /* * Power down the MCU */ if (reset_gpio >= 0) { toggle_gpio(reset_gpio, 0); } /* * Free up the GPIOs */ if (pwdn_gpio >= 0) devm_gpio_free(&client->dev, pwdn_gpio); if (reset_gpio >= 0) devm_gpio_free(&client->dev, reset_gpio); return 0; } static const struct i2c_device_id ar0521_id[] = { {"ar0521", 0}, {} }; MODULE_DEVICE_TABLE(i2c, ar0521_id); static struct i2c_driver ar0521_i2c_driver = { .driver = { .name = "ar0521", .owner = THIS_MODULE }, .probe = ar0521_probe, .remove = ar0521_remove, .id_table = ar0521_id, }; module_i2c_driver(ar0521_i2c_driver); MODULE_DESCRIPTION("AR0521 V4L2 driver"); MODULE_AUTHOR("e-con Systems"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("1.0"); MODULE_ALIAS("CSI");