// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Copyright (c) 2017-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // Copyright (c) 2022, e-con Systems. All rights reserved. /* * ar1335_common.c - AR1335 sensor driver */ #include #include #include #include #include #include #include #include #include #include #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, uint16_t index) { int err; uint8_t orig_crc = 0, calc_crc = 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); 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, index); 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, index); 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, index); 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, index); 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 defined(NV_I2C_DRIVER_STRUCT_PROBE_WITHOUT_I2C_DEVICE_ID_ARG) /* Linux 6.3 */ 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 defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ 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 defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ 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 defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ 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");