Files
linux-nv-oot/drivers/media/i2c/ar1335_common.c
Jon Hunter 79e8e9cf3a media: Allow drivers to be built without V4L2
When building the NVIDIA out-of-tree drivers against 3rd party Linux
kernels where V4L2 support is not be enabled by default, the camera
drivers fail to build. Although this is expected, it is preferred that
the driver can still be built but then fail when loaded. Update the
camera drivers to guard around the V4L2 functions so that the drivers
can still be built but will return an error where needed.

Bug 4119327

Change-Id: I3a1115e5f0451153831c396244c01d7525cb51a1
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2979254
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
2023-09-14 13:32:09 -07:00

2907 lines
68 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// Copyright (c) 2022, e-con Systems. All rights reserved.
/*
* ar1335.c - AR1335 sensor driver
*/
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <media/camera_common.h>
#include "../platform/tegra/camera/camera_gpio.h"
#include "ar1335.h"
#include "mcu_firmware.h"
#define DEBUG_PRINTK
#ifndef DEBUG_PRINTK
#define debug_printk(s , ... )
#else
#define debug_printk printk
#endif
static int mcu_cam_stream_on_off(struct i2c_client *client, int cmd);
static const struct v4l2_ctrl_ops cam_ctrl_ops = {
.g_volatile_ctrl = cam_g_volatile_ctrl,
.s_ctrl = cam_s_ctrl,
};
static void u32_to_u8arr(uint8_t *out_arr, uint32_t val)
{
out_arr[0] = (uint8_t)(((uint32_t)(val) >> 24U) & 0xFFU);
out_arr[1] = (uint8_t)(((uint32_t)(val) >> 16U) & 0xFFU);
out_arr[2] = (uint8_t)(((uint32_t)(val) >> 8U) & 0xFFU);
out_arr[3] = (uint8_t)((uint32_t)(val) & 0xFFU);
}
static uint32_t u8arr_to_uint32(uint8_t *in_arr)
{
return (in_arr[0] << 24 |
in_arr[1] << 16 |
in_arr[2] << 8 |
in_arr[3]);
}
static int mcu_send_ctrl_cmd(struct i2c_client *client, int cmd, uint32_t payload_len)
{
int err;
uint8_t orig_crc = 0, calc_crc = 0;
uint16_t index = 0xFFFF;
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = cmd;
mc_data[2] = payload_len >> 8;
mc_data[3] = payload_len & 0xFF;
mc_data[4] = errorcheck(&mc_data[2], 2);
err = cam_write(client, mc_data, TX_LEN_PKT);
if (err != 0) {
dev_err(&client->dev,"%s(%d) Error - %d\n", __func__,
__LINE__, err);
return -EIO;
}
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = cmd;
mc_data[2] = index >> 8;
mc_data[3] = index & 0xFF;
mc_data[4] = errorcheck(&mc_data[2], 2);
err = cam_write(client, mc_data, 5);
if (err != 0) {
dev_err(&client->dev,"%s(%d) Error - %d\n", __func__,
__LINE__, err);
return -EIO;
}
err = cam_read(client, mc_ret_data, RX_LEN_PKT);
if (err != 0) {
dev_err(&client->dev,"%s(%d) Error - %d\n", __func__,
__LINE__, err);
return -EIO;
}
/* 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);
return -EINVAL;
}
return 0;
}
static int mcu_check_cmd_err(struct i2c_client *client)
{
uint32_t packet_len = 0;
uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0;
int err;
errcode = mc_ret_data[5];
if (errcode != ERRCODE_SUCCESS) {
dev_err(&client->dev,"%s(%d) Errcode - 0x%02x\n",
__func__, __LINE__, errcode);
return -EIO;
}
packet_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) +
HEADER_FOOTER_SIZE;
memset(mc_ret_data, 0x00, packet_len);
err = cam_read(client, mc_ret_data, packet_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[packet_len - 2];
calc_crc = errorcheck(&mc_ret_data[2],
packet_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);
return -EINVAL;
}
/* Verify Errcode */
errcode = mc_ret_data[packet_len - 1];
if (errcode != ERRCODE_SUCCESS) {
dev_err(&client->dev,"%s(%d) Errcode - 0x%02x\n",
__func__, __LINE__, errcode);
return -EINVAL;
}
return 0;
}
static int cam_send_cmd_buf(struct i2c_client *client, uint16_t n_bytes)
{
int ret;
ret = cam_write(client, g_bload_buf, n_bytes);
if (ret < 0) {
dev_err(&client->dev, "Write Failed\n");
return ret;
}
/* Wait for ACK or NACK */
ret = cam_read(client, g_bload_buf, 1);
if (ret < 0) {
dev_err(&client->dev, "Read Failed\n");
return ret;
}
if (g_bload_buf[0] != RESP_ACK) {
/* NACK Received */
dev_err(&client->dev, "NACK Received... exiting..\n");
return -1;
}
return 0;
}
static int cam_power_on(struct camera_common_data *s_data)
{
int err = 0;
struct cam *priv = (struct cam *)s_data->priv;
struct camera_common_power_rail *pw;
if (!priv || !priv->pdata)
return -EINVAL;
pw = &priv->power;
dev_dbg(&priv->i2c_client->dev, "%s: power on\n", __func__);
if (priv->pdata && priv->pdata->power_on) {
err = priv->pdata->power_on(pw);
if (err)
dev_err(&priv->i2c_client->dev, "%s failed.\n", __func__);
else
pw->state = SWITCH_ON;
return err;
}
if (unlikely(!(pw->avdd || pw->iovdd )))
goto skip_power_seqn;
if (pw->avdd) {
err = regulator_enable(pw->avdd);
if (err)
goto cam_avdd_fail;
}
if (pw->iovdd) {
err = regulator_enable(pw->iovdd);
if (err)
goto cam_iovdd_fail;
}
skip_power_seqn:
usleep_range(1350, 1360);
pw->state = SWITCH_ON;
return 0;
cam_iovdd_fail:
regulator_disable(pw->avdd);
cam_avdd_fail:
dev_err(&priv->i2c_client->dev, "%s failed.\n", __func__);
return -ENODEV;
}
static int cam_power_put(struct cam *priv)
{
struct camera_common_power_rail *pw;
if (!priv || !priv->pdata)
return -EINVAL;
pw = &priv->power;
pw->avdd = NULL;
pw->iovdd = NULL;
if (priv->pdata->use_cam_gpio)
cam_gpio_deregister(&priv->i2c_client->dev, pw->pwdn_gpio);
else {
gpio_free(pw->pwdn_gpio);
gpio_free(pw->reset_gpio);
}
return 0;
}
static int cam_power_get(struct cam *priv)
{
const char *mclk_name;
const char *parentclk_name;
struct clk *parent;
int err = 0, ret = 0;
struct camera_common_power_rail *pw = priv ? &priv->power : NULL;
struct camera_common_pdata *pdata = priv ? priv->pdata : NULL;
if (!pw || !pdata)
return -EINVAL;
mclk_name = priv->pdata->mclk_name;
if (priv->pdata->mclk_name) {
pw->mclk = devm_clk_get(&priv->i2c_client->dev, mclk_name);
if (IS_ERR(pw->mclk)) {
dev_err(&priv->i2c_client->dev, "unable to get clock %s\n",
mclk_name);
return PTR_ERR(pw->mclk);
}
parentclk_name = priv->pdata->parentclk_name;
if (parentclk_name) {
parent = devm_clk_get(&priv->i2c_client->dev, parentclk_name);
if (IS_ERR(parent))
dev_err(&priv->i2c_client->dev,
"unable to get parent clcok %s",
parentclk_name);
else {
ret = clk_set_parent(pw->mclk, parent);
if (ret < 0)
dev_dbg(&priv->i2c_client->dev,
"%s unable to set parent clock %d\n",
__func__, ret);
}
}
}
if (pdata->regulators.avdd)
err |= camera_common_regulator_get(&priv->i2c_client->dev, &pw->avdd,
pdata->regulators.avdd);
if (pdata->regulators.iovdd)
err |= camera_common_regulator_get(&priv->i2c_client->dev, &pw->iovdd,
pdata->regulators.iovdd);
if (err) {
dev_err(&priv->i2c_client->dev, "%s: unable to get regulator(s)\n", __func__);
goto done;
}
done :
pw->state = SWITCH_OFF;
return err;
}
static int cam_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int err = 0, retry = 10;;
if (!client)
return -EINVAL;
if (!enable) {
while (retry-- > 0) {
err = mcu_cam_stream_on_off(client, CMD_ID_STREAM_OFF);
if (!err) {
break;
}
dev_err(&client->dev, "%s Unable to stop stream:\n", __func__);
}
if (retry == 0) {
dev_err(&client->dev, "%s Stream Stop Error\n", __func__);
return err;
}
}
retry = 10;
while (retry-- > 0) {
err = mcu_cam_stream_on_off(client, CMD_ID_STREAM_ON);
if (!err) {
break;
}
dev_err(&client->dev, "Unable to Stream on error\n");
}
if (retry == 0 ) {
dev_err(&client->dev, "%s Stream On Error\n", __func__);
return err;
}
return 0;
}
static int cam_g_input_status(struct v4l2_subdev *sd, u32 * status)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
struct camera_common_power_rail *pw;
if (!s_data)
return -EINVAL;
priv = (struct cam *)s_data->priv;
if (!priv || !priv->pdata)
return -EINVAL;
pw = &priv->power;
*status = pw->state == SWITCH_ON;
return 0;
}
static struct v4l2_subdev_video_ops cam_subdev_video_ops = {
.s_stream = cam_s_stream,
.g_input_status = cam_g_input_status,
};
static struct v4l2_subdev_core_ops cam_subdev_core_ops = {
.s_power = camera_common_s_power,
};
static int cam_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *cfg,
struct v4l2_subdev_format *format)
{
return camera_common_g_fmt(sd, &format->format);
}
static int cam_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *cfg,
struct v4l2_subdev_format *format)
{
int ret;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
int flag = 0, err = 0, mode = 0, retry = 10;
mode = s_data->mode;
if (!s_data)
return -EINVAL;
priv = (struct cam *)s_data->priv;
if (!priv || !priv->pdata)
return -EINVAL;
switch (format->format.code) {
case MEDIA_BUS_FMT_UYVY8_1X16:
priv->format_fourcc = V4L2_PIX_FMT_UYVY;
break;
default:
/* Not Implemented */
if (format->which != V4L2_SUBDEV_FORMAT_TRY) {
return -EINVAL;
}
}
/* Delay added for green frame issue */
msleep(50);
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
ret = camera_common_try_fmt(sd, &format->format);
} else {
for (ret = 0; ret < s_data->numfmts ; ret++) {
if ((priv->mcu_cam_frmfmt[ret].size.width ==
format->format.width) &&
(priv->mcu_cam_frmfmt[ret].size.height ==
format->format.height))
{
priv->frmfmt_mode = priv->mcu_cam_frmfmt[ret].mode;
flag = 1;
break;
}
}
if (flag == 0)
return -EINVAL;
/* call stream config with width, height, frame rate */
while (retry-- > 0) {
err = mcu_stream_config(client, priv->format_fourcc,
priv->frmfmt_mode, priv->frate_index);
if (err == 0) {
break;
}
dev_err(&client->dev, "%s: Failed stream_config\n", __func__);
}
if (retry == 0) {
dev_err(&client->dev, "Failed Stream config\n");
return err;
}
mdelay(10);
ret = camera_common_s_fmt(sd, &format->format);
}
if (mode == MODE_UHD && priv->last_sync_mode == 2) {
//calibration_init(0);
priv->last_sync_mode = 1;
}
return ret;
}
static struct v4l2_subdev_pad_ops cam_subdev_pad_ops = {
.enum_mbus_code = camera_common_enum_mbus_code,
.set_fmt = cam_set_fmt,
.get_fmt = cam_get_fmt,
.enum_frame_size = camera_common_enum_framesizes,
.enum_frame_interval = camera_common_enum_frameintervals,
};
static struct v4l2_subdev_ops cam_subdev_ops = {
.core = &cam_subdev_core_ops,
.video = &cam_subdev_video_ops,
.pad = &cam_subdev_pad_ops,
};
static struct of_device_id cam_of_match[] = {
{.compatible = "nvidia,ar1335",},
{},
};
static int cam_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct cam *priv =
container_of(ctrl->handler, struct cam, ctrl_handler);
struct i2c_client *client;
int err = 0;
uint8_t ctrl_type = 0;
int ctrl_val = 0;
client = priv->i2c_client;
if (priv->power.state == SWITCH_OFF)
return 0;
if ((err = mcu_get_ctrl(client, ctrl->id, &ctrl_type, &ctrl_val)) < 0) {
return err;
}
if (ctrl_type == CTRL_STANDARD) {
ctrl->val = ctrl_val;
} else {
/* Not Implemented */
return -EINVAL;
}
return err;
}
static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct cam *priv =
container_of(ctrl->handler, struct cam, ctrl_handler);
int err = 0;
struct i2c_client *client;
client = priv->i2c_client;
if (priv->power.state == SWITCH_OFF)
return 0;
err = mcu_set_ctrl(client, ctrl->id, CTRL_STANDARD, ctrl->val);
if (err < 0) {
dev_err(&client->dev, "%s (%d )\n", __func__, __LINE__);
return -EINVAL;
}
return err;
}
static int cam_try_add_ctrls(struct cam *priv, int index,
ISP_CTRL_INFO * mcu_ctrl)
{
struct i2c_client *client;
struct v4l2_ctrl_config custom_ctrl_config;
if (!priv || !priv->pdata)
return -EINVAL;
client = priv->i2c_client;
priv->ctrl_handler.error = 0;
/* Try Enumerating in standard controls */
priv->ctrls[index] = v4l2_ctrl_new_std(&priv->ctrl_handler,
&cam_ctrl_ops,
mcu_ctrl->ctrl_id,
mcu_ctrl->ctrl_data.std.ctrl_min,
mcu_ctrl->ctrl_data.std.ctrl_max,
mcu_ctrl->ctrl_data.std.ctrl_step,
mcu_ctrl->ctrl_data.std.ctrl_def);
if (priv->ctrls[index] != NULL) {
debug_printk("%d. Initialized Control 0x%08x - %s\n",
index, mcu_ctrl->ctrl_id,
priv->ctrls[index]->name);
return 0;
}
if (mcu_ctrl->ctrl_id == V4L2_CID_EXPOSURE_AUTO)
goto custom;
/* Try Enumerating in standard menu */
priv->ctrl_handler.error = 0;
priv->ctrls[index] =
v4l2_ctrl_new_std_menu(&priv->ctrl_handler,
&cam_ctrl_ops,
mcu_ctrl->ctrl_id,
mcu_ctrl->ctrl_data.std.ctrl_max,
0, mcu_ctrl->ctrl_data.std.ctrl_def);
if (priv->ctrls[index] != NULL) {
debug_printk("%d. Initialized Control Menu 0x%08x - %s\n",
index, mcu_ctrl->ctrl_id,
priv->ctrls[index]->name);
return 0;
}
custom:
priv->ctrl_handler.error = 0;
memset(&custom_ctrl_config, 0x0, sizeof(struct v4l2_ctrl_config));
if (mcu_get_ctrl_ui(client, mcu_ctrl, index)!= ERRCODE_SUCCESS) {
dev_err(&client->dev, "Error Enumerating Control 0x%08x !!\n",
mcu_ctrl->ctrl_id);
return -EIO;
}
/* Fill in Values for Custom Ctrls */
custom_ctrl_config.ops = &cam_ctrl_ops;
custom_ctrl_config.id = mcu_ctrl->ctrl_id;
/* Do not change the name field for the control */
custom_ctrl_config.name = mcu_ctrl->ctrl_ui_data.ctrl_ui_info.ctrl_name;
/* Sample Control Type and Flags */
custom_ctrl_config.type = mcu_ctrl->ctrl_ui_data.ctrl_ui_info.ctrl_ui_type;
custom_ctrl_config.flags = mcu_ctrl->ctrl_ui_data.ctrl_ui_info.ctrl_ui_flags;
custom_ctrl_config.min = mcu_ctrl->ctrl_data.std.ctrl_min;
custom_ctrl_config.max = mcu_ctrl->ctrl_data.std.ctrl_max;
custom_ctrl_config.step = mcu_ctrl->ctrl_data.std.ctrl_step;
custom_ctrl_config.def = mcu_ctrl->ctrl_data.std.ctrl_def;
if (custom_ctrl_config.type == V4L2_CTRL_TYPE_MENU) {
custom_ctrl_config.step = 0;
custom_ctrl_config.type_ops = NULL;
custom_ctrl_config.qmenu =
(const char *const *)(mcu_ctrl->ctrl_ui_data.ctrl_menu_info.menu);
}
priv->ctrls[index] = v4l2_ctrl_new_custom(&priv->ctrl_handler,
&custom_ctrl_config, NULL);
if (priv->ctrls[index] != NULL) {
debug_printk("%d. Initialized Custom Ctrl 0x%08x - %s\n",
index, mcu_ctrl->ctrl_id,
priv->ctrls[index]->name);
return 0;
}
dev_err(&client->dev,
"%d. default: Failed to init 0x%08x ctrl Error - %d\n",
index, mcu_ctrl->ctrl_id, priv->ctrl_handler.error);
return -EINVAL;
}
static int cam_ctrls_init(struct cam *priv, ISP_CTRL_INFO *mcu_cam_ctrls)
{
int err = 0, i = 0;
struct i2c_client *client;
/* Array of Ctrls */
/* Custom Ctrl */
if (!priv || !priv->pdata)
return -EINVAL;
client = priv->i2c_client;
if (mcu_list_ctrls(client, mcu_cam_ctrls, priv) < 0) {
dev_err(&client->dev, "Failed to init ctrls\n");
goto error;
}
v4l2_ctrl_handler_init(&priv->ctrl_handler, priv->num_ctrls+1);
priv->subdev->ctrl_handler = &priv->ctrl_handler;
for (i = 0; i < priv->num_ctrls; i++) {
if (mcu_cam_ctrls[i].ctrl_type == CTRL_STANDARD)
cam_try_add_ctrls(priv, i, &mcu_cam_ctrls[i]);
else {
/* Not Implemented */
}
}
return 0;
error:
v4l2_ctrl_handler_free(&priv->ctrl_handler);
return err;
}
MODULE_DEVICE_TABLE(of, cam_of_match);
static struct camera_common_pdata *cam_parse_dt(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
struct camera_common_pdata *board_priv_pdata;
const struct of_device_id *match;
int gpio;
int err;
if (!node)
return NULL;
match = of_match_device(cam_of_match, &client->dev);
if (!match) {
dev_err(&client->dev, "Failed to find matching dt id\n");
return NULL;
}
board_priv_pdata = devm_kzalloc(&client->dev,
sizeof(*board_priv_pdata), GFP_KERNEL);
if (!board_priv_pdata)
return NULL;
gpio = of_get_named_gpio(node, "pwdn-gpios", 0);
if (gpio < 0) {
dev_err(&client->dev, "pwdn gpios not in DT\n");
goto error;
}
board_priv_pdata->pwdn_gpio = (unsigned int)gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (gpio < 0) {
/* reset-gpio is not absoluctly needed */
dev_dbg(&client->dev, "reset gpios not in DT\n");
gpio = 0;
}
board_priv_pdata->reset_gpio = (unsigned int)gpio;
board_priv_pdata->use_cam_gpio =
of_property_read_bool(node, "cam,use-cam-gpio");
err = of_property_read_string(node, "avdd-reg",
&board_priv_pdata->regulators.avdd);
err |= of_property_read_string(node, "iovdd-reg",
&board_priv_pdata->regulators.iovdd);
if (err) {
dev_dbg(&client->dev, "avdd, iovdd-reg not in DT,"
"assume sensor powered independently\n");
}
board_priv_pdata->has_eeprom =
of_property_read_bool(node, "has-eeprom");
return board_priv_pdata;
error:
devm_kfree(&client->dev, board_priv_pdata);
return NULL;
}
static int cam_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
return 0;
}
static const struct v4l2_subdev_internal_ops cam_subdev_internal_ops = {
.open = cam_open,
};
static const struct media_entity_operations cam_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static int cam_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;
}
static int cam_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 ret = %d!\n",
ret);
return ret;
}
return 0;
}
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 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);
}
}
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;
}
static int mcu_stream_config(struct i2c_client *client, uint32_t format,
int mode, int frate_index)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
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;
if (!s_data)
return -EINVAL;
priv = (struct cam *)s_data->priv;
/* 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)) {
debug_printk
("ISP is Unintialized or Busy STATUS = 0x%04x Errcode = 0x%02x !!\n",
cmd_status, retcode);
ret = -EBUSY;
goto exit;
}
if (priv->streamdb) {
for (loop = 0; priv->streamdb[loop] != mode; loop++) {
;
}
index = loop + frate_index;
}
debug_printk("Index = 0x%04x , format = 0x%08x, width = %hu,"
"height = %hu, frate num = %hu\n", index, format,
priv->mcu_cam_frmfmt[mode].size.width,
priv->mcu_cam_frmfmt[mode].size.height,
priv->mcu_cam_frmfmt[mode].framerates[frate_index]);
if (index == 0xFFFF) {
ret = -EINVAL;
goto exit;
}
if (priv->prev_index == index) {
debug_printk("Skipping Previous mode set ...\n");
ret = 0;
goto exit;
}
/* 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);
cam_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 UYVY */
u32_to_u8arr(&mc_data[4], format);
/* width */
mc_data[8] = priv->mcu_cam_frmfmt[mode].size.width >> 8;
mc_data[9] = priv->mcu_cam_frmfmt[mode].size.width & 0xFF;
/* height */
mc_data[10] = priv->mcu_cam_frmfmt[mode].size.height >> 8;
mc_data[11] = priv->mcu_cam_frmfmt[mode].size.height & 0xFF;
/* frame rate num */
mc_data[12] = priv->mcu_cam_frmfmt[mode].framerates[frate_index] >> 8;
mc_data[13] = priv->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);
issue_cmd:
err = cam_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)
priv->prev_index = index;
/* 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)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
uint32_t payload_len = 0;
uint16_t index = 0xFFFF;
int loop = 0, ret = 0;
uint32_t ctrl_id = 0;
if (!s_data)
return -EINVAL;
priv = (struct cam *)s_data->priv;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
ctrl_id = arg_ctrl_id;
/* Read the Ctrl Value from Micro controller */
for (loop = 0; loop < priv->num_ctrls; loop++) {
if (priv->ctrldb[loop] == ctrl_id) {
index = loop;
break;
}
}
if (index == 0xFFFF) {
ret = -EINVAL;
goto exit;
}
/* First Txn Payload length = 2 */
payload_len = 2;
ret = mcu_send_ctrl_cmd(client, CMD_ID_GET_CTRL, payload_len);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL send failed\n");
goto exit;
}
if (((mc_ret_data[2] << 8) | mc_ret_data[3]) == 0) {
ret = -EIO;
goto exit;
}
ret = mcu_check_cmd_err(client);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL chk err failed\n");
goto exit;
}
/* Ctrl type starts from index 6 */
*ctrl_type = mc_ret_data[6];
switch (*ctrl_type) {
case CTRL_STANDARD:
*curr_val = u8arr_to_uint32(&mc_ret_data[7]);
break;
case CTRL_EXTENDED:
/* Not Implemented */
break;
}
exit:
/* unlock semaphore */
mutex_unlock(&mcu_i2c_mutex);
return ret;
}
static int mcu_set_ctrl(struct i2c_client *client, uint32_t arg_ctrl_id,
uint8_t ctrl_type, int32_t curr_val)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
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;
if (!s_data)
return -EINVAL;
priv = (struct cam *)s_data->priv;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
ctrl_id = arg_ctrl_id;
/* call ISP Ctrl config command */
for (loop = 0; loop < priv->num_ctrls; loop++) {
if (priv->ctrldb[loop] == ctrl_id) {
index = loop;
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);
cam_write(client, mc_data, TX_LEN_PKT);
if (err != 0) {
dev_err(&client->dev, "%s(%d) Error - %d\n", __func__,
__LINE__, err);
ret = -EIO;
goto exit;
}
/* 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 */
u32_to_u8arr(&mc_data[4], ctrl_id);
/* Ctrl Type */
mc_data[8] = ctrl_type;
/* Ctrl Value */
u32_to_u8arr(&mc_data[9], curr_val);
/* CRC */
mc_data[13] = errorcheck(&mc_data[2], 11);
err = cam_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))) {
pr_err("(%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_list_fmts(struct i2c_client *client,ISP_STREAM_INFO *stream_info,
int *frm_fmt_size, struct cam *priv)
{
uint32_t payload_len = 0;
uint8_t skip = 0;
uint16_t index = 0, mode = 0;
int loop = 0, num_frates = 0, ret = 0;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
/* List all formats from MCU and append to mcu_cam_frmfmt array */
for (index = 0;; index++) {
/* First Txn Payload length = 0 */
payload_len = 2;
ret = mcu_send_ctrl_cmd(client, CMD_ID_GET_STREAM_INFO, payload_len);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_STREAM_INFO send failed\n");
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;
}
ret = mcu_check_cmd_err(client);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_STREAM_INFO chk err failed\n");
goto exit;
}
if (stream_info != NULL) {
/* check if any other format than UYVY is queried - do not append in array */
stream_info->fmt_fourcc = u8arr_to_uint32(&mc_ret_data[2]);
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:
debug_printk
("The Stream format at index 0x%04x has FRAME_RATE_CONTINOUS,"
"which is unsupported !!\n", index);
continue;
}
switch (stream_info->fmt_fourcc) {
case V4L2_PIX_FMT_UYVY:
/* cam_codes is already populated with V4L2_MBUS_FMT_UYVY8_1X16 */
/* check if width and height are already in array - update frame rate only */
for (loop = 0; loop < (mode); loop++) {
if ((priv->mcu_cam_frmfmt[loop].size.width ==
stream_info->width) &&
(priv->mcu_cam_frmfmt[loop].size.height ==
stream_info->height))
{
num_frates = priv->mcu_cam_frmfmt
[loop].num_framerates;
*((int *)(priv->mcu_cam_frmfmt[loop].framerates) + num_frates)
= (int)(stream_info->frame_rate.
disc.frame_rate_num /
stream_info->frame_rate.
disc.frame_rate_denom);
priv->mcu_cam_frmfmt[loop].num_framerates++;
priv->streamdb[index] = loop;
skip = 1;
break;
}
}
if (skip) {
skip = 0;
continue;
}
/* Add Width, Height, Frame Rate array,
* Mode into mcu_cam_frmfmt array */
priv->mcu_cam_frmfmt[mode].size.width = stream_info->width;
priv->mcu_cam_frmfmt[mode].size.height = stream_info->height;
num_frates = priv->mcu_cam_frmfmt[mode].num_framerates;
*((int *)(priv->mcu_cam_frmfmt[mode].framerates) + num_frates) =
(int)(stream_info->frame_rate.disc.frame_rate_num /
stream_info->frame_rate.disc.frame_rate_denom);
priv->mcu_cam_frmfmt[mode].num_framerates++;
priv->mcu_cam_frmfmt[mode].mode = mode;
priv->streamdb[index] = mode;
mode++;
break;
default:
debug_printk
("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;
}
static int mcu_get_ctrl_ui(struct i2c_client *client,
ISP_CTRL_INFO * mcu_ui_info, int index)
{
uint32_t payload_len = 0;
int ret = 0, i = 0;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
/* First Txn Payload length = 0 */
payload_len = 2;
ret = mcu_send_ctrl_cmd(client, CMD_ID_GET_CTRL_UI_INFO, payload_len);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL_UI_INFO send failed\n");
goto exit;
}
ret = mcu_check_cmd_err(client);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL_UI_INFO chk err failed\n");
goto exit;
}
strncpy((char *)mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_name, &mc_ret_data[2],
MAX_CTRL_UI_STRING_LEN - 1);
mcu_ui_info->ctrl_ui_data.ctrl_ui_info.ctrl_name[MAX_CTRL_UI_STRING_LEN-1] = '\0';
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);
strscpy((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);
debug_printk("Menu Element %d : %s\n",
i, mcu_ui_info->ctrl_ui_data.ctrl_menu_info.menu[i]);
}
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_lane_configuration(struct i2c_client *client, struct cam *priv)
{
int ret = 0, err;
uint16_t payload_data;
unsigned char mc_data[10];
uint32_t payload_len = 0;
uint16_t cmd_status = 0;
uint8_t retcode = 0, cmd_id = 0;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
payload_len = 2;
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = CMD_ID_LANE_CONFIG;
mc_data[2] = payload_len >> 8;
mc_data[3] = payload_len & 0xFF;
mc_data[4] = errorcheck(&mc_data[2], 2);
cam_write(client, mc_data, TX_LEN_PKT);
/* Second Txn */
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = CMD_ID_LANE_CONFIG;
/* Lane Configuration */
payload_data = priv->mipi_lane_config == 4 ? NUM_LANES_4 : NUM_LANES_2;
mc_data[2] = payload_data >> 8;
mc_data[3] = payload_data & 0xff;
/* CRC */
mc_data[4] = errorcheck(&mc_data[2], payload_len);
err = cam_write(client, mc_data, payload_len+3);
if (err != 0) {
dev_err(&client->dev, "MCU Set Ctrl Error - %d\n", err);
ret = -1;
goto exit;
}
while (1) {
yield();
cmd_id = CMD_ID_LANE_CONFIG;
if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) {
dev_err(&client->dev, "MCU Get CMD Status Error\n");
ret = -1;
goto exit;
}
if ((cmd_status == MCU_CMD_STATUS_ISP_UNINIT) && (retcode == ERRCODE_SUCCESS)) {
ret = 0;
goto exit;
}
if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_ISP_UNINIT))) {
dev_err(&client->dev, "MCU Get CMD Error STATUS = 0x%04x RET = 0x%02x\n",
cmd_status, retcode);
ret = -1;
goto exit;
}
}
exit:
/* unlock semaphore */
mutex_unlock(&mcu_i2c_mutex);
return ret;
}
static int mcu_list_ctrls(struct i2c_client *client,
ISP_CTRL_INFO * mcu_cam_ctrl, struct cam *priv)
{
uint32_t payload_len = 0;
uint16_t index = 0;
int ret = 0;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
/* Array of Ctrl Info */
while (1) {
/* First Txn Payload length = 0 */
payload_len = 2;
ret = mcu_send_ctrl_cmd(client, CMD_ID_GET_CTRL_INFO, payload_len);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL_INFO send failed\n");
goto exit;
}
if (((mc_ret_data[2] << 8) | mc_ret_data[3]) == 0) {
priv->num_ctrls = index;
break;
}
ret = mcu_check_cmd_err(client);
if (ret) {
dev_err(&client->dev, "CMD_ID_GET_CTRL_INFO chk err failed\n");
goto exit;
}
if (mcu_cam_ctrl != NULL) {
/* append ctrl info in array */
mcu_cam_ctrl[index].ctrl_id = u8arr_to_uint32(&mc_ret_data[2]);
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 =
u8arr_to_uint32(&mc_ret_data[7]);
mcu_cam_ctrl[index].ctrl_data.std.ctrl_max =
u8arr_to_uint32(&mc_ret_data[11]);
mcu_cam_ctrl[index].ctrl_data.std.ctrl_def =
u8arr_to_uint32(&mc_ret_data[15]);
mcu_cam_ctrl[index].ctrl_data.std.ctrl_step =
u8arr_to_uint32(&mc_ret_data[19]);
break;
case CTRL_EXTENDED:
/* Not Implemented */
break;
}
priv->ctrldb[index] = mcu_cam_ctrl[index].ctrl_id;
}
index++;
}
exit:
/* unlock semaphore */
mutex_unlock(&mcu_i2c_mutex);
return ret;
}
static int mcu_get_fw_version(struct i2c_client *client,
unsigned char *fw_version,
unsigned char *txt_fw_version)
{
uint32_t payload_len = 0;
uint32_t packet_len = 0;
uint8_t errcode = ERRCODE_SUCCESS, orig_crc = 0, calc_crc = 0;
int ret = 0, err = 0, loop, i=0;
unsigned long txt_fw_pos = ARRAY_SIZE(g_mcu_fw_buf)-VERSION_FILE_OFFSET;
/* lock semaphore */
mutex_lock(&mcu_i2c_mutex);
/* 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++;
}
/* Query firmware version from MCU */
payload_len = 0;
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 = cam_write(client, mc_data, TX_LEN_PKT);
if (err != 0) {
dev_err(&client->dev, "%s(%d) MCU CMD ID Write PKT fw Version Error - %d\n",
__func__, __LINE__, ret);
ret = -EIO;
goto exit;
}
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = CMD_ID_VERSION;
err = cam_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__, ret);
ret = -EIO;
goto exit;
}
err = cam_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__, ret);
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*/
packet_len = ((mc_ret_data[2] << 8) | mc_ret_data[3]) +
HEADER_FOOTER_SIZE;
memset(mc_ret_data, 0x00, packet_len);
err = cam_read(client, mc_ret_data, packet_len);
if (err != 0) {
dev_err(&client->dev, "%s(%d) MCU fw CMD ID Read Version Error - %d\n",
__func__, __LINE__, ret);
ret = -EIO;
goto exit;
}
/* Verify CRC */
orig_crc = mc_ret_data[packet_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[packet_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];
/* Check for forced/always update field in the text firmware version*/
if (txt_fw_version[17] == '1') {
dev_err(&client->dev, "Forced Update Enabled - Firmware Version - (%.32s)\n",
fw_version);
ret = 2;
goto exit;
}
for(i = 0; i < VERSION_SIZE; i++) {
if (txt_fw_version[i] != fw_version[i]) {
debug_printk("Previous Firmware Version - (%.32s)\n",
fw_version);
debug_printk("Current Firmware Version - (%.32s)\n",
txt_fw_version);
ret = 1;
goto exit;
}
}
exit:
/* unlock semaphore */
mutex_unlock(&mcu_i2c_mutex);
return ret;
}
static int mcu_get_sensor_id(struct i2c_client *client, uint16_t * sensor_id)
{
uint32_t payload_len = 0;
uint32_t packet_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);
cam_write(client, mc_data, TX_LEN_PKT);
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = CMD_ID_GET_SENSOR_ID;
err = cam_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 = cam_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;
}
packet_len = ((mc_ret_data[2] << 8) | mc_ret_data[3])
+ HEADER_FOOTER_SIZE;
memset(mc_ret_data, 0x00, packet_len);
err = cam_read(client, mc_ret_data, packet_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[packet_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[packet_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);
cam_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 = cam_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 = cam_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_cam_stream_on_off(struct i2c_client *client, int cmd)
{
uint32_t payload_len = 0;
uint16_t cmd_status = 0;
uint8_t retcode = 0, cmd_id = 0;
int retry = 1000, err = 0;
char *on_off_str = (cmd == CMD_ID_STREAM_ON) ? "ON": "OFF" ;
/* call ISP init command */
/*lock semaphore*/
mutex_lock(&mcu_i2c_mutex);
/* First Txn Payload length = 0 */
payload_len = 0;
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = cmd;
mc_data[2] = payload_len >> 8;
mc_data[3] = payload_len & 0xFF;
mc_data[4] = errorcheck(&mc_data[2], 2);
cam_write(client, mc_data, TX_LEN_PKT);
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = cmd;
err = cam_write(client, mc_data, 2);
if (err != 0) {
dev_err(&client->dev, "MCU Stream %s Write Error - %d\n", on_off_str, err);
goto exit;
}
while (--retry > 0) {
/* Some Sleep for init to process */
yield();
cmd_id = cmd;
if (mcu_get_cmd_status(client, &cmd_id, &cmd_status, &retcode) < 0) {
dev_err(&client->dev, "MCU Get CMD Stream %s Error\n", on_off_str);
err = -1;
goto exit;
}
if ((cmd_status == MCU_CMD_STATUS_SUCCESS) && (retcode == ERRCODE_SUCCESS)) {
debug_printk("MCU Get CMD Stream %s Success !!\n", on_off_str);
err = 0;
goto exit;
}
if ((retcode != ERRCODE_BUSY) && ((cmd_status != MCU_CMD_STATUS_PENDING))) {
dev_err(&client->dev,
"MCU Get CMD Stream %s Error STATUS = 0x%04x RET = 0x%02x\n",
on_off_str, cmd_status, retcode);
err = -1;
goto exit;
}
mdelay(1);
}
exit:
/* unlock */
mutex_unlock(&mcu_i2c_mutex);
return err;
}
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;
pr_info("mcu_isp_init\n");
/* 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)) {
dev_err(&client->dev, "Already Initialized !!\n");
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);
cam_write(client, mc_data, TX_LEN_PKT);
mc_data[0] = CMD_SIGNATURE;
mc_data[1] = CMD_ID_INIT_CAM;
err = cam_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(500);
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))) {
dev_err(&client->dev, "ISP Already Initialized !!\n");
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;
}
}
dev_err(&client->dev, "ETIMEDOUT Error\n");
return -ETIMEDOUT;
}
static unsigned short int mcu_bload_calc_crc16(unsigned char *buf,
unsigned int len)
{
unsigned short int crc = 0;
int i = 0;
if (!buf || !len)
return 0;
for (i = 0; i < len; i++) {
crc ^= buf[i];
}
return crc;
}
static unsigned char mcu_bload_inv_checksum(unsigned char *buf, int len)
{
unsigned int checksum = 0x00;
int i = 0;
if (!buf || !len)
return 0;
for (i = 0; i < len; i++) {
checksum = (checksum + buf[i]);
}
checksum &= (0xFF);
return (~(checksum) + 1);
}
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 = cam_write(client, g_bload_buf, 2);
if (ret < 0) {
dev_err(&client->dev, "Write Failed\n");
return -1;
}
/* Wait for ACK or NACK */
ret = cam_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 = cam_read(client, g_bload_buf, 1);
if (ret < 0) {
dev_err(&client->dev, "Read Failed\n");
return -1;
}
ret = cam_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_parse_send_cmd(struct i2c_client *client,
unsigned char *bytearray, unsigned 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 = (__force unsigned short int) htons(ihex_rec->addr);
if (rec_len == 0)
return -EINVAL;
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 -EINVAL;
}
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);
debug_printk("Updated Flash Addr = 0x%08x\n", g_bload_flashaddr);
} 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 = cam_send_cmd_buf(client, 2);
if (ret < 0) {
dev_err(&client->dev, "Write erase page cmd failed\n");
return ret;
}
u32_to_u8arr(&g_bload_buf[0], g_bload_flashaddr);
g_bload_buf[4] = errorcheck(&g_bload_buf[0], 4);
ret = cam_send_cmd_buf(client, 5);
if (ret < 0) {
dev_err(&client->dev, "Write flash addr cmd failed\n");
return ret;
}
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 = cam_write(client, g_bload_buf, i + 2);
if (ret < 0) {
dev_err(&client->dev, "Write Failed\n");
return -1;
}
do {
/* Wait for ACK or NACK */
ret = cam_read(client, g_bload_buf, 1);
if (ret < 0) {
dev_err(&client->dev, "Read Failed\n");
return -1;
}
} while (g_bload_buf[0] == RESP_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_go(struct i2c_client *client)
{
int ret = 0;
g_bload_buf[0] = BL_GO;
g_bload_buf[1] = ~(BL_GO);
ret = cam_send_cmd_buf(client, 2);
if (ret < 0) {
dev_err(&client->dev, "Write go cmd failed\n");
return ret;
}
/* Start Address */
u32_to_u8arr(&g_bload_buf[0], FLASH_START_ADDRESS);
g_bload_buf[4] = errorcheck(&g_bload_buf[0], 0);
ret = cam_send_cmd_buf(client, 5);
if (ret < 0) {
dev_err(&client->dev, "Write flash addr cmd failed\n");
return ret;
}
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;
unsigned int i = 0, recindex = 0;
int ret = 0;
wbuf = kmalloc(MAX_BUF_LEN, GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
for (i = 0; i < hex_file_size; i++) {
if ((recindex == 0) && (g_mcu_fw_buf[i] == ':')) {
/* No Implementation */
} 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++;
}
}
debug_printk("Program FLASH Success !! - CRC = 0x%04x\n", g_bload_crc16);
kfree(wbuf);
/* ------------ PROGRAM FLASH END --------------- */
return ret;
}
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 = cam_send_cmd_buf(client, 2);
if (ret < 0) {
dev_err(&client->dev, "Write erase pages cmd failed\n");
return ret;
}
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 = cam_send_cmd_buf(client, 3);
if (ret < 0) {
dev_err(&client->dev, "Write max pages cmd failed\n");
return ret;
}
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 = cam_write(client, g_bload_buf, (2 * MAX_PAGES) + 1);
if (ret < 0) {
dev_err(&client->dev, "Write Failed\n");
return -1;
}
do {
/* Wait for ACK or NACK */
ret = cam_read(client, g_bload_buf, 1);
if (ret < 0) {
dev_err(&client->dev, "Read Failed\n");
return -1;
}
} while (g_bload_buf[0] == RESP_BUSY);
if (g_bload_buf[0] != RESP_ACK) {
/* NACK Received */
dev_err(&client->dev, "NACK Received... exiting..\n");
return -1;
}
debug_printk("ERASE Sector %d success !!\n", i + 1);
}
/* ------------ ERASE FLASH END ----------------------- */
return 0;
}
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 = cam_send_cmd_buf(client, 2);
if (ret < 0) {
dev_err(&client->dev, "read mem cmd failed\n");
return ret;
}
u32_to_u8arr(&g_bload_buf[0], g_bload_flashaddr);
g_bload_buf[4] = errorcheck(&g_bload_buf[0], 4);
ret = cam_send_cmd_buf(client, 5);
if (ret < 0) {
dev_err(&client->dev, "write flash addr cmd failed\n");
return ret;
}
g_bload_buf[0] = len - 1;
g_bload_buf[1] = ~(len - 1);
ret = cam_send_cmd_buf(client, 2);
if (ret < 0) {
dev_err(&client->dev, "read len cmd failed\n");
return ret;
}
ret = cam_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;
int ret;
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);
ret = mcu_bload_read(client, flash_addr + i, bytearray, FLASH_SIZE - i);
if (ret < 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);
}
debug_printk("CRC Verification Success 0x%04x == 0x%04x\n",
orig_crc, calc_crc);
return 0;
}
static int mcu_fw_update(struct i2c_client *client, unsigned char *mcu_fw_version)
{
int ret = 0;
g_bload_crc16 = 0;
/* 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;
}
debug_printk("Get Version SUCCESS !!\n");
/* 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;
}
debug_printk("Erase Flash Success !!\n");
/* 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) {
debug_printk("(%s) - Firmware Updated - (%.32s)\n",
__func__, mcu_fw_version);
}
exit:
return ret;
}
#if KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE
static int cam_probe(struct i2c_client *client)
#else
static int cam_probe(struct i2c_client *client,
const struct i2c_device_id *id)
#endif
{
struct camera_common_data *common_data;
struct device_node *node = client->dev.of_node;
struct cam *priv;
uint32_t mipi_lane = 0;
int retry = 10;
unsigned char fw_version[32] = {0}, txt_fw_version[32] = {0};
int ret, frm_fmt_size = 0, loop;
uint16_t sensor_id = 0, reset_gpio = 0, pwdn_gpio = 0;
int err = 0, pwdn_gpio_toggle = 0;
if (!IS_ENABLED(CONFIG_OF) || !node)
return -EINVAL;
reset_gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (err < 0) {
dev_err(&client->dev, "Unable to toggle GPIO\n");
return -EINVAL;
}
pwdn_gpio = of_get_named_gpio(node, "pwdn-gpios", 0);
if (err < 0) {
dev_err(&client->dev, "Unable to toggle GPIO\n");
gpio_free(reset_gpio);
return -EINVAL;
}
err = gpio_request(reset_gpio, "cam-reset");
if (err < 0) {
dev_err(&client->dev, "%s[%d]:GPIO reset Fail, err:%d\n",
__func__, __LINE__, err);
goto exit;
}
err = gpio_request(pwdn_gpio, "cam-boot");
if (err < 0) {
dev_err(&client->dev, "%s[%d]:%dGPIO boot Fail\n",
__func__, __LINE__, err);
goto exit;
}
err = of_property_read_u32(node, "camera_mipi_lanes", &mipi_lane);
if (err < 0) {
dev_err(&client->dev, "Can not get Camera MIPI Lanes\n");
goto exit;
}
common_data = devm_kzalloc(&client->dev, sizeof(struct camera_common_data),
GFP_KERNEL);
if (!common_data) {
err = -ENOMEM;
goto exit;
}
priv = devm_kzalloc(&client->dev, sizeof(struct cam) +
sizeof(struct v4l2_ctrl *) * AR1335_NUM_CONTROLS /**num_ctrls*/,
GFP_KERNEL);
if (!priv) {
err = -ENOMEM;
goto exit;
}
priv->pdata = cam_parse_dt(client);
if (!priv->pdata) {
dev_err(&client->dev, "unable to get platform data\n");
err = -EFAULT;
goto exit;
}
priv->i2c_client = client;
priv->s_data = common_data;
priv->subdev = &common_data->subdev;
priv->subdev->dev = &client->dev;
priv->s_data->dev = &client->dev;
priv->mipi_lane_config = mipi_lane;
common_data->priv = (void *)priv;
err = cam_power_get(priv);
if (err) {
dev_err(&client->dev, "cam_power_get err %d\n", err);
return err;
}
err = cam_power_on(common_data);
if (err) {
dev_err(&client->dev, "cam_power_on err %d\n", err);
goto exit;
}
/* Reset Release for MCU */
toggle_gpio(pwdn_gpio, 0);
msleep(10);
toggle_gpio(reset_gpio, 0);
msleep(10);
toggle_gpio(reset_gpio, 1);
msleep(100);
ret = mcu_get_fw_version(client, fw_version, txt_fw_version);
if (ret != 0) {
dev_err(&client->dev, "Trying to Detect Bootloader mode\n");
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++) {
err = mcu_bload_get_version(client);
if (err < 0) {
/* Trial and Error for 1 second (100ms * 10) */
msleep(100);
continue;
} else {
dev_err(&client->dev, "Get Bload Version Success\n");
pwdn_gpio_toggle = 1;
break;
}
}
if (loop == 10) {
dev_err(&client->dev, "Error updating firmware\n");
err = -EINVAL;
goto exit;
}
if (mcu_fw_update(client, NULL) < 0) {
err = -EFAULT;
goto exit;
}
if ( pwdn_gpio_toggle == 1)
toggle_gpio(pwdn_gpio, 0);
/* Allow FW Updated Driver to reboot */
msleep(500);
for (loop = 0;loop < 100; loop++) {
err = mcu_get_fw_version(client, fw_version, txt_fw_version);
if (err == 0) {
dev_err(&client->dev, "Get FW Version Success\n");
break;
}
/* Trial and Error for 10 seconds (100ms * 100) */
msleep(100);
}
if (loop == 100) {
dev_err(&client->dev, "Error updating firmware\n");
err = -EINVAL;
goto exit;
}
debug_printk("Current Firmware Version - (%.32s)",
fw_version);
} else {
/* Same firmware version in MCU and Text File */
debug_printk("Current Firmware Version - (%.32s)",
fw_version);
}
retry = 10;
while (retry-- > 0) {
if (mcu_lane_configuration(client, priv) == 0) {
break;
}
dev_err(&client->dev, "%s, Failed to send Calibration Data\n",
__func__);
}
if (retry == 0) {
dev_err(&client->dev, "Unable to configure Lane\n");
err = -EFAULT;
goto exit;
}
/* Query the number of controls from MCU*/
retry = 10;
while (retry-- > 0) {
if (mcu_list_ctrls(client, NULL, priv) == 0) {
break;
}
dev_err(&client->dev, "%s, Failed to init controls\n",
__func__);
}
if (retry == 0) {
dev_err(&client->dev, "Unable to Query controls\n");
err = -EFAULT;
goto exit;
}
/* Query the number for Formats available from MCU */
retry = 10;
while (retry-- > 0) {
if (mcu_list_fmts(client, NULL, &frm_fmt_size, priv) == 0) {
break;
}
dev_err(&client->dev, "%s, Failed to init formats\n",
__func__);
}
if (retry == 0) {
dev_err(&client->dev, "Unable to Query the formats\n");
err = -EFAULT;
goto exit;
}
priv->mcu_ctrl_info = devm_kzalloc(&client->dev,
sizeof(ISP_CTRL_INFO) * priv->num_ctrls,
GFP_KERNEL);
if (!priv->mcu_ctrl_info) {
dev_err(&client->dev, "Unable to allocate memory\n");
err = -ENOMEM;
goto exit;
}
priv->ctrldb = devm_kzalloc(&client->dev, sizeof(uint32_t) * priv->num_ctrls, GFP_KERNEL);
if (!priv->ctrldb) {
dev_err(&client->dev, "Unable to allocate memory\n");
err = -ENOMEM;
goto exit;
}
priv->stream_info = devm_kzalloc(&client->dev,
sizeof(ISP_STREAM_INFO) * (frm_fmt_size + 1), GFP_KERNEL);
priv->streamdb = devm_kzalloc(&client->dev, sizeof(int) * (frm_fmt_size + 1), GFP_KERNEL);
if (!priv->streamdb) {
dev_err(&client->dev, "Unable to allocate memory\n");
err = -ENOMEM;
goto exit;
}
priv->mcu_cam_frmfmt = devm_kzalloc(&client->dev,
sizeof(struct camera_common_frmfmt) *
(frm_fmt_size), GFP_KERNEL);
if (!priv->mcu_cam_frmfmt) {
dev_err(&client->dev, "Unable to allocate memory\n");
err = -ENOMEM;
goto exit;
}
retry = 10;
while (retry-- > 0) {
if (mcu_isp_init(client) == 0) {
break;
}
dev_err(&client->dev, "Unable to INIT ISP\n");
}
if (retry == 0) {
dev_err(&client->dev, "Unable to INIT ISP\n");
err = -EFAULT;
goto exit;
}
retry = 10;
while (retry-- > 0) {
if (mcu_get_sensor_id(client, &sensor_id) >= 0) {
break;
}
dev_err(&client->dev, "Unable to get MCU Sensor ID\n");
}
if (retry == 0) {
dev_err(&client->dev, "Unable to get SENSOR ID\n");
err = -EFAULT;
goto exit;
}
printk("SENSOR ID=0x%04x\n",sensor_id);
retry = 10;
while (retry-- > 0) {
if (mcu_cam_stream_on_off(client, CMD_ID_STREAM_OFF) == 0) {
break;
}
dev_err(&client->dev, "%s (%d) Stream_Off\n",
__func__, __LINE__);
}
if (retry == 0) {
dev_err(&client->dev, "Unable to Stream Off\n");
err = -EFAULT;
goto exit;
}
for (loop = 0; loop < frm_fmt_size; loop++) {
priv->mcu_cam_frmfmt[loop].framerates =
devm_kzalloc(&client->dev, sizeof(int) * MAX_NUM_FRATES, GFP_KERNEL);
if (!priv->mcu_cam_frmfmt[loop].framerates) {
dev_err(&client->dev, "Unable to allocate memory\n");
err = -ENOMEM;
goto exit;
}
}
/* Enumerate Formats */
retry = 10;
while (retry-- > 0 ) {
if (mcu_list_fmts(client, priv->stream_info, &frm_fmt_size, priv) == 0) {
break;
}
dev_err(&client->dev, "Unable to List Fmts\n");
}
if (retry == 0) {
dev_err(&client->dev, "Unable to enumerate formats\n");
err = -EFAULT;
goto exit;
}
common_data->ops = NULL;
common_data->ctrl_handler = &priv->ctrl_handler;
common_data->frmfmt = priv->mcu_cam_frmfmt;
common_data->colorfmt = camera_common_find_datafmt(AR1335_DEFAULT_DATAFMT);
common_data->power = &priv->power;
common_data->ctrls = priv->ctrls;
common_data->priv = (void *)priv;
common_data->numctrls = priv->num_ctrls;
common_data->numfmts = frm_fmt_size;
common_data->def_mode = AR1335_DEFAULT_MODE;
common_data->def_width = AR1335_DEFAULT_WIDTH;
common_data->def_height = AR1335_DEFAULT_HEIGHT;
common_data->fmt_width = common_data->def_width;
common_data->fmt_height = common_data->def_height;
common_data->def_clk_freq = 24000000;
priv->i2c_client = client;
priv->s_data = common_data;
priv->subdev = &common_data->subdev;
priv->subdev->dev = &client->dev;
priv->s_data->dev = &client->dev;
priv->prev_index = 0xFFFE;
err = camera_common_initialize(common_data, "cam");
if (err) {
dev_err(&client->dev, "Failed to initialize cam.\n");
goto exit;
}
/* Get CAM FW version to find the availabity of MC chip */
v4l2_i2c_subdev_init(priv->subdev, client, &cam_subdev_ops);
/* Enumerate Ctrls */
retry = 10;
while (retry-- > 0) {
err = cam_ctrls_init(priv, priv->mcu_ctrl_info);
if (err == 0) {
break;
}
dev_err(&client->dev, "Unable to init controls\n");
}
if (retry == 0) {
dev_err(&client->dev, "Unable to Enumerate controls\n");
err = -EFAULT;
goto exit;
}
priv->subdev->internal_ops = &cam_subdev_internal_ops;
priv->subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
/*
* To unload the module driver module,
* Set (struct v4l2_subdev *)priv->subdev->sd to NULL.
* Refer tegracam_v4l2subdev_register() in tegracam_v4l2.c
*/
if (priv->subdev->owner == THIS_MODULE) {
common_data->owner = priv->subdev->owner;
priv->subdev->owner = NULL;
} else {
// It shouldn't come here in probe();
err = -EFAULT;
goto exit;
}
#if defined(CONFIG_MEDIA_CONTROLLER)
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
priv->subdev->entity.ops = &cam_media_ops;
err = tegra_media_entity_init(&priv->subdev->entity, 1, &priv->pad, true, true);
if (err < 0) {
dev_err(&client->dev, "unable to init media entity\n");
goto exit;
}
#endif
#if defined(CONFIG_V4L2_ASYNC)
err = v4l2_async_register_subdev(priv->subdev);
if (err)
goto exit;
#else
dev_err(&client->dev, "CONFIG_V4L2_ASYNC not enabled!\n");
return -ENOTSUPP;
#endif
dev_info(&client->dev, "Detected ar1335 sensor\n");
return 0;
exit:
gpio_free(reset_gpio);
gpio_free(pwdn_gpio);
return err;
}
#define FREE_SAFE(dev, ptr) \
if (ptr) { \
devm_kfree(dev, ptr); \
}
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
static int cam_remove(struct i2c_client *client)
#else
static void cam_remove(struct i2c_client *client)
#endif
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct cam *priv;
struct device_node *node = client->dev.of_node;
int loop = 0;
int reset_gpio = 0, pwdn_gpio = 0;
if (!s_data) {
dev_err(&client->dev, "camera common data is NULL\n");
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return -EINVAL;
#else
return;
#endif
}
priv = (struct cam *)s_data->priv;
/* Release the Gpios */
reset_gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (reset_gpio < 0) {
dev_err(&client->dev, "Unable to get reset GPIO\n");
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return -EINVAL;
#else
return;
#endif
}
pwdn_gpio = of_get_named_gpio(node, "pwdn-gpios", 0);
if (pwdn_gpio < 0) {
dev_err(&client->dev, "Unable to get power GPIO\n");
gpio_free(reset_gpio);
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return -EINVAL;
#else
return;
#endif
}
gpio_free(reset_gpio);
gpio_free(pwdn_gpio);
if (!priv || !priv->pdata)
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return -1;
#else
return;
#endif
#if defined(CONFIG_V4L2_ASYNC)
v4l2_async_unregister_subdev(priv->subdev);
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&priv->subdev->entity);
#endif
v4l2_ctrl_handler_free(&priv->ctrl_handler);
cam_power_put(priv);
camera_common_remove_debugfs(s_data);
/* Free up memory */
for (loop = 0; loop < priv->mcu_ctrl_info->ctrl_ui_data.ctrl_menu_info.num_menu_elem
; loop++) {
FREE_SAFE(&client->dev,
priv->mcu_ctrl_info->ctrl_ui_data.ctrl_menu_info.menu[loop]);
}
FREE_SAFE(&client->dev, priv->mcu_ctrl_info->ctrl_ui_data.ctrl_menu_info.menu);
FREE_SAFE(&client->dev, priv->mcu_ctrl_info);
for(loop = 0; loop < s_data->numfmts; loop++ ) {
FREE_SAFE(&client->dev, (void *)priv->mcu_cam_frmfmt[loop].framerates);
}
FREE_SAFE(&client->dev, priv->mcu_cam_frmfmt);
FREE_SAFE(&client->dev, priv->ctrldb);
FREE_SAFE(&client->dev, priv->streamdb);
FREE_SAFE(&client->dev, priv->stream_info);
FREE_SAFE(&client->dev, fw_version);
FREE_SAFE(&client->dev, priv->pdata);
FREE_SAFE(&client->dev, priv->s_data);
FREE_SAFE(&client->dev, priv);
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return 0;
#else
return;
#endif
}
static const struct i2c_device_id cam_id[] = {
{"ar1335", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cam_id);
static struct i2c_driver cam_i2c_driver = {
.driver = {
.name = "ar1335",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cam_of_match),
},
.probe = cam_probe,
.remove = cam_remove,
.id_table = cam_id,
};
module_i2c_driver(cam_i2c_driver);
MODULE_DESCRIPTION("V4L2 driver for e-con YUV cameras");
MODULE_AUTHOR("E-Con Systems");
MODULE_LICENSE("GPL v2");