forked from rrcarlosr/Jetpack
3132 lines
78 KiB
C
3132 lines
78 KiB
C
/*
|
|
* ar0330.c - AR0330 sensor driver
|
|
* Copyright (c) 2017-2018, 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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 "../../../../nvidia/drivers/media/platform/tegra/camera/camera_gpio.h"
|
|
|
|
#include "ar0330.h"
|
|
#include <media/serdes.h>
|
|
|
|
#include <media/mcu_firmware.h>
|
|
|
|
#define DEBUG_PRINTK
|
|
#ifndef DEBUG_PRINTK
|
|
#define debug_printk(s , ... )
|
|
#else
|
|
#define debug_printk printk
|
|
#endif
|
|
|
|
static const struct v4l2_ctrl_ops ar0330_ctrl_ops = {
|
|
.g_volatile_ctrl = ar0330_g_volatile_ctrl,
|
|
.s_ctrl = ar0330_s_ctrl,
|
|
};
|
|
|
|
static int ar0330_power_on(struct camera_common_data *s_data)
|
|
{
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
struct camera_common_power_rail *pw = &priv->power;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
dev_dbg(&priv->i2c_client->dev, "%s: power on\n", __func__);
|
|
|
|
pw->state = SWITCH_ON;
|
|
return 0;
|
|
}
|
|
|
|
static int ar0330_power_put(struct ar0330 *priv)
|
|
{
|
|
struct camera_common_power_rail *pw = &priv->power;
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
if (unlikely(!pw))
|
|
return -EFAULT;
|
|
|
|
if (likely(pw->avdd))
|
|
regulator_put(pw->avdd);
|
|
|
|
if (likely(pw->iovdd))
|
|
regulator_put(pw->iovdd);
|
|
|
|
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 ar0330_power_get(struct ar0330 *priv)
|
|
{
|
|
struct camera_common_power_rail *pw = &priv->power;
|
|
struct camera_common_pdata *pdata = priv->pdata;
|
|
const char *mclk_name;
|
|
const char *parentclk_name;
|
|
struct clk *parent;
|
|
int err = 0;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
mclk_name =
|
|
priv->pdata->mclk_name ? priv->pdata->mclk_name : "cam_mclk1";
|
|
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
|
|
clk_set_parent(pw->mclk, parent);
|
|
}
|
|
|
|
|
|
err |=
|
|
camera_common_regulator_get(&priv->i2c_client->dev, &pw->avdd,
|
|
pdata->regulators.avdd);
|
|
|
|
err |=
|
|
camera_common_regulator_get(&priv->i2c_client->dev, &pw->iovdd,
|
|
pdata->regulators.iovdd);
|
|
|
|
pw->state = SWITCH_OFF;
|
|
return err;
|
|
}
|
|
|
|
static int ar0330_s_stream(struct v4l2_subdev *sd, int enable)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
if (!enable) {
|
|
/* Perform Stream Off Sequence - if any */
|
|
return 0;
|
|
}
|
|
|
|
/* Perform Stream On Sequence - if any */
|
|
|
|
mdelay(10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ar0330_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 ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
struct camera_common_power_rail *pw = &priv->power;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
*status = pw->state == SWITCH_ON;
|
|
return 0;
|
|
}
|
|
|
|
static int ar0330_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
|
|
if (!priv || !priv->pdata) {
|
|
return -ENOTTY;
|
|
}
|
|
|
|
param->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
|
|
|
|
param->parm.capture.timeperframe.denominator =
|
|
priv->mcu_cam_frmfmt[priv->frmfmt_mode].framerates[priv->frate_index];
|
|
param->parm.capture.timeperframe.numerator = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ar0330_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
int ret = 0, err = 0;
|
|
|
|
if (!priv || !priv->pdata) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (ret = 0; ret < priv->mcu_cam_frmfmt[priv->frmfmt_mode].num_framerates;
|
|
ret++) {
|
|
if ((priv->mcu_cam_frmfmt[priv->frmfmt_mode].framerates[ret] ==
|
|
param->parm.capture.timeperframe.denominator)) {
|
|
priv->frate_index = ret;
|
|
|
|
/* call stream config with width, height, frame rate */
|
|
err =
|
|
mcu_stream_config(client, priv->format_fourcc, priv->frmfmt_mode,
|
|
priv->frate_index);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "%s: Failed stream_config \n", __func__);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
param->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
|
|
param->parm.capture.timeperframe.denominator = priv->mcu_cam_frmfmt[priv->frmfmt_mode].framerates[priv->frate_index];
|
|
param->parm.capture.timeperframe.numerator = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct v4l2_subdev_video_ops ar0330_subdev_video_ops = {
|
|
.s_stream = ar0330_s_stream,
|
|
.g_mbus_config = camera_common_g_mbus_config,
|
|
.g_input_status = ar0330_g_input_status,
|
|
.g_parm = ar0330_g_parm,
|
|
.s_parm = ar0330_s_parm,
|
|
};
|
|
|
|
static struct v4l2_subdev_core_ops ar0330_subdev_core_ops = {
|
|
.s_power = camera_common_s_power,
|
|
};
|
|
|
|
static int ar0330_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *format)
|
|
{
|
|
return camera_common_g_fmt(sd, &format->format);
|
|
}
|
|
|
|
static int ar0330_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *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 ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
int flag = 0, err = 0;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 */
|
|
err =
|
|
mcu_stream_config(client, priv->format_fourcc, priv->frmfmt_mode,
|
|
priv->frate_index);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "%s: Failed stream_config \n", __func__);
|
|
return err;
|
|
}
|
|
|
|
ret = camera_common_s_fmt(sd, &format->format);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct v4l2_subdev_pad_ops ar0330_subdev_pad_ops = {
|
|
.enum_mbus_code = camera_common_enum_mbus_code,
|
|
.set_fmt = ar0330_set_fmt,
|
|
.get_fmt = ar0330_get_fmt,
|
|
.enum_frame_size = camera_common_enum_framesizes,
|
|
.enum_frame_interval = camera_common_enum_frameintervals,
|
|
};
|
|
|
|
static struct v4l2_subdev_ops ar0330_subdev_ops = {
|
|
.core = &ar0330_subdev_core_ops,
|
|
.video = &ar0330_subdev_video_ops,
|
|
.pad = &ar0330_subdev_pad_ops,
|
|
};
|
|
|
|
static struct of_device_id ar0330_of_match[] = {
|
|
{.compatible = "nvidia,ar0330",},
|
|
{},
|
|
};
|
|
|
|
static int ar0330_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct ar0330 *priv =
|
|
container_of(ctrl->handler, struct ar0330, ctrl_handler);
|
|
struct i2c_client *client = priv->i2c_client;
|
|
int err = 0;
|
|
|
|
uint8_t ctrl_type = 0;
|
|
int ctrl_val = 0;
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
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 ar0330_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct ar0330 *priv =
|
|
container_of(ctrl->handler, struct ar0330, ctrl_handler);
|
|
struct i2c_client *client = priv->i2c_client;
|
|
struct camera_common_data *cam_data = priv->s_data;
|
|
int err = 0;
|
|
u16 mode;
|
|
|
|
mode = cam_data->mode;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
if (priv->power.state == SWITCH_OFF)
|
|
return 0;
|
|
|
|
if ((err =
|
|
mcu_set_ctrl(client, ctrl->id, CTRL_STANDARD, ctrl->val)) < 0) {
|
|
dev_err(&client->dev," %s (%d ) \n", __func__, __LINE__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ar0330_try_add_ctrls(struct ar0330 *priv, int index,
|
|
ISP_CTRL_INFO * mcu_ctrl)
|
|
{
|
|
struct i2c_client *client = priv->i2c_client;
|
|
struct v4l2_ctrl_config custom_ctrl_config;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
priv->ctrl_handler.error = 0;
|
|
/* Try Enumerating in standard controls */
|
|
priv->ctrls[index] =
|
|
v4l2_ctrl_new_std(&priv->ctrl_handler,
|
|
&ar0330_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,
|
|
&ar0330_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 = &ar0330_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 ar0330_ctrls_init(struct ar0330 *priv, ISP_CTRL_INFO *mcu_cam_ctrls)
|
|
{
|
|
struct i2c_client *client = priv->i2c_client;
|
|
int err = 0, i = 0;
|
|
|
|
/* Array of Ctrls */
|
|
|
|
/* Custom Ctrl */
|
|
if (!priv || !priv->pdata)
|
|
return -EINVAL;
|
|
|
|
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) {
|
|
ar0330_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, ar0330_of_match);
|
|
|
|
static struct camera_common_pdata *ar0330_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(ar0330_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;
|
|
|
|
err = camera_common_parse_clocks(&client->dev, board_priv_pdata);
|
|
if (err) {
|
|
dev_err(&client->dev, "Failed to find clocks\n");
|
|
goto error;
|
|
}
|
|
|
|
#if 0 /* No PWDN for GMSL cameras */
|
|
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;
|
|
#endif
|
|
|
|
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);
|
|
if (err) {
|
|
dev_err(&client->dev, "avdd-reg not in DT\n");
|
|
goto error;
|
|
}
|
|
err =
|
|
of_property_read_string(node, "iovdd-reg",
|
|
&board_priv_pdata->regulators.iovdd);
|
|
if (err) {
|
|
dev_err(&client->dev, "iovdd-reg not in DT\n");
|
|
goto error;
|
|
}
|
|
|
|
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 ar0330_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_subdev_internal_ops ar0330_subdev_internal_ops = {
|
|
.open = ar0330_open,
|
|
};
|
|
|
|
static const struct media_entity_operations ar0330_media_ops = {
|
|
.link_validate = v4l2_subdev_link_validate,
|
|
};
|
|
|
|
static int ar0330_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;
|
|
|
|
msleep(1);
|
|
return 0;
|
|
|
|
err:
|
|
dev_err(&client->dev, "Failed reading register ret = %d!\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int ar0330_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;
|
|
}
|
|
|
|
msleep(1);
|
|
return 0;
|
|
}
|
|
|
|
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_jump_bload(struct i2c_client *client)
|
|
{
|
|
uint32_t payload_len = 0;
|
|
int 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_FW_UPDT;
|
|
mc_data[2] = payload_len >> 8;
|
|
mc_data[3] = payload_len & 0xFF;
|
|
mc_data[4] = errorcheck(&mc_data[2], 2);
|
|
|
|
err = ar0330_write(client, mc_data, TX_LEN_PKT);
|
|
if (err !=0 ) {
|
|
dev_err(&client->dev, " %s(%d) Error - %d \n",
|
|
__func__, __LINE__, err);
|
|
goto exit;
|
|
}
|
|
|
|
mc_data[0] = CMD_SIGNATURE;
|
|
mc_data[1] = CMD_ID_FW_UPDT;
|
|
err = ar0330_write(client, mc_data, 2);
|
|
if (err != 0) {
|
|
dev_err(&client->dev, " %s(%d) Error - %d \n",
|
|
__func__, __LINE__, err);
|
|
goto exit;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
debug_printk
|
|
(" %s(%d) ISP Status = 0x%04x , Ret code = 0x%02x \n",
|
|
__func__, __LINE__, cmd_status, retcode);
|
|
|
|
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;
|
|
}
|
|
|
|
for (loop = 0;(&priv->streamdb[loop]) != NULL; loop++) {
|
|
if (priv->streamdb[loop] == mode) {
|
|
index = loop + frate_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
ar0330_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 */
|
|
mc_data[4] = format >> 24;
|
|
mc_data[5] = format >> 16;
|
|
mc_data[6] = format >> 8;
|
|
mc_data[7] = format & 0xFF;
|
|
|
|
/* 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);
|
|
err = ar0330_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 ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
|
|
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 < 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;
|
|
|
|
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);
|
|
|
|
ar0330_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;
|
|
err = ar0330_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 = ar0330_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 = ar0330_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;
|
|
}
|
|
|
|
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 ar0330 *priv = (struct ar0330 *)s_data->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;
|
|
|
|
/* 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);
|
|
|
|
ar0330_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 = ar0330_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 ar0330 *priv)
|
|
{
|
|
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_ar0330_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);
|
|
|
|
ar0330_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 = ar0330_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 = ar0330_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) {
|
|
pr_err
|
|
(" %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) {
|
|
pr_err
|
|
(" %s(%d) Errcode - 0x%02x \n",
|
|
__func__, __LINE__, errcode);
|
|
ret = -EIO;
|
|
goto exit;
|
|
}
|
|
|
|
memset(mc_ret_data, 0x00, payload_len);
|
|
err = ar0330_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) {
|
|
pr_err
|
|
(" %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) {
|
|
pr_err
|
|
(" %s(%d) Errcode - 0x%02x \n",
|
|
__func__, __LINE__, errcode);
|
|
ret = -EIO;
|
|
goto exit;
|
|
}
|
|
|
|
if(stream_info != NULL) {
|
|
/* check if any other format than UYVY is queried - do not append in array */
|
|
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:
|
|
debug_printk
|
|
(" The Stream format at index 0x%04x has FRAME_RATE_CONTINOUS,"
|
|
"which is unsupported !! \n", index);
|
|
|
|
#if 0
|
|
stream_info.frame_rate.cont.frame_rate_min_num =
|
|
mc_ret_data[11] << 8 | mc_ret_data[12];
|
|
stream_info.frame_rate.cont.frame_rate_min_denom =
|
|
mc_ret_data[13] << 8 | mc_ret_data[14];
|
|
|
|
stream_info.frame_rate.cont.frame_rate_max_num =
|
|
mc_ret_data[15] << 8 | mc_ret_data[16];
|
|
stream_info.frame_rate.cont.frame_rate_max_denom =
|
|
mc_ret_data[17] << 8 | mc_ret_data[18];
|
|
|
|
stream_info.frame_rate.cont.frame_rate_step_num =
|
|
mc_ret_data[19] << 8 | mc_ret_data[20];
|
|
stream_info.frame_rate.cont.frame_rate_step_denom =
|
|
mc_ret_data[21] << 8 | mc_ret_data[22];
|
|
break;
|
|
#endif
|
|
continue;
|
|
}
|
|
switch (stream_info->fmt_fourcc) {
|
|
case V4L2_PIX_FMT_UYVY:
|
|
/* ar0330_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_ar0330_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;
|
|
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);
|
|
|
|
ar0330_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 = ar0330_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 = ar0330_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 = ar0330_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);
|
|
|
|
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_list_ctrls(struct i2c_client *client,
|
|
ISP_CTRL_INFO * mcu_cam_ctrl, struct ar0330 *priv)
|
|
{
|
|
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);
|
|
|
|
ar0330_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 = ar0330_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 = ar0330_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) {
|
|
priv->num_ctrls = 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 = ar0330_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) {
|
|
|
|
/* 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];
|
|
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;
|
|
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 = ar0330_write(client, mc_data, TX_LEN_PKT);
|
|
|
|
mc_data[0] = CMD_SIGNATURE;
|
|
mc_data[1] = CMD_ID_VERSION;
|
|
err = ar0330_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 = ar0330_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*/
|
|
payload_len =
|
|
((mc_ret_data[2] << 8) | mc_ret_data[3]) + HEADER_FOOTER_SIZE;
|
|
memset(mc_ret_data, 0x00, payload_len);
|
|
err = ar0330_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__, ret);
|
|
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];
|
|
|
|
|
|
/* 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 - (%.4s - %.7s) \n",
|
|
&fw_version[2], &fw_version[18]);
|
|
ret = 2;
|
|
goto exit;
|
|
}
|
|
|
|
for(i = 0; i < VERSION_SIZE; i++) {
|
|
if(txt_fw_version[i] != fw_version[i]) {
|
|
debug_printk("Previous Firmware Version - (%.4s-%.7s)\n",
|
|
&fw_version[2], &fw_version[18]);
|
|
debug_printk("Current Firmware Version - (%.4s-%.7s)\n",
|
|
&txt_fw_version[2], &txt_fw_version[18]);
|
|
ret = 1;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
ret = ERRCODE_SUCCESS;
|
|
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;
|
|
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);
|
|
|
|
ar0330_write(client, mc_data, TX_LEN_PKT);
|
|
|
|
mc_data[0] = CMD_SIGNATURE;
|
|
mc_data[1] = CMD_ID_GET_SENSOR_ID;
|
|
err = ar0330_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 = ar0330_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 = ar0330_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);
|
|
|
|
ar0330_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 = ar0330_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 = ar0330_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)) {
|
|
debug_printk(" 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);
|
|
|
|
ar0330_write(client, mc_data, TX_LEN_PKT);
|
|
|
|
mc_data[0] = CMD_SIGNATURE;
|
|
mc_data[1] = CMD_ID_INIT_CAM;
|
|
err = ar0330_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))) {
|
|
debug_printk(" 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;
|
|
}
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_read(client, g_bload_buf, 1);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Read Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
ret = ar0330_read(client, g_bload_buf, 1);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Read Failed\n");
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------- GET VERSION END ------------------- */
|
|
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
|
|
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 = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_write(client, g_bload_buf, 5);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_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 = ar0330_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;
|
|
}
|
|
|
|
int mcu_bload_go(struct i2c_client *client)
|
|
{
|
|
int ret = 0;
|
|
|
|
g_bload_buf[0] = BL_GO;
|
|
g_bload_buf[1] = ~(BL_GO);
|
|
|
|
ret = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
ret = ar0330_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 = ar0330_write(client, g_bload_buf, 5);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
ret = ar0330_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;
|
|
}
|
|
|
|
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] == ':')) {
|
|
/* debug_printk("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++;
|
|
}
|
|
}
|
|
|
|
debug_printk("Program FLASH Success !! - CRC = 0x%04x \n",
|
|
g_bload_crc16);
|
|
|
|
/* ------------ PROGRAM FLASH END ----------------------- */
|
|
|
|
return ret;
|
|
}
|
|
|
|
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 = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_write(client, g_bload_buf, 3);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_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 = ar0330_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;
|
|
}
|
|
|
|
debug_printk(" ERASE Sector %d success !! \n", i + 1);
|
|
}
|
|
|
|
/* ------------ ERASE FLASH END ----------------------- */
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_write(client, g_bload_buf, 5);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_write(client, g_bload_buf, 2);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Write Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
/* Wait for ACK or NACK */
|
|
ret = ar0330_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 = ar0330_read(client, bytearray, len);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,"Read Failed \n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
// return -1;
|
|
}
|
|
|
|
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 - (%.4s - %.7s)\n",
|
|
__func__, &mcu_fw_version[2], &mcu_fw_version[18]);
|
|
}
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int enable_phy(struct i2c_client *client, struct ar0330 *priv, uint8_t phy)
|
|
{
|
|
uint8_t linken = 0;
|
|
serdes_read_16b_reg(client, priv->des_addr, 0x0F00, &linken);
|
|
printk(" LINKEN = 0x%02x \n", linken);
|
|
|
|
if(phy == PHY_A)
|
|
linken |= 0x01;
|
|
else if(phy == PHY_B)
|
|
linken |= 0x02;
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0F00, linken);
|
|
|
|
linken = 0;
|
|
serdes_read_16b_reg(client, priv->des_addr, 0x0F00, &linken);
|
|
printk(" Changed LINKEN to = 0x%02x \n", linken);
|
|
return 0;
|
|
}
|
|
|
|
int disable_phy(struct i2c_client *client, struct ar0330 *priv, uint8_t phy)
|
|
{
|
|
uint8_t linken = 0;
|
|
serdes_read_16b_reg(client, priv->des_addr, 0x0F00, &linken);
|
|
printk(" LINKEN = 0x%02x \n", linken);
|
|
|
|
if(phy == PHY_A)
|
|
linken &= ~0x01;
|
|
else if(phy == PHY_B)
|
|
linken &= ~0x02;
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0F00, linken);
|
|
|
|
linken = 0;
|
|
serdes_read_16b_reg(client, priv->des_addr, 0x0F00, &linken);
|
|
printk(" Changed LINKEN to = 0x%02x \n", linken);
|
|
return 0;
|
|
}
|
|
|
|
static int ar0330_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct camera_common_data *common_data;
|
|
struct device_node *node = client->dev.of_node;
|
|
struct ar0330 *priv;
|
|
|
|
unsigned char fw_version[32] = {0}, txt_fw_version[32] = {0};
|
|
int ret, frm_fmt_size = 0, poc_enable = 0, loop;
|
|
uint16_t sensor_id = 0;
|
|
const char *str;
|
|
|
|
int err = 0;
|
|
|
|
if (!(IS_ENABLED(CONFIG_OF)) || !node)
|
|
return -EINVAL;
|
|
|
|
poc_enable = of_get_named_gpio(node, "poc-gpio", 0);
|
|
if(poc_enable > 0) {
|
|
debug_printk("poc_enable = %d \n", poc_enable);
|
|
err = gpio_request(poc_enable,"poc-en");
|
|
if (err < 0) {
|
|
dev_err(&client->dev,"%s[%d]:GPIO POC Fail, err:%d",__func__,__LINE__, err);
|
|
goto skip_poc;
|
|
}
|
|
toggle_gpio(poc_enable, 1);
|
|
msleep(500);
|
|
toggle_gpio(poc_enable, 0);
|
|
msleep(500);
|
|
toggle_gpio(poc_enable, 1);
|
|
msleep(500);
|
|
serdes_write_16b_reg(client, DES_ADDR1, 0x0010, 0x80);
|
|
msleep(200);
|
|
}
|
|
|
|
skip_poc:
|
|
common_data =
|
|
devm_kzalloc(&client->dev,
|
|
sizeof(struct camera_common_data), GFP_KERNEL);
|
|
if (!common_data)
|
|
return -ENOMEM;
|
|
|
|
priv =
|
|
devm_kzalloc(&client->dev,
|
|
sizeof(struct ar0330) +
|
|
sizeof(struct v4l2_ctrl *) * AR0330_NUM_CONTROLS,
|
|
GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
|
|
priv->pdata = ar0330_parse_dt(client);
|
|
if (!priv->pdata) {
|
|
dev_err(&client->dev, "unable to get platform data\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
err = of_property_read_string(node, "phy-id", &str);
|
|
if (!err) {
|
|
if (!strcmp(str, "A"))
|
|
priv->phy = PHY_A;
|
|
else
|
|
priv->phy = PHY_B;
|
|
} else {
|
|
return -EFAULT;
|
|
}
|
|
|
|
priv->ser_addr = SER_ADDR1;
|
|
priv->des_addr = DES_ADDR1;
|
|
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;
|
|
common_data->priv = (void *)priv;
|
|
|
|
err = ar0330_power_get(priv);
|
|
if (err)
|
|
return err;
|
|
|
|
err = ar0330_power_on(common_data);
|
|
if (err)
|
|
return err;
|
|
|
|
if(priv->phy == PHY_A || priv->phy == PHY_B) {
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B07, 0x0C);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C07, 0x0C);
|
|
msleep(10);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B0D, 0x80);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C0D, 0x80);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0F05, 0x26);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0F06, 0x56);
|
|
|
|
/* Address translate */
|
|
if(priv->phy == PHY_A) {
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x00);
|
|
serdes_write_8b_reg(client, SER_ADDR1, 0x04, 0x43);
|
|
msleep(100);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B0D, 0x00);
|
|
|
|
/* Change Serializer slave address */
|
|
serdes_write_8b_reg(client, SER_ADDR1, 0x00, SER_ADDR2 << 1);
|
|
|
|
if(serdes_write_8b_reg(client, SER_ADDR2, 0x04, 0x43) < 0) {
|
|
printk(" Error Accessing PHYA serializer \n");
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x03);
|
|
return -EIO;
|
|
}
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x03);
|
|
} else if (priv->phy == PHY_B) {
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x00);
|
|
serdes_write_8b_reg(client, SER_ADDR1, 0x04, 0x43);
|
|
msleep(100);
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C0D, 0x00);
|
|
|
|
/* Change Serializer slave address */
|
|
serdes_write_8b_reg(client, SER_ADDR1, 0x00, SER_ADDR3 << 1);
|
|
|
|
if(serdes_write_8b_reg(client, SER_ADDR3, 0x04, 0x43) < 0) {
|
|
printk(" Error Accessing PHYB serializer \n");
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x03);
|
|
return -EIO;
|
|
}
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x03);
|
|
}
|
|
|
|
|
|
/* Link configuration */
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0320, 0x2F);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0323, 0x2F);
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x044A, 0xC8);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x048A, 0xC8);
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0313, 0x82);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0314, 0x10);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0316, 0x5E);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0317, 0x0E);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0319, 0x10);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x031D, 0xEF);
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B96, 0x9B);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C96, 0x9B);
|
|
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B06, 0xE8);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C06, 0xE8);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x01DA, 0x18);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x01FA, 0x18);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0BA7, 0x45);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0CA7, 0x45);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x040B, 0x07);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x042D, 0x15);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x040D, 0x1E);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x040E, 0x1E);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x040F, 0x00);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0410, 0x00);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0411, 0x01);
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0412, 0x01);
|
|
}
|
|
|
|
|
|
/* i2c address translated */
|
|
if(priv->phy == PHY_A) {
|
|
if(enable_phy(client, priv, PHY_A) < 0) {
|
|
printk("Error Enabling PHYA \n");
|
|
return -EIO;
|
|
}
|
|
|
|
printk(" Translating i2c address for PHYA... \n");
|
|
priv->ser_addr = SER_ADDR2;
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x00);
|
|
|
|
/* MCU RESET */
|
|
if(serdes_write_8b_reg(client, SER_ADDR2, 0x0D, 0x8F) < 0) {
|
|
printk(" Error Accessing PHYA serializer \n");
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x03);
|
|
disable_phy(client, priv, PHY_A);
|
|
return -EIO;
|
|
}
|
|
|
|
msleep(100);
|
|
printk("MCU address modification - PHYA \n");
|
|
priv->ser_addr = SER_ADDR2;
|
|
serdes_write_8b_reg(client, SER_ADDR2, 0x0F, MCU_ADDR2 << 1);
|
|
serdes_write_8b_reg(client, SER_ADDR2, 0x10, (MCU_ADDR1 << 1));
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0C04, 0x03);
|
|
} else if(priv->phy == PHY_B) {
|
|
if(enable_phy(client, priv, PHY_B) < 0) {
|
|
printk("Error Enabling PHYB \n");
|
|
return -EIO;
|
|
}
|
|
|
|
printk(" Translating i2c address for PHYB... \n");
|
|
priv->ser_addr = SER_ADDR3;
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x00);
|
|
/* MCU RESET */
|
|
if(serdes_write_8b_reg(client, SER_ADDR3, 0x0D, 0x8F) < 0) {
|
|
printk(" Error Accessing PHYA serializer \n");
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x03);
|
|
disable_phy(client, priv, PHY_B);
|
|
return -EIO;
|
|
}
|
|
msleep(100);
|
|
printk("MCU address modification - PHYB \n");
|
|
priv->ser_addr = SER_ADDR3;
|
|
serdes_write_8b_reg(client, SER_ADDR3, 0x0F, MCU_ADDR3 << 1);
|
|
serdes_write_8b_reg(client, SER_ADDR3, 0x10, (MCU_ADDR1 << 1));
|
|
serdes_write_16b_reg(client, priv->des_addr, 0x0B04, 0x03);
|
|
}
|
|
|
|
ret = mcu_get_fw_version(client, fw_version, txt_fw_version);
|
|
if (ret != 0) {
|
|
|
|
if(ret > 0) {
|
|
if((err = mcu_jump_bload(client)) < 0) {
|
|
dev_err(&client->dev," Cannot go into bootloader mode\n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EIO;
|
|
}
|
|
msleep(1);
|
|
}
|
|
|
|
dev_err(&client->dev," Trying to Detect Bootloader mode\n");
|
|
|
|
for(loop = 0;loop < 10; loop++) {
|
|
err = mcu_bload_get_version(client);
|
|
if (err < 0) {
|
|
/* Trial and Error for 1 second (100ms * 10) */
|
|
msleep(1);
|
|
continue;
|
|
} else {
|
|
dev_err(&client->dev," Get Bload Version Success\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(loop == 10) {
|
|
dev_err(&client->dev, "Error updating firmware \n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for( loop = 0; loop < 10; loop++) {
|
|
err = mcu_fw_update(client, NULL);
|
|
if(err < 0) {
|
|
dev_err(&client->dev, "%s(%d) Error updating firmware... Retry.. \n\n", __func__, __LINE__);
|
|
|
|
continue;
|
|
} else {
|
|
dev_err (&client->dev, "Firmware Updated Successfully\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
if( loop == 10) {
|
|
dev_err( &client->dev, "Error Updating Firmware\n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* Allow FW Updated Driver to reboot */
|
|
msleep(10);
|
|
|
|
for(loop = 0;loop < 10; loop++) {
|
|
err = mcu_get_fw_version(client, fw_version, txt_fw_version);
|
|
if (err < 0) {
|
|
msleep(1);
|
|
|
|
/* See if it is a empty MCU */
|
|
err = mcu_bload_get_version(client);
|
|
if (err < 0) {
|
|
dev_err(&client->dev," Get Bload Version Fail\n");
|
|
} else {
|
|
dev_err(&client->dev," Get Bload Version Success\n");
|
|
|
|
/* Re-issue GO command to get into user mode */
|
|
if (mcu_bload_go(client) < 0) {
|
|
dev_err(&client->dev," i2c_bload_go FAIL !! \n");
|
|
}
|
|
msleep(1);
|
|
}
|
|
|
|
continue;
|
|
} else {
|
|
dev_err(&client->dev," Get FW Version Success\n");
|
|
break;
|
|
}
|
|
}
|
|
if(loop == 10) {
|
|
dev_err(&client->dev, "Error updating firmware \n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EINVAL;
|
|
}
|
|
|
|
debug_printk("Current Firmware Version - (%.4s-%.7s).",
|
|
&fw_version[2],&fw_version[18]);
|
|
|
|
} else {
|
|
/* Same firmware version in MCU and Text File */
|
|
debug_printk("Current Firmware Version - (%.4s-%.7s)",
|
|
&fw_version[2],&fw_version[18]);
|
|
}
|
|
|
|
if(mcu_list_ctrls(client, NULL, priv) < 0) {
|
|
dev_err(&client->dev, "%s, Failed to init controls \n", __func__);
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*Query the number for Formats available from MCU */
|
|
if(mcu_list_fmts(client, NULL, &frm_fmt_size,priv) < 0) {
|
|
dev_err(&client->dev, "%s, Failed to init formats \n", __func__);
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
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");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -ENOMEM;
|
|
}
|
|
if (mcu_get_sensor_id(client, &sensor_id) < 0) {
|
|
dev_err(&client->dev, "Unable to get MCU Sensor ID \n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
|
|
if (mcu_isp_init(client) < 0) {
|
|
dev_err(&client->dev, "Unable to INIT ISP \n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
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");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
/* Enumerate Formats */
|
|
if (mcu_list_fmts(client, priv->stream_info, &frm_fmt_size,priv) < 0) {
|
|
dev_err(&client->dev, "Unable to List Fmts \n");
|
|
disable_phy(client, priv, priv->phy);
|
|
return -EFAULT;
|
|
}
|
|
|
|
|
|
/* Enable Data Link in GMSL */
|
|
if(priv->phy == PHY_A) {
|
|
serdes_write_8b_reg(client, SER_ADDR2, 0x04, 0x83);
|
|
} else if(priv->phy == PHY_B) {
|
|
serdes_write_8b_reg(client, SER_ADDR3, 0x04, 0x83);
|
|
}
|
|
msleep(100);
|
|
|
|
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(AR0330_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 = AR0330_DEFAULT_MODE;
|
|
common_data->def_width = AR0330_DEFAULT_WIDTH;
|
|
common_data->def_height = AR0330_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, "ar0330");
|
|
if (err) {
|
|
dev_err(&client->dev, "Failed to initialize ar0330.\n");
|
|
return err;
|
|
}
|
|
|
|
|
|
/* Get CAM FW version to find the availabity of MC chip */
|
|
|
|
v4l2_i2c_subdev_init(priv->subdev, client, &ar0330_subdev_ops);
|
|
|
|
/* Enumerate Ctrls */
|
|
err = ar0330_ctrls_init(priv, priv->mcu_ctrl_info);
|
|
if (err)
|
|
return err;
|
|
|
|
priv->subdev->internal_ops = &ar0330_subdev_internal_ops;
|
|
priv->subdev->flags |=
|
|
V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
|
|
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
|
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
priv->subdev->entity.ops = &ar0330_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");
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
err = v4l2_async_register_subdev(priv->subdev);
|
|
if (err)
|
|
return err;
|
|
printk("Detected ar0330 sensor\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define FREE_SAFE(dev, ptr) \
|
|
if(ptr) { \
|
|
devm_kfree(dev, ptr); \
|
|
}
|
|
|
|
static int ar0330_remove(struct i2c_client *client)
|
|
{
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct ar0330 *priv = (struct ar0330 *)s_data->priv;
|
|
int loop = 0;
|
|
|
|
if (!priv || !priv->pdata)
|
|
return -1;
|
|
|
|
v4l2_async_unregister_subdev(priv->subdev);
|
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
|
media_entity_cleanup(&priv->subdev->entity);
|
|
#endif
|
|
|
|
v4l2_ctrl_handler_free(&priv->ctrl_handler);
|
|
ar0330_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);
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id ar0330_id[] = {
|
|
{"ar0330", 0},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, ar0330_id);
|
|
|
|
static struct i2c_driver ar0330_i2c_driver = {
|
|
.driver = {
|
|
.name = "ar0330",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(ar0330_of_match),
|
|
},
|
|
.probe = ar0330_probe,
|
|
.remove = ar0330_remove,
|
|
.id_table = ar0330_id,
|
|
};
|
|
|
|
module_i2c_driver(ar0330_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("V4L2 driver for e-con Cameras");
|
|
MODULE_AUTHOR("E-Con Systems");
|
|
MODULE_LICENSE("GPL v2");
|