// SPDX-License-Identifier: GPL-2.0 /* * Rockchip CIF Driver * * Copyright (C) 2020 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dev.h" #include #include #include "common.h" #include "../../../i2c/cam-tb-setup.h" static inline struct sditf_priv *to_sditf_priv(struct v4l2_subdev *subdev) { return container_of(subdev, struct sditf_priv, sd); } void sditf_event_inc_sof(struct sditf_priv *priv) { if (priv) { struct v4l2_event event = { .type = V4L2_EVENT_FRAME_SYNC, .u.frame_sync.frame_sequence = atomic_inc_return(&priv->frm_sync_seq) - 1, }; v4l2_event_queue(priv->sd.devnode, &event); if (priv->cif_dev->exp_dbg) dev_info(priv->dev, "sof %d\n", atomic_read(&priv->frm_sync_seq) - 1); } } void sditf_event_exposure_notifier(struct sditf_priv *priv, struct sditf_effect_exp *effect_exp) { if (priv) { struct v4l2_event event = { .type = V4L2_EVENT_EXPOSURE, }; v4l2_event_queue(priv->sd.devnode, &event); } } u32 sditf_get_sof(struct sditf_priv *priv) { if (priv) return atomic_read(&priv->frm_sync_seq) - 1; return 0; } void sditf_set_sof(struct sditf_priv *priv, u32 seq) { if (priv) atomic_set(&priv->frm_sync_seq, seq); } static int sditf_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type == V4L2_EVENT_FRAME_SYNC || sub->type == V4L2_EVENT_EXPOSURE) return v4l2_event_subscribe(fh, sub, RKCIF_V4L2_EVENT_ELEMS, NULL); else return -EINVAL; } static void sditf_buffree_work(struct work_struct *work) { struct sditf_work_struct *buffree_work = container_of(work, struct sditf_work_struct, work); struct sditf_priv *priv = container_of(buffree_work, struct sditf_priv, buffree_work); struct rkcif_rx_buffer *rx_buf = NULL; unsigned long flags; LIST_HEAD(local_list); spin_lock_irqsave(&priv->cif_dev->buffree_lock, flags); list_replace_init(&priv->buf_free_list, &local_list); while (!list_empty(&local_list)) { rx_buf = list_first_entry(&local_list, struct rkcif_rx_buffer, list_free); if (rx_buf) { list_del(&rx_buf->list_free); rkcif_free_reserved_mem_buf(priv->cif_dev, rx_buf); } } spin_unlock_irqrestore(&priv->cif_dev->buffree_lock, flags); } static void sditf_get_hdr_mode(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; struct rkmodule_hdr_cfg hdr_cfg; int ret = 0; if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_HDR_CFG, &hdr_cfg); if (!ret) priv->hdr_cfg = hdr_cfg; else priv->hdr_cfg.hdr_mode = NO_HDR; } else { priv->hdr_cfg.hdr_mode = NO_HDR; } } static int sditf_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *fi) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev *sensor_sd; if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { sensor_sd = cif_dev->terminal_sensor.sd; return v4l2_subdev_call(sensor_sd, video, g_frame_interval, fi); } return -EINVAL; } static int sditf_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, struct v4l2_mbus_config *config) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev *sensor_sd; if (!cif_dev->active_sensor) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->active_sensor) { sensor_sd = cif_dev->active_sensor->sd; return v4l2_subdev_call(sensor_sd, pad, get_mbus_config, 0, config); } return -EINVAL; } static int sditf_get_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev_selection input_sel; struct v4l2_pix_format_mplane pixm; const struct cif_output_fmt *out_fmt; int ret = -EINVAL; bool is_uncompact = false; if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { sditf_get_hdr_mode(priv); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = 0; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_fmt, NULL, fmt); if (ret) { v4l2_err(&priv->sd, "%s: get sensor format failed\n", __func__); return ret; } input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; input_sel.pad = 0; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, pad, get_selection, NULL, &input_sel); if (!ret) { fmt->format.width = input_sel.r.width; fmt->format.height = input_sel.r.height; } priv->cap_info.width = fmt->format.width; priv->cap_info.height = fmt->format.height; pixm.pixelformat = rkcif_mbus_pixelcode_to_v4l2(fmt->format.code); pixm.width = priv->cap_info.width; pixm.height = priv->cap_info.height; out_fmt = rkcif_find_output_fmt(NULL, pixm.pixelformat); if (priv->toisp_inf.link_mode == TOISP_UNITE && ((pixm.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL) * out_fmt->raw_bpp / 8) & 0xf) is_uncompact = true; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "%s, width %d, height %d, hdr mode %d\n", __func__, fmt->format.width, fmt->format.height, priv->hdr_cfg.hdr_mode); if (priv->hdr_cfg.hdr_mode == NO_HDR || priv->hdr_cfg.hdr_mode == HDR_COMPR) { rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE && priv->toisp_inf.link_mode == TOISP_UNITE) { if (is_uncompact) { cif_dev->stream[0].is_compact = false; cif_dev->stream[0].is_high_align = true; } else { cif_dev->stream[0].is_compact = true; } } rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); rkcif_set_fmt(&cif_dev->stream[1], &pixm, false); } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE && priv->toisp_inf.link_mode == TOISP_UNITE) { if (is_uncompact) { cif_dev->stream[0].is_compact = false; cif_dev->stream[0].is_high_align = true; cif_dev->stream[1].is_compact = false; cif_dev->stream[1].is_high_align = true; } else { cif_dev->stream[0].is_compact = true; cif_dev->stream[1].is_compact = true; } } rkcif_set_fmt(&cif_dev->stream[0], &pixm, false); rkcif_set_fmt(&cif_dev->stream[1], &pixm, false); rkcif_set_fmt(&cif_dev->stream[2], &pixm, false); } } else { if (priv->sensor_sd) { fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = 0; ret = v4l2_subdev_call(priv->sensor_sd, pad, get_fmt, NULL, fmt); if (ret) { v4l2_err(&priv->sd, "%s: get sensor format failed\n", __func__); return ret; } input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS; input_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; input_sel.pad = 0; ret = v4l2_subdev_call(priv->sensor_sd, pad, get_selection, NULL, &input_sel); if (!ret) { fmt->format.width = input_sel.r.width; fmt->format.height = input_sel.r.height; } priv->cap_info.width = fmt->format.width; priv->cap_info.height = fmt->format.height; pixm.pixelformat = rkcif_mbus_pixelcode_to_v4l2(fmt->format.code); pixm.width = priv->cap_info.width; pixm.height = priv->cap_info.height; } else { fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = 0; fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; fmt->format.width = 640; fmt->format.height = 480; } } return 0; } static int sditf_init_buf(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; int ret = 0; if (priv->hdr_cfg.hdr_mode == HDR_X2) { if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) { if (cif_dev->is_thunderboot) cif_dev->resmem_size /= 2; ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); if (cif_dev->is_thunderboot) cif_dev->resmem_pa += cif_dev->resmem_size; ret |= rkcif_init_rx_buf(&cif_dev->stream[1], priv->buf_num); } else { ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) ret |= rkcif_init_rx_buf(&cif_dev->stream[1], priv->buf_num); } } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) { if (cif_dev->is_thunderboot) cif_dev->resmem_size /= 3; ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); if (cif_dev->is_thunderboot) cif_dev->resmem_pa += cif_dev->resmem_size; ret |= rkcif_init_rx_buf(&cif_dev->stream[1], priv->buf_num); if (cif_dev->is_thunderboot) cif_dev->resmem_pa += cif_dev->resmem_size; ret |= rkcif_init_rx_buf(&cif_dev->stream[2], priv->buf_num); } else { ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); ret |= rkcif_init_rx_buf(&cif_dev->stream[1], priv->buf_num); if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) ret |= rkcif_init_rx_buf(&cif_dev->stream[2], priv->buf_num); } } else { if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO || priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) ret = rkcif_init_rx_buf(&cif_dev->stream[0], priv->buf_num); else ret = -EINVAL; } priv->is_buf_init = true; return ret; } static void sditf_free_buf(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; if (priv->hdr_cfg.hdr_mode == HDR_X2) { rkcif_free_rx_buf(&cif_dev->stream[0], cif_dev->stream[0].rx_buf_num); rkcif_free_rx_buf(&cif_dev->stream[1], cif_dev->stream[1].rx_buf_num); } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { rkcif_free_rx_buf(&cif_dev->stream[0], cif_dev->stream[0].rx_buf_num); rkcif_free_rx_buf(&cif_dev->stream[1], cif_dev->stream[1].rx_buf_num); rkcif_free_rx_buf(&cif_dev->stream[2], cif_dev->stream[2].rx_buf_num); } else { rkcif_free_rx_buf(&cif_dev->stream[0], cif_dev->stream[0].rx_buf_num); } if (cif_dev->is_thunderboot) { cif_dev->wait_line_cache = 0; cif_dev->wait_line = 0; cif_dev->wait_line_bak = 0; cif_dev->is_thunderboot = false; } priv->is_buf_init = false; } static int sditf_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { return -EINVAL; } static void sditf_reinit_mode(struct sditf_priv *priv, struct rkisp_vicap_mode *mode) { if (mode->rdbk_mode == RKISP_VICAP_RDBK_AIQ) { priv->toisp_inf.link_mode = TOISP_NONE; } else { if (strstr(mode->name, RKISP0_DEVNAME)) priv->toisp_inf.link_mode = TOISP0; else if (strstr(mode->name, RKISP1_DEVNAME)) priv->toisp_inf.link_mode = TOISP1; else if (strstr(mode->name, RKISP_UNITE_DEVNAME)) priv->toisp_inf.link_mode = TOISP_UNITE; else priv->toisp_inf.link_mode = TOISP0; } v4l2_dbg(1, rkcif_debug, &priv->cif_dev->v4l2_dev, "%s, mode->rdbk_mode %d, mode->name %s, link_mode %d\n", __func__, mode->rdbk_mode, mode->name, priv->toisp_inf.link_mode); } #ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_SETUP static void sditf_select_sensor_setting_for_thunderboot(struct sditf_priv *priv) { struct rkcif_device *dev = priv->cif_dev; struct v4l2_subdev_format fmt; struct rk_sensor_setting sensor_setting = {0}; struct v4l2_subdev_frame_interval fi = {0}; struct rkmodule_hdr_cfg hdr_cfg; int width = 0; int height = 0; int hdr_mode = 0; int max_fps = 0; int ret = 0; bool is_match = false; if (!dev->terminal_sensor.sd) rkcif_update_sensor_info(&dev->stream[0]); if (dev->terminal_sensor.sd) { if (priv->mode.dev_id == 0) { width = get_rk_cam_w(); height = get_rk_cam_h(); hdr_mode = get_rk_cam_hdr(); max_fps = get_rk_cam1_max_fps(); } else { width = get_rk_cam2_w(); height = get_rk_cam2_h(); hdr_mode = get_rk_cam2_hdr(); max_fps = get_rk_cam2_max_fps(); } fmt.pad = 0; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt.reserved[0] = 0; fmt.format.field = V4L2_FIELD_NONE; ret = v4l2_subdev_call(dev->terminal_sensor.sd, pad, get_fmt, NULL, &fmt); if (!ret) { if (dev->rdbk_debug) v4l2_info(&dev->v4l2_dev, "cmdline get %dx%d@%dfps, hdr_mode %d\n", width, height, max_fps, hdr_mode); sensor_setting.fmt = fmt.format.code; sensor_setting.width = width; sensor_setting.height = height; sensor_setting.mode = hdr_mode; sensor_setting.fps = max_fps; ret = v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKCIS_CMD_SELECT_SETTING, &sensor_setting); if (!ret) is_match = true; } if (!is_match) { fmt.format.width = width; fmt.format.height = height; v4l2_subdev_call(dev->terminal_sensor.sd, pad, set_fmt, NULL, &fmt); v4l2_subdev_call(dev->terminal_sensor.sd, video, g_frame_interval, &fi); fi.interval.numerator = 1; fi.interval.denominator = max_fps; v4l2_subdev_call(dev->terminal_sensor.sd, video, s_frame_interval, &fi); v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_HDR_CFG, &hdr_cfg); hdr_cfg.hdr_mode = hdr_mode; v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, RKMODULE_SET_HDR_CFG, &hdr_cfg); } } } #endif static void sditf_enable_immediately(struct sditf_priv *priv); static long sditf_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkisp_vicap_mode *mode; struct v4l2_subdev_format fmt; struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev *sensor_sd; struct rkcif_exp *exp; struct rkcif_effect_exp *effect_exposure; struct sditf_time *time; struct sditf_gain *gain; struct sditf_effect_exp *effect_exp; struct rkisp_init_buf *pisp_buf_info = NULL; int ret = 0; int *on = NULL; int *connect_id = NULL; int sync_type = NO_SYNC_MODE; switch (cmd) { case RKISP_VICAP_CMD_MODE: mode = (struct rkisp_vicap_mode *)arg; if (mode->rdbk_mode == RKISP_VICAP_ONLINE_UNITE && priv->cif_dev->chip_id < CHIP_RV1103B_CIF) return -EINVAL; memcpy(&priv->mode_src, mode, sizeof(*mode)); if (cif_dev->is_thunderboot && cif_dev->is_thunderboot_start) { if (mode->rdbk_mode < RKISP_VICAP_RDBK_AIQ) cif_dev->is_rdbk_to_online = true; else cif_dev->is_rdbk_to_online = false; return 0; } mutex_lock(&cif_dev->stream_lock); memcpy(&priv->mode, mode, sizeof(*mode)); mutex_unlock(&cif_dev->stream_lock); sditf_reinit_mode(priv, &priv->mode); if (priv->is_combine_mode) mode->input.merge_num = cif_dev->sditf_cnt; else mode->input.merge_num = 1; mode->input.index = priv->combine_index; ret = v4l2_subdev_call(cif_dev->terminal_sensor.sd, core, ioctl, RKMODULE_GET_SYNC_MODE, &sync_type); if (ret || sync_type == NO_SYNC_MODE) mode->input.multi_sync = 0; else mode->input.multi_sync = 1; #ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_SETUP if (cif_dev->is_thunderboot) sditf_select_sensor_setting_for_thunderboot(priv); #endif return 0; case RKISP_VICAP_CMD_INIT_BUF: pisp_buf_info = (struct rkisp_init_buf *)arg; priv->buf_num = pisp_buf_info->buf_cnt; priv->cif_dev->fb_res_bufs = pisp_buf_info->buf_cnt; sditf_get_set_fmt(&priv->sd, NULL, &fmt); if (pisp_buf_info->hdr_wrap_line <= priv->cap_info.height) { priv->hdr_wrap_line = pisp_buf_info->hdr_wrap_line; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "hdr_wrap_line %d\n", priv->hdr_wrap_line); } else { dev_info(priv->dev, "set hdr_wap_line failed, val %d, max %d\n", pisp_buf_info->hdr_wrap_line, priv->cap_info.height); } if (!priv->is_buf_init) ret = sditf_init_buf(priv); return ret; case RKMODULE_GET_HDR_CFG: if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { sensor_sd = cif_dev->terminal_sensor.sd; return v4l2_subdev_call(sensor_sd, core, ioctl, cmd, arg); } break; case RKISP_VICAP_CMD_QUICK_STREAM: on = (int *)arg; if (*on) { rkcif_stream_resume(cif_dev, RKCIF_RESUME_ISP); } else { rkcif_stream_suspend(cif_dev, RKCIF_RESUME_ISP); if (cif_dev->chip_id == CHIP_RV1106_CIF) sditf_disable_immediately(priv); } break; case RKISP_VICAP_CMD_SET_RESET: if (priv->mode.rdbk_mode < RKISP_VICAP_RDBK_AIQ) { cif_dev->is_toisp_reset = true; return 0; } break; case RKCIF_CMD_SET_EXPOSURE: exp = (struct rkcif_exp *)arg; time = kzalloc(sizeof(*time), GFP_KERNEL); if (!time) { ret = -ENOMEM; return ret; } gain = kzalloc(sizeof(*gain), GFP_KERNEL); if (!gain) { ret = -ENOMEM; kfree(time); return ret; } time->time = exp->time; gain->gain = exp->gain; mutex_lock(&priv->mutex); list_add_tail(&time->list, &priv->time_head); list_add_tail(&gain->list, &priv->gain_head); mutex_unlock(&priv->mutex); if (cif_dev->exp_dbg) dev_info(priv->dev, "RKCIF_CMD_SET_EXPOSURE %d\n", ret); return ret; case RKCIF_CMD_GET_EFFECT_EXPOSURE: if (!list_empty(&priv->effect_exp_head)) { effect_exp = list_first_entry(&priv->effect_exp_head, struct sditf_effect_exp, list); if (effect_exp) { effect_exposure = (struct rkcif_effect_exp *)arg; mutex_lock(&priv->mutex); list_del(&effect_exp->list); mutex_unlock(&priv->mutex); *effect_exposure = effect_exp->exp; kfree(effect_exp); effect_exp = NULL; if (cif_dev->exp_dbg) dev_info(priv->dev, "RKCIF_CMD_GET_EFFECT_EXPOSURE seq %d, time 0x%x, gain 0x%x\n", effect_exposure->sequence, effect_exposure->time, effect_exposure->gain); } } else { ret = -EINVAL; } return ret; case RKCIF_CMD_GET_CONNECT_ID: connect_id = (int *)arg; *connect_id = priv->connect_id; return ret; case RKISP_VICAP_CMD_HW_LINK: on = (int *)arg; if (*on) { sditf_enable_immediately(priv); } else { if (priv->mode.rdbk_mode != RKISP_VICAP_ONLINE_MULTI) sditf_disable_immediately(priv); } return 0; default: break; } return -EINVAL; } #ifdef CONFIG_COMPAT static long sditf_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) { void __user *up = compat_ptr(arg); struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev *sensor_sd; struct rkisp_vicap_mode *mode; struct rkmodule_hdr_cfg *hdr_cfg; struct rkcif_exp *exp; struct rkcif_effect_exp *effect_expsure; int buf_num; int ret = 0; int on; int connect_id = 0; switch (cmd) { case RKISP_VICAP_CMD_MODE: mode = kzalloc(sizeof(*mode), GFP_KERNEL); if (!mode) { ret = -ENOMEM; return ret; } if (copy_from_user(mode, up, sizeof(*mode))) { kfree(mode); return -EFAULT; } ret = sditf_ioctl(sd, cmd, mode); kfree(mode); return ret; case RKISP_VICAP_CMD_INIT_BUF: if (copy_from_user(&buf_num, up, sizeof(int))) return -EFAULT; ret = sditf_ioctl(sd, cmd, &buf_num); return ret; case RKMODULE_GET_HDR_CFG: hdr_cfg = kzalloc(sizeof(*hdr_cfg), GFP_KERNEL); if (!hdr_cfg) { ret = -ENOMEM; return ret; } ret = sditf_ioctl(sd, cmd, hdr_cfg); if (!ret) { ret = copy_to_user(up, hdr_cfg, sizeof(*hdr_cfg)); if (ret) ret = -EFAULT; } kfree(hdr_cfg); return ret; case RKCIF_CMD_SET_EXPOSURE: exp = kzalloc(sizeof(*exp), GFP_KERNEL); if (!exp) { ret = -ENOMEM; return ret; } if (copy_from_user(exp, up, sizeof(*exp))) { kfree(exp); return -EFAULT; } ret = sditf_ioctl(sd, cmd, exp); kfree(exp); return ret; case RKCIF_CMD_GET_EFFECT_EXPOSURE: effect_expsure = kzalloc(sizeof(*effect_expsure), GFP_KERNEL); if (!effect_expsure) { ret = -ENOMEM; return ret; } ret = sditf_ioctl(sd, cmd, effect_expsure); if (!ret) { ret = copy_to_user(up, effect_expsure, sizeof(*effect_expsure)); if (ret) ret = -EFAULT; } kfree(effect_expsure); return ret; case RKCIF_CMD_GET_CONNECT_ID: ret = sditf_ioctl(sd, cmd, &connect_id); if (!ret) { ret = copy_to_user(up, &connect_id, sizeof(int)); if (ret) ret = -EFAULT; } return ret; case RKISP_VICAP_CMD_QUICK_STREAM: if (copy_from_user(&on, up, sizeof(int))) return -EFAULT; ret = sditf_ioctl(sd, cmd, &on); return ret; case RKISP_VICAP_CMD_SET_RESET: ret = sditf_ioctl(sd, cmd, NULL); return ret; case RKISP_VICAP_CMD_HW_LINK: if (copy_from_user(&on, up, sizeof(int))) return -EFAULT; ret = sditf_ioctl(sd, cmd, &on); return ret; default: break; } if (!cif_dev->terminal_sensor.sd) rkcif_update_sensor_info(&cif_dev->stream[0]); if (cif_dev->terminal_sensor.sd) { sensor_sd = cif_dev->terminal_sensor.sd; return v4l2_subdev_call(sensor_sd, core, compat_ioctl32, cmd, arg); } return -EINVAL; } #endif static int sditf_channel_enable_rv1103b(struct sditf_priv *priv, int user) { struct rkcif_device *cif_dev = priv->cif_dev; struct rkmodule_capture_info *capture_info = &cif_dev->channels[0].capture_info; unsigned int ch0 = 0, ch1 = 0, ch2 = 0; unsigned int ctrl_ch0 = 0; unsigned int ctrl_ch1 = 0; unsigned int ctrl_ch2 = 0; unsigned int int_en = 0; unsigned int offset_x = 0; unsigned int offset_y = 0; unsigned int width = priv->cap_info.width; unsigned int height = priv->cap_info.height; int csi_idx = cif_dev->csi_host_idx; unsigned int read_ctrl_ch0 = 0; unsigned int read_ctrl_ch1 = 0; unsigned int read_ctrl_ch2 = 0; if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE && priv->toisp_inf.link_mode == TOISP_UNITE) { if (capture_info->multi_dev.dev_num != 2 || capture_info->multi_dev.pixel_offset != RKMOUDLE_UNITE_EXTEND_PIXEL) { v4l2_err(&cif_dev->v4l2_dev, "param error of online mode, combine dev num %d, offset %d\n", capture_info->multi_dev.dev_num, capture_info->multi_dev.pixel_offset); return -EINVAL; } csi_idx = capture_info->multi_dev.dev_idx[user]; } if (priv->hdr_cfg.hdr_mode == NO_HDR || priv->hdr_cfg.hdr_mode == HDR_COMPR) { if (cif_dev->inf_id == RKCIF_MIPI_LVDS) ch0 = csi_idx * 4; else ch0 = 24;//dvp ctrl_ch0 = (ch0 << 3) | 0x1; if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FE_RK3576(0); priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { ch0 = cif_dev->csi_host_idx * 4 + 1; ch1 = cif_dev->csi_host_idx * 4; ctrl_ch0 = (ch0 << 3) | 0x1; ctrl_ch1 = (ch1 << 3) | 0x1; if (cif_dev->chip_id < CHIP_RK3576_CIF) { if (user == 0) int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1); else int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1); } else { if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FS_RK3576(1) | CIF_TOISP0_FE_RK3576(0) | CIF_TOISP0_FE_RK3576(1); } priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].is_valid = true; priv->toisp_inf.ch_info[1].id = ch1; } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { ch0 = cif_dev->csi_host_idx * 4 + 2; ch1 = cif_dev->csi_host_idx * 4 + 1; ch2 = cif_dev->csi_host_idx * 4; ctrl_ch0 = (ch0 << 3) | 0x1; ctrl_ch1 = (ch1 << 3) | 0x1; ctrl_ch2 = (ch2 << 3) | 0x1; if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FS_RK3576(1) | CIF_TOISP0_FS_RK3576(2) | CIF_TOISP0_FE_RK3576(0) | CIF_TOISP0_FE_RK3576(1) | CIF_TOISP0_FE_RK3576(2); priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].is_valid = true; priv->toisp_inf.ch_info[1].id = ch1; priv->toisp_inf.ch_info[2].is_valid = true; priv->toisp_inf.ch_info[2].id = ch2; } if (!width || !height) return -EINVAL; rkcif_write_register_or(cif_dev, CIF_REG_GLB_INTEN, int_en); if (user == 0) { if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) { width /= 2; width += RKMOUDLE_UNITE_EXTEND_PIXEL; } else if (priv->toisp_inf.link_mode == TOISP_UNITE) { width = priv->cap_info.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL; } rkcif_write_register(cif_dev, CIF_REG_TOISP0_CTRL, ctrl_ch0); rkcif_write_register(cif_dev, CIF_REG_TOISP0_CROP, offset_x | (offset_y << 16)); rkcif_write_register(cif_dev, CIF_REG_TOISP0_SIZE, width | (height << 16)); if (priv->hdr_cfg.hdr_mode != NO_HDR && priv->hdr_cfg.hdr_mode != HDR_COMPR) { rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH1_CTRL, ctrl_ch1); rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH1_CROP, offset_x | (offset_y << 16)); rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH1_SIZE, width | (height << 16)); } if (priv->hdr_cfg.hdr_mode == HDR_X3) { rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH2_CTRL, ctrl_ch2); rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH2_CROP, offset_x | (offset_y << 16)); rkcif_write_register(cif_dev, CIF_REG_TOISP0_CH2_SIZE, width | (height << 16)); } } if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_MULTI) rkcif_write_register_or(cif_dev, CIF_REG_MIPI_LVDS_CTRL, CSI_ENABLE_CAPTURE); read_ctrl_ch0 = rkcif_read_register(cif_dev, CIF_REG_TOISP0_CTRL); v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "isp%d, toisp ch0 %d, width %d, height %d, reg w:0x%x r:0x%x\n", user, ch0, width, height, ctrl_ch0, read_ctrl_ch0); if (priv->hdr_cfg.hdr_mode != NO_HDR) { read_ctrl_ch1 = rkcif_read_register(cif_dev, CIF_REG_TOISP0_CH1_CTRL); v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "isp%d, toisp ch1 %d, width %d, height %d, reg w:0x%x r:0x%x\n", user, ch1, width, height, ctrl_ch1, read_ctrl_ch1); } if (priv->hdr_cfg.hdr_mode == HDR_X3) { read_ctrl_ch2 = rkcif_read_register(cif_dev, CIF_REG_TOISP0_CH2_CTRL); v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "isp%d, toisp ch2 %d, width %d, height %d, reg w:0x%x r:0x%x\n", user, ch2, width, height, ctrl_ch2, read_ctrl_ch2); } return 0; } static int sditf_channel_enable(struct sditf_priv *priv, int user) { struct rkcif_device *cif_dev = priv->cif_dev; struct rkmodule_capture_info *capture_info = &cif_dev->channels[0].capture_info; unsigned int ch0 = 0, ch1 = 0, ch2 = 0; unsigned int ctrl_val = 0; unsigned int int_en = 0; unsigned int offset_x = 0; unsigned int offset_y = 0; unsigned int width = priv->cap_info.width; unsigned int height = priv->cap_info.height; int csi_idx = cif_dev->csi_host_idx; if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE && priv->toisp_inf.link_mode == TOISP_UNITE) { if (capture_info->multi_dev.dev_num != 2 || capture_info->multi_dev.pixel_offset != RKMOUDLE_UNITE_EXTEND_PIXEL) { v4l2_err(&cif_dev->v4l2_dev, "param error of online mode, combine dev num %d, offset %d\n", capture_info->multi_dev.dev_num, capture_info->multi_dev.pixel_offset); return -EINVAL; } csi_idx = capture_info->multi_dev.dev_idx[user]; } if (priv->hdr_cfg.hdr_mode == NO_HDR || priv->hdr_cfg.hdr_mode == HDR_COMPR) { if (cif_dev->inf_id == RKCIF_MIPI_LVDS) ch0 = csi_idx * 4; else ch0 = 24;//dvp ctrl_val = (ch0 << 3) | 0x1; if (cif_dev->chip_id < CHIP_RK3576_CIF) { if (user == 0) int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FE(0); else int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FE(0); } else { if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FE_RK3576(0); } priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { ch0 = cif_dev->csi_host_idx * 4 + 1; ch1 = cif_dev->csi_host_idx * 4; ctrl_val = (ch0 << 3) | 0x1; ctrl_val |= (ch1 << 11) | 0x100; if (cif_dev->chip_id < CHIP_RK3576_CIF) { if (user == 0) int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1); else int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1); } else { if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FS_RK3576(1) | CIF_TOISP0_FE_RK3576(0) | CIF_TOISP0_FE_RK3576(1); } priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].id = ch1; } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { ch0 = cif_dev->csi_host_idx * 4 + 2; ch1 = cif_dev->csi_host_idx * 4 + 1; ch2 = cif_dev->csi_host_idx * 4; ctrl_val = (ch0 << 3) | 0x1; ctrl_val |= (ch1 << 11) | 0x100; ctrl_val |= (ch2 << 19) | 0x10000; if (cif_dev->chip_id < CHIP_RK3576_CIF) { if (user == 0) int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FS(2) | CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1) | CIF_TOISP0_FE(2); else int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FS(2) | CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1) | CIF_TOISP1_FE(2); } else { if (user == 0) int_en = CIF_TOISP0_FS_RK3576(0) | CIF_TOISP0_FS_RK3576(1) | CIF_TOISP0_FS_RK3576(2) | CIF_TOISP0_FE_RK3576(0) | CIF_TOISP0_FE_RK3576(1) | CIF_TOISP0_FE_RK3576(2); } priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].id = ch1; priv->toisp_inf.ch_info[2].id = ch2; } if (cif_dev->chip_id > CHIP_RK3562_CIF) ctrl_val |= BIT(28); if (user == 0) { if (priv->toisp_inf.link_mode == TOISP_UNITE) width = priv->cap_info.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL; rkcif_write_register(cif_dev, CIF_REG_TOISP0_CTRL, ctrl_val); if (width && height) { rkcif_write_register(cif_dev, CIF_REG_TOISP0_CROP, offset_x | (offset_y << 16)); rkcif_write_register(cif_dev, CIF_REG_TOISP0_SIZE, width | (height << 16)); } else { return -EINVAL; } } else { if (priv->toisp_inf.link_mode == TOISP_UNITE) { if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) offset_x = 0; else offset_x = priv->cap_info.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL; width = priv->cap_info.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL; } rkcif_write_register(cif_dev, CIF_REG_TOISP1_CTRL, ctrl_val); if (width && height) { rkcif_write_register(cif_dev, CIF_REG_TOISP1_CROP, offset_x | (offset_y << 16)); rkcif_write_register(cif_dev, CIF_REG_TOISP1_SIZE, width | (height << 16)); } else { return -EINVAL; } } v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "isp%d, toisp ch0 %d, width %d, height %d, reg 0x%x\n", user, ch0, width, height, ctrl_val); #if IS_ENABLED(CONFIG_CPU_RV1106) rv1106_sdmmc_get_lock(); #endif rkcif_write_register_or(cif_dev, CIF_REG_GLB_INTEN, int_en); #if IS_ENABLED(CONFIG_CPU_RV1106) rv1106_sdmmc_put_lock(); #endif return 0; } static void sditf_channel_disable(struct sditf_priv *priv, int user) { struct rkcif_device *cif_dev = priv->cif_dev; u32 ctrl_val = 0x10101; if (user == 0) rkcif_write_register_and(cif_dev, CIF_REG_TOISP0_CTRL, ~ctrl_val); else rkcif_write_register_and(cif_dev, CIF_REG_TOISP1_CTRL, ~ctrl_val); } static void sditf_channel_disable_rv1103b(struct sditf_priv *priv, int user) { struct rkcif_device *cif_dev = priv->cif_dev; u32 ctrl_val = 0x1; u32 read_ctrl_ch0 = 0; if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_MULTI) rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_CTRL, ~CSI_ENABLE_CAPTURE); rkcif_write_register_and(cif_dev, CIF_REG_TOISP0_CTRL, ~ctrl_val); read_ctrl_ch0 = rkcif_read_register(cif_dev, CIF_REG_TOISP0_CTRL); v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "isp%d, toisp disable reg w_and:0x%x r:0x%x\n", user, ~ctrl_val, read_ctrl_ch0); if (priv->hdr_cfg.hdr_mode != NO_HDR) rkcif_write_register_and(cif_dev, CIF_REG_TOISP0_CH1_CTRL, ~ctrl_val); if (priv->hdr_cfg.hdr_mode == HDR_X3) rkcif_write_register_and(cif_dev, CIF_REG_TOISP0_CH2_CTRL, ~ctrl_val); } void sditf_change_to_online(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; struct rkcif_stream *cur_stream = NULL; priv->mode = priv->mode_src; if (priv->mode.rdbk_mode != RKISP_VICAP_ONLINE_UNITE && priv->mode.rdbk_mode != RKISP_VICAP_ONLINE_MULTI) sditf_enable_immediately(priv); if (cif_dev->is_thunderboot) { if (priv->hdr_cfg.hdr_mode == NO_HDR) { cur_stream = &cif_dev->stream[0]; cif_dev->stream[0].is_line_wake_up = false; } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { cur_stream = &cif_dev->stream[1]; cif_dev->stream[0].is_line_wake_up = false; cif_dev->stream[1].is_line_wake_up = false; } else if (priv->hdr_cfg.hdr_mode == HDR_X3) { cur_stream = &cif_dev->stream[2]; cif_dev->stream[0].is_line_wake_up = false; cif_dev->stream[1].is_line_wake_up = false; cif_dev->stream[2].is_line_wake_up = false; } if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) cur_stream->is_m_online_fb_res = true; rkcif_free_rx_buf(cur_stream, cur_stream->rx_buf_num); cif_dev->wait_line_cache = 0; cif_dev->wait_line = 0; cif_dev->wait_line_bak = 0; cif_dev->is_thunderboot = false; if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE) rkcif_reinit_right_half_config(cur_stream); } } void sditf_disable_immediately(struct sditf_priv *priv) { if (priv->toisp_inf.link_mode == TOISP0) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) sditf_channel_disable_rv1103b(priv, 0); else sditf_channel_disable(priv, 0); } else if (priv->toisp_inf.link_mode == TOISP1) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) sditf_channel_disable_rv1103b(priv, 1); else sditf_channel_disable(priv, 1); } else if (priv->toisp_inf.link_mode == TOISP_UNITE) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) { sditf_channel_disable_rv1103b(priv, 0); } else { sditf_channel_disable(priv, 0); if (priv->cif_dev->chip_id == CHIP_RK3588_CIF) sditf_channel_disable(priv, 1); } } priv->is_toisp_off = true; } static void sditf_enable_immediately(struct sditf_priv *priv) { if (priv->toisp_inf.link_mode == TOISP0) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) sditf_channel_enable_rv1103b(priv, 0); else sditf_channel_enable(priv, 0); } else if (priv->toisp_inf.link_mode == TOISP1) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) sditf_channel_enable_rv1103b(priv, 1); else sditf_channel_enable(priv, 1); } else if (priv->toisp_inf.link_mode == TOISP_UNITE) { if (priv->cif_dev->chip_id == CHIP_RV1103B_CIF) { sditf_channel_enable_rv1103b(priv, 0); } else { sditf_channel_enable(priv, 0); if (priv->cif_dev->chip_id == CHIP_RK3588_CIF) sditf_channel_enable(priv, 1); } } priv->is_toisp_off = false; } static int sditf_start_stream(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_subdev_format fmt; unsigned int mode = RKCIF_STREAM_MODE_TOISP; int stream_cnt = 0; int i = 0; sditf_get_set_fmt(&priv->sd, NULL, &fmt); if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) { sditf_enable_immediately(priv); mode = RKCIF_STREAM_MODE_TOISP; } else if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) { mode = RKCIF_STREAM_MODE_TOISP_RDBK; } if (priv->hdr_cfg.hdr_mode == NO_HDR || priv->hdr_cfg.hdr_mode == HDR_COMPR) stream_cnt = 1; else if (priv->hdr_cfg.hdr_mode == HDR_X2) stream_cnt = 2; else if (priv->hdr_cfg.hdr_mode == HDR_X3) stream_cnt = 3; cif_dev->is_thunderboot_start = true; for (i = 0; i < stream_cnt; i++) rkcif_do_start_stream(&cif_dev->stream[i], mode); INIT_LIST_HEAD(&priv->buf_free_list); return 0; } static int sditf_stop_stream(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; unsigned int mode = RKCIF_STREAM_MODE_TOISP; struct rkcif_hw *hw_dev = cif_dev->hw_dev; int stream_cnt = 0; int i = 0; bool toisp_off = true; if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) mode = RKCIF_STREAM_MODE_TOISP; else if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) mode = RKCIF_STREAM_MODE_TOISP_RDBK; if (priv->hdr_cfg.hdr_mode == NO_HDR || priv->hdr_cfg.hdr_mode == HDR_COMPR) stream_cnt = 1; else if (priv->hdr_cfg.hdr_mode == HDR_X2) stream_cnt = 2; else if (priv->hdr_cfg.hdr_mode == HDR_X3) stream_cnt = 3; for (i = 0; i < stream_cnt; i++) rkcif_do_stop_stream(&cif_dev->stream[i], mode); mutex_lock(&hw_dev->dev_lock); for (i = 0; i < hw_dev->dev_num; i++) { if (atomic_read(&hw_dev->cif_dev[i]->pipe.stream_cnt) != 0) { toisp_off = false; break; } } mutex_unlock(&hw_dev->dev_lock); if (toisp_off) sditf_disable_immediately(priv); priv->toisp_inf.ch_info[0].is_valid = false; priv->toisp_inf.ch_info[1].is_valid = false; priv->toisp_inf.ch_info[2].is_valid = false; return 0; } static int sditf_s_stream(struct v4l2_subdev *sd, int on) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; int ret = 0; if (!on && atomic_dec_return(&priv->stream_cnt)) return 0; if (on && atomic_inc_return(&priv->stream_cnt) > 1) return 0; if (cif_dev->chip_id >= CHIP_RK3588_CIF) { if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AIQ) return 0; v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "%s, toisp mode %d, hdr %d, stream on %d\n", __func__, priv->toisp_inf.link_mode, priv->hdr_cfg.hdr_mode, on); if (on) { ret = sditf_start_stream(priv); } else { ret = sditf_stop_stream(priv); sditf_free_buf(priv); priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AIQ; } } if (on && ret) atomic_dec(&priv->stream_cnt); return ret; } static int sditf_s_power(struct v4l2_subdev *sd, int on) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct rkcif_vdev_node *node = &cif_dev->stream[0].vnode; int ret = 0; if (!on && atomic_dec_return(&priv->power_cnt)) return 0; if (on && atomic_inc_return(&priv->power_cnt) > 1) return 0; if (cif_dev->chip_id >= CHIP_RK3588_CIF) { v4l2_dbg(1, rkcif_debug, &cif_dev->v4l2_dev, "%s, toisp mode %d, hdr %d, set power %d\n", __func__, priv->toisp_inf.link_mode, priv->hdr_cfg.hdr_mode, on); mutex_lock(&cif_dev->stream_lock); if (on) { ret = pm_runtime_resume_and_get(cif_dev->dev); ret |= v4l2_pipeline_pm_get(&node->vdev.entity); } else { v4l2_pipeline_pm_put(&node->vdev.entity); pm_runtime_put_sync(cif_dev->dev); priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AIQ; } v4l2_dbg(1, rkcif_debug, &node->vdev, "s_power %d, entity use_count %d\n", on, node->vdev.entity.use_count); mutex_unlock(&cif_dev->stream_lock); } return ret; } static int sditf_s_rx_buffer(struct v4l2_subdev *sd, void *buf, unsigned int *size) { struct sditf_priv *priv = to_sditf_priv(sd); struct rkcif_device *cif_dev = priv->cif_dev; struct rkcif_stream *stream = NULL; struct rkisp_rx_buf *dbufs; struct rkcif_rx_buffer *rx_buf = NULL; unsigned long flags, buffree_flags; u32 diff_time = 1000000; u32 early_time = 0; bool is_free = false; if (!buf) { v4l2_err(&cif_dev->v4l2_dev, "buf is NULL\n"); return -EINVAL; } dbufs = buf; if (cif_dev->hdr.hdr_mode == NO_HDR) { if (dbufs->type == BUF_SHORT) stream = &cif_dev->stream[0]; else return -EINVAL; } else if (cif_dev->hdr.hdr_mode == HDR_X2) { if (dbufs->type == BUF_SHORT) stream = &cif_dev->stream[1]; else if (dbufs->type == BUF_MIDDLE) stream = &cif_dev->stream[0]; else return -EINVAL; } else if (cif_dev->hdr.hdr_mode == HDR_X3) { if (dbufs->type == BUF_SHORT) stream = &cif_dev->stream[2]; else if (dbufs->type == BUF_MIDDLE) stream = &cif_dev->stream[1]; else if (dbufs->type == BUF_LONG) stream = &cif_dev->stream[0]; else return -EINVAL; } if (!stream) return -EINVAL; if (dbufs->sequence == 0 && stream->thunderboot_skip_interval) { spin_lock_irqsave(&stream->vbq_lock, flags); cif_dev->is_stop_skip = true; spin_unlock_irqrestore(&stream->vbq_lock, flags); } rx_buf = to_cif_rx_buf(dbufs); v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n", (u32)rx_buf->dummy.dma_addr); spin_lock_irqsave(&stream->vbq_lock, flags); stream->last_rx_buf_idx = dbufs->sequence + 1; atomic_inc(&stream->buf_cnt); if (stream->total_buf_num > cif_dev->fb_res_bufs && cif_dev->is_thunderboot && (dbufs->sequence > 2) && (!dbufs->is_switch)) { spin_lock_irqsave(&cif_dev->buffree_lock, buffree_flags); list_add_tail(&rx_buf->list_free, &priv->buf_free_list); spin_unlock_irqrestore(&cif_dev->buffree_lock, buffree_flags); atomic_dec(&stream->buf_cnt); stream->total_buf_num--; schedule_work(&priv->buffree_work.work); is_free = true; } if (!is_free && (!dbufs->is_switch)) { list_add_tail(&rx_buf->list, &stream->rx_buf_head); rkcif_assign_check_buffer_update_toisp(stream); if (cif_dev->resume_mode != RKISP_RTT_MODE_ONE_FRAME) { if (!stream->dma_en) { stream->to_en_dma = RKCIF_DMAEN_BY_ISP; rkcif_enable_dma_capture(stream, true); if (atomic_read(&cif_dev->sensor_off)) { atomic_set(&cif_dev->sensor_off, 0); cif_dev->sensor_work.on = 1; rkcif_dphy_quick_stream(stream->cifdev, cif_dev->sensor_work.on); schedule_work(&cif_dev->sensor_work.work); } } } if (cif_dev->rdbk_debug) { u32 offset = 0; offset = rx_buf->dummy.size - stream->pixm.plane_fmt[0].bytesperline * 3; memset(rx_buf->dummy.vaddr + offset, 0x00, stream->pixm.plane_fmt[0].bytesperline * 3); if (cif_dev->is_thunderboot || cif_dev->is_rtt_suspend || cif_dev->is_aov_reserved) dma_sync_single_for_device(cif_dev->dev, rx_buf->dummy.dma_addr + rx_buf->dummy.size - stream->pixm.plane_fmt[0].bytesperline * 3, stream->pixm.plane_fmt[0].bytesperline * 3, DMA_FROM_DEVICE); else cif_dev->hw_dev->mem_ops->prepare(rx_buf->dummy.mem_priv); } } if (dbufs->is_switch && dbufs->type == BUF_SHORT) { if (stream->is_in_vblank) { sditf_change_to_online(priv); rkcif_modify_line_int(stream, false); stream->is_line_inten = false; } else { stream->is_change_toisp = true; } v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "switch to online mode\n"); } spin_unlock_irqrestore(&stream->vbq_lock, flags); spin_lock_irqsave(&stream->cifdev->stream_spinlock, flags); stream->is_finish_single_cap = true; if (stream->is_wait_single_cap && (cif_dev->hdr.hdr_mode == NO_HDR || (cif_dev->hdr.hdr_mode == HDR_X2 && stream->id == 1) || (cif_dev->hdr.hdr_mode == HDR_X3 && stream->id == 2))) { stream->is_wait_single_cap = false; spin_unlock_irqrestore(&stream->cifdev->stream_spinlock, flags); rkcif_quick_stream_on(cif_dev, true); } else { spin_unlock_irqrestore(&stream->cifdev->stream_spinlock, flags); } if (!cif_dev->is_thunderboot || cif_dev->is_rdbk_to_online == false) return 0; if (dbufs->runtime_us && cif_dev->early_line == 0) { if (!cif_dev->sensor_linetime) cif_dev->sensor_linetime = rkcif_get_linetime(stream); cif_dev->isp_runtime_max = dbufs->runtime_us; if (cif_dev->is_thunderboot) diff_time = 200000; else diff_time = 1000000; if (dbufs->runtime_us * 1000 < cif_dev->sensor_linetime * stream->pixm.height && dbufs->runtime_us * 1000 + cif_dev->sensor_linetime > diff_time) early_time = dbufs->runtime_us * 1000 - diff_time; else early_time = diff_time; cif_dev->early_line = div_u64(early_time, cif_dev->sensor_linetime); cif_dev->wait_line_cache = stream->pixm.height - cif_dev->early_line; if (cif_dev->rdbk_debug && dbufs->sequence < 15) v4l2_info(&cif_dev->v4l2_dev, "%s, isp runtime %d, line time %d, early_line %d, line_intr_cnt %d, seq %d, type %d, dma_addr %x\n", __func__, dbufs->runtime_us, cif_dev->sensor_linetime, cif_dev->early_line, cif_dev->wait_line_cache, dbufs->sequence, dbufs->type, (u32)rx_buf->dummy.dma_addr); } else { if (dbufs->runtime_us < cif_dev->isp_runtime_max) { cif_dev->isp_runtime_max = dbufs->runtime_us; if (cif_dev->is_thunderboot) diff_time = 200000; else diff_time = 1000000; if (dbufs->runtime_us * 1000 < cif_dev->sensor_linetime * stream->pixm.height && dbufs->runtime_us * 1000 + cif_dev->sensor_linetime > diff_time) early_time = dbufs->runtime_us * 1000 - diff_time; else early_time = diff_time; cif_dev->early_line = div_u64(early_time, cif_dev->sensor_linetime); cif_dev->wait_line_cache = stream->pixm.height - cif_dev->early_line; } if (cif_dev->rdbk_debug && dbufs->sequence < 15) v4l2_info(&cif_dev->v4l2_dev, "isp runtime %d, seq %d, type %d, early_line %d, dma addr %x\n", dbufs->runtime_us, dbufs->sequence, dbufs->type, cif_dev->early_line, (u32)rx_buf->dummy.dma_addr); } return 0; } static const struct v4l2_subdev_pad_ops sditf_subdev_pad_ops = { .set_fmt = sditf_get_set_fmt, .get_fmt = sditf_get_set_fmt, .get_selection = sditf_get_selection, .get_mbus_config = sditf_g_mbus_config, }; static const struct v4l2_subdev_video_ops sditf_video_ops = { .g_frame_interval = sditf_g_frame_interval, .s_stream = sditf_s_stream, .s_rx_buffer = sditf_s_rx_buffer, }; static const struct v4l2_subdev_core_ops sditf_core_ops = { .subscribe_event = sditf_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, .ioctl = sditf_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = sditf_compat_ioctl32, #endif .s_power = sditf_s_power, }; static const struct v4l2_subdev_ops sditf_subdev_ops = { .core = &sditf_core_ops, .video = &sditf_video_ops, .pad = &sditf_subdev_pad_ops, }; static int rkcif_sditf_attach_cifdev(struct sditf_priv *sditf) { struct device_node *np; struct platform_device *pdev; struct rkcif_device *cif_dev; np = of_parse_phandle(sditf->dev->of_node, "rockchip,cif", 0); if (!np || !of_device_is_available(np)) { dev_err(sditf->dev, "failed to get cif dev node\n"); return -ENODEV; } pdev = of_find_device_by_node(np); of_node_put(np); if (!pdev) { dev_err(sditf->dev, "failed to get cif dev from node\n"); return -ENODEV; } cif_dev = platform_get_drvdata(pdev); if (!cif_dev) { dev_err(sditf->dev, "failed attach cif dev\n"); return -EINVAL; } cif_dev->sditf[cif_dev->sditf_cnt] = sditf; sditf->cif_dev = cif_dev; sditf->connect_id = cif_dev->sditf_cnt; cif_dev->sditf_cnt++; return 0; } struct sensor_async_subdev { struct v4l2_async_subdev asd; struct v4l2_mbus_config mbus; int lanes; }; static int sditf_fwnode_parse(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) { struct sensor_async_subdev *s_asd = container_of(asd, struct sensor_async_subdev, asd); struct v4l2_mbus_config *config = &s_asd->mbus; if (vep->base.port != 0) { dev_err(dev, "sditf has only port 0\n"); return -EINVAL; } if (vep->bus_type == V4L2_MBUS_CSI2_DPHY || vep->bus_type == V4L2_MBUS_CSI2_CPHY) { config->type = vep->bus_type; config->bus.mipi_csi2.flags = vep->bus.mipi_csi2.flags; s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes; } else if (vep->bus_type == V4L2_MBUS_CCP2) { config->type = vep->bus_type; s_asd->lanes = vep->bus.mipi_csi1.data_lane; } else { dev_err(dev, "type is not supported\n"); return -EINVAL; } return 0; } static int rkcif_sditf_get_ctrl(struct v4l2_ctrl *ctrl) { struct sditf_priv *priv = container_of(ctrl->handler, struct sditf_priv, ctrl_handler); struct v4l2_ctrl *sensor_ctrl = NULL; switch (ctrl->id) { case V4L2_CID_PIXEL_RATE: if (priv->cif_dev->terminal_sensor.sd) { sensor_ctrl = v4l2_ctrl_find(priv->cif_dev->terminal_sensor.sd->ctrl_handler, V4L2_CID_PIXEL_RATE); if (sensor_ctrl) { ctrl->val = v4l2_ctrl_g_ctrl_int64(sensor_ctrl); __v4l2_ctrl_s_ctrl_int64(priv->pixel_rate, ctrl->val); v4l2_dbg(1, rkcif_debug, &priv->cif_dev->v4l2_dev, "%s, %s pixel rate %d\n", __func__, priv->cif_dev->terminal_sensor.sd->name, ctrl->val); return 0; } else { return -EINVAL; } } return -EINVAL; default: return -EINVAL; } } static const struct v4l2_ctrl_ops rkcif_sditf_ctrl_ops = { .g_volatile_ctrl = rkcif_sditf_get_ctrl, }; void sditf_get_default_exp(struct sditf_priv *sditf) { struct v4l2_ctrl *ctrl = NULL; struct rkcif_device *dev = sditf->cif_dev; if (dev->terminal_sensor.sd == NULL) return; ctrl = v4l2_ctrl_find(dev->terminal_sensor.sd->ctrl_handler, V4L2_CID_EXPOSURE); if (ctrl) sditf->cur_time = ctrl->default_value; else sditf->cur_time = 16; ctrl = v4l2_ctrl_find(dev->terminal_sensor.sd->ctrl_handler, V4L2_CID_ANALOGUE_GAIN); if (ctrl) sditf->cur_gain = ctrl->default_value; else sditf->cur_gain = 16; if (dev->exp_dbg) dev_info(sditf->dev, "get default time 0x%x gain 0x%x\n", sditf->cur_time, sditf->cur_gain); } static int sditf_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { struct sditf_priv *sditf = container_of(notifier, struct sditf_priv, notifier); struct media_entity *source_entity, *sink_entity; int ret = 0; sditf->sensor_sd = subdev; if (sditf->num_sensors == 1) { v4l2_err(subdev, "%s: the num of subdev is beyond %d\n", __func__, sditf->num_sensors); return -EBUSY; } if (sditf->sd.entity.pads[0].flags & MEDIA_PAD_FL_SINK) { source_entity = &subdev->entity; sink_entity = &sditf->sd.entity; ret = media_create_pad_link(source_entity, 0, sink_entity, 0, MEDIA_LNK_FL_ENABLED); if (ret) v4l2_err(&sditf->sd, "failed to create link for %s\n", sditf->sensor_sd->name); } sditf->sensor_sd = subdev; ++sditf->num_sensors; v4l2_err(subdev, "Async registered subdev\n"); return 0; } static void sditf_notifier_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { struct sditf_priv *sditf = container_of(notifier, struct sditf_priv, notifier); sditf->sensor_sd = NULL; } static const struct v4l2_async_notifier_operations sditf_notifier_ops = { .bound = sditf_notifier_bound, .unbind = sditf_notifier_unbind, }; static int sditf_subdev_notifier(struct sditf_priv *sditf) { struct v4l2_async_notifier *ntf = &sditf->notifier; int ret; v4l2_async_nf_init(ntf); ret = v4l2_async_nf_parse_fwnode_endpoints(sditf->dev, ntf, sizeof(struct sensor_async_subdev), sditf_fwnode_parse); if (ret < 0) return ret; sditf->sd.subdev_notifier = &sditf->notifier; sditf->notifier.ops = &sditf_notifier_ops; ret = v4l2_async_subdev_nf_register(&sditf->sd, &sditf->notifier); if (ret) { v4l2_err(&sditf->sd, "failed to register async notifier : %d\n", ret); v4l2_async_nf_cleanup(&sditf->notifier); return ret; } return v4l2_async_register_subdev(&sditf->sd); } static int sditf_count_port_nodes(struct device_node *root_node) { int count = 0; struct device_node *node = NULL; for_each_child_of_node(root_node, node) { if (of_node_cmp(node->name, "port") == 0) count++; count += sditf_count_port_nodes(node); } return count; } static int rkcif_subdev_media_init(struct sditf_priv *priv) { struct rkcif_device *cif_dev = priv->cif_dev; struct v4l2_ctrl_handler *handler = &priv->ctrl_handler; unsigned long flags = V4L2_CTRL_FLAG_VOLATILE; int ret; int pad_num = 0; priv->port_count = sditf_count_port_nodes(priv->dev->of_node); if (priv->port_count > 1) { priv->pads[0].flags = MEDIA_PAD_FL_SINK; priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; pad_num = 2; } else { priv->pads[0].flags = MEDIA_PAD_FL_SOURCE; pad_num = 1; } priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER; ret = media_entity_pads_init(&priv->sd.entity, pad_num, priv->pads); if (ret < 0) return ret; ret = v4l2_ctrl_handler_init(handler, 1); if (ret) return ret; priv->pixel_rate = v4l2_ctrl_new_std(handler, &rkcif_sditf_ctrl_ops, V4L2_CID_PIXEL_RATE, 0, SDITF_PIXEL_RATE_MAX, 1, SDITF_PIXEL_RATE_MAX); if (priv->pixel_rate) priv->pixel_rate->flags |= flags; priv->sd.ctrl_handler = handler; if (handler->error) { v4l2_ctrl_handler_free(handler); return handler->error; } strncpy(priv->sd.name, dev_name(cif_dev->dev), sizeof(priv->sd.name)); priv->cap_info.width = 0; priv->cap_info.height = 0; priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AIQ; priv->toisp_inf.link_mode = TOISP_NONE; priv->toisp_inf.ch_info[0].is_valid = false; priv->toisp_inf.ch_info[1].is_valid = false; priv->toisp_inf.ch_info[2].is_valid = false; priv->is_toisp_off = true; if (priv->port_count > 1) sditf_subdev_notifier(priv); atomic_set(&priv->power_cnt, 0); atomic_set(&priv->stream_cnt, 0); INIT_WORK(&priv->buffree_work.work, sditf_buffree_work); INIT_LIST_HEAD(&priv->buf_free_list); INIT_LIST_HEAD(&priv->time_head); INIT_LIST_HEAD(&priv->gain_head); INIT_LIST_HEAD(&priv->effect_exp_head); priv->frame_idx.cur_frame_idx = 0; atomic_set(&priv->frm_sync_seq, 0); mutex_init(&priv->mutex); priv->hdr_wrap_line = 0; priv->is_buf_init = false; return 0; } static int rkcif_subdev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct v4l2_subdev *sd; struct sditf_priv *priv; struct device_node *node = dev->of_node; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; sd = &priv->sd; v4l2_subdev_init(sd, &sditf_subdev_ops); sd->owner = THIS_MODULE; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(sd->name, sizeof(sd->name), "rockchip-cif-sditf"); sd->dev = dev; platform_set_drvdata(pdev, &sd->entity); ret = rkcif_sditf_attach_cifdev(priv); if (ret < 0) return ret; ret = of_property_read_u32(node, "rockchip,combine-index", &priv->combine_index); if (ret) { priv->is_combine_mode = false; priv->combine_index = 0; } else { priv->is_combine_mode = true; } ret = rkcif_subdev_media_init(priv); if (ret < 0) return ret; pm_runtime_enable(&pdev->dev); return 0; } static int rkcif_subdev_remove(struct platform_device *pdev) { struct media_entity *me = platform_get_drvdata(pdev); struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me); media_entity_cleanup(&sd->entity); pm_runtime_disable(&pdev->dev); return 0; } static const struct of_device_id rkcif_subdev_match_id[] = { { .compatible = "rockchip,rkcif-sditf", }, {} }; MODULE_DEVICE_TABLE(of, rkcif_subdev_match_id); struct platform_driver rkcif_subdev_driver = { .probe = rkcif_subdev_probe, .remove = rkcif_subdev_remove, .driver = { .name = "rkcif_sditf", .of_match_table = rkcif_subdev_match_id, }, }; EXPORT_SYMBOL(rkcif_subdev_driver); MODULE_AUTHOR("Rockchip Camera/ISP team"); MODULE_DESCRIPTION("Rockchip CIF platform driver"); MODULE_LICENSE("GPL v2");