/*
* 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");