mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Upstream commit "7d0b80647f73" [gpiolib: remove unused gpio_cansleep()] Removed gpio_cansleep() API as there are no upstream users. Replace the usage of gpio_cansleep() with gpiod_cansleep(). This is also backwards compatible with older Kernels. Bug 4276500 Change-Id: I37230d4371298d724e2311c803409108e93a4fed Signed-off-by: Kartik Rajput <kkartik@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2978834 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
2907 lines
68 KiB
C
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 (gpiod_cansleep(gpio_to_desc(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");
|