/* Copyright (c) 1999-2006 Mark McClelland <mark@ovcam.org>, David Brownell
 * http://ovcam.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. NO WARRANTY OF ANY KIND is expressed or implied.
 */

#ifndef __LINUX_OVFX2_H
#define __LINUX_OVFX2_H

#include <linux/config.h>
#include <asm/uaccess.h>
#include <linux/videodev.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/i2c.h>
#if defined (HAVE_V4L2)
#  include <media/v4l2-common.h>
#endif

#include "ovcamchip.h"

#define OVFX2_DEBUG

#ifdef OVFX2_DEBUG
	#define PDEBUG(level, fmt, args...) \
		if (debug >= (level)) info("[%s:%d] " fmt, \
		__FUNCTION__, __LINE__ , ## args)
#else
	#define PDEBUG(level, fmt, args...) do {} while(0)
#endif

/* This macro restricts an int variable to an inclusive range */
#define RESTRICT_TO_RANGE(v,mi,ma) { \
	if ((v) < (mi)) (v) = (mi); \
	else if ((v) > (ma)) (v) = (ma); \
}

/* --------------------------------- */
/*  DEFINES FOR FX2 AND OTHER CHIPS  */
/* --------------------------------- */

/* USB IDs */
#define VEND_OMNIVISION		0x05a9
#define PROD_2800		0x2800

#define VEND_ORANGE_MICRO	0x0b62
#define PROD_IBOT2		0x0059

#define VEND_APLUX		0x0e96
#define PROD_MU2CAM		0xc001

/* --------------------------------- */
/*      FX2 REGISTER MNEMONICS       */
/* --------------------------------- */

#define REG_I2C_ADDR		0x00
#define REG_SAT			0xe9
#define REG_HUE			0xea
#define REG_CNTR		0xeb
#define REG_BRIGHT		0xec
#define REG_SHARP		0xee

/* --------------------------------- */
/*       MISCELLANEOUS DEFINES       */
/* --------------------------------- */

/* USB bRequest values */
#define OVFX2_REQ_I2C_WRITE	0x02
#define OVFX2_REQ_I2C_READ	0x03
#define OVFX2_REQ_REG_WRITE	0x0a
#define OVFX2_REQ_REG_READ	0x0b

/* Endpoints */
#define OVFX2_INT_ENDPOINT_ADDR		1
#define OVFX2_BULK_ENDPOINT_ADDR	2

/* # of BULK scratch buffers */
#define OVFX2_NUMSBUF		8

#define OVFX2_BULK_SIZE		32768

#define OVFX2_NUMFRAMES		2
#if OVFX2_NUMFRAMES > VIDEO_MAX_FRAME
	#error "OVFX2_NUMFRAMES is too high"
#endif

/* All known registers are only 1 byte */
#define OVFX2_CBUF_SIZE		1

/* Size of usb_make_path() buffer */
#define OVFX2_USB_PATH_LEN	64

/* Bridge types */
enum {
	BRG_UNKNOWN,
	BRG_2800,	/* iBot2, OV "2800" ref design */
	BRG_9000,	/* OV "9000" ref design */
};

enum {
	STATE_SCANNING,		/* Scanning for start */
	STATE_LOCKED,		/* Found start, waiting for data */
	STATE_RECEIVING,	/* Receiving image data */
};

/* Buffer states */
enum {
	BUF_NOT_ALLOCATED,
	BUF_ALLOCATED,
};

struct usb_ovfx2;		/* Forward declaration */

struct ovfx2_sbuf {
	struct usb_ovfx2 *ov;
	unsigned char *data;
	struct urb *urb;
	spinlock_t lock;
	int n;
};

enum {
	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
	FRAME_READY,		/* Ready to start grabbing */
	FRAME_GRABBING,		/* In the process of being grabbed into */
	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
	FRAME_ERROR,		/* Something bad happened while processing */
};

struct regval {
	unsigned char reg;
	unsigned char val;
	unsigned char mask;
};

struct ovfx2_frame {
	int framenum;		/* Index of this frame */
	unsigned char *data;	/* Frame buffer */
	unsigned char *tempdata; /* Temp buffer for multi-stage conversions */
	unsigned char *rawdata;	/* Raw camera data buffer */

	int depth;		/* Bytes per pixel */
	int width;		/* Width application is expecting */
	int height;		/* Height application is expecting */

	int rawwidth;		/* Actual width of frame sent from camera */
	int rawheight;		/* Actual height of frame sent from camera */

	int sub_flag;		/* Sub-capture mode for this frame? */
	unsigned int format;	/* Format for this frame */

	volatile int grabstate;	/* State of grabbing */
	int scanstate;		/* State of scanning */

	int bytes_recvd;	/* Number of image bytes received from camera */
	long bytes_read;	/* Amount that has been read() */

	wait_queue_head_t wq;	/* Processes waiting */
};

struct usb_ovfx2 {
	struct video_device *vdev;
	struct usb_device *dev;

	int customid;
	char *desc;
	char usb_path[OVFX2_USB_PATH_LEN];

	/* Determined by sensor type */
	int maxwidth;
	int maxheight;
	int minwidth;
	int minheight;

	struct semaphore lock;	/* Serializes user-accessible operations */
	int user;		/* user count for exclusive use */
	int present;		/* Device is plugged in */

	int streaming;		/* Are we streaming BULK? */
	int grabbing;		/* Are we grabbing? */

	unsigned char *fbuf;	/* Videodev buffer area */
	unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */
	unsigned char *rawfbuf;	/* Raw camera data buffer area */

	int sub_flag;		/* Pix Array subcapture on flag */
	int subx;		/* Pix Array subcapture x offset */
	int suby;		/* Pix Array subcapture y offset */
	int subw;		/* Pix Array subcapture width */
	int subh;		/* Pix Array subcapture height */

	int bridge;		/* Type of bridge (BRG_*) */
	int sensor;		/* Type of image sensor chip (SEN_*) */

	/* /proc entries, relative to /proc/video/ov511/ */
	struct proc_dir_entry *proc_devdir;   /* Per-device proc directory */
	struct proc_dir_entry *proc_info;     /* <minor#>/info entry */

	/* Framebuffer/sbuf management */
	int buf_state;
	struct semaphore buf_lock;
	int curframe;		/* Frame being written to */
	struct ovfx2_frame frame[OVFX2_NUMFRAMES];	
	struct ovfx2_sbuf sbuf[OVFX2_NUMSBUF];
	wait_queue_head_t wq;	/* Processes waiting */

	/* Stop streaming while changing picture settings */
	int stop_during_set;

	int stopped;		/* Streaming is temporarily paused */

	/* Internal I2C interface */
	struct i2c_client internal_client; /* Client for internal use */

	/* I2C interface to kernel */
	struct i2c_adapter i2c_adap;
	struct i2c_client *sensor_client;
	unsigned char last_slave;	   /* Last accessed I2C slave */

	/* Control transaction stuff */
	unsigned char *cbuf;		/* Buffer for payload */
	struct semaphore cbuf_lock;
};

/* Used to represent a list of values and their respective symbolic names */
struct symbolic_list {
	int num;
	char *name;
};

#define NOT_DEFINED_STR "Unknown"

/* Returns the name of the matching element in the symbolic_list array. The
 * end of the list must be marked with an element that has a NULL name.
 */
static inline char * 
symbolic(struct symbolic_list list[], int num)
{
	int i;

	for (i = 0; list[i].name != NULL; i++)
			if (list[i].num == num)
				return (list[i].name);

	return (NOT_DEFINED_STR);
}

#endif
