aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Corbet <corbet@lwn.net>2007-08-17 01:02:33 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-08-20 12:18:04 -0300
commit6d77444aca298b43a88086be446f943cd0442ef7 (patch)
tree3d0b4c4834fcd1048e4f80c5f60847efbe102fdf
parent01659f2a0067d855089811529fa596cbc40f1e75 (diff)
V4L/DVB (6027): Get rid of an ill-behaved msleep in i2c write
Configuring the OLPC camera requires something over 150 register writes. Unfortunately, querying the CAFE i2c controller too soon after a write causes the hardware to flake. The problem had been "solved" with an msleep() call, but, between the number of registers and how msleep() behaves, that resulted in a 3-second delay on camera initialization. Instead, we hand-code a wait for the completion interrupt which avoids reading the status registers. Signed-off-by: Jonathan Corbet <corbet@lwn.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--drivers/media/video/cafe_ccic.c22
-rw-r--r--drivers/media/video/ov7670.c5
2 files changed, 25 insertions, 2 deletions
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 88090107cd4..acf64b19730 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -356,6 +356,7 @@ static int cafe_smbus_write_data(struct cafe_camera *cam,
{
unsigned int rval;
unsigned long flags;
+ DEFINE_WAIT(the_wait);
spin_lock_irqsave(&cam->dev_lock, flags);
rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
@@ -369,10 +370,29 @@ static int cafe_smbus_write_data(struct cafe_camera *cam,
rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
cafe_reg_write(cam, REG_TWSIC1, rval);
spin_unlock_irqrestore(&cam->dev_lock, flags);
- msleep(2); /* Required or things flake */
+ /*
+ * Time to wait for the write to complete. THIS IS A RACY
+ * WAY TO DO IT, but the sad fact is that reading the TWSIC1
+ * register too quickly after starting the operation sends
+ * the device into a place that may be kinder and better, but
+ * which is absolutely useless for controlling the sensor. In
+ * practice we have plenty of time to get into our sleep state
+ * before the interrupt hits, and the worst case is that we
+ * time out and then see that things completed, so this seems
+ * the best way for now.
+ */
+ do {
+ prepare_to_wait(&cam->smbus_wait, &the_wait,
+ TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1); /* even 1 jiffy is too long */
+ finish_wait(&cam->smbus_wait, &the_wait);
+ } while (!cafe_smbus_write_done(cam));
+
+#ifdef IF_THE_CAFE_HARDWARE_WORKED_RIGHT
wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
CAFE_SMBUS_TIMEOUT);
+#endif
spin_lock_irqsave(&cam->dev_lock, flags);
rval = cafe_reg_read(cam, REG_TWSIC1);
spin_unlock_irqrestore(&cam->dev_lock, flags);
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index f8f21ddd984..c4c5bd67f79 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -416,7 +416,10 @@ static int ov7670_read(struct i2c_client *c, unsigned char reg,
static int ov7670_write(struct i2c_client *c, unsigned char reg,
unsigned char value)
{
- return i2c_smbus_write_byte_data(c, reg, value);
+ int ret = i2c_smbus_write_byte_data(c, reg, value);
+ if (reg == REG_COM7 && (value & COM7_RESET))
+ msleep(2); /* Wait for reset to run */
+ return ret;
}