/** * m5mols_s_power - Main sensor power control function * * To prevent breaking the lens when the sensor is powered off the Soft-Landing * algorithm is called where available. The Soft-Landing algorithm availability * dependends on the firmware provider. */ static int m5mols_s_power(struct v4l2_subdev *sd, int on) { struct m5mols_info *info = to_m5mols(sd); int ret; if (on) { ret = m5mols_sensor_power(info, true); if (!ret) ret = m5mols_fw_start(sd); return ret; } if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); if (!ret) ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); if (!ret) ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE, 0xff, -1); if (ret < 0) v4l2_warn(sd, "Soft landing lens failed\n"); } ret = m5mols_sensor_power(info, false); info->ctrl_sync = 0; return ret; }
/** * m5mols_fw_start - M-5MOLS internal ARM controller initialization * * Execute the M-5MOLS internal ARM controller initialization sequence. * This function should be called after the supply voltage has been * applied and before any requests to the device are made. */ static int m5mols_fw_start(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); int ret; atomic_set(&info->irq_done, 0); /* Wait until I2C slave is initialized in Flash Writer mode */ ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); if (!ret) ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); if (!ret) ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); if (ret < 0) return ret; info->isp_ready = 1; ret = m5mols_get_version(sd); if (!ret) ret = m5mols_update_fw(sd, m5mols_sensor_power); if (ret) return ret; v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); if (!ret) ret = m5mols_enable_interrupt(sd, REG_INT_AF | REG_INT_CAPTURE); return ret; }
static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) { bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; int ret = 0; if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? REG_AE_LOCK : REG_AE_UNLOCK); if (ret) return ret; } if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) && info->auto_wb->val) { bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); if (ret) return ret; } if (!info->ver.af || !af_lock) return ret; if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); return ret; }
/* Execute the lens soft-landing algorithm */ static int m5mols_auto_focus_stop(struct m5mols_info *info) { int ret; ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); if (!ret) ret = m5mols_write(&info->sd, AF_MODE, REG_AF_POWEROFF); if (!ret) ret = m5mols_busy_wait(&info->sd, SYSTEM_STATUS, REG_AF_IDLE, 0xff, -1); return ret; }
int m5mols_start_capture(struct m5mols_info *info) { unsigned int framesize = info->cap.buf_size - M5MOLS_JPEG_TAGS_SIZE; struct v4l2_subdev *sd = &info->sd; int ret; /* * Synchronize the controls, set the capture frame resolution and color * format. The frame capture is initiated during switching from Monitor * to Capture mode. */ ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); if (!ret) ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); if (!ret) ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); if (!ret) ret = m5mols_write(sd, CAPP_JPEG_SIZE_MAX, framesize); if (!ret) ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) /* Wait until a frame is captured to ISP internal memory */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); if (ret) return ret; /* * Initiate the captured data transfer to a MIPI-CSI receiver. */ ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); if (!ret) ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); if (!ret) { bool captured = false; unsigned int size; /* Wait for the capture completion interrupt */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); if (!ret) { captured = true; ret = m5mols_capture_info(info); } size = captured ? info->cap.main : 0; v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", __func__, size, info->cap.thumb); v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); } return ret; }
/** * m5mols_start_monitor - Start the monitor mode * * Before applying the controls setup the resolution and frame rate * in PARAMETER mode, and then switch over to MONITOR mode. */ static int m5mols_start_monitor(struct m5mols_info *info) { struct v4l2_subdev *sd = &info->sd; int ret; ret = m5mols_set_mode(info, REG_PARAMETER); if (!ret) ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); if (!ret) ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); if (!ret) ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); return ret; }
/** * m5mols_reg_mode - Write the mode and check busy status * * It always accompanies a little delay changing the M-5MOLS mode, so it is * needed checking current busy status to guarantee right mode. */ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) { int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); if (ret < 0) return ret; return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, M5MOLS_MODE_CHANGE_TIMEOUT); }
/** * m5mols_s_power - Main sensor power control function * * To prevent breaking the lens when the sensor is powered off the Soft-Landing * algorithm is called where available. The Soft-Landing algorithm availability * dependends on the firmware provider. */ static int m5mols_s_power(struct v4l2_subdev *sd, int on) { struct m5mols_info *info = to_m5mols(sd); int ret; if (on) { ret = m5mols_sensor_power(info, true); if (!ret) ret = m5mols_sensor_armboot(sd); if (!ret) ret = m5mols_init_controls(info); if (ret) return ret; info->ffmt[M5MOLS_RESTYPE_MONITOR] = m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR]; info->ffmt[M5MOLS_RESTYPE_CAPTURE] = m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE]; return ret; } if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { ret = m5mols_mode(info, REG_MONITOR); if (!ret) ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); if (!ret) ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); if (!ret) ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS, REG_AF_IDLE); if (!ret) v4l2_info(sd, "Success soft-landing lens\n"); } ret = m5mols_sensor_power(info, false); if (!ret) { v4l2_ctrl_handler_free(&info->handle); info->ctrl_sync = false; } return ret; }
/** * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts * * Before writing desired interrupt value the INT_FACTOR register should * be read to clear pending interrupts. */ int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) { struct m5mols_info *info = to_m5mols(sd); u8 mask = is_available_af(info) ? REG_INT_AF : 0; u8 dummy; int ret; ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); if (!ret) ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); return ret; }
static int m5mols_set_exposure(struct m5mols_info *info, int exposure) { struct v4l2_subdev *sd = &info->sd; int ret = 0; if (exposure == V4L2_EXPOSURE_AUTO) { /* Unlock auto exposure */ info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; m5mols_3a_lock(info, info->lock_3a); ret = m5mols_set_metering_mode(info, info->metering->val); if (ret < 0) return ret; v4l2_dbg(1, m5mols_debug, sd, "%s: exposure bias: %#x, metering: %#x\n", __func__, info->exposure_bias->val, info->metering->val); return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); } if (exposure == V4L2_EXPOSURE_MANUAL) { ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); if (ret == 0) ret = m5mols_write(sd, AE_MAN_GAIN_MON, info->exposure->val); if (ret == 0) ret = m5mols_write(sd, AE_MAN_GAIN_CAP, info->exposure->val); v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", __func__, info->exposure->val); } return ret; }
/** * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core. * * Booting internal ARM core makes the M-5MOLS is ready for getting commands * with I2C. It's the first thing to be done after it powered up. It must wait * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting. */ static int m5mols_sensor_armboot(struct v4l2_subdev *sd) { int ret; ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); if (ret < 0) return ret; msleep(520); ret = m5mols_get_version(sd); if (!ret) ret = m5mols_update_fw(sd, m5mols_sensor_power); if (ret) return ret; v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); if (!ret) ret = m5mols_enable_interrupt(sd, REG_INT_AF); return ret; }
static int m5mols_capture_error_handler(struct m5mols_info *info, int timeout) { int ret; /* Disable all interrupts and clear relevant interrupt staus bits */ ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE, info->interrupt & ~(REG_INT_CAPTURE)); if (ret) return ret; if (timeout == 0) return -ETIMEDOUT; return 0; }
static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) { unsigned int metering; switch (mode) { case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: metering = REG_AE_CENTER; break; case V4L2_EXPOSURE_METERING_SPOT: metering = REG_AE_SPOT; break; default: metering = REG_AE_ALL; break; } return m5mols_write(&info->sd, AE_MODE, metering); }
int m5mols_start_capture(struct m5mols_info *info) { struct v4l2_subdev *sd = &info->sd; u8 resolution = info->resolution; int timeout; int ret; /* * Preparing capture. Setting control & interrupt before entering * capture mode * * 1) change to MONITOR mode for operating control & interrupt * 2) set controls (considering v4l2_control value & lock 3A) * 3) set interrupt * 4) change to CAPTURE mode */ ret = m5mols_mode(info, REG_MONITOR); if (!ret) ret = m5mols_sync_controls(info); if (!ret) ret = m5mols_lock_3a(info, true); if (!ret) ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); if (!ret) ret = m5mols_mode(info, REG_CAPTURE); if (!ret) { /* Wait for capture interrupt, after changing capture mode */ timeout = wait_event_interruptible_timeout(info->irq_waitq, test_bit(ST_CAPT_IRQ, &info->flags), msecs_to_jiffies(2000)); if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) ret = m5mols_capture_error_handler(info, timeout); } if (!ret) ret = m5mols_lock_3a(info, false); if (ret) return ret; /* * Starting capture. Setting capture frame count and resolution and * the format(available format: JPEG, Bayer RAW, YUV). * * 1) select single or multi(enable to 25), format, size * 2) set interrupt * 3) start capture(for main image, now) * 4) get information * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device) */ ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); if (!ret) ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); if (!ret) ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); if (!ret) ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE); if (!ret) ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); if (!ret) { /* Wait for the capture completion interrupt */ timeout = wait_event_interruptible_timeout(info->irq_waitq, test_bit(ST_CAPT_IRQ, &info->flags), msecs_to_jiffies(2000)); if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) { ret = m5mols_capture_info(info); if (!ret) v4l2_subdev_notify(sd, 0, &info->cap.total); } } return m5mols_capture_error_handler(info, timeout); }
/** * m5mols_reg_mode - Write the mode and check busy status * * It always accompanies a little delay changing the M-5MOLS mode, so it is * needed checking current busy status to guarantee right mode. */ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) { int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode); }
/** * m5mols_do_scenemode() - Change current scenemode * @mode: Desired mode of the scenemode * * WARNING: The execution order is important. Do not change the order. */ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; int ret; if (mode > REG_SCENE_CANDLE) return -EINVAL; ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); if (!ret) ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); if (!ret) ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); if (!ret) ret = m5mols_write(sd, AE_MODE, scenemode.metering); if (!ret) ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); if (!ret) ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); if (!ret) ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); if (!ret) ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); if (!ret) ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); if (!ret) ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); if (!ret) ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); if (!ret && is_available_af(info)) ret = m5mols_write(sd, AF_MODE, scenemode.af_range); if (!ret && is_available_af(info)) ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); if (!ret) ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); if (!ret) ret = m5mols_write(sd, AE_ISO, scenemode.iso); if (!ret) ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); if (!ret) ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); if (!ret) ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); if (!ret) ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); if (!ret) ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); if (!ret) ret = m5mols_set_mode(info, REG_MONITOR); return ret; }