/* Shared Code for OmniVision Camera Chip Drivers
 *
 * Copyright (c) 2002 Mark McClelland <mark@alpha.dyndns.org>
 * http://alpha.dyndns.org/ov511/
 *
 * 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 the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 */

/* This file is not a module yet */
#define __NO_VERSION__

#include <linux/config.h>
#include <linux/version.h>

#include "ov511.h"

extern int debug;	/* Exists in ov511.c and ov518.c */

int
ov_write_regvals(struct i2c_client *client, struct ovsensor_regvals *rvals)
{
	int rc;

	while (rvals->reg != 0xff) {
		rc = ov_write(client, rvals->reg, rvals->val);
		if (rc < 0)
			return rc;
		rvals++;
	}

	return 0;
}

/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
 * the same position as 1's in "mask" are cleared and set to "value". Bits
 * that are in the same position as 0's in "mask" are preserved, regardless
 * of their respective state in "value".
 */
int
ov_write_mask(struct i2c_client *client,
	      unsigned char reg,
	      unsigned char value,
	      unsigned char mask)
{
	int rc;
	unsigned char oldval, newval;

	if (mask == 0xff) {
		newval = value;
	} else {
		rc = ov_read(client, reg, &oldval);
		if (rc < 0)
			return rc;

		oldval &= (~mask);		/* Clear the masked bits */
		value &= mask;			/* Enforce mask on value */
		newval = oldval | value;	/* Set the desired bits */
	}

	return ov_write(client, reg, newval);
}

/* Reset the sensor and ensures that I2C is synchronized. Returns <0 if failure.
 */
int
init_ov_sensor(struct i2c_client *client)
{
	int i, success;
	unsigned char high, low;

	/* Reset the sensor */
	ov_write(client, 0x12, 0x80);

	/* Wait for it to initialize */
	schedule_timeout(1 + 150 * HZ / 1000);

	for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) {
		if (ov_read(client, OV7610_REG_ID_HIGH, &high) >= 0) {
			if (ov_read(client, OV7610_REG_ID_LOW, &low) >= 0) {
				if (high == 0x7F && low == 0xA2) {
					success = 1;
					continue;
				}
			}
		}

		/* Reset the sensor */
		ov_write(client, 0x12, 0x80);

		/* Wait for it to initialize */
		schedule_timeout(1 + 150 * HZ / 1000);

		/* Dummy read to sync I2C */
		ov_read(client, 0x00, &low);
	}

	if (!success)
		return -EIO;

	PDEBUG(1, "I2C synced in %d attempt(s)", i);

	return 0;
}
