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>
This commit is contained in:
Родитель
01659f2a00
Коммит
6d77444aca
|
@ -356,6 +356,7 @@ static int cafe_smbus_write_data(struct cafe_camera *cam,
|
||||||
{
|
{
|
||||||
unsigned int rval;
|
unsigned int rval;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
DEFINE_WAIT(the_wait);
|
||||||
|
|
||||||
spin_lock_irqsave(&cam->dev_lock, flags);
|
spin_lock_irqsave(&cam->dev_lock, flags);
|
||||||
rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
|
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);
|
rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
|
||||||
cafe_reg_write(cam, REG_TWSIC1, rval);
|
cafe_reg_write(cam, REG_TWSIC1, rval);
|
||||||
spin_unlock_irqrestore(&cam->dev_lock, flags);
|
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),
|
wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
|
||||||
CAFE_SMBUS_TIMEOUT);
|
CAFE_SMBUS_TIMEOUT);
|
||||||
|
#endif
|
||||||
spin_lock_irqsave(&cam->dev_lock, flags);
|
spin_lock_irqsave(&cam->dev_lock, flags);
|
||||||
rval = cafe_reg_read(cam, REG_TWSIC1);
|
rval = cafe_reg_read(cam, REG_TWSIC1);
|
||||||
spin_unlock_irqrestore(&cam->dev_lock, flags);
|
spin_unlock_irqrestore(&cam->dev_lock, flags);
|
||||||
|
|
|
@ -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,
|
static int ov7670_write(struct i2c_client *c, unsigned char reg,
|
||||||
unsigned char value)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче