summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Lilliebjerg <elilliebjerg@nvidia.com>2011-11-22 03:53:54 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:50:24 -0800
commit17973b45b17e336269a144e245acdcb30a05c06c (patch)
tree75be6a49112e89c1cd77a74b61e94c10623a0027
parent86079a77cf695abe752bcfbf166f40a36deeb264 (diff)
media: video: tegra: sh532u driver
- Added multi-instance support with sync capability - Added GLOS power scheme - Standardized the IOCTL API Bug 865305 Change-Id: I37bcf306477d30589f3985d9370c59450842d340 Signed-off-by: Erik Lilliebjerg <elilliebjerg@nvidia.com> Reviewed-on: http://git-master/r/66116 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Tested-by: Gerrit_Virtual_Submit Rebase-Id: Rb4fb40adf45cfd5359ebc4da6efc0ff2b0beeb9e
-rw-r--r--drivers/media/video/tegra/sh532u.c1804
-rw-r--r--include/media/nvc_focus.h48
-rw-r--r--include/media/sh532u.h116
3 files changed, 1393 insertions, 575 deletions
diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c
index ec760e2ec6dc..5781621e6875 100644
--- a/drivers/media/video/tegra/sh532u.c
+++ b/drivers/media/video/tegra/sh532u.c
@@ -18,314 +18,751 @@
* 02111-1307, USA
*/
-#include <linux/delay.h>
+/* Implementation
+ * --------------
+ * The board level details about the device need to be provided in the board
+ * file with the sh532u_platform_data structure.
+ * Standard among NVC kernel drivers in this structure is:
+ * .cfg = Use the NVC_CFG_ defines that are in nvc.h.
+ * Descriptions of the configuration options are with the defines.
+ * This value is typically 0.
+ * .num = The number of the instance of the device. This should start at 1 and
+ * and increment for each device on the board. This number will be
+ * appended to the MISC driver name, Example: /dev/focuser.1
+ * If not used or 0, then nothing is appended to the name.
+ * .sync = If there is a need to synchronize two devices, then this value is
+ * the number of the device instance (.num above) this device is to
+ * sync to. For example:
+ * Device 1 platform entries =
+ * .num = 1,
+ * .sync = 2,
+ * Device 2 platfrom entries =
+ * .num = 2,
+ * .sync = 1,
+ * The above example sync's device 1 and 2.
+ * This is typically used for stereo applications.
+ * .dev_name = The MISC driver name the device registers as. If not used,
+ * then the part number of the device is used for the driver name.
+ * If using the NVC user driver then use the name found in this
+ * driver under _default_pdata.
+ *
+ * The following is specific to NVC kernel focus drivers:
+ * .nvc = Pointer to the nvc_focus_nvc structure. This structure needs to
+ * be defined and populated if overriding the driver defaults.
+ * .cap = Pointer to the nvc_focus_cap structure. This structure needs to
+ * be defined and populated if overriding the driver defaults.
+ *
+ * The following is specific to only this NVC kernel focus driver:
+ * .info = Pointer to the sh532u_pdata_info structure. This structure does
+ * not need to be defined and populated unless overriding ROM data.
+.* .i2c_addr_rom = The I2C address of the onboard ROM.
+ * .gpio_reset = The GPIO connected to the devices reset. If not used then
+ * leave blank.
+ * .gpio_en = Due to a Linux limitation, a GPIO is defined to "enable" the
+ * device. This workaround is for when the device's power GPIO's
+ * are behind an I2C expander. The Linux limitation doesn't allow
+ * the I2C GPIO expander to be ready for use when this device is
+ * probed. When this problem is solved, this driver needs to
+ * remove the gpio_en WAR.
+ *
+ * Power Requirements
+ * The board power file must contain the following labels for the power
+ * regulator(s) of this device:
+ * "vdd" = the power regulator for the device's power.
+ * "vdd_i2c" = the power regulator for the I2C power.
+ *
+ * The above values should be all that is needed to use the device with this
+ * driver. Modifications of this driver should not be needed.
+ */
+
+
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/uaccess.h>
-#include <media/ov5650.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/gpio.h>
+#include <media/nvc.h>
#include <media/sh532u.h>
-#include <media/tegra_camera.h>
-#include <asm/traps.h>
+#define SH532U_ID 0xF0
+/* defaults if no ROM data */
+#define SH532U_HYPERFOCAL_RATIO 1836 /* 41.2f/224.4f Ratio source: SEMCO */
+/* _HYPERFOCAL_RATIO is multiplied and _HYPERFOCAL_DIV divides for float */
+#define SH532U_HYPERFOCAL_DIV 10000
+#define SH532U_FOCAL_LENGTH 0x408D70A4
+#define SH532U_FNUMBER 0x40333333
+#define SH532U_MAX_APERATURE 0x3FCA0EA1
+/* SH532U_CAPS_VER = 0: invalid value */
+/* SH532U_CAPS_VER = 1: added NVC_PARAM_STS */
+/* SH532U_CAPS_VER = 2: expanded nvc_focus_cap */
+#define SH532U_CAPS_VER 2
+#define SH532U_ACTUATOR_RANGE 1000
+#define SH532U_SETTLETIME 30
+#define SH532U_FOCUS_MACRO 900
+#define SH532U_FOCUS_HYPER 250
+#define SH532U_FOCUS_INFINITY 100
+#define SH532U_TIMEOUT_MS 200
-#define POS_LOW (0xA000)
-#define POS_HIGH (0x6000)
-#define SETTLETIME_MS (7)
-#define FOCAL_LENGTH 0x408d70a4 /* (4.42f) */
-#define FNUMBER 0x40333333 /* (2.8f) */
-struct sh532u_sensor {
+struct sh532u_info {
+ atomic_t in_use;
struct i2c_client *i2c_client;
- struct sh532u_config config;
- struct sh532u_platform_data pdata;
+ struct sh532u_platform_data *pdata;
+ struct miscdevice miscdev;
+ struct list_head list;
+ int pwr_api;
+ int pwr_dev;
+ struct nvc_regulator vreg_vdd;
+ struct nvc_regulator vreg_i2c;
+ u8 s_mode;
+ struct sh532u_info *s_info;
+ unsigned i2c_addr_rom;
+ struct nvc_focus_nvc nvc;
+ struct nvc_focus_cap cap;
+ enum nvc_focus_sts sts;
+ struct sh532u_pdata_info cfg;
+ bool gpio_flag_reset;
+ bool init_cal_flag;
+ u32 pos_rel;
+ s16 pos_abs;
+ long pos_time_wr;
};
-struct sh532u_info {
- enum StereoCameraMode camera_mode;
- struct sh532u_sensor *left;
- struct sh532u_sensor *right;
+static struct sh532u_pdata_info sh532u_default_info = {
+ .move_timeoutms = SH532U_TIMEOUT_MS,
+ .focus_hyper_ratio = SH532U_HYPERFOCAL_RATIO,
+ .focus_hyper_div = SH532U_HYPERFOCAL_DIV,
+};
+
+static struct nvc_focus_cap sh532u_default_cap = {
+ .version = SH532U_CAPS_VER,
+ .actuator_range = SH532U_ACTUATOR_RANGE,
+ .settle_time = SH532U_SETTLETIME,
+ .focus_macro = SH532U_FOCUS_MACRO,
+ .focus_hyper = SH532U_FOCUS_HYPER,
+ .focus_infinity = SH532U_FOCUS_INFINITY,
+};
+
+static struct nvc_focus_nvc sh532u_default_nvc = {
+ .focal_length = SH532U_FOCAL_LENGTH,
+ .fnumber = SH532U_FNUMBER,
+ .max_aperature = SH532U_MAX_APERATURE,
+};
+
+static struct sh532u_platform_data sh532u_default_pdata = {
+ .cfg = 0,
+ .num = 0,
+ .sync = 0,
+ .dev_name = "focuser",
+ .i2c_addr_rom = 0x50,
+};
+
+static u32 sh532u_a2buf[] = {
+ 0x0018019c,
+ 0x0018019d,
+ 0x0000019e,
+ 0x007f0192,
+ 0x00000194,
+ 0x00f00184,
+ 0x00850187,
+ 0x0000018a,
+ 0x00fd7187,
+ 0x007f7183,
+ 0x0008025a,
+ 0x05042218,
+ 0x80010216,
+ 0x000601a0,
+ 0x00808183,
+ 0xffffffff
};
-static struct sh532u_info *stereo_sh532u_info; /* set to NULL by compiler */
+static LIST_HEAD(sh532u_info_list);
+static DEFINE_SPINLOCK(sh532u_spinlock);
-static int sh532u_read_u8(struct i2c_client *client, u8 dev, u8 addr, u8 *val)
+
+static int sh532u_i2c_rd8(struct sh532u_info *info, u8 addr, u8 reg, u8 *val)
{
struct i2c_msg msg[2];
- unsigned char data[3];
+ u8 buf[2];
- if (dev)
- msg[0].addr = dev;
- else
- msg[0].addr = client->addr;
+ buf[0] = reg;
+ if (addr) {
+ msg[0].addr = addr;
+ msg[1].addr = addr;
+ } else {
+ msg[0].addr = info->i2c_client->addr;
+ msg[1].addr = info->i2c_client->addr;
+ }
msg[0].flags = 0;
msg[0].len = 1;
- msg[0].buf = data;
-
- data[0] = (u8)addr;
-
- if (dev)
- msg[1].addr = dev;
- else
- msg[1].addr = client->addr;
+ msg[0].buf = &buf[0];
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
- msg[1].buf = data + 2;
+ msg[1].buf = &buf[1];
+ *val = 0;
+ if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+ return -EIO;
- if (i2c_transfer(client->adapter, msg, 2) != 2)
- return -1;
- *val = data[2];
+ *val = buf[1];
return 0;
}
-static int sh532u_read_u16(struct i2c_client *client, u8 addr, u16 *val)
+static int sh532u_i2c_wr8(struct sh532u_info *info, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ msg.addr = info->i2c_client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[0];
+ if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int sh532u_i2c_rd16(struct sh532u_info *info, u8 reg, u16 *val)
{
struct i2c_msg msg[2];
- u8 buf[4];
+ u8 buf[3];
- msg[0].addr = client->addr;
+ buf[0] = reg;
+ msg[0].addr = info->i2c_client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &buf[0];
-
- /* high byte goes out first */
- buf[0] = (u8) (addr);
-
- msg[1].addr = client->addr;
+ msg[1].addr = info->i2c_client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = &buf[1];
+ if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+ return -EIO;
- if (i2c_transfer(client->adapter, msg, 2) != 2)
- return -1;
*val = (((u16)buf[1] << 8) | (u16)buf[2]);
return 0;
}
-static int eeprom_read_u32(struct i2c_client *client, u8 addr, u32 *val)
+
+static int sh532u_i2c_wr16(struct sh532u_info *info, u8 reg, u16 val)
+{
+ struct i2c_msg msg;
+ u8 buf[3];
+
+ buf[0] = reg;
+ buf[1] = (u8)(val >> 8);
+ buf[2] = (u8)(val & 0xff);
+ msg.addr = info->i2c_client->addr;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = &buf[0];
+ if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int sh532u_i2c_rd32(struct sh532u_info *info, u8 addr, u8 reg, u32 *val)
{
struct i2c_msg msg[2];
- union {
- u8 dataU8[8];
- u32 dataU32[2];
- } buffer;
+ u8 buf[5];
- msg[0].addr = 0x50;
+ buf[0] = reg;
+ if (addr) {
+ msg[0].addr = addr;
+ msg[1].addr = addr;
+ } else {
+ msg[0].addr = info->i2c_client->addr;
+ msg[1].addr = info->i2c_client->addr;
+ }
msg[0].flags = 0;
msg[0].len = 1;
- msg[0].buf = &(buffer.dataU8[0]);
-
- /* high byte goes out first */
- buffer.dataU8[0] = (u8) (addr);
- buffer.dataU8[1] = (u8) (0);
-
- msg[1].addr = 0x50;
+ msg[0].buf = &buf[0];
msg[1].flags = I2C_M_RD;
msg[1].len = 4;
- msg[1].buf = (u8 *)&(buffer.dataU32[1]);
+ msg[1].buf = &buf[1];
+ if (i2c_transfer(info->i2c_client->adapter, msg, 2) != 2)
+ return -EIO;
- if (i2c_transfer(client->adapter, msg, 2) != 2)
- return -1;
- *val = buffer.dataU32[1];
+ *val = (((u32)buf[4] << 24) | ((u32)buf[3] << 16) |
+ ((u32)buf[2] << 8) | ((u32)buf[1]));
return 0;
}
-static int sh532u_write_u8(struct i2c_client *client, u16 addr, u8 val)
+static void sh532u_gpio_en(struct sh532u_info *info, int val)
{
- struct i2c_msg msg;
- unsigned char data[2];
+ if (info->pdata->gpio_en)
+ gpio_set_value(info->pdata->gpio_en, val);
+}
- data[0] = (u8) (addr & 0xff);
- data[1] = (u8) (val & 0xff);
+static void sh532u_gpio_reset(struct sh532u_info *info, int val)
+{
+ if (val) {
+ if (!info->gpio_flag_reset && info->pdata->gpio_reset) {
+ gpio_set_value(info->pdata->gpio_reset, 0);
+ mdelay(1);
+ gpio_set_value(info->pdata->gpio_reset, 1);
+ mdelay(10); /* delay for device startup */
+ info->gpio_flag_reset = 1;
+ }
+ } else {
+ info->gpio_flag_reset = 0;
+ }
+}
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = data;
+static void sh532u_pm_regulator_put(struct nvc_regulator *sreg)
+{
+ regulator_put(sreg->vreg);
+ sreg->vreg = NULL;
+}
+
+static int sh532u_pm_regulator_get(struct sh532u_info *info,
+ struct nvc_regulator *sreg,
+ char vreg_name[])
+{
+ int err = 0;
+
+ sreg->vreg_flag = 0;
+ sreg->vreg = regulator_get(&info->i2c_client->dev, vreg_name);
+ if (IS_ERR_OR_NULL(sreg->vreg)) {
+ dev_err(&info->i2c_client->dev,
+ "%s err for regulator: %s err: %d\n",
+ __func__, vreg_name, (int)sreg->vreg);
+ err = PTR_ERR(sreg->vreg);
+ sreg->vreg = NULL;
+ } else {
+ sreg->vreg_name = vreg_name;
+ dev_dbg(&info->i2c_client->dev,
+ "%s vreg_name: %s\n",
+ __func__, sreg->vreg_name);
+ }
+ return err;
+}
+
+static int sh532u_pm_regulator_en(struct sh532u_info *info,
+ struct nvc_regulator *sreg)
+{
+ int err = 0;
+
+ if (!sreg->vreg_flag && (sreg->vreg != NULL)) {
+ err = regulator_enable(sreg->vreg);
+ if (!err) {
+ dev_dbg(&info->i2c_client->dev,
+ "%s vreg_name: %s\n",
+ __func__, sreg->vreg_name);
+ sreg->vreg_flag = 1;
+ err = 1; /* flag regulator state change */
+ } else {
+ dev_err(&info->i2c_client->dev,
+ "%s err, regulator: %s\n",
+ __func__, sreg->vreg_name);
+ }
+ }
+ return err;
+}
+
+static int sh532u_pm_regulator_dis(struct sh532u_info *info,
+ struct nvc_regulator *sreg)
+{
+ int err = 0;
+
+ if (sreg->vreg_flag && (sreg->vreg != NULL)) {
+ err = regulator_disable(sreg->vreg);
+ if (err)
+ dev_err(&info->i2c_client->dev,
+ "%s err, regulator: %s\n",
+ __func__, sreg->vreg_name);
+ }
+ sreg->vreg_flag = 0;
+ return err;
+}
- if (i2c_transfer(client->adapter, &msg, 1) != 1)
- return -1;
+static int sh532u_pm_wr(struct sh532u_info *info, int pwr)
+{
+ int err = 0;
+
+ if (pwr == info->pwr_dev)
+ return 0;
+
+ switch (pwr) {
+ case NVC_PWR_OFF_DELAYED:
+ case NVC_PWR_OFF:
+ if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) ||
+ (info->pdata->cfg & NVC_CFG_BOOT_INIT)) {
+ pwr = NVC_PWR_STDBY;
+ } else {
+ sh532u_gpio_en(info, 0);
+ err = sh532u_pm_regulator_dis(info, &info->vreg_vdd);
+ err |= sh532u_pm_regulator_dis(info, &info->vreg_i2c);
+ sh532u_gpio_reset(info, 0);
+ break;
+ }
+ case NVC_PWR_STDBY_OFF:
+ if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) ||
+ (info->pdata->cfg & NVC_CFG_BOOT_INIT)) {
+ pwr = NVC_PWR_STDBY;
+ } else {
+ err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+ err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+ sh532u_gpio_en(info, 1);
+ sh532u_gpio_reset(info, 0);
+ break;
+ }
+ case NVC_PWR_STDBY:
+ err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+ err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+ sh532u_gpio_en(info, 1);
+ sh532u_gpio_reset(info, 1);
+ err |= sh532u_i2c_wr8(info, STBY_211, 0x80);
+ err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38);
+ err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x39);
+ break;
+
+ case NVC_PWR_COMM:
+ case NVC_PWR_ON:
+ err = sh532u_pm_regulator_en(info, &info->vreg_vdd);
+ err |= sh532u_pm_regulator_en(info, &info->vreg_i2c);
+ sh532u_gpio_en(info, 1);
+ sh532u_gpio_reset(info, 1);
+ err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x38);
+ err |= sh532u_i2c_wr8(info, CLKSEL_211, 0x34);
+ err |= sh532u_i2c_wr8(info, STBY_211, 0xF0);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ dev_err(&info->i2c_client->dev, "%s pwr err: %d\n",
+ __func__, pwr);
+ pwr = NVC_PWR_ERR;
+ }
+ info->pwr_dev = pwr;
+ if (err > 0)
+ return 0;
+
+ return err;
+}
+
+static int sh532u_pm_wr_s(struct sh532u_info *info, int pwr)
+{
+ int err1 = 0;
+ int err2 = 0;
+
+ if ((info->s_mode == NVC_SYNC_OFF) ||
+ (info->s_mode == NVC_SYNC_MASTER) ||
+ (info->s_mode == NVC_SYNC_STEREO))
+ err1 = sh532u_pm_wr(info, pwr);
+ if ((info->s_mode == NVC_SYNC_SLAVE) ||
+ (info->s_mode == NVC_SYNC_STEREO))
+ err2 = sh532u_pm_wr(info->s_info, pwr);
+ return err1 | err2;
+}
+
+static int sh532u_pm_api_wr(struct sh532u_info *info, int pwr)
+{
+ int err = 0;
+
+ if (!pwr || (pwr > NVC_PWR_ON))
+ return 0;
+
+ if (pwr > info->pwr_dev)
+ err = sh532u_pm_wr_s(info, pwr);
+ if (!err)
+ info->pwr_api = pwr;
else
+ info->pwr_api = NVC_PWR_ERR;
+ if (info->pdata->cfg & NVC_CFG_NOERR)
return 0;
+
+ return err;
}
-static int sh532u_write_u16(struct i2c_client *client, u16 addr, u16 val)
+static int sh532u_pm_dev_wr(struct sh532u_info *info, int pwr)
{
- struct i2c_msg msg;
- unsigned char data[3];
+ if (pwr < info->pwr_api)
+ pwr = info->pwr_api;
+ if (info->sts == NVC_FOCUS_STS_WAIT_FOR_MOVE_END)
+ pwr = NVC_PWR_ON;
+ return sh532u_pm_wr(info, pwr);
+}
- data[0] = (u8) (addr & 0xff);
- data[1] = (u8) (val >> 8);
- data[2] = (u8) (val & 0xff);
+static void sh532u_pm_exit(struct sh532u_info *info)
+{
+ sh532u_pm_wr_s(info, NVC_PWR_OFF);
+ sh532u_pm_regulator_put(&info->vreg_vdd);
+ sh532u_pm_regulator_put(&info->vreg_i2c);
+}
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 3;
- msg.buf = data;
+static void sh532u_pm_init(struct sh532u_info *info)
+{
+ sh532u_pm_regulator_get(info, &info->vreg_vdd, "vdd");
+ sh532u_pm_regulator_get(info, &info->vreg_i2c, "vdd_i2c");
+}
- if (i2c_transfer(client->adapter, &msg, 1) != 1)
- return -1;
- return 0;
+static int sh532u_dev_id(struct sh532u_info *info)
+{
+ u8 val;
+ int err;
+
+ err = sh532u_i2c_rd8(info, 0, HVCA_DEVICE_ID, &val);
+ if (!err && (val == SH532U_ID))
+ return 0;
+
+ return -ENODEV;
}
-static void move_driver(struct i2c_client *client, s16 tarPos)
+static void sh532u_sts_rd(struct sh532u_info *info)
{
- s16 curPos, moveStep;
- u16 moveDistance;
+ u8 us_tmp;
+ u16 us_smv_fin;
int err;
- /* Read Current Position */
- err = sh532u_read_u16(client, RZ_211H, &curPos);
+ if (info->sts == NVC_FOCUS_STS_INITIALIZING)
+ return;
+
+ info->sts = NVC_FOCUS_STS_NO_DEVICE; /* assume I2C err */
+ err = sh532u_i2c_rd8(info, 0, STMVEN_211, &us_tmp);
+ err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin);
if (err)
- goto move_driver_error;
- /* Check move distance to Target Position */
- moveDistance = abs((int)curPos - (int)tarPos);
+ return;
- /* if move distance is shorter than MS1Z12(=Step width) */
- if (moveDistance <= STMV_SIZE) {
- err = sh532u_write_u8(client, MSSET_211,
- (INI_MSSET_211 | 0x01));
- err = err | sh532u_write_u16(client, MS1Z22_211H, tarPos);
- if (err)
- goto move_driver_error;
+ /* StepMove Error Handling, Unexpected Position */
+ if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001))
+ /* Stop StepMove Operation */
+ sh532u_i2c_wr8(info, STMVEN_211, us_tmp & 0xFE);
+ if (us_tmp & STMVEN_ON) {
+ err = sh532u_i2c_rd8(info, 0, MSSET_211, &us_tmp);
+ if (!err) {
+ if (us_tmp & CHTGST_ON)
+ info->sts = NVC_FOCUS_STS_WAIT_FOR_SETTLE;
+ else
+ info->sts = NVC_FOCUS_STS_LENS_SETTLED;
+ }
} else {
- if (curPos < tarPos)
- moveStep = STMV_SIZE;
- else
- moveStep = -STMV_SIZE;
-
- /* Set StepMove Target Positon */
- err = sh532u_write_u16(client, MS1Z12_211H, moveStep);
- err = err | sh532u_write_u16(client, STMVENDH_211, tarPos);
- /* Start StepMove */
- err = err |
- sh532u_write_u8(client, STMVEN_211,
- (STMCHTG_ON | STMSV_ON | STMLFF_OFF | STMVEN_ON));
- if (err)
- goto move_driver_error;
+ info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END;
}
-
- return;
-move_driver_error:
- pr_err("sh532u: %s failed!\n", __func__);
}
-static void wait_for_move(struct i2c_client *client)
+static s16 sh532u_rel2abs(struct sh532u_info *info, u32 rel_position)
{
- u16 usSmvFin;
- u8 moveTime, ucParMod, tmp;
- int err;
+ s16 abs_pos;
- moveTime = 0;
- do {
- mdelay(1);
- err = sh532u_read_u8(client, 0, STMVEN_211, &ucParMod);
- err = err | sh532u_read_u16(client, RZ_211H, &usSmvFin);
- if (err)
- goto wait_for_move_error;
- /* StepMove Error Handling, Unexpected Position */
- if ((usSmvFin == 0x7FFF) || (usSmvFin == 0x8001)) {
- /* Stop StepMove Operation */
- err = sh532u_write_u8(client, STMVEN_211,
- ucParMod & 0xFE);
- if (err)
- goto wait_for_move_error;
- }
- moveTime++;
- /* Wait StepMove operation end */
- } while ((ucParMod & STMVEN_ON) && (moveTime < 50));
+ if (info->cap.actuator_range > 0) {
+ abs_pos = info->cfg.pos_high -
+ (((info->cfg.pos_high - info->cfg.pos_low) *
+ rel_position) / info->cap.actuator_range);
+ return abs_pos;
+ } else {
+ return 0;
+ }
+}
- moveTime = 0;
- if ((ucParMod & 0x08) == STMCHTG_ON) {
- mdelay(5);
- do {
- mdelay(1);
- moveTime++;
- err = sh532u_read_u8(client, 0, MSSET_211, &tmp);
- if (err)
- goto wait_for_move_error;
- } while ((tmp & CHTGST_ON) && (moveTime < 15));
+static u32 sh532u_abs2rel(struct sh532u_info *info, s16 abs_position)
+{
+ u32 rel_pos;
+
+ if (abs_position > info->cfg.pos_high)
+ abs_position = info->cfg.pos_high;
+ if ((info->cfg.pos_high - info->cfg.pos_low) > 0) {
+ rel_pos = (((info->cfg.pos_high - abs_position) *
+ info->cap.actuator_range) /
+ (info->cfg.pos_high - info->cfg.pos_low));
+ return rel_pos;
+ } else {
+ return 0;
}
+}
- return;
-wait_for_move_error:
- pr_err("sh532u: %s failed!\n", __func__);
+static int sh532u_abs_pos_rd(struct sh532u_info *info, s16 *position)
+{
+ int err;
+ u16 abs_pos = 0;
+
+ err = sh532u_i2c_rd16(info, RZ_211H, &abs_pos);
+ *position = (s16)abs_pos;
+ return err;
}
-static void lens_move_pulse(struct i2c_client *client, s16 position)
+static int sh532u_rel_pos_rd(struct sh532u_info *info, u32 *position)
{
- move_driver(client, position);
- wait_for_move(client);
+ s16 abs_pos;
+ long msec;
+ int pos;
+ int err;
+
+ err = sh532u_abs_pos_rd(info, &abs_pos);
+ if (err)
+ return -EINVAL;
+
+ if ((abs_pos >= (info->pos_abs - STMV_SIZE)) &&
+ (abs_pos <= (info->pos_abs + STMV_SIZE))) {
+ pos = (int)info->pos_rel;
+ } else {
+ msec = jiffies;
+ msec -= info->pos_time_wr;
+ msec = msec * 1000 / HZ;
+ sh532u_sts_rd(info);
+ if ((info->sts == NVC_FOCUS_STS_LENS_SETTLED) ||
+ (msec > info->cfg.move_timeoutms)) {
+ pos = (int)info->pos_rel;
+ } else {
+ pos = (int)sh532u_abs2rel(info, abs_pos);
+ if ((pos == (info->pos_rel - 1)) ||
+ (pos == (info->pos_rel + 1)))
+ pos = (int)info->pos_rel;
+ }
+ }
+ if (pos < 0)
+ pos = 0;
+ *position = (u32)pos;
+ return 0;
}
-static void get_rom_info(struct sh532u_sensor *info)
+static int sh532u_calibration(struct sh532u_info *info)
{
- struct i2c_client *client = info->i2c_client;
- u8 tmp;
+ u8 reg;
+ s16 abs_focus_macro;
+ s16 abs_focus_infinity;
+ u32 abs_focus_hyper;
+ u32 abs_range;
+ u32 rel_range;
+ u32 step;
int err;
+ int ret = 0;
+
+ if (info->init_cal_flag)
+ return 0;
+ /* set defaults */
+ memcpy(&info->cfg, &sh532u_default_info, sizeof(info->cfg));
+ memcpy(&info->nvc, &sh532u_default_nvc, sizeof(info->nvc));
+ memcpy(&info->cap, &sh532u_default_cap, sizeof(info->cap));
+ if (info->pdata->i2c_addr_rom)
+ info->i2c_addr_rom = info->pdata->i2c_addr_rom;
+ else
+ info->i2c_addr_rom = sh532u_default_pdata.i2c_addr_rom;
+ /* set overrides if any */
+ if (info->pdata->nvc) {
+ if (info->pdata->nvc->fnumber)
+ info->nvc.fnumber = info->pdata->nvc->fnumber;
+ if (info->pdata->nvc->focal_length)
+ info->nvc.focal_length =
+ info->pdata->nvc->focal_length;
+ if (info->pdata->nvc->max_aperature)
+ info->nvc.max_aperature =
+ info->pdata->nvc->max_aperature;
+ }
+ if (info->pdata->cap) {
+ if (info->pdata->cap->actuator_range)
+ info->cap.actuator_range =
+ info->pdata->cap->actuator_range;
+ if (info->pdata->cap->settle_time)
+ info->cap.settle_time = info->pdata->cap->settle_time;
+ if (info->pdata->cap->focus_macro)
+ info->cap.focus_macro = info->pdata->cap->focus_macro;
+ if (info->pdata->cap->focus_hyper)
+ info->cap.focus_hyper = info->pdata->cap->focus_hyper;
+ if (info->pdata->cap->focus_infinity)
+ info->cap.focus_infinity =
+ info->pdata->cap->focus_infinity;
+ }
/* Get Inf1, Mac1
Inf1 and Mac1 are the mechanical limit position.
Inf1 : Bottom limit.
Mac1 : Top limit. */
- err = sh532u_read_u8(client, 0x50, addrMac1, &tmp);
- if (err)
- goto get_rom_info_error;
- info->config.limit_low = (tmp<<8) & 0xff00;
- err = sh532u_read_u8(client, 0x50, addrInf1, &tmp);
- if (err)
- goto get_rom_info_error;
- info->config.limit_high = (tmp<<8) & 0xff00;
-
+ err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac1, &reg);
+ if (!err && (reg != 0) && (reg != 0xFF))
+ info->cfg.limit_low = (reg<<8) & 0xff00;
+ ret = err;
+ err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf1, &reg);
+ if (!err && (reg != 0) && (reg != 0xFF))
+ info->cfg.limit_high = (reg<<8) & 0xff00;
+ ret |= err;
/* Get Inf2, Mac2
Inf2 and Mac2 are the calibration data for SEMCO AF lens.
Inf2: Best focus (lens position) when object distance is 1.2M.
Mac2: Best focus (lens position) when object distance is 10cm. */
- err = sh532u_read_u8(client, 0x50, addrMac2, &tmp);
- if (err)
- goto get_rom_info_error;
- info->config.pos_low = (tmp << 8) & 0xff00;
- err = sh532u_read_u8(client, 0x50, addrInf2, &tmp);
- if (err)
- goto get_rom_info_error;
- info->config.pos_high = (tmp << 8) & 0xff00;
-
- return;
-get_rom_info_error:
- pr_err("sh532u: %s failed!\n", __func__);
- info->config.limit_high = POS_HIGH;
- info->config.limit_low = POS_LOW;
- info->config.pos_high = POS_HIGH;
- info->config.pos_low = POS_LOW;
+ err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrMac2, &reg);
+ if (!err && (reg != 0) && (reg != 0xFF))
+ info->cfg.pos_low = (reg << 8) & 0xff00;
+ ret |= err;
+ err = sh532u_i2c_rd8(info, info->i2c_addr_rom, addrInf2, &reg);
+ if (!err && (reg != 0) && (reg != 0xFF))
+ info->cfg.pos_high = (reg << 8) & 0xff00;
+ ret |= err;
+ /* set overrides */
+ if (info->pdata->info) {
+ if (info->pdata->info->pos_low)
+ info->cfg.pos_low = info->pdata->info->pos_low;
+ if (info->pdata->info->pos_high)
+ info->cfg.pos_high = info->pdata->info->pos_high;
+ if (info->pdata->info->limit_low)
+ info->cfg.limit_low = info->pdata->info->limit_low;
+ if (info->pdata->info->limit_high)
+ info->cfg.limit_high = info->pdata->info->limit_high;
+ if (info->pdata->info->move_timeoutms)
+ info->cfg.move_timeoutms =
+ info->pdata->info->move_timeoutms;
+ if (info->pdata->info->focus_hyper_ratio)
+ info->cfg.focus_hyper_ratio =
+ info->pdata->info->focus_hyper_ratio;
+ if (info->pdata->info->focus_hyper_div)
+ info->cfg.focus_hyper_div =
+ info->pdata->info->focus_hyper_div;
+ }
+ if (ret || !info->cfg.pos_low || !info->cfg.pos_high ||
+ !info->cfg.limit_low || !info->cfg.limit_high) {
+ dev_err(&info->i2c_client->dev, "%s ERR\n", __func__);
+ return -EIO;
+ }
+ /* calculate relative and absolute positions */
+ abs_focus_macro = info->cfg.pos_low;
+ abs_focus_infinity = info->cfg.pos_high;
+ abs_range = (u32)(abs_focus_infinity - abs_focus_macro);
+ rel_range = (info->cap.focus_macro - info->cap.focus_infinity);
+ /* calculate absolute hyperfocus position */
+ abs_focus_hyper = ((abs_range * info->cfg.focus_hyper_ratio) /
+ info->cfg.focus_hyper_div);
+ abs_focus_hyper = abs_focus_infinity - abs_focus_hyper;
+ /* calculate absolute high end */
+ step = (abs_range * info->cap.focus_infinity) / rel_range;
+ info->cfg.pos_high += step;
+ if (abs_focus_infinity < info->cfg.limit_high) {
+ if (info->cfg.pos_high > info->cfg.limit_high)
+ info->cfg.pos_high = info->cfg.limit_high;
+ }
+ /* calculate absolute low end */
+ step = (abs_range * (info->cap.actuator_range -
+ info->cap.focus_macro)) / rel_range;
+ info->cfg.pos_low -= step;
+ if (abs_focus_macro > info->cfg.limit_low) {
+ if (info->cfg.pos_low < info->cfg.limit_low)
+ info->cfg.pos_low = info->cfg.limit_low;
+ }
+ /* update actual relative positions */
+ info->cap.focus_macro = sh532u_abs2rel(info, abs_focus_macro);
+ info->cap.focus_infinity = sh532u_abs2rel(info, abs_focus_infinity);
+ info->cap.focus_hyper = sh532u_abs2rel(info, (s16)abs_focus_hyper);
+ info->init_cal_flag = 1;
+ dev_dbg(&info->i2c_client->dev, "%s complete\n", __func__);
+ return 0;
}
-static unsigned int a2buf[] = {
- 0x0018019c,
- 0x0018019d,
- 0x0000019e,
- 0x007f0192,
- 0x00000194,
- 0x00f00184,
- 0x00850187,
- 0x0000018a,
- 0x00fd7187,
- 0x007f7183,
- 0x0008025a,
- 0x05042218,
- 0x80010216,
- 0x000601a0,
- 0x00808183,
- 0xffffffff
-};
-
-/* Write 1 byte data to the HVCA Drive IC by data type */
-static void sh532u_hvca_wr1(struct sh532u_sensor *info,
- u8 ep_type, u8 ep_data1, u8 ep_addr)
+ /* Write 1 byte data to the HVCA Drive IC by data type */
+static int sh532u_hvca_wr1(struct sh532u_info *info,
+ u8 ep_type, u8 ep_data1, u8 ep_addr)
{
- struct i2c_client *client = info->i2c_client;
- int err = 0;
u8 us_data;
+ int err = 0;
switch (ep_type & 0xF0) {
case DIRECT_MODE:
@@ -333,45 +770,42 @@ static void sh532u_hvca_wr1(struct sh532u_sensor *info,
break;
case INDIRECT_EEPROM:
- err = sh532u_read_u8(client, 0x50, ep_data1, &us_data);
+ err = sh532u_i2c_rd8(info,
+ info->i2c_addr_rom,
+ ep_data1,
+ &us_data);
break;
case INDIRECT_HVCA:
- err = sh532u_read_u8(client, 0, (u16)ep_data1, &us_data);
+ err = sh532u_i2c_rd8(info, 0, ep_data1, &us_data);
break;
case MASK_AND:
- err = sh532u_read_u8(client, 0, (u16)ep_addr, &us_data);
- us_data = us_data & ep_data1;
+ err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data);
+ us_data &= ep_data1;
break;
case MASK_OR:
- err = sh532u_read_u8(client, 0, (u16)ep_addr, &us_data);
- us_data = us_data | ep_data1;
+ err = sh532u_i2c_rd8(info, 0, ep_addr, &us_data);
+ us_data |= ep_data1;
break;
default:
- err = 1;
+ err = -EINVAL;
}
if (!err)
- err = sh532u_write_u8(client, (u16)ep_addr, us_data);
-
- /* we output error message when there is I2C error, but we can't do
- * anything about it nor recover from it. */
- if (err)
- pr_err("sh532u: %s: Failed to init!: client=0x%x, ep_addr=0x%2x, us_data=0x%x, ret=%d\n",
- __func__, (u32)client, (u32)ep_addr, (u32)us_data, err);
+ err = sh532u_i2c_wr8(info, ep_addr, us_data);
+ return err;
}
-/* Write 2 byte data to the HVCA Drive IC by data type */
-static void sh532u_hvca_wr2(struct sh532u_sensor *info, u8 ep_type,
+ /* Write 2 byte data to the HVCA Drive IC by data type */
+static int sh532u_hvca_wr2(struct sh532u_info *info, u8 ep_type,
u8 ep_data1, u8 ep_data2, u8 ep_addr)
{
- struct i2c_client *client = info->i2c_client;
- int err = 0;
u8 uc_data1;
u8 uc_data2;
u16 us_data;
+ int err = 0;
switch (ep_type & 0xF0) {
case DIRECT_MODE:
@@ -380,63 +814,77 @@ static void sh532u_hvca_wr2(struct sh532u_sensor *info, u8 ep_type,
break;
case INDIRECT_EEPROM:
- err = sh532u_read_u8(client, 0x50, (u16)ep_data1,
- &uc_data1);
- err = err | sh532u_read_u8(client, 0x50, (u16)ep_data2,
- &uc_data2);
+ err = sh532u_i2c_rd8(info,
+ info->i2c_addr_rom,
+ ep_data1,
+ &uc_data1);
+ err |= sh532u_i2c_rd8(info,
+ info->i2c_addr_rom,
+ ep_data2,
+ &uc_data2);
us_data = (((u16)uc_data1 << 8) & 0xFF00) |
- ((u16)uc_data2 & 0x00FF);
+ ((u16)uc_data2 & 0x00FF);
break;
case INDIRECT_HVCA:
- err = sh532u_read_u8(client, 0, (u16)ep_data1, &uc_data1);
- err = err | sh532u_read_u8(client, 0, (u16)ep_data2, &uc_data2);
+ err = sh532u_i2c_rd8(info, 0, ep_data1, &uc_data1);
+ err |= sh532u_i2c_rd8(info, 0, ep_data2, &uc_data2);
us_data = (((u16)uc_data1 << 8) & 0xFF00) |
- ((u16)uc_data2 & 0x00FF);
+ ((u16)uc_data2 & 0x00FF);
break;
case MASK_AND:
- err = sh532u_read_u16(client, (u16)ep_addr, &us_data);
- us_data = us_data & ((((u16)ep_data1 << 8) & 0xFF00) |
- ((u16)ep_data2 & 0x00FF));
+ err = sh532u_i2c_rd16(info, ep_addr, &us_data);
+ us_data &= ((((u16)ep_data1 << 8) & 0xFF00) |
+ ((u16)ep_data2 & 0x00FF));
break;
case MASK_OR:
- err = sh532u_read_u16(client, (u16)ep_addr, &us_data);
- us_data = us_data | ((((u16)ep_data1 << 8) & 0xFF00) |
- ((u16)ep_data2 & 0x00FF));
+ err = sh532u_i2c_rd16(info, ep_addr, &us_data);
+ us_data |= ((((u16)ep_data1 << 8) & 0xFF00) |
+ ((u16)ep_data2 & 0x00FF));
break;
default:
- err = 1;
+ err = -EINVAL;
}
if (!err)
- err = sh532u_write_u16(client, (u16)ep_addr, us_data);
-
- /* we output error message when there is I2C error, but we can't do
- * anything about it nor recover from it. */
- if (err)
- pr_err("sh532u: %s: Failed to init!: client=0x%x, ep_addr=0x%2x, us_data=0x%x, ret=%d\n",
- __func__, (u32)client, (u32)ep_addr, (u32)us_data, err);
+ err = sh532u_i2c_wr16(info, ep_addr, us_data);
+ return err;
}
-static void init_driver(struct sh532u_sensor *info)
+static int sh532u_dev_init(struct sh532u_info *info)
{
- int eeprom_addr;
- unsigned int eeprom_data = 0;
- u8 ep_addr, ep_type, ep_data1, ep_data2;
-
- pr_info("sh532u: init_driver: i2c_client = 0x%x\n",
- (u32)info->i2c_client);
-
- for (eeprom_addr = 0x30; eeprom_addr <= 0x013C; eeprom_addr += 4) {
- if (eeprom_addr > 0xff) {
+ int eeprom_reg;
+ unsigned eeprom_data = 0;
+ u8 ep_addr;
+ u8 ep_type;
+ u8 ep_data1;
+ u8 ep_data2;
+ int err;
+ int ret = 0;
+
+ err = sh532u_i2c_rd8(info, 0, SWTCH_211, &ep_data1);
+ ep_data2 = ep_data1;
+ err |= sh532u_i2c_rd8(info, 0, ANA1_211, &ep_data1);
+ ep_data2 |= ep_data1;
+ if (!err && ep_data2)
+ return 0; /* Already initialized */
+
+ info->sts = NVC_FOCUS_STS_INITIALIZING;
+ for (eeprom_reg = 0x30; eeprom_reg <= 0x013C; eeprom_reg += 4) {
+ if (eeprom_reg > 0xFF) {
/* use hardcoded data instead */
- eeprom_data = a2buf[(eeprom_addr & 0xFF) / 4];
+ eeprom_data = sh532u_a2buf[(eeprom_reg & 0xFF) / 4];
} else {
- if (eeprom_read_u32(info->i2c_client,
- eeprom_addr & 0xFF, &eeprom_data))
- pr_info("sh532u: cannot read eeprom\n");
+ err = (sh532u_i2c_rd32(info,
+ info->i2c_addr_rom,
+ eeprom_reg & 0xFF,
+ &eeprom_data));
+ if (err) {
+ ret |= err;
+ continue;
+ }
}
/* HVCA Address to write eeprom Data1,Data2 by the Data type */
@@ -444,7 +892,6 @@ static void init_driver(struct sh532u_sensor *info)
ep_type = (u8)((eeprom_data & 0x0000ff00) >> 8);
ep_data1 = (u8)((eeprom_data & 0x00ff0000) >> 16);
ep_data2 = (u8)((eeprom_data & 0xff000000) >> 24);
-
if (ep_addr == 0xFF)
break;
@@ -452,377 +899,698 @@ static void init_driver(struct sh532u_sensor *info)
mdelay((unsigned int)((ep_data1 << 8) | ep_data2));
} else {
if ((ep_type & 0x0F) == DATA_1BYTE) {
- sh532u_hvca_wr1(info, ep_type, ep_data1,
- ep_addr);
+ err = sh532u_hvca_wr1(info,
+ ep_type,
+ ep_data1,
+ ep_addr);
} else {
- sh532u_hvca_wr2(info,
- ep_type,
- ep_data1,
- ep_data2,
- ep_addr);
+ err = sh532u_hvca_wr2(info,
+ ep_type,
+ ep_data1,
+ ep_data2,
+ ep_addr);
}
}
+ ret |= err;
}
- get_rom_info(info);
+ err = ret;
+ if (err)
+ dev_err(&info->i2c_client->dev, "%s programming err=%d\n",
+ __func__, err);
+ err |= sh532u_calibration(info);
+ info->sts = NVC_FOCUS_STS_LENS_SETTLED;
+ return err;
}
-
-static int sh532u_set_position(struct sh532u_sensor *info, s16 position)
+static int sh532u_move_lens(struct sh532u_info *info, s16 tar_pos)
{
- if (position > info->config.limit_high)
- return -1;
- /* Caller's responsibility to check motor status. */
- move_driver(info->i2c_client, position);
- return 0;
-}
+ s16 cur_pos;
+ s16 move_step;
+ u16 move_distance;
+ int err;
-static int sh532u_get_move_status(struct sh532u_sensor *info, unsigned long arg)
-{
- struct i2c_client *client = info->i2c_client;
- enum sh532u_move_status status = SH532U_Forced32;
- u8 ucTmp;
- u16 usSmvFin;
- int err = sh532u_read_u8(client, 0, STMVEN_211, &ucTmp) |
- sh532u_read_u16(client, RZ_211H, &usSmvFin);
+ sh532u_pm_dev_wr(info, NVC_PWR_ON);
+ err = sh532u_dev_init(info);
if (err)
return err;
- /* StepMove Error Handling, Unexpected Position */
- if ((usSmvFin == 0x7FFF) || (usSmvFin == 0x8001)) {
- /* Stop StepMove Operation */
- err = sh532u_write_u8(client, STMVEN_211, ucTmp & 0xFE);
- if (err)
- return err;
- }
+ /* Read Current Position */
+ err = sh532u_abs_pos_rd(info, &cur_pos);
+ if (err)
+ return err;
- if (ucTmp & STMVEN_ON) {
- err = sh532u_read_u8(client, 0, MSSET_211, &ucTmp);
- if (err)
- return err;
- if (ucTmp & CHTGST_ON)
- status = SH532U_WAIT_FOR_SETTLE;
+ info->sts = NVC_FOCUS_STS_WAIT_FOR_MOVE_END;
+ /* Check move distance to Target Position */
+ move_distance = abs((int)cur_pos - (int)tar_pos);
+ /* if move distance is shorter than MS1Z12(=Step width) */
+ if (move_distance <= STMV_SIZE) {
+ err = sh532u_i2c_wr8(info, MSSET_211,
+ (INI_MSSET_211 | 0x01));
+ err |= sh532u_i2c_wr16(info, MS1Z22_211H, tar_pos);
+ } else {
+ if (cur_pos < tar_pos)
+ move_step = STMV_SIZE;
else
- status = SH532U_LENS_SETTLED;
- } else
- status = SH532U_WAIT_FOR_MOVE_END;
-
- if (copy_to_user((void __user *) arg, &status,
- sizeof(enum sh532u_move_status))) {
- pr_info("Error in copying move status: %s: %d\n",
- __func__, __LINE__);
- return -EFAULT;
+ move_step = -STMV_SIZE;
+ /* Set StepMove Target Positon */
+ err = sh532u_i2c_wr16(info, MS1Z12_211H, move_step);
+ err |= sh532u_i2c_wr16(info, STMVENDH_211, tar_pos);
+ /* Start StepMove */
+ err |= sh532u_i2c_wr8(info, STMVEN_211,
+ (STMCHTG_ON |
+ STMSV_ON |
+ STMLFF_OFF |
+ STMVEN_ON));
}
- return 0;
+ return err;
}
-static long sh532u_ioctl_helper(
- struct sh532u_sensor *info,
- unsigned int cmd,
- unsigned long arg)
+static int sh532u_move_wait(struct sh532u_info *info)
{
- switch (cmd) {
- case SH532U_IOCTL_GET_CONFIG:
- if (copy_to_user((void __user *) arg, &info->config,
- sizeof(info->config))) {
- pr_err("Error in copying config: %s: %d\n",
- __func__, __LINE__);
- return -EFAULT;
+ u16 us_smv_fin;
+ u8 moveTime;
+ u8 ucParMod;
+ u8 tmp;
+ int err;
+
+ moveTime = 0;
+ do {
+ mdelay(1);
+ err = sh532u_i2c_rd8(info, 0, STMVEN_211, &ucParMod);
+ err |= sh532u_i2c_rd16(info, RZ_211H, &us_smv_fin);
+ if (err)
+ return err;
+
+ /* StepMove Error Handling, Unexpected Position */
+ if ((us_smv_fin == 0x7FFF) || (us_smv_fin == 0x8001)) {
+ /* Stop StepMove Operation */
+ err = sh532u_i2c_wr8(info, STMVEN_211,
+ ucParMod & 0xFE);
+ if (err)
+ return err;
}
- return 0;
- case SH532U_IOCTL_SET_POSITION:
- return sh532u_set_position(info, (s16)(arg & 0xffff));
+ moveTime++;
+ /* Wait StepMove operation end */
+ } while ((ucParMod & STMVEN_ON) && (moveTime < 50));
- case SH532U_IOCTL_GET_MOVE_STATUS:
- return sh532u_get_move_status(info, arg);
+ moveTime = 0;
+ if ((ucParMod & 0x08) == STMCHTG_ON) {
+ mdelay(5);
+ do {
+ mdelay(1);
+ moveTime++;
+ err = sh532u_i2c_rd8(info, 0, MSSET_211, &tmp);
+ if (err)
+ return err;
- default:
- return -EINVAL;
+ } while ((tmp & CHTGST_ON) && (moveTime < 15));
}
+ return err;
}
-static long sh532u_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
+static int sh532u_move_pulse(struct sh532u_info *info, s16 position)
{
- struct sh532u_info *stereo_info = file->private_data;
- int ret;
-
- /* select a camera */
- if (cmd == SH532U_IOCTL_SET_CAMERA_MODE) {
- stereo_info->camera_mode = arg;
- return 0;
- }
-
- if (StereoCameraMode_Left & stereo_info->camera_mode) {
- ret = sh532u_ioctl_helper(stereo_info->left, cmd, arg);
- if (ret)
- return ret;
-
- if (stereo_info->camera_mode == StereoCameraMode_Stereo) {
- /* To be finalized for stereo */
- if (cmd != SH532U_IOCTL_GET_CONFIG)
- ret = sh532u_ioctl_helper(stereo_info->right,
- cmd, arg);
- }
- return ret;
- }
-
- if (StereoCameraMode_Right & stereo_info->camera_mode)
- return sh532u_ioctl_helper(stereo_info->right, cmd, arg);
+ int err;
- return 0;
+ err = sh532u_move_lens(info, position);
+ err |= sh532u_move_wait(info);
+ return err;
}
-static void sh532u_open_helper(struct sh532u_sensor *info)
+static int sh532u_hvca_pos_init(struct sh532u_info *info)
{
- if (info->pdata.board_init)
- info->pdata.board_init(info->pdata.context_data);
- init_driver(info);
+ s16 limit_bottom;
+ s16 limit_top;
+ int err;
+
+ limit_bottom = (((int)info->cfg.limit_low * 5) >> 3) & 0xFFC0;
+ if (limit_bottom < info->cfg.limit_low)
+ limit_bottom = info->cfg.limit_low;
+ limit_top = (((int)info->cfg.limit_high * 5) >> 3) & 0xFFC0;
+ if (limit_top > info->cfg.limit_high)
+ limit_top = info->cfg.limit_high;
+ err = sh532u_move_pulse(info, limit_bottom);
+ err |= sh532u_move_pulse(info, limit_top);
+ err |= sh532u_move_pulse(info, info->cfg.pos_high);
+ return err;
}
-static void sh532u_release_helper(struct sh532u_sensor *info)
+static int sh532u_pos_abs_wr(struct sh532u_info *info, s16 position)
{
- if (info->pdata.board_deinit)
- info->pdata.board_deinit(info->pdata.context_data);
+ if (position > info->cfg.limit_high || position < info->cfg.limit_low)
+ return -EINVAL;
+
+ return sh532u_move_lens(info, position);
}
-static int sh532u_open(struct inode *inode, struct file *file)
+static int sh532u_pos_rel_wr(struct sh532u_info *info, u32 position)
{
- pr_info("sh532u open: camera_mode: %2d\n",
- stereo_sh532u_info->camera_mode);
+ s16 abs_pos;
- file->private_data = stereo_sh532u_info;
+ abs_pos = sh532u_rel2abs(info, position);
+ info->pos_rel = position;
+ info->pos_abs = abs_pos;
+ info->pos_time_wr = jiffies;
+ return sh532u_pos_abs_wr(info, abs_pos);
+}
- if (StereoCameraMode_Left & stereo_sh532u_info->camera_mode)
- sh532u_open_helper(stereo_sh532u_info->left);
- if (StereoCameraMode_Right & stereo_sh532u_info->camera_mode)
- sh532u_open_helper(stereo_sh532u_info->right);
+static int sh532u_param_rd(struct sh532u_info *info, unsigned long arg)
+{
+ struct nvc_param params;
+ const void *data_ptr;
+ u32 data_size = 0;
+ u32 position;
+ int err;
- return 0;
-}
+ if (copy_from_user(&params,
+ (const void __user *)arg,
+ sizeof(struct nvc_param))) {
+ dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
-int sh532u_release(struct inode *inode, struct file *file)
-{
- struct sh532u_info *info = file->private_data;
+ if (info->s_mode == NVC_SYNC_SLAVE)
+ info = info->s_info;
+ switch (params.param) {
+ case NVC_PARAM_LOCUS:
+ sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+ err = sh532u_rel_pos_rd(info, &position);
+ if (err && !(info->pdata->cfg & NVC_CFG_NOERR))
+ return -EINVAL;
+
+ data_ptr = &position;
+ data_size = sizeof(position);
+ sh532u_pm_dev_wr(info, NVC_PWR_STDBY);
+ dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n",
+ __func__, position);
+ break;
- pr_info("sh532u release: camera_mode: %2d\n",
- info->camera_mode);
+ case NVC_PARAM_FOCAL_LEN:
+ data_ptr = &info->nvc.focal_length;
+ data_size = sizeof(info->nvc.focal_length);
+ dev_dbg(&info->i2c_client->dev, "%s FOCAL_LEN: %x\n",
+ __func__, info->nvc.focal_length);
+ break;
- if (StereoCameraMode_Left & info->camera_mode)
- sh532u_release_helper(info->left);
+ case NVC_PARAM_MAX_APERTURE:
+ data_ptr = &info->nvc.max_aperature;
+ data_size = sizeof(info->nvc.max_aperature);
+ dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n",
+ __func__, info->nvc.max_aperature);
+ break;
- if (StereoCameraMode_Right & info->camera_mode)
- sh532u_release_helper(info->right);
+ case NVC_PARAM_FNUMBER:
+ data_ptr = &info->nvc.fnumber;
+ data_size = sizeof(info->nvc.fnumber);
+ dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %x\n",
+ __func__, info->nvc.fnumber);
+ break;
- file->private_data = NULL;
- return 0;
-}
+ case NVC_PARAM_CAPS:
+ sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+ err = sh532u_calibration(info);
+ sh532u_pm_dev_wr(info, NVC_PWR_STDBY);
+ if (err)
+ return -EIO;
+ data_ptr = &info->cap;
+ /* there are different sizes depending on the version */
+ /* send back just what's requested or our max size */
+ if (params.sizeofvalue < sizeof(info->cap))
+ data_size = params.sizeofvalue;
+ else
+ data_size = sizeof(info->cap);
+ dev_dbg(&info->i2c_client->dev, "%s CAPS\n",
+ __func__);
+ break;
-static const struct file_operations sh532u_fileops = {
- .owner = THIS_MODULE,
- .open = sh532u_open,
- .unlocked_ioctl = sh532u_ioctl,
- .release = sh532u_release,
-};
+ case NVC_PARAM_STS:
+ data_ptr = &info->sts;
+ data_size = sizeof(info->sts);
+ dev_dbg(&info->i2c_client->dev, "%s STS: %d\n",
+ __func__, info->sts);
+ break;
-static struct miscdevice sh532u_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "sh532u",
- .fops = &sh532u_fileops,
-};
+ case NVC_PARAM_STEREO:
+ data_ptr = &info->s_mode;
+ data_size = sizeof(info->s_mode);
+ dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n",
+ __func__, info->s_mode);
+ break;
-static int sh532u_probe_init(struct i2c_client *client,
- struct sh532u_sensor **info)
-{
- struct sh532u_platform_data *pdata = client->dev.platform_data;
- struct sh532u_sensor *p_info =
- kzalloc(sizeof(struct sh532u_sensor), GFP_KERNEL);
- if (!p_info) {
- pr_err("%s\n", "sh532u_sensor: Unable to allocate memory!\n");
- return -ENOMEM;
+ default:
+ dev_err(&info->i2c_client->dev,
+ "%s unsupported parameter: %d\n",
+ __func__, params.param);
+ return -EINVAL;
}
- p_info->i2c_client = client;
- p_info->config.settle_time = SETTLETIME_MS;
- p_info->config.focal_length = FOCAL_LENGTH;
- p_info->config.fnumber = FNUMBER;
- p_info->config.pos_low = POS_LOW;
- p_info->config.pos_high = POS_HIGH;
- i2c_set_clientdata(client, p_info);
+ if (params.sizeofvalue < data_size) {
+ dev_err(&info->i2c_client->dev, "%s %d data size err\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
- if (pdata) {
- p_info->pdata.context_data = pdata->context_data;
- p_info->pdata.board_init = pdata->board_init;
- p_info->pdata.board_deinit = pdata->board_deinit;
+ if (copy_to_user((void __user *)params.p_value,
+ data_ptr,
+ data_size)) {
+ dev_err(&info->i2c_client->dev, "%s %d copy_to_user err\n",
+ __func__, __LINE__);
+ return -EFAULT;
}
- *info = p_info;
return 0;
}
-static int sh532u_probe_helper(struct i2c_client *client)
+static int sh532u_param_wr_s(struct sh532u_info *info,
+ struct nvc_param *params,
+ u32 u32_val)
{
int err;
- if (!stereo_sh532u_info) {
- stereo_sh532u_info = kzalloc(sizeof(struct sh532u_info),
- GFP_KERNEL);
- if (!stereo_sh532u_info) {
- pr_err("%s\n",
- "sh532u_info: Unable to allocate memory!\n");
- return -ENOMEM;
- }
+ switch (params->param) {
+ case NVC_PARAM_LOCUS:
+ dev_dbg(&info->i2c_client->dev, "%s LOCUS: %u\n",
+ __func__, u32_val);
+ err = sh532u_pos_rel_wr(info, u32_val);
+ return err;
- err = misc_register(&sh532u_device);
- if (err) {
- pr_err("sh532u: Unable to register sh532u device!\n");
- kfree(stereo_sh532u_info);
- stereo_sh532u_info = NULL;
- return err;
- }
+ case NVC_PARAM_RESET:
+ err = sh532u_pm_wr(info, NVC_PWR_OFF);
+ err |= sh532u_pm_wr(info, NVC_PWR_ON);
+ err |= sh532u_pm_wr(info, info->pwr_api);
+ dev_dbg(&info->i2c_client->dev, "%s RESET: %d\n",
+ __func__, err);
+ return err;
+
+ case NVC_PARAM_SELF_TEST:
+ err = sh532u_hvca_pos_init(info);
+ dev_dbg(&info->i2c_client->dev, "%s SELF_TEST: %d\n",
+ __func__, err);
+ return err;
+
+ default:
+ dev_err(&info->i2c_client->dev,
+ "%s unsupported parameter: %d\n",
+ __func__, params->param);
+ return -EINVAL;
+ }
+}
+
+static int sh532u_param_wr(struct sh532u_info *info, unsigned long arg)
+{
+ struct nvc_param params;
+ u8 val;
+ u32 u32_val;
+ int err = 0;
+
+ if (copy_from_user(&params,
+ (const void __user *)arg,
+ sizeof(struct nvc_param))) {
+ dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&u32_val, (const void __user *)params.p_value,
+ sizeof(u32_val))) {
+ dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ /* parameters independent of sync mode */
+ switch (params.param) {
+ case NVC_PARAM_STEREO:
+ dev_dbg(&info->i2c_client->dev, "%s STEREO: %u\n",
+ __func__, u32_val);
+ val = (u8)u32_val;
+ if (val == info->s_mode)
+ return 0;
+
+ switch (val) {
+ case NVC_SYNC_OFF:
+ info->s_mode = val;
+ if (info->s_info != NULL) {
+ info->s_info->s_mode = val;
+ sh532u_pm_wr(info->s_info, NVC_PWR_OFF);
+ }
+ break;
+
+ case NVC_SYNC_MASTER:
+ info->s_mode = val;
+ if (info->s_info != NULL)
+ info->s_info->s_mode = val;
+ break;
+
+ case NVC_SYNC_SLAVE:
+ if (info->s_info != NULL) {
+ /* default slave lens position */
+ err = sh532u_pos_rel_wr(info->s_info,
+ info->s_info->cap.focus_infinity);
+ if (!err) {
+ info->s_mode = val;
+ info->s_info->s_mode = val;
+ } else {
+ if (info->s_mode != NVC_SYNC_STEREO)
+ sh532u_pm_wr(info->s_info,
+ NVC_PWR_OFF);
+ err = -EIO;
+ }
+ } else {
+ err = -EINVAL;
+ }
+ break;
+
+ case NVC_SYNC_STEREO:
+ if (info->s_info != NULL) {
+ /* sync power */
+ info->s_info->pwr_api = info->pwr_api;
+ /* move slave lens to master position */
+ err = sh532u_pos_rel_wr(info->s_info,
+ info->pos_rel);
+ if (!err) {
+ info->s_mode = val;
+ info->s_info->s_mode = val;
+ } else {
+ if (info->s_mode != NVC_SYNC_SLAVE)
+ sh532u_pm_wr(info->s_info,
+ NVC_PWR_OFF);
+ err = -EIO;
+ }
+ } else {
+ err = -EINVAL;
+ }
+ break;
- err = sh532u_probe_init(client, &stereo_sh532u_info->left);
- if (err) {
- kfree(stereo_sh532u_info);
- stereo_sh532u_info = NULL;
- return -ENOMEM;
+ default:
+ err = -EINVAL;
}
+ if (info->pdata->cfg & NVC_CFG_NOERR)
+ return 0;
+
+ return err;
+
+ default:
+ /* parameters dependent on sync mode */
+ switch (info->s_mode) {
+ case NVC_SYNC_OFF:
+ case NVC_SYNC_MASTER:
+ return sh532u_param_wr_s(info, &params, u32_val);
+
+ case NVC_SYNC_SLAVE:
+ return sh532u_param_wr_s(info->s_info,
+ &params,
+ u32_val);
+
+ case NVC_SYNC_STEREO:
+ err = sh532u_param_wr_s(info, &params, u32_val);
+ if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX))
+ err |= sh532u_param_wr_s(info->s_info,
+ &params,
+ u32_val);
+ return err;
- err = sh532u_probe_init(client, &stereo_sh532u_info->right);
- if (err) {
- kfree(stereo_sh532u_info);
- stereo_sh532u_info = NULL;
- kfree(stereo_sh532u_info->left);
- return -ENOMEM;
+ default:
+ dev_err(&info->i2c_client->dev, "%s %d internal err\n",
+ __func__, __LINE__);
+ return -EINVAL;
}
- stereo_sh532u_info->camera_mode = StereoCameraMode_Left;
}
-
- return 0;
}
-static int left_sh532u_probe(
- struct i2c_client *client,
- const struct i2c_device_id *id)
+static long sh532u_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
{
- int err ;
+ struct sh532u_info *info = file->private_data;
+ int pwr;
- pr_info("left_sh532u: probing sensor: i2c_client=0x%x\n", (u32)client);
+ switch (cmd) {
+ case NVC_IOCTL_PARAM_WR:
+ return sh532u_param_wr(info, arg);
+
+ case NVC_IOCTL_PARAM_RD:
+ return sh532u_param_rd(info, arg);
+
+ case NVC_IOCTL_PWR_WR:
+ /* This is a Guaranteed Level of Service (GLOS) call */
+ pwr = (int)arg * 2;
+ dev_dbg(&info->i2c_client->dev, "%s PWR: %d\n",
+ __func__, pwr);
+ return sh532u_pm_api_wr(info, pwr);
+
+ case NVC_IOCTL_PWR_RD:
+ if (info->s_mode == NVC_SYNC_SLAVE)
+ pwr = info->s_info->pwr_api / 2;
+ else
+ pwr = info->pwr_api / 2;
+ dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n",
+ __func__, pwr);
+ if (copy_to_user((void __user *)arg, (const void *)&pwr,
+ sizeof(pwr))) {
+ dev_err(&info->i2c_client->dev,
+ "%s copy_to_user err line %d\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
- err = sh532u_probe_helper(client);
+ return 0;
- return err;
+ default:
+ dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n",
+ __func__, cmd);
+ return -EINVAL;
+ }
+}
+
+static int sh532u_sync_en(int dev1, int dev2)
+{
+ struct sh532u_info *sync1 = NULL;
+ struct sh532u_info *sync2 = NULL;
+ struct sh532u_info *pos = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+ if (pos->pdata->num == dev1) {
+ sync1 = pos;
+ break;
+ }
+ }
+ pos = NULL;
+ list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+ if (pos->pdata->num == dev2) {
+ sync2 = pos;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ if (sync1 != NULL)
+ sync1->s_info = NULL;
+ if (sync2 != NULL)
+ sync2->s_info = NULL;
+ if (!dev1 && !dev2)
+ return 0; /* no err if default instance 0's used */
+
+ if (dev1 == dev2)
+ return -EINVAL; /* err if sync instance is itself */
+
+ if ((sync1 != NULL) && (sync2 != NULL)) {
+ sync1->s_info = sync2;
+ sync2->s_info = sync1;
+ }
+ return 0;
}
-static int left_sh532u_remove(struct i2c_client *client)
+static int sh532u_sync_dis(struct sh532u_info *info)
{
- pr_info("left_sh532u to be removed\n");
- if (!stereo_sh532u_info || !client) {
- pr_info("left_sh532u_remove(): NULL pointers\n");
+ if (info->s_info != NULL) {
+ info->s_info->s_mode = 0;
+ info->s_info->s_info = NULL;
+ info->s_mode = 0;
+ info->s_info = NULL;
return 0;
}
- kfree(stereo_sh532u_info->left);
- stereo_sh532u_info->left = NULL;
+ return -EINVAL;
+}
- if (!stereo_sh532u_info->right) {
- misc_deregister(&sh532u_device);
- kfree(stereo_sh532u_info);
- stereo_sh532u_info = NULL;
+static int sh532u_open(struct inode *inode, struct file *file)
+{
+ struct sh532u_info *info = NULL;
+ struct sh532u_info *pos = NULL;
+ int err;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(pos, &sh532u_info_list, list) {
+ if (pos->miscdev.minor == iminor(inode)) {
+ info = pos;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ if (!info)
+ return -ENODEV;
+
+ err = sh532u_sync_en(info->pdata->num, info->pdata->sync);
+ if (err == -EINVAL)
+ dev_err(&info->i2c_client->dev,
+ "%s err: invalid num (%u) and sync (%u) instance\n",
+ __func__, info->pdata->num, info->pdata->sync);
+ if (atomic_xchg(&info->in_use, 1))
+ return -EBUSY;
+
+ if (info->s_info != NULL) {
+ if (atomic_xchg(&info->s_info->in_use, 1))
+ return -EBUSY;
}
+ file->private_data = info;
+ dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+ sh532u_pos_rel_wr(info, info->cap.focus_infinity);
return 0;
}
-static const struct i2c_device_id left_sh532u_id[] = {
- { "sh532u", 0 },
- { "sh532uL", 0 },
- { },
-};
+int sh532u_release(struct inode *inode, struct file *file)
+{
+ struct sh532u_info *info = file->private_data;
-MODULE_DEVICE_TABLE(i2c, left_sh532u_id);
+ dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+ sh532u_pm_wr_s(info, NVC_PWR_OFF);
+ file->private_data = NULL;
+ WARN_ON(!atomic_xchg(&info->in_use, 0));
+ if (info->s_info != NULL)
+ WARN_ON(!atomic_xchg(&info->s_info->in_use, 0));
+ sh532u_sync_dis(info);
+ return 0;
+}
-static struct i2c_driver left_sh532u_i2c_driver = {
- .driver = {
- .name = "sh532uL",
- .owner = THIS_MODULE,
- },
- .probe = left_sh532u_probe,
- .remove = left_sh532u_remove,
- .id_table = left_sh532u_id,
+static const struct file_operations sh532u_fileops = {
+ .owner = THIS_MODULE,
+ .open = sh532u_open,
+ .unlocked_ioctl = sh532u_ioctl,
+ .release = sh532u_release,
};
-static int right_sh532u_probe(
- struct i2c_client *client,
- const struct i2c_device_id *id)
+static void sh532u_del(struct sh532u_info *info)
{
- int err ;
-
- pr_info("right_sh532u: probing sensor: i2c_client=0x%x\n", (u32)client);
+ sh532u_pm_exit(info);
+ sh532u_sync_dis(info);
+ spin_lock(&sh532u_spinlock);
+ list_del_rcu(&info->list);
+ spin_unlock(&sh532u_spinlock);
+ synchronize_rcu();
+}
- err = sh532u_probe_helper(client);
+static int sh532u_remove(struct i2c_client *client)
+{
+ struct sh532u_info *info = i2c_get_clientdata(client);
- return err;
+ dev_dbg(&info->i2c_client->dev, "%s\n", __func__);
+ misc_deregister(&info->miscdev);
+ sh532u_del(info);
+ return 0;
}
-static int right_sh532u_remove(struct i2c_client *client)
+static int sh532u_probe(
+ struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- if (!stereo_sh532u_info || !client) {
- pr_info("right_sh532u_remove(): NULL pointers\n");
- return 0;
+ struct sh532u_info *info = NULL;
+ char dname[16];
+ int err;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ dev_err(&client->dev, "%s: kzalloc error\n", __func__);
+ return -ENOMEM;
}
- kfree(stereo_sh532u_info->right);
- stereo_sh532u_info->right = NULL;
+ info->i2c_client = client;
+ if (client->dev.platform_data) {
+ info->pdata = client->dev.platform_data;
+ } else {
+ info->pdata = &sh532u_default_pdata;
+ dev_dbg(&client->dev,
+ "%s No platform data. Using defaults.\n",
+ __func__);
+ }
+ i2c_set_clientdata(client, info);
+ INIT_LIST_HEAD(&info->list);
+ spin_lock(&sh532u_spinlock);
+ list_add_rcu(&info->list, &sh532u_info_list);
+ spin_unlock(&sh532u_spinlock);
+ sh532u_pm_init(info);
+ sh532u_pm_dev_wr(info, NVC_PWR_COMM);
+ err = sh532u_dev_id(info);
+ if (err < 0) {
+ dev_err(&client->dev, "%s device not found\n", __func__);
+ sh532u_pm_wr(info, NVC_PWR_OFF);
+ if (info->pdata->cfg & NVC_CFG_NODEV) {
+ sh532u_del(info);
+ return -ENODEV;
+ }
+ } else {
+ dev_dbg(&client->dev, "%s device found\n", __func__);
+ sh532u_calibration(info);
+ if (info->pdata->cfg & NVC_CFG_BOOT_INIT) {
+ /* initial move causes full initialization */
+ sh532u_pos_rel_wr(info, info->cap.focus_infinity);
+ } else {
+ sh532u_pm_wr(info, NVC_PWR_OFF);
+ }
+ }
- if (!stereo_sh532u_info->left) {
- misc_deregister(&sh532u_device);
- kfree(stereo_sh532u_info);
- stereo_sh532u_info = NULL;
+ if (info->pdata->dev_name != 0)
+ strcpy(dname, info->pdata->dev_name);
+ else
+ strcpy(dname, "sh532u");
+ if (info->pdata->num)
+ snprintf(dname, sizeof(dname), "%s.%u",
+ dname, info->pdata->num);
+ info->miscdev.name = dname;
+ info->miscdev.fops = &sh532u_fileops;
+ info->miscdev.minor = MISC_DYNAMIC_MINOR;
+ if (misc_register(&info->miscdev)) {
+ dev_err(&client->dev, "%s unable to register misc device %s\n",
+ __func__, dname);
+ sh532u_del(info);
+ return -ENODEV;
}
return 0;
}
-static const struct i2c_device_id right_sh532u_id[] = {
- { "sh532uR", 0 },
+static const struct i2c_device_id sh532u_id[] = {
+ { "sh532u", 0 },
{ },
};
-MODULE_DEVICE_TABLE(i2c, right_sh532u_id);
+MODULE_DEVICE_TABLE(i2c, sh532u_id);
-static struct i2c_driver right_sh532u_i2c_driver = {
+static struct i2c_driver sh532u_i2c_driver = {
.driver = {
- .name = "sh532uR",
+ .name = "sh532u",
.owner = THIS_MODULE,
},
- .probe = right_sh532u_probe,
- .remove = right_sh532u_remove,
- .id_table = right_sh532u_id,
+ .id_table = sh532u_id,
+ .probe = sh532u_probe,
+ .remove = sh532u_remove,
};
static int __init sh532u_init(void)
{
- int ret;
- pr_info("sh532u focuser driver loading\n");
- ret = i2c_add_driver(&left_sh532u_i2c_driver);
- if (ret)
- return ret;
-
- ret = i2c_add_driver(&right_sh532u_i2c_driver);
-
- return ret;
+ return i2c_add_driver(&sh532u_i2c_driver);
}
static void __exit sh532u_exit(void)
{
- i2c_del_driver(&left_sh532u_i2c_driver);
- i2c_del_driver(&right_sh532u_i2c_driver);
+ i2c_del_driver(&sh532u_i2c_driver);
}
module_init(sh532u_init);
diff --git a/include/media/nvc_focus.h b/include/media/nvc_focus.h
new file mode 100644
index 000000000000..fd83258abab3
--- /dev/null
+++ b/include/media/nvc_focus.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef __NVC_FOCUS_H__
+#define __NVC_FOCUS_H__
+
+enum nvc_focus_sts {
+ NVC_FOCUS_STS_UNKNOWN = 1,
+ NVC_FOCUS_STS_NO_DEVICE,
+ NVC_FOCUS_STS_INITIALIZING,
+ NVC_FOCUS_STS_INIT_ERR,
+ NVC_FOCUS_STS_WAIT_FOR_MOVE_END,
+ NVC_FOCUS_STS_WAIT_FOR_SETTLE,
+ NVC_FOCUS_STS_LENS_SETTLED,
+ NVC_FOCUS_STS_FORCE32 = 0x7FFFFFFF
+};
+
+struct nvc_focus_nvc {
+ __u32 focal_length;
+ __u32 fnumber;
+ __u32 max_aperature;
+} __packed;
+
+struct nvc_focus_cap {
+ __u32 version;
+ __u32 actuator_range;
+ __u32 settle_time;
+ __u32 focus_macro;
+ __u32 focus_hyper;
+ __u32 focus_infinity;
+} __packed;
+
+#endif /* __NVC_FOCUS_H__ */
+
diff --git a/include/media/sh532u.h b/include/media/sh532u.h
index 166caad65f88..19da2070b70f 100644
--- a/include/media/sh532u.h
+++ b/include/media/sh532u.h
@@ -19,36 +19,38 @@
#ifndef __SH532U_H__
#define __SH532U_H__
-#include <linux/ioctl.h> /* For IOCTL macros */
+#include <media/nvc_focus.h>
-#define SH532U_IOCTL_GET_CONFIG _IOR('o', 1, struct sh532u_config)
-#define SH532U_IOCTL_SET_POSITION _IOW('o', 2, u32)
-#define SH532U_IOCTL_GET_MOVE_STATUS _IOW('o', 3, unsigned char)
-#define SH532U_IOCTL_SET_CAMERA_MODE _IOW('o', 4, unsigned char)
-enum sh532u_move_status {
- SH532U_STATE_UNKNOWN = 1,
- SH532U_WAIT_FOR_MOVE_END,
- SH532U_WAIT_FOR_SETTLE,
- SH532U_LENS_SETTLED,
- SH532U_Forced32 = 0x7FFFFFFF
+struct sh532u_platform_data {
+ int cfg;
+ int num;
+ int sync;
+ const char *dev_name;
+ struct nvc_focus_nvc (*nvc);
+ struct nvc_focus_cap (*cap);
+ struct sh532u_pdata_info (*info);
+ __u8 i2c_addr_rom;
+ unsigned gpio_reset;
+/* Due to a Linux limitation, a GPIO is defined to "enable" the device. This
+ * workaround is for when the device's power GPIO's are behind an I2C expander.
+ * The Linux limitation doesn't allow the I2C GPIO expander to be ready for
+ * use when this device is probed.
+ */
+ unsigned gpio_en;
};
-struct sh532u_config {
- __u32 settle_time;
- __u32 focal_length;
- __u32 fnumber;
- s16 pos_low;
- s16 pos_high;
- s16 limit_low;
- s16 limit_high;
+struct sh532u_pdata_info {
+ __s16 pos_low;
+ __s16 pos_high;
+ __s16 limit_low;
+ __s16 limit_high;
+ int move_timeoutms;
+ __u32 focus_hyper_ratio;
+ __u32 focus_hyper_div;
};
-struct sh532u_platform_data {
- void *context_data;
- int (*board_init)(void *context_data);
- int (*board_deinit)(void *context_data);
-};
+
/* Register Definition : Sany Driver IC */
/* EEPROM addresses */
#define addrHallOffset 0x10
@@ -104,8 +106,8 @@ struct sh532u_platform_data {
#define ADHXI_211L 0x01
#define PIDZO_211H 0x02
#define PIDZO_211L 0x03
-#define RZ_211H 0x04
-#define RZ_211L 0x05
+#define RZ_211H 0x04
+#define RZ_211L 0x05
#define DZ1_211H 0x06
#define DZ1_211L 0x07
#define DZ2_211H 0x08
@@ -148,8 +150,8 @@ struct sh532u_platform_data {
#define OZ4_211L 0x2D
#define OZ5_211H 0x2E
#define OZ5_211L 0x2F
-#define oe_211H 0x30
-#define oe_211L 0x31
+#define oe_211H 0x30
+#define oe_211L 0x31
#define MSR1CMAX_211H 0x32
#define MSR1CMAX_211L 0x33
#define MSR1CMIN_211H 0x34
@@ -162,34 +164,34 @@ struct sh532u_platform_data {
#define OFFSET_211L 0x3B
#define ADOFFSET_211H 0x3C
#define ADOFFSET_211L 0x3D
-#define EZ_211H 0x3E
-#define EZ_211L 0x3F
+#define EZ_211H 0x3E
+#define EZ_211L 0x3F
/* Coefficient RAM 40h ~ 7Fh */
-#define ag_211H 0x40
-#define ag_211L 0x41
-#define da_211H 0x42
-#define da_211L 0x43
-#define db_211H 0x44
-#define db_211L 0x45
-#define dc_211H 0x46
-#define dc_211L 0x47
-#define dg_211H 0x48
-#define dg_211L 0x49
-#define pg_211H 0x4A
-#define pg_211L 0x4B
+#define ag_211H 0x40
+#define ag_211L 0x41
+#define da_211H 0x42
+#define da_211L 0x43
+#define db_211H 0x44
+#define db_211L 0x45
+#define dc_211H 0x46
+#define dc_211L 0x47
+#define dg_211H 0x48
+#define dg_211L 0x49
+#define pg_211H 0x4A
+#define pg_211L 0x4B
#define gain1_211H 0x4C
#define gain1_211L 0x4D
#define gain2_211H 0x4E
#define gain2_211L 0x4F
-#define ua_211H 0x50
-#define ua_211L 0x51
-#define uc_211H 0x52
-#define uc_211L 0x53
-#define ia_211H 0x54
-#define ia_211L 0x55
-#define ib_211H 0x56
-#define ib_211L 0x57
+#define ua_211H 0x50
+#define ua_211L 0x51
+#define uc_211H 0x52
+#define uc_211L 0x53
+#define ia_211H 0x54
+#define ia_211L 0x55
+#define ib_211H 0x56
+#define ib_211L 0x57
#define i_c_211H 0x58
#define i_c_211L 0x59
#define ms11a_211H 0x5A
@@ -216,10 +218,10 @@ struct sh532u_platform_data {
#define ms22e_211L 0x6F
#define ms23p_211H 0x70
#define ms23p_211L 0x71
-#define oa_211H 0x72
-#define oa_211L 0x73
-#define oc_211H 0x74
-#define oc_211L 0x75
+#define oa_211H 0x72
+#define oa_211L 0x73
+#define oc_211H 0x74
+#define oc_211L 0x75
#define PX12_211H 0x76
#define PX12_211L 0x77
#define PX3_211H 0x78
@@ -237,7 +239,7 @@ struct sh532u_platform_data {
#define PWMSEL_211 0x82
#define SWTCH_211 0x83
#define STBY_211 0x84
-#define CLR_211 0x85
+#define CLR_211 0x85
#define DSSEL_211 0x86
#define ENBL_211 0x87
#define ANA1_211 0x88
@@ -296,10 +298,10 @@ P0 P1
/* E2P data type define of HVCA Initial Value Section */
#define DIRECT_MODE 0x00
-#define INDIRECT_EEPROM 0x10
+#define INDIRECT_EEPROM 0x10
#define INDIRECT_HVCA 0x20
#define MASK_AND 0x70
-#define MASK_OR 0x80
+#define MASK_OR 0x80
#define DATA_1BYTE 0x01
#define DATA_2BYTE 0x02