Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hch/hfsplus
[pandora-kernel.git] / drivers / media / video / m5mols / m5mols_core.c
index 76eac26..43c68f5 100644 (file)
@@ -2,10 +2,10 @@
  * Driver for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -133,13 +133,13 @@ static u32 m5mols_swap_byte(u8 *data, u8 length)
 /**
  * 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
  */
-int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
+static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
-       u8 size = I2C_SIZE(reg);
        u8 category = I2C_CATEGORY(reg);
        u8 cmd = I2C_COMMAND(reg);
        struct i2c_msg msg[2];
@@ -149,11 +149,6 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
        if (!client->adapter)
                return -ENODEV;
 
-       if (size != 1 && size != 2 && size != 4) {
-               v4l2_err(sd, "Wrong data size\n");
-               return -EINVAL;
-       }
-
        msg[0].addr = client->addr;
        msg[0].flags = 0;
        msg[0].len = 5;
@@ -184,6 +179,52 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
        return 0;
 }
 
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
+{
+       u32 val_32;
+       int ret;
+
+       if (I2C_SIZE(reg) != 1) {
+               v4l2_err(sd, "Wrong data size\n");
+               return -EINVAL;
+       }
+
+       ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+       if (ret)
+               return ret;
+
+       *val = (u8)val_32;
+       return ret;
+}
+
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
+{
+       u32 val_32;
+       int ret;
+
+       if (I2C_SIZE(reg) != 2) {
+               v4l2_err(sd, "Wrong data size\n");
+               return -EINVAL;
+       }
+
+       ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+       if (ret)
+               return ret;
+
+       *val = (u16)val_32;
+       return ret;
+}
+
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+       if (I2C_SIZE(reg) != 4) {
+               v4l2_err(sd, "Wrong data size\n");
+               return -EINVAL;
+       }
+
+       return m5mols_read(sd, I2C_SIZE(reg), reg, val);
+}
+
 /**
  * m5mols_write - I2C command write function
  * @reg: combination of size, category and command for the I2C packet
@@ -231,13 +272,14 @@ int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
        return 0;
 }
 
-int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 mask)
 {
-       u32 busy, i;
+       u8 busy;
+       int i;
        int ret;
 
        for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
-               ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
+               ret = m5mols_read_u8(sd, I2C_REG(category, cmd, 1), &busy);
                if (ret < 0)
                        return ret;
                if ((busy & mask) == mask)
@@ -252,14 +294,14 @@ int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
  * Before writing desired interrupt value the INT_FACTOR register should
  * be read to clear pending interrupts.
  */
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
 {
        struct m5mols_info *info = to_m5mols(sd);
-       u32 mask = is_available_af(info) ? REG_INT_AF : 0;
-       u32 dummy;
+       u8 mask = is_available_af(info) ? REG_INT_AF : 0;
+       u8 dummy;
        int ret;
 
-       ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
+       ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
        if (!ret)
                ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
        return ret;
@@ -271,7 +313,7 @@ int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
  * 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, u32 mode)
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
 {
        int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
 
@@ -286,16 +328,16 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
  * can be guaranteed only when the sensor is operating in mode which which
  * a command belongs to.
  */
-int m5mols_mode(struct m5mols_info *info, u32 mode)
+int m5mols_mode(struct m5mols_info *info, u8 mode)
 {
        struct v4l2_subdev *sd = &info->sd;
        int ret = -EINVAL;
-       u32 reg;
+       u8 reg;
 
        if (mode < REG_PARAMETER && mode > REG_CAPTURE)
                return ret;
 
-       ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
+       ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
        if ((!ret && reg == mode) || ret)
                return ret;
 
@@ -344,41 +386,37 @@ int m5mols_mode(struct m5mols_info *info, u32 mode)
 static int m5mols_get_version(struct v4l2_subdev *sd)
 {
        struct m5mols_info *info = to_m5mols(sd);
-       union {
-               struct m5mols_version ver;
-               u8 bytes[VERSION_SIZE];
-       } version;
-       u32 *value;
-       u8 cmd = CAT0_VER_CUSTOMER;
+       struct m5mols_version *ver = &info->ver;
+       u8 *str = ver->str;
+       int i;
        int ret;
 
-       do {
-               value = (u32 *)&version.bytes[cmd];
-               ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
-               if (ret)
-                       return ret;
-       } while (cmd++ != CAT0_VER_AWB);
+       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;
 
-       do {
-               value = (u32 *)&version.bytes[cmd];
-               ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
+       for (i = 0; i < VERSION_STRING_SIZE; i++) {
+               ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
                if (ret)
                        return ret;
-               if (cmd >= VERSION_SIZE - 1)
-                       return -EINVAL;
-       } while (version.bytes[cmd++]);
-
-       value = (u32 *)&version.bytes[cmd];
-       ret = m5mols_read(sd, AF_VERSION, value);
-       if (ret)
-               return ret;
+       }
 
-       /* store version information swapped for being readable */
-       info->ver       = version.ver;
-       info->ver.fw    = be16_to_cpu(info->ver.fw);
-       info->ver.hw    = be16_to_cpu(info->ver.hw);
-       info->ver.param = be16_to_cpu(info->ver.param);
-       info->ver.awb   = be16_to_cpu(info->ver.awb);
+       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) ?
@@ -722,7 +760,7 @@ static int m5mols_init_controls(struct m5mols_info *info)
        int ret;
 
        /* Determine value's range & step of controls for various FW version */
-       ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
+       ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure);
        if (!ret)
                step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
        if (ret)
@@ -842,18 +880,18 @@ 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;
-       u32 reg;
+       u8 reg;
        int ret;
 
        if (!is_powered(info) ||
-                       m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
+                       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(sd, AF_STATUS, &reg);
+               ret = m5mols_read_u8(sd, AF_STATUS, &reg);
                v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
                         reg == REG_AF_FAIL ? "Failed" :
                         reg == REG_AF_SUCCESS ? "Success" :