static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); struct v4l2_mbus_framefmt *format = &fmt->format; struct v4l2_mbus_framefmt *sfmt; enum m5mols_restype type; u32 resolution = 0; int ret; ret = __find_resolution(sd, format, &type, &resolution); if (ret < 0) return ret; sfmt = __find_format(info, fh, fmt->which, type); if (!sfmt) return 0; format->code = m5mols_default_ffmt[type].code; format->colorspace = V4L2_COLORSPACE_JPEG; format->field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { *sfmt = *format; info->resolution = resolution; info->res_type = type; } return 0; }
static int m5mols_log_status(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); v4l2_ctrl_handler_log_status(&info->handle, sd->name); return 0; }
static irqreturn_t m5mols_irq_handler(int irq, void *data) { struct m5mols_info *info = to_m5mols(data); atomic_set(&info->irq_done, 1); wake_up_interruptible(&info->irq_waitq); return IRQ_HANDLED; }
static irqreturn_t m5mols_irq_handler(int irq, void *data) { struct v4l2_subdev *sd = data; struct m5mols_info *info = to_m5mols(sd); schedule_work(&info->work_irq); return IRQ_HANDLED; }
/** * 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; }
int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) { struct m5mols_info *info = to_m5mols(sd); int ret = wait_event_interruptible_timeout(info->irq_waitq, atomic_add_unless(&info->irq_done, -1, 0), msecs_to_jiffies(timeout)); if (ret <= 0) return ret ? ret : -ETIMEDOUT; return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); }
static void m5mols_show_version(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; struct m5mols_info *info = to_m5mols(sd); dev_info(dev, "customer code\t0x%02x\n", info->ver.ctm_code); dev_info(dev, "project code\t0x%02x\n", info->ver.pj_code); dev_info(dev, "firmware version\t0x%04x\n", info->ver.fw); dev_info(dev, "hardware version\t0x%04x\n", info->ver.hw); dev_info(dev, "parameter version\t0x%04x\n", info->ver.parm); dev_info(dev, "AWB version\t0x%04x\n", info->ver.awb); }
static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); struct v4l2_mbus_framefmt *format; format = __find_format(info, fh, fmt->which, info->res_type); if (!format) return -EINVAL; fmt->format = *format; return 0; }
static int __devexit m5mols_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct m5mols_info *info = to_m5mols(sd); v4l2_device_unregister_subdev(sd); free_irq(info->pdata->irq, sd); regulator_bulk_free(ARRAY_SIZE(supplies), supplies); gpio_free(info->pdata->gpio_reset); media_entity_cleanup(&sd->entity); kfree(info); return 0; }
static int m5mols_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct m5mols_info *info = to_m5mols(sd); struct v4l2_captureparm *cp = &parms->parm.capture; if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; cp->capability = V4L2_CAP_TIMEPERFRAME; cp->timeperframe = info->tpf; return 0; }
/** * m5mols_get_version - retrieve full revisions information of M-5MOLS * * The version information includes revisions of hardware and firmware, * AutoFocus alghorithm version and the version string. */ static int m5mols_get_version(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); struct m5mols_version *ver = &info->ver; u8 *str = ver->str; int i; int ret; ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); if (!ret) ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); if (!ret) ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); if (!ret) ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); if (!ret) ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); if (!ret) ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); if (!ret) ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); if (ret) return ret; for (i = 0; i < VERSION_STRING_SIZE; i++) { ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); if (ret) return ret; } ver->fw = be16_to_cpu(ver->fw); ver->hw = be16_to_cpu(ver->hw); ver->param = be16_to_cpu(ver->param); ver->awb = be16_to_cpu(ver->awb); v4l2_info(sd, "Manufacturer\t[%s]\n", is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? "Samsung Electro-Machanics" : is_manufacturer(info, REG_SAMSUNG_OPTICS) ? "Samsung Fiber-Optics" : is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? "Samsung Techwin" : "None"); v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", info->ver.customer, info->ver.project); if (!is_available_af(info)) v4l2_info(sd, "No support Auto Focus on this firmware\n"); return ret; }
static int m5mols_into_capture(struct v4l2_subdev *sd, int res_size) { struct m5mols_info *info = to_m5mols(sd); u32 reg; int ret, timeout = 1; u32 temp = 0; info->captured = false; /* * The sequence of preparing Capture mode. * 1. Clear Interrupt bit (for dummy) * 2. Enable Capture bit at Interrupt * 3. Lock AE/AWB * 4. Enter Still Capture mode */ ret = m5mols_set_mode(sd, MODE_MONITOR); if (!ret) /* FIXME: setting capture exposure at the middle of a amount. */ ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_CAP, 0x90); if (!ret) ret = m5mols_set_ae_lock(info, true); if (!ret) ret = m5mols_set_awb_lock(info, true); if (!ret) ret = i2c_r8_system(sd, CAT0_INT_FACTOR, ®); if (!ret) ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 1 << INT_BIT_CAPTURE); if (!ret) ret = m5mols_set_mode(sd, MODE_CAPTURE); if (!ret) timeout = wait_event_interruptible_timeout(info->cap_wait, info->captured, msecs_to_jiffies(2000)); /* disable all interrupt & clear interrupt */ ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 0x0); if (!ret) ret = i2c_r8_system(sd, CAT0_INT_FACTOR, ®); if (ret) return -EPERM; /* If all timeout exhausted, return error. */ if (!timeout) return -ETIMEDOUT; ret = i2c_r32_capt_ctrl(sd, CATC_CAP_IMAGE_SIZE, &temp); info->captured = false; return ret; }
static int m5mols_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *ffmt) { struct m5mols_info *info = to_m5mols(sd); enum m5mols_res_type res_type; res_type = to_res_type(sd, ffmt->code); if (res_type == M5MOLS_RES_MAX) return -EINVAL; *ffmt = info->fmt[res_type]; info->code = ffmt->code; return 0; }
static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); int ret; info->mode_save = info->mode; ret = m5mols_mode(info, REG_PARAMETER); if (!ret) ret = m5mols_set_ctrl(ctrl); if (!ret) ret = m5mols_mode(info, info->mode_save); return ret; }
static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); int ret = 0; switch (ctrl->id) { case V4L2_CID_CAM_JPEG_ENCODEDSIZE: ctrl->cur.val = info->cap.total; break; default: ret = -EINVAL; break; } return ret; }
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_setup_default(sd); } else { ret = m5mols_sensor_power(info, false); } return ret; }
static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) { struct m5mols_info *info = to_m5mols(sd); if (enable) { int ret = -EINVAL; if (is_code(info->code, M5MOLS_RESTYPE_MONITOR)) ret = m5mols_start_monitor(info); if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE)) ret = m5mols_start_capture(info); return ret; } return m5mols_mode(info, REG_PARAMETER); }
/** * m5mols_read - I2C read function * @reg: combination of size, category and command for the I2C packet * @size: desired size of I2C packet * @val: read value * * Returns 0 on success, or else negative errno. */ static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct m5mols_info *info = to_m5mols(sd); u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; u8 category = I2C_CATEGORY(reg); u8 cmd = I2C_COMMAND(reg); struct i2c_msg msg[2]; u8 wbuf[5]; int ret; if (!client->adapter) return -ENODEV; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 5; msg[0].buf = wbuf; wbuf[0] = 5; wbuf[1] = M5MOLS_BYTE_READ; wbuf[2] = category; wbuf[3] = cmd; wbuf[4] = size; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = size + 1; msg[1].buf = rbuf; /* minimum stabilization time */ usleep_range(200, 200); ret = i2c_transfer(client->adapter, msg, 2); if (ret == 2) { *val = m5mols_swap_byte(&rbuf[1], size); return 0; } if (info->isp_ready) v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", size, category, cmd, ret); return ret < 0 ? ret : -EIO; }
static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { struct m5mols_info *info = to_m5mols(sd); struct v4l2_mbus_framefmt *format; int ret = 0; mutex_lock(&info->lock); format = __find_format(info, fh, fmt->which, info->res_type); if (format) fmt->format = *format; else ret = -EINVAL; mutex_unlock(&info->lock); return ret; }
/** * m5mols_write - I2C command write function * @reg: combination of size, category and command for the I2C packet * @val: value to write * * Returns 0 on success, or else negative errno. */ int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct m5mols_info *info = to_m5mols(sd); u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; u8 category = I2C_CATEGORY(reg); u8 cmd = I2C_COMMAND(reg); u8 size = I2C_SIZE(reg); u32 *buf = (u32 *)&wbuf[4]; struct i2c_msg msg[1]; int ret; if (!client->adapter) return -ENODEV; if (size != 1 && size != 2 && size != 4) { v4l2_err(sd, "Wrong data size\n"); return -EINVAL; } msg->addr = client->addr; msg->flags = 0; msg->len = (u16)size + 4; msg->buf = wbuf; wbuf[0] = size + 4; wbuf[1] = M5MOLS_BYTE_WRITE; wbuf[2] = category; wbuf[3] = cmd; *buf = m5mols_swap_byte((u8 *)&val, size); usleep_range(200, 200); ret = i2c_transfer(client->adapter, msg, 1); if (ret == 1) return 0; if (info->isp_ready) v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", category, cmd, ret); return ret < 0 ? ret : -EIO; }
/** * 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; }
static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { struct m5mols_info *info = to_m5mols(sd); struct v4l2_mbus_framefmt *mf = &info->ffmt[M5MOLS_RESTYPE_CAPTURE]; if (pad != 0 || fd == NULL) return -EINVAL; fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; fd->num_entries = 1; fd->entry[0].length = clamp_t(u32, fd->entry[0].length, mf->width * mf->height, M5MOLS_MAIN_JPEG_SIZE_MAX); mutex_lock(&info->lock); info->cap.buf_size = fd->entry[0].length; mutex_unlock(&info->lock); return 0; }
static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) { struct m5mols_info *info = to_m5mols(sd); if (enable) { if (info->code == to_code(M5MOLS_RES_MON)) { v4l2_info(sd, "%s : monitor mode\n", __func__); return m5mols_start_monitor(sd); } if (info->code == to_code(M5MOLS_RES_CAPTURE)) { v4l2_info(sd, "%s : capture mode\n", __func__); return m5mols_start_capture(sd); } return -EINVAL; } else { if (is_streaming(sd)) return m5mols_set_mode(sd, MODE_PARMSET); return -EINVAL; } }
/* * get_res_preset - find out M5MOLS register value from requested resolution. * * @width: requested width * @height: requested height * @type: requested type of each modes. It supports only monitor mode now. */ static int get_res_preset(struct v4l2_subdev *sd, u16 width, u16 height, enum m5mols_res_type type) { struct m5mols_info *info = to_m5mols(sd); int i; for (i = 0; i < ARRAY_SIZE(m5mols_resolutions); i++) { if ((m5mols_resolutions[i].type == type) && (m5mols_resolutions[i].width == width) && (m5mols_resolutions[i].height == height)) break; } if (i >= ARRAY_SIZE(m5mols_resolutions)) { v4l2msg("no matching resolution\n"); return -EINVAL; } return m5mols_resolutions[i].value; }
static int m5mols_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd) { struct m5mols_info *info = to_m5mols(sd); if (pad != 0 || fd == NULL) return -EINVAL; mutex_lock(&info->lock); /* * .get_frame_desc is only used for compressed formats, * thus we always return the capture frame parameters here. */ fd->entry[0].length = info->cap.buf_size; fd->entry[0].pixelcode = info->ffmt[M5MOLS_RESTYPE_CAPTURE].code; mutex_unlock(&info->lock); fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; fd->num_entries = 1; return 0; }
static int m5mols_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *ffmt) { struct m5mols_info *info = to_m5mols(sd); enum m5mols_res_type res_type; int size; int ret = -EINVAL; res_type = to_res_type(sd, ffmt->code); if (res_type == M5MOLS_RES_MAX) return -EINVAL; /* If user set portrait for preview, it is substitued width width height * unless get_res_preset will fail that M5MOLS did not support * reverse WVGA */ if (ffmt->width < ffmt->height) { int temp; temp = ffmt->width; ffmt->width = ffmt->height; ffmt->height = temp; } size = get_res_preset(sd, ffmt->width, ffmt->height, res_type); if (size < 0) return -EINVAL; if (ffmt->code == m5mols_formats[M5MOLS_RES_MON].code) ret = m5mols_into_monitor(sd, size); else ret = m5mols_into_capture(sd, 0); info->fmt[res_type] = default_fmt[res_type]; info->fmt[res_type].width = ffmt->width; info->fmt[res_type].height = ffmt->height; *ffmt = info->fmt[res_type]; info->code = ffmt->code; return ret; }
static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) { struct m5mols_info *info = to_m5mols(sd); u32 code; int ret; mutex_lock(&info->lock); code = info->ffmt[info->res_type].code; if (enable) { if (is_code(code, M5MOLS_RESTYPE_MONITOR)) ret = m5mols_start_monitor(info); if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) ret = m5mols_start_capture(info); else ret = -EINVAL; } else { ret = m5mols_set_mode(info, REG_PARAMETER); } mutex_unlock(&info->lock); return ret; }
/* * get_version - get M5MOLS sensor versions. */ static int get_version(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); union { struct m5mols_version ver; u8 bytes[10]; } value; int ret, i; for (i = CAT0_CUSTOMER_CODE; i <= CAT0_VERSION_AWB_L; i++) { ret = i2c_r8_system(sd, i, (u32 *)&value.bytes[i]); if (ret) return ret; } info->ver = value.ver; info->ver.fw = be16_to_cpu(info->ver.fw); info->ver.hw = be16_to_cpu(info->ver.hw); info->ver.parm = be16_to_cpu(info->ver.parm); info->ver.awb = be16_to_cpu(info->ver.awb); return ret; }
static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); int ispstate = info->mode; int ret; /* * If needed, defer restoring the controls until * the device is fully initialized. */ if (!info->isp_ready) { info->ctrl_sync = 0; return 0; } ret = m5mols_mode(info, REG_PARAMETER); if (ret < 0) return ret; ret = m5mols_set_ctrl(ctrl); if (ret < 0) return ret; return m5mols_mode(info, ispstate); }
/* TODO: not verified. */ static int m5mols_start_capture(struct v4l2_subdev *sd) { struct m5mols_info *info = to_m5mols(sd); u32 reg, size; int ret, timeout; u8 reg_capt_fmt[] = { 0x10, /* JPEG with header + Thumbnail JPEG(YUV422@QVGA) */ }; /* YUV422, JPEG(422), JPEG(420) */ info->captured = false; size = get_res_preset(sd, info->fmt[M5MOLS_RES_CAPTURE].width, info->fmt[M5MOLS_RES_CAPTURE].height, M5MOLS_RES_CAPTURE); if (size < 0) return -EINVAL; ret = 0; /* * The sequence of Starting Capture mode. * 1. Select capture Single or Multi * 2. Select format (YUV422, JPEG(YUV420, YUV422)) * 3. Set image size preset of Capture * 4. Read Interrupt bit (for dummy) * 5. Enable Capture bit at Interrupt * 6. Start Capture * 7. Check interrupt and register value * 8. Get Image & Thumb size */ ret = i2c_w8_capt_ctrl(sd, CATC_CAP_SEL_FRAME, true); /* single capture */ if (!ret) ret = i2c_w8_capt_parm(sd, CATB_YUVOUT_MAIN, reg_capt_fmt[0]); if (!ret) ret = i2c_w8_capt_parm(sd, CATB_MAIN_IMAGE_SIZE, size); if (!ret) ret = i2c_r8_system(sd, CAT0_INT_FACTOR, ®); if (!ret) ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 1 << INT_BIT_CAPTURE); if (!ret) ret = i2c_w8_capt_ctrl(sd, CATC_CAP_START, true); if (!ret) { timeout = wait_event_interruptible_timeout(info->cap_wait, info->captured, msecs_to_jiffies(2000)); if (info->captured) { ret = m5mols_get_info_capture(sd); if (!ret) v4l2_subdev_notify(sd, info->cap.total, NULL); else return ret; } /* disable all interrupt & clear interrupt */ ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 0x0); if (!ret) ret = i2c_r8_system(sd, CAT0_INT_FACTOR, ®); if (ret) return -EPERM; /* If all timeout exhausted, return error. */ if (!timeout) return -ETIMEDOUT; info->captured = false; ret = 0; } /* TODO: complete capture. */ return ret; }