static void m5mols_irq_work(struct work_struct *work) { struct m5mols_info *info = container_of(work, struct m5mols_info, work_irq); struct v4l2_subdev *sd = &info->sd; u8 reg; int ret; if (!is_powered(info) || m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &info->interrupt)) return; switch (info->interrupt & REG_INT_MASK) { case REG_INT_AF: if (!is_available_af(info)) break; ret = m5mols_read_u8(sd, AF_STATUS, ®); v4l2_dbg(2, m5mols_debug, sd, "AF %s\n", reg == REG_AF_FAIL ? "Failed" : reg == REG_AF_SUCCESS ? "Success" : reg == REG_AF_IDLE ? "Idle" : "Busy"); break; case REG_INT_CAPTURE: if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags)) wake_up_interruptible(&info->irq_waitq); v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n"); break; default: v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg); break; }; }
/** * 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; }
/** * 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_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 mask) { u8 busy; int i; int ret; for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) { ret = m5mols_read_u8(sd, I2C_REG(category, cmd, 1), &busy); if (ret < 0) return ret; if ((busy & mask) == mask) return 0; } return -EBUSY; }
/** * m5mols_set_mode - set the M-5MOLS controller mode * @mode: the required operation mode * * The commands of M-5MOLS are grouped into specific modes. Each functionality * can be guaranteed only when the sensor is operating in mode which a command * belongs to. */ int m5mols_set_mode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; int ret = -EINVAL; u8 reg; if (mode < REG_PARAMETER || mode > REG_CAPTURE) return ret; ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); if (ret || reg == mode) return ret; switch (reg) { case REG_PARAMETER: ret = m5mols_reg_mode(sd, REG_MONITOR); if (mode == REG_MONITOR) break; if (!ret) ret = m5mols_reg_mode(sd, REG_CAPTURE); break; case REG_MONITOR: if (mode == REG_PARAMETER) { ret = m5mols_reg_mode(sd, REG_PARAMETER); break; } ret = m5mols_reg_mode(sd, REG_CAPTURE); break; case REG_CAPTURE: ret = m5mols_reg_mode(sd, REG_MONITOR); if (mode == REG_MONITOR) break; if (!ret) ret = m5mols_reg_mode(sd, REG_PARAMETER); break; default: v4l2_warn(sd, "Wrong mode: %d\n", mode); } if (!ret) info->mode = mode; return ret; }
/** * m5mols_busy_wait - Busy waiting with I2C register polling * @reg: the I2C_REG() address of an 8-bit status register to check * @value: expected status register value * @mask: bit mask for the read status register value * @timeout: timeout in miliseconds, or -1 for default timeout * * The @reg register value is ORed with @mask before comparing with @value. * * Return: 0 if the requested condition became true within less than * @timeout ms, or else negative errno. */ int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, int timeout) { int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; unsigned long end = jiffies + msecs_to_jiffies(ms); u8 status; do { int ret = m5mols_read_u8(sd, reg, &status); if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) return ret; if (!ret && (status & mask & 0xff) == (value & 0xff)) return 0; usleep_range(100, 250); } while (ms > 0 && time_is_after_jiffies(end)); return -EBUSY; }