summaryrefslogtreecommitdiff
path: root/linux/drivers/media
diff options
context:
space:
mode:
authorMichael Hunold <devnull@localhost>2002-12-17 14:56:17 +0000
committerMichael Hunold <devnull@localhost>2002-12-17 14:56:17 +0000
commit1f0cdfa2a5aba72c3c69340a21ff9e8e9afd3fe1 (patch)
treeae992b2fd4a53a84fb8a829f39fbea15174e7742 /linux/drivers/media
parentbb7af53caa24320a10340061af6b9c3f7f4ceb26 (diff)
downloadmediapointer-dvb-s2-1f0cdfa2a5aba72c3c69340a21ff9e8e9afd3fe1.tar.gz
mediapointer-dvb-s2-1f0cdfa2a5aba72c3c69340a21ff9e8e9afd3fe1.tar.bz2
Add the new saa7146 driver core to media/common.
Diffstat (limited to 'linux/drivers/media')
-rw-r--r--linux/drivers/media/common/Kconfig11
-rw-r--r--linux/drivers/media/common/Makefile8
-rw-r--r--linux/drivers/media/common/saa7146.h645
-rw-r--r--linux/drivers/media/common/saa7146_core.c910
-rw-r--r--linux/drivers/media/common/saa7146_hlp.c974
-rw-r--r--linux/drivers/media/common/saa7146_i2c.c426
-rw-r--r--linux/drivers/media/common/saa7146_vbi.c440
-rw-r--r--linux/drivers/media/common/saa7146_video.c1297
8 files changed, 4711 insertions, 0 deletions
diff --git a/linux/drivers/media/common/Kconfig b/linux/drivers/media/common/Kconfig
new file mode 100644
index 000000000..9061b1af1
--- /dev/null
+++ b/linux/drivers/media/common/Kconfig
@@ -0,0 +1,11 @@
+config VIDEO_SAA7146
+ tristate
+ default y if DVB_AV7110=y
+ default m if DVB_AV7110=m
+ depends on VIDEO_DEV && PCI && I2C
+
+config VIDEO_VIDEOBUF
+ tristate
+ default y if VIDEO_SAA7134=y || VIDEO_BT848=y || VIDEO_SAA7146=y
+ default m if VIDEO_SAA7134=m || VIDEO_BT848=m || VIDEO_SAA7146=m
+ depends on VIDEO_DEV
diff --git a/linux/drivers/media/common/Makefile b/linux/drivers/media/common/Makefile
new file mode 100644
index 000000000..5ececd228
--- /dev/null
+++ b/linux/drivers/media/common/Makefile
@@ -0,0 +1,8 @@
+export-objs := video-buf.o
+
+saa7146-objs := saa7146_core.o saa7146_hlp.o saa7146_i2c.o \
+ saa7146_vbi.o saa7146_video.o
+
+obj-$(CONFIG_VIDEO_VIDEOBUF) += video-buf.o
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+
diff --git a/linux/drivers/media/common/saa7146.h b/linux/drivers/media/common/saa7146.h
new file mode 100644
index 000000000..27889ec19
--- /dev/null
+++ b/linux/drivers/media/common/saa7146.h
@@ -0,0 +1,645 @@
+#ifndef __SAA7146__
+#define __SAA7146__
+
+/* fixme: this triggers bugs in video_read() */
+//#define DEBUG_SPINLOCKS 1
+
+#include <linux/module.h> /* for module-version */
+#include <linux/delay.h> /* for delay-stuff */
+#include <linux/slab.h> /* for kmalloc/kfree */
+#include <linux/pci.h> /* for pci-config-stuff, vendor ids etc. */
+#include <linux/wrapper.h> /* for mem_map_reserve */
+#include <linux/init.h> /* for "__init" */
+#include <linux/interrupt.h> /* for IMMEDIATE_BH */
+#include <linux/kmod.h> /* for kernel module loader */
+#include <linux/i2c.h> /* for i2c subsystem */
+#include <linux/i2c-id.h>
+#include <asm/io.h> /* for accessing devices */
+
+#include <linux/videodev2.h>
+#include "video-buf.h"
+
+#define MAX_SAA7146_CAPTURE_BUFFERS 32 /* arbitrary */
+
+#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */
+
+#define SAA7146_VERSION_CODE KERNEL_VERSION(0,5,0)
+
+#define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr)))
+#define saa7146_read(sxy,adr) readl(sxy->mem+(adr))
+
+#define BUG_ON(condition) do { if ((condition)!=0) BUG(); } while(0)
+#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && 0 != dev->ext->vbi)
+
+extern int debug;
+// #define DEBUG_PROLOG printk("(0x%08x): %s: %s(): ",dev == 0 ? 0 : (dev->mem == 0?0:saa7146_read(dev,IER)),KBUILD_MODNAME,__FUNCTION__)
+#define DEBUG_PROLOG printk("%s: %s(): ",KBUILD_MODNAME,__FUNCTION__)
+
+#define DEB_S(x) if (0!=(debug&0x01)) { DEBUG_PROLOG; printk x; } /* simple debug messages */
+#define DEB_D(x) if (0!=(debug&0x02)) { DEBUG_PROLOG; printk x; } /* more detailed debug messages */
+#define DEB_EE(x) if (0!=(debug&0x04)) { DEBUG_PROLOG; printk x; } /* print enter and exit of functions */
+#define DEB_I2C(x) if (0!=(debug&0x08)) { DEBUG_PROLOG; printk x; } /* i2c debug messages */
+#define DEB_VBI(x) if (0!=(debug&0x10)) { DEBUG_PROLOG; printk x; } /* vbi debug messages */
+#define DEB_INT(x) if (0!=(debug&0x20)) { DEBUG_PROLOG; printk x; } /* interrupt debug messages */
+#define DEB_CAP(x) if (0!=(debug&0x40)) { DEBUG_PROLOG; printk x; } /* capture debug messages */
+
+#define ERR(x) { DEBUG_PROLOG; printk x; }
+#define INFO(x) { printk("%s: ",KBUILD_MODNAME); printk x; }
+
+#define IER_DISABLE(x,y) \
+ saa7146_write(x, IER, saa7146_read(x, IER) & ~(y));
+#define IER_ENABLE(x,y) \
+ saa7146_write(x, IER, saa7146_read(x, IER) | (y));
+
+struct saa7146_dev;
+struct saa7146_fh;
+struct saa7146_standard;
+
+struct saa7146_extension_ioctls
+{
+ unsigned int cmd;
+ int flags;
+};
+
+struct saa7146_sub_info {
+ unsigned int subvendor, subdevice;
+};
+
+#define SAA7146_EXCLUSIVE 0x1
+#define SAA7146_BEFORE 0x2
+#define SAA7146_AFTER 0x4
+
+struct saa7146_extension
+{
+ char name[32]; /* name of the device */
+ int inputs;
+ int audios;
+ u32 capabilities;
+
+ /* pairs of subvendor and subdevice ids for
+ supported devices, last entry 0xffff, 0xfff */
+ struct saa7146_sub_info *devices;
+
+ struct module* module;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ void (*inc_use)(struct saa7146_dev*);
+ void (*dec_use)(struct saa7146_dev*);
+#endif
+
+ /* additionally supported transmission standards */
+ struct saa7146_standard *stds;
+ int num_stds;
+ int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *);
+
+ int use_kernel_i2c;
+ int (*vbi)(struct saa7146_dev*); // want vbi
+
+ struct saa7146_extension_ioctls *ioctls;
+
+ /* extension functions */
+ int (*preinit)(struct saa7146_dev*);
+ int (*probe)(struct saa7146_dev*, unsigned int subvendor, unsigned int subdevice);
+
+ int (*attach)(struct saa7146_dev*);
+ int (*detach)(struct saa7146_dev*);
+
+ int (*ioctl)(struct saa7146_dev*, unsigned int cmd, void *arg);
+
+ u32 irq_mask; /* mask to indicate, which irq-events are handled by the extension */
+ void (*irq_func)(struct saa7146_dev*, u32* irq_mask);
+};
+
+struct saa7146_video_dma {
+ u32 base_odd;
+ u32 base_even;
+ u32 prot_addr;
+ u32 pitch;
+ u32 base_page;
+ u32 num_line_byte;
+};
+
+struct saa7146_format {
+ char *name;
+ int pixelformat;
+ u32 trans;
+ u8 depth;
+ unsigned long flags;
+};
+
+struct saa7146_standard
+{
+ char *name;
+ v4l2_std_id id;
+
+ int v_offset;
+ int v_field;
+ int v_calc;
+
+ int h_offset;
+ int h_pixels;
+ int h_calc;
+
+ int v_max_out;
+ int h_max_out;
+};
+
+/* saa7146 page table */
+struct saa7146_pgtable {
+ unsigned int size;
+ u32 *cpu;
+ dma_addr_t dma;
+ /* used for offsets for u,v planes for planar capture modes */
+ unsigned long offset;
+};
+
+/* buffer for one video/vbi frame */
+struct saa7146_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* saa7146 specific */
+ struct v4l2_pix_format *fmt;
+ int (*activate)(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next);
+
+ /* page tables */
+ struct saa7146_pgtable pt[3];
+};
+
+struct saa7146_dmaqueue {
+ struct saa7146_dev *dev;
+ struct saa7146_buf *curr;
+ struct list_head queue;
+ struct timer_list timeout;
+};
+
+struct saa7146_overlay {
+ struct saa7146_fh *fh;
+ struct v4l2_window win;
+ struct v4l2_clip clips[16];
+ int nclips;
+};
+
+/* per open data */
+struct saa7146_fh {
+ struct saa7146_dev *dev;
+ /* if this is a vbi or capture open */
+ enum v4l2_buf_type type;
+
+ /* video overlay */
+ struct saa7146_overlay ov;
+
+ /* video capture */
+ struct videobuf_queue video_q;
+ struct v4l2_pix_format video_fmt;
+
+ /* vbi capture */
+ struct videobuf_queue vbi_q;
+ struct v4l2_vbi_format vbi_fmt;
+ struct timer_list vbi_read_timeout;
+};
+
+struct saa7146_dev
+{
+ struct video_device video_dev;
+ struct video_device vbi_dev;
+
+ /* used for loadable modules */
+ struct module *module;
+
+ struct list_head item;
+
+ /* different device locks */
+ spinlock_t slock;
+ struct semaphore lock;
+ struct semaphore i2c_lock;
+
+ unsigned char* mem; /* pointer to mapped IO memory */
+ int revision; /* chip revision; needed for bug-workarounds*/
+
+ /* pci-device & irq stuff*/
+ char name[32];
+ struct pci_dev *pci;
+
+ /* extension handling */
+ struct saa7146_extension* ext; /* indicates if handled by extension */
+ void* ext_priv; /* pointer for extension private use (most likely some private data) */
+
+ /* i2c-stuff */
+ struct i2c_adapter *i2c_adapter;
+ u32 i2c_bitrate;
+ u32* i2c_mem; /* pointer to i2c memory */
+
+ /* memories */
+ u32* clipping; /* pointer to clipping memory */
+ u32* rps0;
+ u32* rps1;
+
+ /* vbi capture */
+ struct saa7146_dmaqueue vbi_q;
+ /* vbi workaround interrupt queue */
+ wait_queue_head_t vbi_wq;
+ int vbi_fieldcount;
+ struct saa7146_fh *vbi_streaming;
+
+ /* video overlay */
+ struct v4l2_framebuffer ov_fb;
+ struct saa7146_format *ov_fmt;
+ struct saa7146_overlay *ov_data;
+
+ /* video capture */
+ struct saa7146_dmaqueue video_q;
+ struct saa7146_fh *streaming;
+
+ /* common: fixme? shouldn't this be in saa7146_fh?
+ (this leads to a more complicated question: shall the driver
+ store the different settings (for example S_INPUT) for every open
+ and restore it appropriately, or should all settings be common for
+ all opens? currently, we do the latter, like all other
+ drivers do... */
+ struct saa7146_standard *standard;
+ int vflip;
+ int hflip;
+ int current_hps_source;
+ int current_hps_sync;
+
+};
+
+struct saa7146_use_ops {
+ void (*init)(struct saa7146_dev *dev);
+ void(*open)(struct saa7146_dev *dev, struct saa7146_fh *fh);
+ void (*release)(struct saa7146_dev *dev, struct saa7146_fh *fh,struct file *file);
+ void (*irq_done)(struct saa7146_dev *dev, unsigned long status);
+ ssize_t (*read)(struct file *file, char *data, size_t count, loff_t *ppos);
+ int (*capture_begin)(struct saa7146_fh *fh);
+ int (*capture_end)(struct saa7146_fh *fh);
+};
+
+/* from saa7146_i2c.c */
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev);
+int saa7146_i2c_transfer(struct saa7146_dev *saa, const struct i2c_msg msgs[], int num, int retries);
+
+/* from saa7146_hlp.c */
+void saa7146_set_overlay(struct saa7146_dev *dev, struct saa7146_fh *fh, int v);
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next);
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) ;
+
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sync);
+void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data);
+
+/* from saa7146_core.c */
+extern struct list_head devices;
+int saa7146_register_extension(struct saa7146_extension*);
+int saa7146_unregister_extension(struct saa7146_extension*);
+struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc);
+
+void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state);
+void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi);
+int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf);
+void saa7146_buffer_timeout(unsigned long data);
+void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf);
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt);
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt);
+void saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length );
+
+/* from saa7146_video.c */
+extern struct saa7146_use_ops saa7146_video_uops;
+int video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg);
+
+/* from saa7146_vbi.c */
+extern struct saa7146_use_ops saa7146_vbi_uops;
+
+/* saa7146 source inputs */
+#define SAA7146_HPS_SOURCE_PORT_A 0x00
+#define SAA7146_HPS_SOURCE_PORT_B 0x01
+#define SAA7146_HPS_SOURCE_YPB_CPA 0x02
+#define SAA7146_HPS_SOURCE_YPA_CPB 0x03
+
+/* sync inputs */
+#define SAA7146_HPS_SYNC_PORT_A 0x00
+#define SAA7146_HPS_SYNC_PORT_B 0x01
+
+/* number of vertical active lines */
+#define V_ACTIVE_LINES_PAL 576
+#define V_ACTIVE_LINES_NTSC 480
+#define V_ACTIVE_LINES_SECAM 576
+
+/* number of lines in a field for HPS to process */
+#define V_FIELD_PAL 288
+#define V_FIELD_NTSC 240
+#define V_FIELD_SECAM 288
+
+/* number of lines of vertical offset before processing */
+#define V_OFFSET_PAL 0x17
+#define V_OFFSET_NTSC 0x16
+#define V_OFFSET_SECAM 0x14
+
+/* number of horizontal pixels to process */
+#define H_PIXELS_PAL 680
+#define H_PIXELS_NTSC 708
+#define H_PIXELS_SECAM 720
+
+/* horizontal offset of processing window */
+#define H_OFFSET_PAL 0x14
+#define H_OFFSET_NTSC 0x06
+#define H_OFFSET_SECAM 0x14
+
+#define SAA7146_PAL_VALUES V_OFFSET_PAL, V_FIELD_PAL, V_ACTIVE_LINES_PAL, H_OFFSET_PAL, H_PIXELS_PAL, H_PIXELS_PAL+1, V_ACTIVE_LINES_PAL, 768
+#define SAA7146_NTSC_VALUES V_OFFSET_NTSC, V_FIELD_NTSC, V_ACTIVE_LINES_NTSC, H_OFFSET_NTSC, H_PIXELS_NTSC, H_PIXELS_NTSC+1, V_ACTIVE_LINES_NTSC, 640
+#define SAA7146_SECAM_VALUES V_OFFSET_SECAM, V_FIELD_SECAM, V_ACTIVE_LINES_SECAM, H_OFFSET_SECAM, H_PIXELS_SECAM, H_PIXELS_SECAM+1, V_ACTIVE_LINES_SECAM, 768
+
+/* some memory sizes */
+#define SAA7146_CLIPPING_MEM (14*PAGE_SIZE)
+#define SAA7146_RPS_MEM ( 1*PAGE_SIZE)
+#define SAA7146_I2C_MEM ( 1*PAGE_SIZE)
+#define SAA7146_DEBI_MEM ( 8*PAGE_SIZE)
+
+/* some i2c constants */
+#define SAA7146_I2C_TIMEOUT 100 /* i2c-timeout-value in ms */
+#define SAA7146_I2C_RETRIES 3 /* how many times shall we retry an i2c-operation? */
+#define SAA7146_I2C_DELAY 5 /* time we wait after certain i2c-operations */
+
+/* unsorted defines */
+#define ME1 0x0000000800
+#define PV1 0x0000000008
+
+/* some defines for the various clipping-modes */
+#define SAA7146_CLIPPING_RECT 0x4
+#define SAA7146_CLIPPING_RECT_INVERTED 0x5
+#define SAA7146_CLIPPING_MASK 0x6
+#define SAA7146_CLIPPING_MASK_INVERTED 0x7
+
+/* define for the register programming sequencer (rps) */
+#define CMD_NOP 0x00000000 /* No operation */
+#define CMD_CLR_EVENT 0x00000000 /* Clear event */
+#define CMD_SET_EVENT 0x10000000 /* Set signal event */
+#define CMD_PAUSE 0x20000000 /* Pause */
+#define CMD_CHECK_LATE 0x30000000 /* Check late */
+#define CMD_UPLOAD 0x40000000 /* Upload */
+#define CMD_STOP 0x50000000 /* Stop */
+#define CMD_INTERRUPT 0x60000000 /* Interrupt */
+#define CMD_JUMP 0x80000000 /* Jump */
+#define CMD_WR_REG 0x90000000 /* Write (load) register */
+#define CMD_RD_REG 0xa0000000 /* Read (store) register */
+#define CMD_WR_REG_MASK 0xc0000000 /* Write register with mask */
+
+#define CMD_OAN MASK_27
+#define CMD_INV MASK_26
+#define CMD_SIG4 MASK_25
+#define CMD_SIG3 MASK_24
+#define CMD_SIG2 MASK_23
+#define CMD_SIG1 MASK_22
+#define CMD_SIG0 MASK_21
+#define CMD_O_FID_B MASK_14
+#define CMD_E_FID_B MASK_13
+#define CMD_O_FID_A MASK_12
+#define CMD_E_FID_A MASK_11
+
+/* output formats: each entry holds four informations */
+#define RGB08_COMPOSED 0x0217 /* composed is used in the sense of "not-planar" */
+/* this means: planar?=0, yuv2rgb-conversation-mode=2, dither=yes(=1), format-mode = 7 */
+#define RGB15_COMPOSED 0x0213
+#define RGB16_COMPOSED 0x0210
+#define RGB24_COMPOSED 0x0201
+#define RGB32_COMPOSED 0x0202
+
+#define Y8 0x0006
+#define YUV411_COMPOSED 0x0003
+#define YUV422_COMPOSED 0x0000
+/* this means: planar?=1, yuv2rgb-conversion-mode=0, dither=no(=0), format-mode = b */
+#define YUV411_DECOMPOSED 0x100b
+#define YUV422_DECOMPOSED 0x1009
+#define YUV420_DECOMPOSED 0x100a
+
+#define IS_PLANAR(x) (x & 0xf000)
+
+/* misc defines */
+#define SAA7146_NO_SWAP (0x0)
+#define SAA7146_TWO_BYTE_SWAP (0x1)
+#define SAA7146_FOUR_BYTE_SWAP (0x2)
+
+/* Bit mask constants */
+#define MASK_00 0x00000001 /* Mask value for bit 0 */
+#define MASK_01 0x00000002 /* Mask value for bit 1 */
+#define MASK_02 0x00000004 /* Mask value for bit 2 */
+#define MASK_03 0x00000008 /* Mask value for bit 3 */
+#define MASK_04 0x00000010 /* Mask value for bit 4 */
+#define MASK_05 0x00000020 /* Mask value for bit 5 */
+#define MASK_06 0x00000040 /* Mask value for bit 6 */
+#define MASK_07 0x00000080 /* Mask value for bit 7 */
+#define MASK_08 0x00000100 /* Mask value for bit 8 */
+#define MASK_09 0x00000200 /* Mask value for bit 9 */
+#define MASK_10 0x00000400 /* Mask value for bit 10 */
+#define MASK_11 0x00000800 /* Mask value for bit 11 */
+#define MASK_12 0x00001000 /* Mask value for bit 12 */
+#define MASK_13 0x00002000 /* Mask value for bit 13 */
+#define MASK_14 0x00004000 /* Mask value for bit 14 */
+#define MASK_15 0x00008000 /* Mask value for bit 15 */
+#define MASK_16 0x00010000 /* Mask value for bit 16 */
+#define MASK_17 0x00020000 /* Mask value for bit 17 */
+#define MASK_18 0x00040000 /* Mask value for bit 18 */
+#define MASK_19 0x00080000 /* Mask value for bit 19 */
+#define MASK_20 0x00100000 /* Mask value for bit 20 */
+#define MASK_21 0x00200000 /* Mask value for bit 21 */
+#define MASK_22 0x00400000 /* Mask value for bit 22 */
+#define MASK_23 0x00800000 /* Mask value for bit 23 */
+#define MASK_24 0x01000000 /* Mask value for bit 24 */
+#define MASK_25 0x02000000 /* Mask value for bit 25 */
+#define MASK_26 0x04000000 /* Mask value for bit 26 */
+#define MASK_27 0x08000000 /* Mask value for bit 27 */
+#define MASK_28 0x10000000 /* Mask value for bit 28 */
+#define MASK_29 0x20000000 /* Mask value for bit 29 */
+#define MASK_30 0x40000000 /* Mask value for bit 30 */
+#define MASK_31 0x80000000 /* Mask value for bit 31 */
+
+#define MASK_B0 0x000000ff /* Mask value for byte 0 */
+#define MASK_B1 0x0000ff00 /* Mask value for byte 1 */
+#define MASK_B2 0x00ff0000 /* Mask value for byte 2 */
+#define MASK_B3 0xff000000 /* Mask value for byte 3 */
+
+#define MASK_W0 0x0000ffff /* Mask value for word 0 */
+#define MASK_W1 0xffff0000 /* Mask value for word 1 */
+
+#define MASK_PA 0xfffffffc /* Mask value for physical address */
+#define MASK_PR 0xfffffffe /* Mask value for protection register */
+#define MASK_ER 0xffffffff /* Mask value for the entire register */
+
+#define MASK_NONE 0x00000000 /* No mask */
+
+/* register aliases */
+#define BASE_ODD1 0x00 /* Video DMA 1 registers */
+#define BASE_EVEN1 0x04
+#define PROT_ADDR1 0x08
+#define PITCH1 0x0C
+#define BASE_PAGE1 0x10 /* Video DMA 1 base page */
+#define NUM_LINE_BYTE1 0x14
+
+#define BASE_ODD2 0x18 /* Video DMA 2 registers */
+#define BASE_EVEN2 0x1C
+#define PROT_ADDR2 0x20
+#define PITCH2 0x24
+#define BASE_PAGE2 0x28 /* Video DMA 2 base page */
+#define NUM_LINE_BYTE2 0x2C
+
+#define BASE_ODD3 0x30 /* Video DMA 3 registers */
+#define BASE_EVEN3 0x34
+#define PROT_ADDR3 0x38
+#define PITCH3 0x3C
+#define BASE_PAGE3 0x40 /* Video DMA 3 base page */
+#define NUM_LINE_BYTE3 0x44
+
+#define PCI_BT_V1 0x48 /* Video/FIFO 1 */
+#define PCI_BT_V2 0x49 /* Video/FIFO 2 */
+#define PCI_BT_V3 0x4A /* Video/FIFO 3 */
+#define PCI_BT_DEBI 0x4B /* DEBI */
+#define PCI_BT_A 0x4C /* Audio */
+
+#define DD1_INIT 0x50 /* Init setting of DD1 interface */
+
+#define DD1_STREAM_B 0x54 /* DD1 B video data stream handling */
+#define DD1_STREAM_A 0x56 /* DD1 A video data stream handling */
+
+#define BRS_CTRL 0x58 /* BRS control register */
+#define HPS_CTRL 0x5C /* HPS control register */
+#define HPS_V_SCALE 0x60 /* HPS vertical scale */
+#define HPS_V_GAIN 0x64 /* HPS vertical ACL and gain */
+#define HPS_H_PRESCALE 0x68 /* HPS horizontal prescale */
+#define HPS_H_SCALE 0x6C /* HPS horizontal scale */
+#define BCS_CTRL 0x70 /* BCS control */
+#define CHROMA_KEY_RANGE 0x74
+#define CLIP_FORMAT_CTRL 0x78 /* HPS outputs formats & clipping */
+
+#define DEBI_CONFIG 0x7C
+#define DEBI_COMMAND 0x80
+#define DEBI_PAGE 0x84
+#define DEBI_AD 0x88
+
+#define I2C_TRANSFER 0x8C
+#define I2C_STATUS 0x90
+
+#define BASE_A1_IN 0x94 /* Audio 1 input DMA */
+#define PROT_A1_IN 0x98
+#define PAGE_A1_IN 0x9C
+
+#define BASE_A1_OUT 0xA0 /* Audio 1 output DMA */
+#define PROT_A1_OUT 0xA4
+#define PAGE_A1_OUT 0xA8
+
+#define BASE_A2_IN 0xAC /* Audio 2 input DMA */
+#define PROT_A2_IN 0xB0
+#define PAGE_A2_IN 0xB4
+
+#define BASE_A2_OUT 0xB8 /* Audio 2 output DMA */
+#define PROT_A2_OUT 0xBC
+#define PAGE_A2_OUT 0xC0
+
+#define RPS_PAGE0 0xC4 /* RPS task 0 page register */
+#define RPS_PAGE1 0xC8 /* RPS task 1 page register */
+
+#define RPS_THRESH0 0xCC /* HBI threshold for task 0 */
+#define RPS_THRESH1 0xD0 /* HBI threshold for task 1 */
+
+#define RPS_TOV0 0xD4 /* RPS timeout for task 0 */
+#define RPS_TOV1 0xD8 /* RPS timeout for task 1 */
+
+#define IER 0xDC /* Interrupt enable register */
+
+#define GPIO_CTRL 0xE0 /* GPIO 0-3 register */
+
+#define EC1SSR 0xE4 /* Event cnt set 1 source select */
+#define EC2SSR 0xE8 /* Event cnt set 2 source select */
+#define ECT1R 0xEC /* Event cnt set 1 thresholds */
+#define ECT2R 0xF0 /* Event cnt set 2 thresholds */
+
+#define ACON1 0xF4
+#define ACON2 0xF8
+
+#define MC1 0xFC /* Main control register 1 */
+#define MC2 0x100 /* Main control register 2 */
+
+#define RPS_ADDR0 0x104 /* RPS task 0 address register */
+#define RPS_ADDR1 0x108 /* RPS task 1 address register */
+
+#define ISR 0x10C /* Interrupt status register */
+#define PSR 0x110 /* Primary status register */
+#define SSR 0x114 /* Secondary status register */
+
+#define EC1R 0x118 /* Event counter set 1 register */
+#define EC2R 0x11C /* Event counter set 2 register */
+
+#define PCI_VDP1 0x120 /* Video DMA pointer of FIFO 1 */
+#define PCI_VDP2 0x124 /* Video DMA pointer of FIFO 2 */
+#define PCI_VDP3 0x128 /* Video DMA pointer of FIFO 3 */
+#define PCI_ADP1 0x12C /* Audio DMA pointer of audio out 1 */
+#define PCI_ADP2 0x130 /* Audio DMA pointer of audio in 1 */
+#define PCI_ADP3 0x134 /* Audio DMA pointer of audio out 2 */
+#define PCI_ADP4 0x138 /* Audio DMA pointer of audio in 2 */
+#define PCI_DMA_DDP 0x13C /* DEBI DMA pointer */
+
+#define LEVEL_REP 0x140,
+#define A_TIME_SLOT1 0x180, /* from 180 - 1BC */
+#define A_TIME_SLOT2 0x1C0, /* from 1C0 - 1FC */
+
+/* isr masks */
+#define SPCI_PPEF 0x80000000 /* PCI parity error */
+#define SPCI_PABO 0x40000000 /* PCI access error (target or master abort) */
+#define SPCI_PPED 0x20000000 /* PCI parity error on 'real time data' */
+#define SPCI_RPS_I1 0x10000000 /* Interrupt issued by RPS1 */
+#define SPCI_RPS_I0 0x08000000 /* Interrupt issued by RPS0 */
+#define SPCI_RPS_LATE1 0x04000000 /* RPS task 1 is late */
+#define SPCI_RPS_LATE0 0x02000000 /* RPS task 0 is late */
+#define SPCI_RPS_E1 0x01000000 /* RPS error from task 1 */
+#define SPCI_RPS_E0 0x00800000 /* RPS error from task 0 */
+#define SPCI_RPS_TO1 0x00400000 /* RPS timeout task 1 */
+#define SPCI_RPS_TO0 0x00200000 /* RPS timeout task 0 */
+#define SPCI_UPLD 0x00100000 /* RPS in upload */
+#define SPCI_DEBI_S 0x00080000 /* DEBI status */
+#define SPCI_DEBI_E 0x00040000 /* DEBI error */
+#define SPCI_IIC_S 0x00020000 /* I2C status */
+#define SPCI_IIC_E 0x00010000 /* I2C error */
+#define SPCI_A2_IN 0x00008000 /* Audio 2 input DMA protection / limit */
+#define SPCI_A2_OUT 0x00004000 /* Audio 2 output DMA protection / limit */
+#define SPCI_A1_IN 0x00002000 /* Audio 1 input DMA protection / limit */
+#define SPCI_A1_OUT 0x00001000 /* Audio 1 output DMA protection / limit */
+#define SPCI_AFOU 0x00000800 /* Audio FIFO over- / underflow */
+#define SPCI_V_PE 0x00000400 /* Video protection address */
+#define SPCI_VFOU 0x00000200 /* Video FIFO over- / underflow */
+#define SPCI_FIDA 0x00000100 /* Field ID video port A */
+#define SPCI_FIDB 0x00000080 /* Field ID video port B */
+#define SPCI_PIN3 0x00000040 /* GPIO pin 3 */
+#define SPCI_PIN2 0x00000020 /* GPIO pin 2 */
+#define SPCI_PIN1 0x00000010 /* GPIO pin 1 */
+#define SPCI_PIN0 0x00000008 /* GPIO pin 0 */
+#define SPCI_ECS 0x00000004 /* Event counter 1, 2, 4, 5 */
+#define SPCI_EC3S 0x00000002 /* Event counter 3 */
+#define SPCI_EC0S 0x00000001 /* Event counter 0 */
+
+/* i2c */
+#define SAA7146_I2C_ABORT (1<<7)
+#define SAA7146_I2C_SPERR (1<<6)
+#define SAA7146_I2C_APERR (1<<5)
+#define SAA7146_I2C_DTERR (1<<4)
+#define SAA7146_I2C_DRERR (1<<3)
+#define SAA7146_I2C_AL (1<<2)
+#define SAA7146_I2C_ERR (1<<1)
+#define SAA7146_I2C_BUSY (1<<0)
+
+#define SAA7146_I2C_START (0x3)
+#define SAA7146_I2C_CONT (0x2)
+#define SAA7146_I2C_STOP (0x1)
+#define SAA7146_I2C_NOP (0x0)
+
+#define SAA7146_I2C_BUS_BIT_RATE_6400 (0x500)
+#define SAA7146_I2C_BUS_BIT_RATE_3200 (0x100)
+#define SAA7146_I2C_BUS_BIT_RATE_480 (0x400)
+#define SAA7146_I2C_BUS_BIT_RATE_320 (0x600)
+#define SAA7146_I2C_BUS_BIT_RATE_240 (0x700)
+#define SAA7146_I2C_BUS_BIT_RATE_120 (0x000)
+#define SAA7146_I2C_BUS_BIT_RATE_80 (0x200)
+#define SAA7146_I2C_BUS_BIT_RATE_60 (0x300)
+
+#endif
+
diff --git a/linux/drivers/media/common/saa7146_core.c b/linux/drivers/media/common/saa7146_core.c
new file mode 100644
index 000000000..3c4717755
--- /dev/null
+++ b/linux/drivers/media/common/saa7146_core.c
@@ -0,0 +1,910 @@
+/*
+ saa7146.c - video4linux driver for saa7146-based video hardware
+
+ Copyright (C) 1998-2002 Michael Hunold <michael@mihu.de>
+
+ With ideas taken from: "device driver for philips saa7134 based TV cards"
+ (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+
+ 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "saa7146.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "saa7146"
+#endif
+
+/* global variables */
+struct list_head devices;
+struct semaphore devices_lock;
+
+int saa7146_num = 0;
+/* fixme */
+int debug = 255;
+
+#ifdef MODULE
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "debug level (default: 0)");
+#endif
+
+static void dump_registers(struct saa7146_dev* dev)
+{
+ int i = 0;
+
+ INFO((" @ %li jiffies:\n",jiffies));
+ for(i = 0; i <= 0x148; i+=4) {
+ printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
+ }
+}
+
+/********************************************************************************/
+/* common dma and page table functions */
+
+void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf)
+{
+ DEB_EE(("dev:%p, buf:%p\n",dev,buf));
+
+ if (in_interrupt())
+ BUG();
+
+ videobuf_waiton(&buf->vb,0,0);
+ videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma);
+ videobuf_dma_free(&buf->vb.dma);
+ buf->vb.state = STATE_NEEDS_INIT;
+}
+
+#define SAA7146_PGTABLE_SIZE 4096
+
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+//fm DEB_EE(("pci:%p, pt:%p\n",pci,pt));
+
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+//fm DEB_EE(("pci:%p, pt:%p\n",pci,pt));
+
+ cpu = pci_alloc_consistent(pci, SAA7146_PGTABLE_SIZE, &dma_addr);
+ if (NULL == cpu) {
+//fm ERR(("pci_alloc_consistent() failed."));
+ return -ENOMEM;
+ }
+ pt->size = SAA7146_PGTABLE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+
+ return 0;
+}
+
+void saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length )
+{
+ u32 *ptr, fill;
+ int i,p;
+
+//fm DEB_EE(("pci:%p, pt:%p, sl:%p, len:%d\n",pci,pt,list,length));
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt->offset = list->offset;
+
+ ptr = pt->cpu;
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < list->length; p++, ptr++) {
+ *ptr = sg_dma_address(list) - list->offset;
+ }
+ }
+
+
+ /* safety; fill the page table up with the last valid page */
+ fill = *(ptr-1);
+ for(;i<1024;i++) {
+ *ptr++ = fill;
+ }
+/*
+ ptr = pt->cpu;
+ for(j=0;j<60;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr[j]);
+ }
+*/
+}
+
+/********************************************************************************/
+/* file operations */
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = minor(inode->i_rdev);
+ struct saa7146_dev *h = NULL, *dev = NULL;
+ struct list_head *list;
+ struct saa7146_fh *fh = NULL;
+ int result = 0;
+
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ DEB_EE(("inode:%p, file:%p, minor:%d\n",inode,file,minor));
+
+ down(&devices_lock);
+ list_for_each(list,&devices) {
+ h = list_entry(list, struct saa7146_dev, item);
+ DEB_D(("trying: %p @ major %d,%d\n",h,h->video_dev.minor,h->vbi_dev.minor));
+ if (h->video_dev.minor == minor) {
+ dev = h;
+ }
+ if (h->vbi_dev.minor == minor) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ dev = h;
+ }
+ }
+ DEB_D(("using: %p\n",dev));
+
+ if (NULL == dev) {
+ DEB_S(("no such video device.\n"));
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* check if an extension is registered */
+ if( NULL == dev->ext ) {
+ DEB_S(("no extension registered for this device.\n"));
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* allocate per open data */
+ fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ DEB_S(("cannot allocate memory for per open data.\n"));
+ result = -ENOMEM;
+ goto out;
+ }
+ memset(fh,0,sizeof(*fh));
+
+ /* fixme: increase some usage counts */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ dev->ext->inc_use(dev);
+ MOD_INC_USE_COUNT;
+#else
+ if( 0 == try_module_get(dev->ext->module)) {
+ result = -EINVAL;
+ goto out;
+ }
+#endif
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+
+ dev->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ dev->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+
+ saa7146_video_uops.open(dev,fh);
+ if( 0 != BOARD_CAN_DO_VBI(dev) ) {
+ saa7146_vbi_uops.open(dev,fh);
+ }
+
+ result = 0;
+out:
+ if( fh != 0 && result != 0 ) {
+ kfree(fh);
+ }
+ up(&devices_lock);
+ return result;
+}
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_EE(("inode:%p, file:%p\n",inode,file));
+
+ down(&devices_lock);
+
+ saa7146_video_uops.release(dev,fh,file);
+ if( 0 != BOARD_CAN_DO_VBI(dev) ) {
+ saa7146_vbi_uops.release(dev,fh,file);
+ }
+
+ /* fixme: decrease some usage counts */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ dev->ext->dec_use(dev);
+ MOD_DEC_USE_COUNT;
+#else
+ module_put(dev->ext->module);
+#endif
+ file->private_data = NULL;
+ kfree(fh);
+
+ up(&devices_lock);
+
+ return 0;
+}
+
+static int video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+/*
+ DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
+*/
+ return video_usercopy(inode, file, cmd, arg, video_do_ioctl);
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct videobuf_queue *q;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
+ q = &fh->video_q;
+ break;
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ return 0;
+ }
+ return videobuf_mmap_mapper(vma,q);
+}
+
+static unsigned int video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct videobuf_buffer *buf = NULL;
+ struct videobuf_queue *q;
+
+ DEB_EE(("file:%p, poll:%p\n",file, wait));
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if( 0 == fh->vbi_q.streaming )
+ return videobuf_poll_stream(file, &fh->vbi_q, wait);
+ q = &fh->vbi_q;
+ } else {
+ q = &fh->video_q;
+ }
+
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+
+ if (!buf) {
+ return POLLERR;
+ }
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
+ return POLLIN|POLLRDNORM;
+ }
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%d\n",file, data, count));
+ return saa7146_video_uops.read(file,data,count,ppos);
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%d\n",file, data, count));
+ return saa7146_vbi_uops.read(file,data,count,ppos);
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static struct file_operations video_fops =
+{
+ owner: THIS_MODULE,
+ open: video_open,
+ release: video_release,
+ read: video_read,
+ poll: video_poll,
+ mmap: video_mmap,
+ ioctl: video_ioctl,
+ llseek: no_llseek,
+};
+
+/********************************************************************************/
+/* interrupt handler */
+
+static void interrupt_hw(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct saa7146_dev *dev = (struct saa7146_dev*)dev_id;
+ u32 isr = 0;
+
+ /* read out the interrupt status register */
+ isr = saa7146_read(dev, ISR);
+
+ /* is this our interrupt? */
+ if ( 0 == isr ) {
+ /* nope, some other device */
+ return;
+ }
+
+ saa7146_write(dev, ISR, isr);
+
+ if( 0 != (dev->ext->irq_mask & isr )) {
+ if( 0 != dev->ext->irq_func ) {
+ dev->ext->irq_func(dev, &isr);
+ }
+ isr &= ~dev->ext->irq_mask;
+ }
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT(("irq: RPS0.\n"));
+ saa7146_video_uops.irq_done(dev,isr);
+ isr &= ~MASK_27;
+ }
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ isr &= ~MASK_28;
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT(("irq: RPS1 vbi workaround.\n"));
+ wake_up(&dev->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT(("irq: RPS1.\n"));
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+ if( 0 != isr ) {
+ ERR(("warning: interrupt enabled, but not handled properly. (0x%08x)\n",isr));
+ }
+}
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+#if DEBUG_SPINLOCKS
+ BUG_ON(!spin_is_locked(&dev->slock));
+#endif
+ DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
+
+ if( NULL == q ) {
+ ERR(("internal error: fatal NULL pointer for q.\n"));
+ return 0;
+ }
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D(("immediately activating buffer %p\n", buf));
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = STATE_QUEUED;
+ DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+#if DEBUG_SPINLOCKS
+ BUG_ON(!spin_is_locked(&dev->slock));
+#endif
+ if( NULL == q->curr ) {
+ ERR(("internal error: fatal NULL pointer for q->curr.\n"));
+ return;
+ }
+
+ DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
+
+ /* finish current buffer */
+ q->curr->vb.state = state;
+ do_gettimeofday(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+
+ q->curr = NULL;
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ if( NULL == q ) {
+ ERR(("internal error: fatal NULL pointer for q.\n"));
+ return;
+ }
+
+ DEB_EE(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
+
+#if DEBUG_SPINLOCKS
+ BUG_ON(!spin_is_locked(&dev->slock));
+#endif
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
+ q->curr = buf;
+ DEB_D(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_D(("no next buffer. stopping.\n"));
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, virt_to_bus(&dev->rps0[ 0]));
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+ }
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(unsigned long data)
+{
+ struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D(("timeout on %p\n", q->curr));
+ saa7146_buffer_finish(dev,q,STATE_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+/*
+ saa7146_buffer_next(dev,q);
+*/
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/*********************************************************************************/
+/* extension handling functions */
+
+static struct video_device device_template =
+{
+ .hardware = VID_HARDWARE_SAA7146,
+ .fops = &video_fops,
+ .minor = -1,
+};
+
+int saa7146_register_extension(struct saa7146_extension* ext)
+{
+ struct saa7146_dev *dev = NULL;
+ struct list_head *list = NULL;;
+
+ DEB_EE(("ext:%p\n",ext));
+
+ down(&devices_lock);
+ list_for_each(list,&devices) {
+ int i = 0;
+ int found = 0;
+ dev = list_entry(list, struct saa7146_dev, item);
+
+ /* check if already handled by extension */
+ if( 0 != dev->ext ) {
+ continue;
+ }
+
+ dev->vbi_dev.minor = -1;
+ dev->video_dev.minor = -1;
+
+ DEB_S(("Trying device %p...\n",dev));
+
+ /* first check the subvendor and subdevice ids */
+ for(i = 0;;i++) {
+ if( 0xffff == ext->devices[i].subvendor
+ && 0xffff == ext->devices[i].subdevice ) {
+ break;
+ }
+ if( ext->devices[i].subvendor == dev->pci->subsystem_vendor
+ && ext->devices[i].subdevice == dev->pci->subsystem_device ) {
+ found = 1;
+ break;
+ }
+ }
+ if( 0 == found ) {
+ DEB_S(("extension %p does not handle this device. skipping.\n",ext));
+ continue;
+ }
+
+ if( 0 != ext->preinit(dev) ) {
+ DEB_S(("ext->preinit() failed for %p. skipping.\n",dev));
+ continue;
+ }
+
+ if( 0 != ext->use_kernel_i2c ) {
+ /* register new i2c-bus */
+ if(i2c_add_adapter(dev->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ continue;
+ }
+ }
+
+ if( 0 != ext->probe(dev, dev->pci->subsystem_vendor, dev->pci->subsystem_device) ) {
+ DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
+ if( 0 != ext->use_kernel_i2c )
+ i2c_del_adapter(dev->i2c_adapter);
+ continue;
+ }
+
+ dev->ext = ext;
+
+ if( 0 != ext->attach(dev) ) {
+ DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
+ if( 0 != ext->use_kernel_i2c )
+ i2c_del_adapter(dev->i2c_adapter);
+ dev->ext = NULL;
+ continue;
+ }
+
+ /* v4l2 initialization stuff */
+ dev->video_dev = device_template;
+ strncpy(dev->video_dev.name, ext->name, 32);
+ dev->video_dev.priv = dev;
+
+ // fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
+ if (video_register_device(&dev->video_dev,VFL_TYPE_GRABBER,-1) < 0) {
+ ERR(("cannot register capture v4l2 device. skipping.\n"));
+ /* in this case the extension has probably
+ already "done something", but the v4l2-device
+ could not be registered. this is not as bad as
+ it looks - you cannot access the device.
+ we simply call the 'release' function. */
+ ext->detach(dev);
+ if( 0 != ext->use_kernel_i2c )
+ i2c_del_adapter(dev->i2c_adapter);
+ dev->ext = NULL;
+ continue;
+ }
+ INFO(("%s: registered device video%d [v4l2],%d\n", dev->name,dev->video_dev.minor & 0x1f,dev->vbi_dev.minor & 0x1f));
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if( 0 != BOARD_CAN_DO_VBI(dev)) {
+ dev->vbi_dev = device_template;
+ strncpy(dev->vbi_dev.name, ext->name, 32);
+ dev->vbi_dev.priv = dev;
+ if (video_register_device(&dev->vbi_dev,VFL_TYPE_VBI,-1) < 0) {
+ ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ }
+ INFO(("%s: registered device vbi%d [v4l2]\n", dev->name,dev->vbi_dev.minor & 0x1f));
+ }
+ }
+ up(&devices_lock);
+
+ return 0;
+}
+
+int saa7146_unregister_extension(struct saa7146_extension* ext)
+{
+ struct saa7146_dev *dev = NULL;
+ struct list_head *list = NULL;;
+
+ DEB_EE(("ext:%p\n",ext));
+
+ down(&devices_lock);
+ list_for_each(list,&devices) {
+ dev = list_entry(list, struct saa7146_dev, item);
+
+ /* check if handled by this extension */
+ if( ext != dev->ext ) {
+ continue;
+ }
+
+ if( 0 != ext->detach(dev) ) {
+ DEB_D(("ext->detach() failed. ignoring.\n"));
+ }
+
+ if( 0 != ext->use_kernel_i2c )
+ i2c_del_adapter(dev->i2c_adapter);
+
+ video_unregister_device(&dev->video_dev);
+ if( 0 != BOARD_CAN_DO_VBI(dev)) {
+ video_unregister_device(&dev->vbi_dev);
+ }
+
+ dev->ext = NULL;
+
+ /* make sure to erase the minor number... */
+ dev->video_dev = device_template;
+
+// dump_registers(dev);
+
+ }
+ up(&devices_lock);
+
+ return 0;
+}
+
+/*********************************************************************************/
+/* configuration-functions */
+
+static int config_a_device(struct pci_dev *pci)
+{
+ unsigned long adr = 0, len = 0;
+ struct saa7146_dev* dev = (struct saa7146_dev*)kmalloc(sizeof(struct saa7146_dev),GFP_KERNEL);
+
+ if( NULL == dev) {
+ ERR(("out of memory.\n"));
+ return -ENOMEM;
+ }
+ /* clear out mem for sure */
+ memset(dev, 0x0, sizeof(struct saa7146_dev));
+
+ DEB_EE(("pci:%p\n",pci));
+
+ if (pci_enable_device(pci)) {
+ ERR(("pci_enable_device() failed.\n"));
+ kfree(dev);
+ return -EIO;
+ }
+
+ /* enable bus-mastering */
+ pci_set_master(pci);
+
+ dev->pci = pci;
+ /* get chip-revision; this is needed to enable bug-fixes */
+ if( 0 > pci_read_config_dword(dev->pci, PCI_CLASS_REVISION, &dev->revision)) {
+ ERR(("pci_read_config_dword() failed.\n"));
+ kfree(dev);
+ return -ENODEV;
+ }
+ dev->revision &= 0xf;
+
+ /* remap the memory from virtual to physical adress */
+ adr = pci_resource_start(pci,0);
+ len = pci_resource_len(pci,0);
+
+ if (!request_mem_region(pci_resource_start(pci,0), pci_resource_len(pci,0), "saa7146")) {
+ ERR(("request_mem_region() failed.\n"));
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ dev->mem=ioremap(adr,len);
+ if ( 0 == dev->mem ) {
+ ERR(("ioremap() failed.\n"));
+ release_mem_region(adr,len);
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ /* we don't do a master reset here anymore, it screws up
+ some boards that don't have an i2c-eeprom for configuration
+ values */
+/*
+ saa7146_write(dev, MC1, MASK_31);
+*/
+
+ /* disable alle irqs, clear irq-mask */
+ saa7146_write(dev, ISR, 0xffffffff);
+
+ /* shut down all dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* clear out any rps-signals pending */
+ saa7146_write(dev, MC2, 0xf8000000);
+
+ /* request an interrupt for the saa7146 */
+ if( 0 != request_irq(dev->pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT, dev->name, (void *)dev)) {
+ ERR(("request_irq() failed.\n"));
+ iounmap(dev->mem);
+ release_mem_region(adr,len);
+ kfree(dev);
+ return -EINVAL;
+ }
+
+ /* get memory for various stuff */
+ dev->rps0 = (u32*)kmalloc(SAA7146_RPS_MEM, GFP_KERNEL);
+ if( NULL == dev->rps0 )
+ goto kmalloc_error_1;
+ memset(dev->rps0, 0x0, SAA7146_RPS_MEM);
+
+ dev->rps1 = (u32*)kmalloc(SAA7146_RPS_MEM, GFP_KERNEL);
+ if( NULL == dev->rps1 )
+ goto kmalloc_error_2;
+ memset(dev->rps1, 0x0, SAA7146_RPS_MEM);
+
+ dev->clipping = (u32*)kmalloc(SAA7146_CLIPPING_MEM, GFP_KERNEL);
+ if( NULL == dev->clipping )
+ goto kmalloc_error_3;
+ memset(dev->clipping, 0x0, SAA7146_CLIPPING_MEM);
+
+ dev->i2c_adapter = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if ( NULL == dev->i2c_adapter )
+ goto kmalloc_error_4;
+ memset(dev->i2c_adapter, 0x0, sizeof(struct i2c_adapter));
+
+ dev->i2c_mem = (u32*)kmalloc(SAA7146_I2C_MEM, GFP_KERNEL);
+ if( NULL == dev->i2c_mem )
+ goto kmalloc_error_5;
+ memset(dev->i2c_mem, 0x00, SAA7146_I2C_MEM);
+
+ /* i2c-adapter preparations */
+ saa7146_i2c_adapter_prepare(dev);
+
+ /* enable i2c-port pins, video-port-pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ /* the rest + print status message */
+
+ /* create a nice device name */
+ sprintf(&dev->name[0], "saa7146 (%d)",saa7146_num);
+
+ INFO(("found saa7146 @ mem 0x%08x (revision %d, irq %d) (0x%04x,0x%04x).\n", (unsigned int)dev->mem, dev->revision,dev->pci->irq,dev->pci->subsystem_vendor,dev->pci->subsystem_device));
+ dev->ext = NULL;
+
+ pci_set_drvdata(pci,dev);
+
+ init_MUTEX(&dev->lock);
+ dev->slock = SPIN_LOCK_UNLOCKED;
+
+ init_MUTEX(&dev->i2c_lock);
+
+ INIT_LIST_HEAD(&dev->item);
+ list_add_tail(&dev->item,&devices);
+ saa7146_num++;
+
+ saa7146_video_uops.init(dev);
+ saa7146_vbi_uops.init(dev);
+
+ dev->module = THIS_MODULE;
+
+ return 0;
+
+kmalloc_error_5:
+ kfree( dev->i2c_adapter );
+kmalloc_error_4:
+ kfree( dev->clipping );
+kmalloc_error_3:
+ kfree( dev->rps1 );
+kmalloc_error_2:
+ kfree( dev->rps0 );
+kmalloc_error_1:
+ ERR(("kmalloc() failed.\n"));
+ iounmap(dev->mem);
+ release_mem_region(adr,len);
+ kfree(dev);
+
+ return -ENOMEM;
+}
+
+static void unconfig_a_device(struct saa7146_dev* dev)
+{
+ DEB_EE(("dev:%p\n",dev));
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* disable all irqs, release irq-routine */
+ saa7146_write(dev, IER, 0);
+ saa7146_write(dev, ISR, 0xffffffff);
+
+ free_irq(dev->pci->irq, (void *)dev);
+
+ /* free kernel memory */
+ kfree(dev->rps0 );
+ kfree(dev->rps1 );
+ kfree(dev->clipping);
+ kfree(dev->i2c_mem);
+ kfree(dev->i2c_adapter);
+
+ iounmap(dev->mem);
+ release_mem_region(pci_resource_start(dev->pci,0), pci_resource_len(dev->pci,0));
+
+ list_del(&dev->item);
+ kfree(dev);
+
+ saa7146_num--;
+}
+
+#ifdef MODULE
+int __devinit saa7146_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ if( 0 != config_a_device(pdev)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void __devexit saa7146_remove_one(struct pci_dev *pdev)
+{
+ struct saa7146_dev* dev = (struct saa7146_dev*)pci_get_drvdata(pdev);
+
+ unconfig_a_device(dev);
+}
+
+struct pci_device_id saa7146_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0,},
+};
+
+MODULE_DEVICE_TABLE(pci, saa7146_pci_tbl);
+
+struct pci_driver saa7146_driver = {
+ name: "saa7146",
+ id_table: saa7146_pci_tbl,
+ probe: saa7146_init_one,
+ remove: saa7146_remove_one,
+};
+
+int __init saa7146_init_module(void)
+{
+ INIT_LIST_HEAD(&devices);
+ init_MUTEX(&devices_lock);
+ return pci_module_init(&saa7146_driver);
+}
+
+void __exit saa7146_cleanup_module(void)
+{
+ pci_unregister_driver(&saa7146_driver);
+}
+
+module_init(saa7146_init_module);
+module_exit(saa7146_cleanup_module);
+
+EXPORT_SYMBOL_GPL(saa7146_register_extension);
+EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
+
+/* misc functions used by extension modules */
+EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
+EXPORT_SYMBOL_GPL(saa7146_i2c_transfer);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based video hardware");
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/linux/drivers/media/common/saa7146_hlp.c b/linux/drivers/media/common/saa7146_hlp.c
new file mode 100644
index 000000000..89959ae93
--- /dev/null
+++ b/linux/drivers/media/common/saa7146_hlp.c
@@ -0,0 +1,974 @@
+#include "saa7146.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "saa7146"
+#endif
+
+#define my_min(type,x,y) \
+ ({ type __x = (x), __y = (y); __x < __y ? __x: __y; })
+#define my_max(type,x,y) \
+ ({ type __x = (x), __y = (y); __x > __y ? __x: __y; })
+
+void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
+{
+ /* clear out the necessary bits */
+ *clip_format &= 0x0000ffff;
+ /* set these bits new */
+ *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16));
+}
+
+void calculate_bcs_ctrl_register(struct saa7146_dev *dev, int brightness, int contrast, int colour, u32 *bcs_ctrl)
+{
+ *bcs_ctrl = ((brightness << 24) | (contrast << 16) | (colour << 0));
+}
+
+void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl)
+{
+ *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28);
+ *hps_ctrl |= (source << 30) | (sync << 28);
+}
+
+void calculate_hxo_and_hyo(struct saa7146_dev *dev, u32* hps_h_scale, u32* hps_ctrl)
+{
+ int hyo = 0, hxo = 0;
+
+ hyo = dev->standard->v_offset;
+ hxo = dev->standard->h_offset;
+
+ *hps_h_scale &= ~(MASK_B0 | 0xf00);
+ *hps_h_scale |= (hxo << 0);
+
+ *hps_ctrl &= ~(MASK_W0 | MASK_B2);
+ *hps_ctrl |= (hyo << 12);
+}
+
+/* helper functions for the calculation of the horizontal- and vertical
+ scaling registers, clip-format-register etc ...
+ these functions take pointers to the (most-likely read-out
+ original-values) and manipulate them according to the requested
+ changes.
+*/
+
+/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */
+struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_h_coeff_tab [] = {
+ {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8},
+ {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8},
+ {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8},
+ {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8},
+ {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8},
+ {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8},
+ {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8},
+ {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8},
+ {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8},
+ {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8},
+ {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16}
+};
+
+/* table of attenuation values for horizontal scaling */
+u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0};
+
+/* calculate horizontal scale registers */
+int calculate_h_scale_registers(struct saa7146_dev *dev,
+ int in_x, int out_x, int flip_lr,
+ u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale)
+{
+ /* horizontal prescaler */
+ u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0;
+ /* horizontal scaler */
+ u32 xim = 0, xp = 0, xsci =0;
+ /* vertical scale & gain */
+ u32 pfuv = 0;
+
+ /* helper variables */
+ u32 h_atten = 0, i = 0;
+
+ if ( 0 == out_x ) {
+ return -EINVAL;
+ }
+
+ /* mask out vanity-bit */
+ *hps_ctrl &= ~MASK_29;
+
+ /* calculate prescale-(xspc)-value: [n .. 1/2) : 1
+ [1/2 .. 1/3) : 2
+ [1/3 .. 1/4) : 3
+ ... */
+ if (in_x > out_x) {
+ xpsc = in_x / out_x;
+ }
+ else {
+ /* zooming */
+ xpsc = 1;
+ }
+
+ /* if flip_lr-bit is set, number of pixels after
+ horizontal prescaling must be < 384 */
+ if ( 0 != flip_lr ) {
+
+ /* set vanity bit */
+ *hps_ctrl |= MASK_29;
+
+ while (in_x / xpsc >= 384 )
+ xpsc++;
+ }
+ /* if zooming is wanted, number of pixels after
+ horizontal prescaling must be < 768 */
+ else {
+ while ( in_x / xpsc >= 768 )
+ xpsc++;
+ }
+
+ /* maximum prescale is 64 (p.69) */
+ if ( xpsc > 64 )
+ xpsc = 64;
+
+ /* keep xacm clear*/
+ xacm = 0;
+
+ /* set horizontal filter parameters (CXY = CXUV) */
+ cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
+ cxuv = cxy;
+
+ /* calculate and set horizontal fine scale (xsci) */
+
+ /* bypass the horizontal scaler ? */
+ if ( (in_x == out_x) && ( 1 == xpsc ) )
+ xsci = 0x400;
+ else
+ xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc;
+
+ /* set start phase for horizontal fine scale (xp) to 0 */
+ xp = 0;
+
+ /* set xim, if we bypass the horizontal scaler */
+ if ( 0x400 == xsci )
+ xim = 1;
+ else
+ xim = 0;
+
+ /* if the prescaler is bypassed, enable horizontal
+ accumulation mode (xacm) and clear dcgx */
+ if( 1 == xpsc ) {
+ xacm = 1;
+ dcgx = 0;
+ }
+ else {
+ xacm = 0;
+ /* get best match in the table of attenuations
+ for horizontal scaling */
+ h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
+
+ for (i = 0; h_attenuation[i] != 0; i++) {
+ if (h_attenuation[i] >= h_atten)
+ break;
+ }
+
+ dcgx = i;
+ }
+
+ /* the horizontal scaling increment controls the UV filter
+ to reduce the bandwith to improve the display quality,
+ so set it ... */
+ if ( xsci == 0x400)
+ pfuv = 0x00;
+ else if ( xsci < 0x600)
+ pfuv = 0x01;
+ else if ( xsci < 0x680)
+ pfuv = 0x11;
+ else if ( xsci < 0x700)
+ pfuv = 0x22;
+ else
+ pfuv = 0x33;
+
+
+ *hps_v_gain &= MASK_W0|MASK_B2;
+ *hps_v_gain |= (pfuv << 24);
+
+ *hps_h_scale &= ~(MASK_W1 | 0xf000);
+ *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12);
+
+ *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0);
+
+ return 0;
+}
+
+struct {
+ u16 hps_coeff;
+ u16 weight_sum;
+} hps_v_coeff_tab [] = {
+ {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8},
+ {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16},
+ {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16},
+ {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32},
+ {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32},
+ {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32},
+ {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64},
+ {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64},
+ {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64},
+ {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64},
+ {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128}
+};
+
+/* table of attenuation values for vertical scaling */
+u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0};
+
+/* calculate vertical scale registers */
+int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field,
+ int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain)
+{
+ int lpi = 0;
+
+ /* vertical scaling */
+ u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0;
+ /* vertical scale & gain */
+ u32 dcgy = 0, cya_cyb = 0;
+
+ /* helper variables */
+ u32 v_atten = 0, i = 0;
+
+ /* error, if vertical zooming */
+ if ( in_y < out_y ) {
+ return -EINVAL;
+ }
+
+ /* linear phase interpolation may be used
+ if scaling is between 1 and 1/2 (both fields used)
+ or scaling is between 1/2 and 1/4 (if only one field is used) */
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ if( 2*out_y >= in_y) {
+ lpi = 1;
+ }
+ } else if (field == V4L2_FIELD_TOP || field == V4L2_FIELD_BOTTOM) {
+ if( 4*out_y >= in_y ) {
+ lpi = 1;
+ }
+ out_y *= 2;
+ }
+ if( 0 != lpi ) {
+
+ yacm = 0;
+ yacl = 0;
+ cya_cyb = 0x00ff;
+
+ /* calculate scaling increment */
+ if ( in_y > out_y )
+ ysci = ((1024 * in_y) / (out_y + 1)) - 1024;
+ else
+ ysci = 0;
+
+ dcgy = 0;
+
+ /* calculate ype and ypo */
+ ype = ysci / 16;
+ ypo = ype + (ysci / 64);
+
+ }
+ else {
+ yacm = 1;
+
+ /* calculate scaling increment */
+ ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10;
+
+ /* calculate ype and ypo */
+ ypo = ype = ((ysci + 15) / 16);
+
+ /* the sequence length interval (yacl) has to be set according
+ to the prescale value, e.g. [n .. 1/2) : 0
+ [1/2 .. 1/3) : 1
+ [1/3 .. 1/4) : 2
+ ... */
+ if ( ysci < 512) {
+ yacl = 0;
+ }
+ else {
+ yacl = ( ysci / (1024 - ysci) );
+ }
+
+ /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
+ cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
+
+ /* get best match in the table of attenuations for vertical scaling */
+ v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
+
+ for (i = 0; v_attenuation[i] != 0; i++) {
+ if (v_attenuation[i] >= v_atten)
+ break;
+ }
+
+ dcgy = i;
+ }
+
+ /* ypo and ype swapped in spec ? */
+ *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1);
+
+ *hps_v_gain &= ~(MASK_W0|MASK_B2);
+ *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0);
+
+ return 0;
+}
+
+/* simple bubble-sort algorithm with duplicate elimination */
+int sort_and_eliminate(u32* values, int* count)
+{
+ int low = 0, high = 0, top = 0, temp = 0;
+ int cur = 0, next = 0;
+
+ /* sanity checks */
+ if( (0 > *count) || (NULL == values) ) {
+ return -EINVAL;
+ }
+
+ /* bubble sort the first ´count´ items of the array ´values´ */
+ for( top = *count; top > 0; top--) {
+ for( low = 0, high = 1; high < top; low++, high++) {
+ if( values[low] > values[high] ) {
+ temp = values[low];
+ values[low] = values[high];
+ values[high] = temp;
+ }
+ }
+ }
+
+ /* remove duplicate items */
+ for( cur = 0, next = 1; next < *count; next++) {
+ if( values[cur] != values[next])
+ values[++cur] = values[next];
+ }
+
+ *count = cur + 1;
+
+ return 0;
+}
+
+void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh,
+ struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field)
+{
+ int td_flip = dev->vflip;
+ int width = fh->ov.win.w.width;
+ int height = fh->ov.win.w.height;
+ int clipcount = fh->ov.nclips;
+ u32 *clipping = dev->clipping;
+
+ u32 line_list[32];
+ u32 pixel_list[32];
+ int numdwords = 0;
+
+ int i = 0, j = 0;
+ int cnt_line = 0, cnt_pixel = 0;
+
+ int x[32], y[32], w[32], h[32];
+
+ /* clear out memory */
+ memset(&line_list[0], 0x00, sizeof(u32)*32);
+ memset(&pixel_list[0], 0x00, sizeof(u32)*32);
+ memset(clipping, 0x00, SAA7146_CLIPPING_MEM);
+
+
+ /* fill the line and pixel-lists */
+ for(i = 0; i < clipcount; i++) {
+ int l = 0, r = 0, t = 0, b = 0;
+
+ x[i] = fh->ov.clips[i].c.left;
+ y[i] = fh->ov.clips[i].c.top;
+ w[i] = fh->ov.clips[i].c.width;
+ h[i] = fh->ov.clips[i].c.height;
+
+ if( w[i] < 0) {
+ x[i] += w[i]; w[i] = -w[i];
+ }
+ if( h[i] < 0) {
+ y[i] += h[i]; h[i] = -h[i];
+ }
+ if( x[i] < 0) {
+ w[i] += x[i]; x[i] = 0;
+ }
+ if( y[i] < 0) {
+ h[i] += y[i]; y[i] = 0;
+ }
+ if( 0 != td_flip ) {
+ y[i] = height - y[i] - h[i];
+ }
+
+ l = x[i];
+ r = x[i]+w[i];
+ t = y[i];
+ b = y[i]+h[i];
+
+ /* insert left/right coordinates */
+ pixel_list[ 2*i ] = my_min(int, l, width);
+ pixel_list[(2*i)+1] = my_min(int, r, width);
+ /* insert top/bottom coordinates */
+ line_list[ 2*i ] = my_min(int, t, height);
+ line_list[(2*i)+1] = my_min(int, b, height);
+ }
+
+ /* sort and eliminate lists */
+ cnt_line = cnt_pixel = 2*clipcount;
+ sort_and_eliminate( &pixel_list[0], &cnt_pixel );
+ sort_and_eliminate( &line_list[0], &cnt_line );
+
+ /* calculate the number of used u32s */
+ numdwords = my_max(int, (cnt_line+1), (cnt_pixel+1))*2;
+ numdwords = my_max(int, 4, numdwords);
+ numdwords = my_min(int, 64, numdwords);
+
+ /* fill up cliptable */
+ for(i = 0; i < cnt_pixel; i++) {
+ clipping[2*i] |= (pixel_list[i] << 16);
+ }
+ for(i = 0; i < cnt_line; i++) {
+ clipping[(2*i)+1] |= (line_list[i] << 16);
+ }
+
+ /* fill up cliptable with the display infos */
+ for(j = 0; j < clipcount; j++) {
+
+ for(i = 0; i < cnt_pixel; i++) {
+
+ if( x[j] < 0)
+ x[j] = 0;
+
+ if( pixel_list[i] < (x[j] + w[j])) {
+
+ if ( pixel_list[i] >= x[j] ) {
+ clipping[2*i] |= (1 << j);
+ }
+ }
+ }
+ for(i = 0; i < cnt_line; i++) {
+
+ if( y[j] < 0)
+ y[j] = 0;
+
+ if( line_list[i] < (y[j] + h[j]) ) {
+
+ if( line_list[i] >= y[j] ) {
+ clipping[(2*i)+1] |= (1 << j);
+ }
+ }
+ }
+ }
+
+ /* adjust arbitration control register */
+ *arbtr_ctrl &= 0xffff00ff;
+ *arbtr_ctrl |= 0x00001c00;
+
+ vdma2->base_even = virt_to_bus(clipping);
+ vdma2->base_odd = virt_to_bus(clipping);
+ vdma2->prot_addr = virt_to_bus(clipping)+((sizeof(u32))*(numdwords));
+ vdma2->base_page = 0x04;
+ vdma2->pitch = 0x00;
+ vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) );
+
+ /* set clipping-mode. this depends on the field(s) used */
+ *clip_format &= 0xfffffff7;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ *clip_format |= 0x00000008;
+ } else if (field == V4L2_FIELD_TOP) {
+ *clip_format |= 0x00000000;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ *clip_format |= 0x00000000;
+ }
+}
+
+/* disable clipping */
+void saa7146_disable_clipping(struct saa7146_dev *dev)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* mask out relevant bits (=lower word)*/
+ clip_format &= MASK_W1;
+
+ /* upload clipping-registers*/
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ /* disable video dma2 */
+ saa7146_write(dev, MC1, (MASK_21));
+}
+
+void saa7146_set_clipping_rect(struct saa7146_dev *dev, struct saa7146_fh *fh)
+{
+ enum v4l2_field field = fh->ov.win.field;
+ int clipcount = fh->ov.nclips;
+
+ struct saa7146_video_dma vdma2;
+
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+ u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+
+ // fixme: is this used at all? SAA7146_CLIPPING_RECT_INVERTED;
+ u32 type = SAA7146_CLIPPING_RECT;
+
+ /* check clipcount, disable clipping if clipcount == 0*/
+ if( clipcount == 0 ) {
+ saa7146_disable_clipping(dev);
+ return;
+ }
+
+ calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
+
+ /* set clipping format */
+ clip_format &= 0xffff0008;
+ clip_format |= (type << 4);
+
+ /* prepare video dma2 */
+ saa7146_write(dev, BASE_EVEN2, vdma2.base_even);
+ saa7146_write(dev, BASE_ODD2, vdma2.base_odd);
+ saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr);
+ saa7146_write(dev, BASE_PAGE2, vdma2.base_page);
+ saa7146_write(dev, PITCH2, vdma2.pitch);
+ saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte);
+
+ /* prepare the rest */
+ saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format);
+ saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
+
+ /* upload clip_control-register, clipping-registers, enable video dma2 */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19));
+ saa7146_write(dev, MC1, (MASK_05 | MASK_21));
+}
+
+void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field)
+{
+ int flip_lr = dev->hflip;
+ int source = dev->current_hps_source;
+ int sync = dev->current_hps_sync;
+
+ u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0;
+
+ /* set vertical scale */
+ hps_v_scale = 0; /* all bits get set by the function-call */
+ hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
+ calculate_v_scale_registers(dev, field, dev->standard->v_calc, height, &hps_v_scale, &hps_v_gain);
+
+ /* set horizontal scale */
+ hps_ctrl = 0;
+ hps_h_prescale = 0; /* all bits get set in the function */
+ hps_h_scale = 0;
+ calculate_h_scale_registers(dev, dev->standard->h_calc, width, flip_lr, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+
+ /* set hyo and hxo */
+ calculate_hxo_and_hyo(dev, &hps_h_scale, &hps_ctrl);
+ calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl);
+
+ /* write out new register contents */
+ saa7146_write(dev, HPS_V_SCALE, hps_v_scale);
+ saa7146_write(dev, HPS_V_GAIN, hps_v_gain);
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale);
+ saa7146_write(dev, HPS_H_SCALE, hps_h_scale);
+
+ /* upload shadow-ram registers */
+ saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) );
+}
+
+/* calculate the new memory offsets for a desired position */
+void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field)
+{
+ int td_flip = dev->vflip;
+ int b_depth = dev->ov_fmt->depth;
+ int b_bpl = dev->ov_fb.fmt.bytesperline;
+ u32 base = (u32)dev->ov_fb.base;
+
+ struct saa7146_video_dma vdma1;
+
+ /* calculate memory offsets for picture, look if we shall top-down-flip */
+ vdma1.pitch = 2*b_bpl;
+ if ( 0 == td_flip ) {
+ vdma1.base_odd = (u32)base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_even = vdma1.base_odd + (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_odd + (w_height * (vdma1.pitch / 2));
+ }
+ else {
+ vdma1.base_even = (u32)base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8));
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2);
+ vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2));
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if ( 0 != td_flip ) {
+ vdma1.pitch *= -1;
+ }
+
+ vdma1.base_page = 0;
+ vdma1.num_line_byte = (dev->standard->v_field<<16)+dev->standard->h_pixels;
+
+ saa7146_write(dev, BASE_EVEN1, vdma1.base_even);
+ saa7146_write(dev, BASE_ODD1, vdma1.base_odd);
+ saa7146_write(dev, PROT_ADDR1, vdma1.prot_addr);
+ saa7146_write(dev, BASE_PAGE1, vdma1.base_page);
+ saa7146_write(dev, PITCH1, vdma1.pitch);
+ saa7146_write(dev, NUM_LINE_BYTE1, vdma1.num_line_byte);
+
+ /* update the video dma 1 registers */
+ saa7146_write(dev, MC2, (MASK_02 | MASK_18));
+}
+
+void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette)
+{
+ u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+
+ /* call helper function */
+ calculate_output_format_register(dev,palette,&clip_format);
+
+ /* update the hps registers */
+ saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+}
+
+void saa7146_set_picture_prop(struct saa7146_dev *dev, int brightness, int contrast, int colour)
+{
+ u32 bcs_ctrl = 0;
+
+ calculate_bcs_ctrl_register(dev, brightness, contrast, colour, &bcs_ctrl);
+ saa7146_write(dev, BCS_CTRL, bcs_ctrl);
+
+ /* update the bcs register */
+ saa7146_write(dev, MC2, (MASK_06 | MASK_22));
+}
+
+
+/* select input-source */
+void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync)
+{
+ u32 hps_ctrl = 0;
+
+ /* read old state */
+ hps_ctrl = saa7146_read(dev, HPS_CTRL);
+
+ hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 );
+ hps_ctrl |= (source << 30) | (sync << 28);
+
+ /* write back & upload register */
+ saa7146_write(dev, HPS_CTRL, hps_ctrl);
+ saa7146_write(dev, MC2, (MASK_05 | MASK_21));
+
+ dev->current_hps_source = source;
+ dev->current_hps_sync = sync;
+}
+
+/* write "data" to the gpio-pin "pin" */
+void saa7146_set_gpio(struct saa7146_dev *dev, u8 pin, u8 data)
+{
+ u32 value = 0;
+
+ /* sanity check */
+ if(pin > 3)
+ return;
+
+ /* read old register contents */
+ value = saa7146_read(dev, GPIO_CTRL );
+
+ value &= ~(0xff << (8*pin));
+ value |= (data << (8*pin));
+
+ saa7146_write(dev, GPIO_CTRL, value);
+}
+
+/* reprogram hps, enable(1) / disable(0) video */
+void saa7146_set_overlay(struct saa7146_dev *dev, struct saa7146_fh *fh, int v)
+{
+ /* enable ? */
+ if( 0 == v) {
+ /* disable video dma1 */
+ saa7146_write(dev, MC1, MASK_22);
+ return;
+ }
+
+ saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field);
+ saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field);
+ saa7146_set_output_format(dev, dev->ov_fmt->trans);
+ saa7146_set_clipping_rect(dev, fh);
+
+ /* enable video dma1 */
+ saa7146_write(dev, MC1, (MASK_06 | MASK_22));
+}
+
+void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma)
+{
+ int where = 0;
+
+ if( which < 1 || which > 3) {
+ return;
+ }
+
+ /* calculate starting address */
+ where = (which-1)*0x18;
+
+ saa7146_write(dev, where, vdma->base_odd);
+ saa7146_write(dev, where+0x04, vdma->base_even);
+ saa7146_write(dev, where+0x08, vdma->prot_addr);
+ saa7146_write(dev, where+0x0c, vdma->pitch);
+ saa7146_write(dev, where+0x10, vdma->base_page);
+ saa7146_write(dev, where+0x14, vdma->num_line_byte);
+
+ /* upload */
+ saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1)));
+/*
+ printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even);
+ printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd);
+ printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr);
+ printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page);
+ printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch);
+ printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte);
+*/
+}
+
+int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_video_dma vdma1;
+
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int flip_td = dev->vflip;
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ enum v4l2_field field = buf->fmt->field;
+
+ int depth = sfmt->depth;
+ unsigned long flags = sfmt->flags;
+
+ DEB_CAP(("[size=%dx%d,fields=%s,flags=%lu]\n",
+ width,height,v4l2_field_names[field],flags));
+
+ vdma1.pitch = (width*depth*2)/8;
+ vdma1.num_line_byte = ((dev->standard->v_field<<16) + dev->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | flags;
+
+ if( 0 != flip_td ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ }
+
+ if( 0 != flip_td ) {
+ vdma1.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ return 0;
+}
+
+int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf)
+{
+ struct saa7146_video_dma vdma1;
+ struct saa7146_video_dma vdma2;
+ struct saa7146_video_dma vdma3;
+
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ int flip_td = dev->vflip;
+
+ int width = buf->fmt->width;
+ int height = buf->fmt->height;
+ enum v4l2_field field = buf->fmt->field;
+
+ unsigned long flags = sfmt->flags;
+
+ BUG_ON(0 == buf->pt[0].dma);
+ BUG_ON(0 == buf->pt[1].dma);
+ BUG_ON(0 == buf->pt[2].dma);
+
+ DEB_CAP(("[size=%dx%d,fields=%s,flags=%lu]\n",
+ width,height,v4l2_field_names[field],flags));
+
+ vdma1.pitch = width*2;
+ vdma1.num_line_byte = ((dev->standard->v_field<<16) + dev->standard->h_pixels);
+ vdma1.base_page = buf->pt[0].dma | ME1 | flags;
+
+ /* fscking saa7146! due to the "byte-swap bug", video-dma2
+ writes the v data and not the u data, video-dma3 writes
+ the u data, not the v data. but because we have a planar
+ format, we can simply switch the video-dmas... */
+
+ vdma3.pitch = width;
+ vdma3.num_line_byte = 0; /* unused */
+ vdma3.base_page = buf->pt[1].dma | ME1 | flags;
+
+ vdma2.pitch = width;
+ vdma2.num_line_byte = 0; /* unused */
+ vdma2.base_page = buf->pt[2].dma | ME1 | flags;
+
+ /* fixme: what happens for user space buffers here?. The offsets are
+ most likely wrong, this version here only works for page-aligned
+ buffers, modifications to the pagetable-functions are necessary...*/
+
+ if( 0 != flip_td ) {
+ vdma1.prot_addr = buf->pt[0].offset;
+ vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2);
+
+ vdma2.prot_addr = buf->pt[2].offset;
+ vdma2.base_even = ((vdma2.pitch/2)*height)+buf->pt[2].offset;
+ vdma2.base_odd = vdma2.base_even - (vdma2.pitch/2);
+
+ vdma3.prot_addr = buf->pt[1].offset;
+ vdma3.base_even = ((vdma3.pitch/2)*height)+buf->pt[1].offset;
+ vdma3.base_odd = vdma3.base_even - (vdma3.pitch/2);
+
+ } else {
+ vdma1.base_even = buf->pt[0].offset;
+ vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2);
+ vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset;
+
+ /* switched, see above */
+ vdma3.base_even = buf->pt[1].offset;
+ vdma3.base_odd = vdma3.base_even + (vdma3.pitch/2);
+ vdma3.prot_addr = (vdma3.pitch/2)*height+buf->pt[1].offset;
+
+ vdma2.base_even = buf->pt[2].offset;
+ vdma2.base_odd = vdma2.base_even + (vdma2.pitch/2);
+ vdma2.prot_addr = (vdma2.pitch/2)*height+buf->pt[2].offset;
+ }
+
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ } else if (field == V4L2_FIELD_TOP) {
+ vdma1.base_odd = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ vdma1.base_odd = vdma1.base_even;
+ vdma1.base_even = vdma1.prot_addr;
+ vdma1.pitch /= 2;
+ vdma2.base_odd = vdma2.base_even;
+ vdma2.base_even = vdma2.prot_addr;
+ vdma2.pitch /= 2;
+ vdma3.base_odd = vdma3.base_even;
+ vdma3.base_even = vdma3.prot_addr;
+ vdma3.pitch /= 2;
+ }
+
+ if( 0 != flip_td ) {
+ vdma1.pitch *= -1;
+ vdma2.pitch *= -1;
+ vdma3.pitch *= -1;
+ }
+
+ saa7146_write_out_dma(dev, 1, &vdma1);
+ saa7146_write_out_dma(dev, 2, &vdma2);
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ return 0;
+}
+
+void program_capture_engine(struct saa7146_dev *dev, int planar)
+{
+ int count = 0;
+
+ unsigned long e_wait = dev->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = dev->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 0 is not set*/
+ dev->rps0[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait;
+ dev->rps0[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait;
+
+ /* set bit 0 */
+ dev->rps0[ count++ ] = CMD_WR_REG | (1 << 8) | (MC2/4);
+ dev->rps0[ count++ ] = MASK_27 | MASK_11;
+
+ /* turn on video-dma1 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_06 | MASK_22; /* => mask */
+ dev->rps0[ count++ ] = MASK_06 | MASK_22; /* => values */
+ if( 0 != planar ) {
+ /* turn on video-dma2 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_05 | MASK_21; /* => mask */
+ dev->rps0[ count++ ] = MASK_05 | MASK_21; /* => values */
+
+ /* turn on video-dma3 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_04 | MASK_20; /* => mask */
+ dev->rps0[ count++ ] = MASK_04 | MASK_20; /* => values */
+ }
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ dev->rps0[ count++ ] = CMD_PAUSE | e_wait;
+ dev->rps0[ count++ ] = CMD_PAUSE | o_wait;
+
+ /* turn off video-dma1 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_22 | MASK_06; /* => mask */
+ dev->rps0[ count++ ] = MASK_22; /* => values */
+ if( 0 != planar ) {
+ /* turn off video-dma2 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_05 | MASK_21; /* => mask */
+ dev->rps0[ count++ ] = MASK_21; /* => values */
+
+ /* turn off video-dma3 */
+ dev->rps0[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps0[ count++ ] = MASK_04 | MASK_20; /* => mask */
+ dev->rps0[ count++ ] = MASK_20; /* => values */
+ }
+
+ /* generate interrupt */
+ dev->rps0[ count++ ] = CMD_INTERRUPT;
+
+ /* stop */
+ dev->rps0[ count++ ] = CMD_STOP;
+}
+
+void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_CAP(("buf:%p, next:%p\n",buf,next));
+
+ saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
+ saa7146_set_output_format(dev, sfmt->trans);
+ saa7146_disable_clipping(dev);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ calculate_video_dma_grab_planar(dev, buf);
+ program_capture_engine(dev,1);
+ } else {
+ calculate_video_dma_grab_packed(dev, buf);
+ program_capture_engine(dev,0);
+ }
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, virt_to_bus(&dev->rps0[ 0]));
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+}
+
diff --git a/linux/drivers/media/common/saa7146_i2c.c b/linux/drivers/media/common/saa7146_i2c.c
new file mode 100644
index 000000000..995ef5883
--- /dev/null
+++ b/linux/drivers/media/common/saa7146_i2c.c
@@ -0,0 +1,426 @@
+#include "saa7146.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "saa7146"
+#endif
+
+/* helper function */
+void my_wait(struct saa7146_dev *dev, long ms)
+{
+ unsigned long timeout = jiffies + ((ms+9)/10);
+ while(time_before(jiffies, timeout)) {
+ schedule();
+ }
+}
+
+u32 saa7146_i2c_func(struct i2c_adapter *adapter)
+{
+//fm DEB_I2C(("'%s'.\n", adapter->name));
+
+ return I2C_FUNC_I2C
+ | I2C_FUNC_SMBUS_QUICK
+ | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+/* this function returns the status-register of our i2c-device */
+u32 saa7146_i2c_status(struct saa7146_dev *dev)
+{
+ u32 iicsta = saa7146_read(dev, I2C_STATUS);
+/*
+ DEB_I2C(("status: 0x%08x\n",iicsta));
+*/
+ return iicsta;
+}
+
+/* this function runs through the i2c-messages and prepares the data to be
+ sent through the saa7146. have a look at the specifications p. 122 ff
+ to understand this. it returns the number of u32s to send, or -1
+ in case of an error. */
+int saa7146_i2c_msg_prepare(const struct i2c_msg m[], int num, u32 *op)
+{
+ int h1, h2;
+ int i, j, addr;
+ int mem = 0, op_count = 0;
+
+ /* first determine size of needed memory */
+ for(i = 0; i < num; i++) {
+ mem += m[i].len + 1;
+ }
+
+ /* worst case: we need one u32 for three bytes to be send
+ plus one extra byte to address the device */
+ mem = 1 + ((mem-1) / 3);
+
+ /* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
+ size. if we exceed this limit... */
+ if ( (4*mem) > SAA7146_I2C_MEM ) {
+//fm DEB_I2C(("cannot prepare i2c-message.\n"));
+ return -1;
+ }
+
+ /* be careful: clear out the i2c-mem first */
+ memset(op,0,sizeof(u32)*mem);
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ /* insert the address of the i2c-slave.
+ note: we get 7 bit i2c-addresses, so we have to perform a translation */
+ addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= ( (u8)addr << ((3-h2)*8));
+ op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
+ op_count++;
+
+ /* loop through all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+
+ /* insert the data bytes */
+ h1 = op_count/3; h2 = op_count%3;
+ op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
+ op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2));
+ op_count++;
+ }
+
+ }
+
+ /* have a look at the last byte inserted:
+ if it was: ...CONT change it to ...STOP */
+ h1 = (op_count-1)/3; h2 = (op_count-1)%3;
+ if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
+ op[h1] &= ~(0x2 << ((3-h2)*2));
+ op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
+ }
+
+ /* return the number of u32s to send */
+ return mem;
+}
+
+/* this functions loops through all i2c-messages. normally, it should determine
+ which bytes were read through the adapter and write them back to the corresponding
+ i2c-message. but instead, we simply write back all bytes.
+ fixme: this could be improved. */
+int saa7146_i2c_msg_cleanup(const struct i2c_msg m[], int num, u32 *op)
+{
+ int i, j;
+ int op_count = 0;
+
+ /* loop through all messages */
+ for(i = 0; i < num; i++) {
+
+ op_count++;
+
+ /* loop throgh all bytes of message i */
+ for(j = 0; j < m[i].len; j++) {
+ /* write back all bytes that could have been read */
+ m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
+ op_count++;
+ }
+ }
+
+ return 0;
+}
+
+/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
+int saa7146_i2c_reset(struct saa7146_dev *dev)
+{
+ /* get current status */
+ u32 status = saa7146_i2c_status(dev);
+
+ /* clear registers for sure */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, 0);
+
+ /* check if any operation is still in progress */
+ if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
+
+ /* yes, kill ongoing operation */
+ DEB_I2C(("busy_state detected.\n"));
+
+ /* set "ABORT-OPERATION"-bit (bit 7)*/
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ my_wait(dev,SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending; this is needed because p.123, note 1 */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ my_wait(dev,SAA7146_I2C_DELAY);
+ }
+
+ /* check if any error is (still) present. (this can be necessary because p.123, note 1) */
+ status = saa7146_i2c_status(dev);
+
+ if ( dev->i2c_bitrate != status ) {
+
+ DEB_I2C(("error_state detected. status:0x%08x\n",status));
+
+ /* Repeat the abort operation. This seems to be necessary
+ after serious protocol errors caused by e.g. the SAA7740 */
+ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ my_wait(dev,SAA7146_I2C_DELAY);
+
+ /* clear all error-bits pending */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ my_wait(dev,SAA7146_I2C_DELAY);
+
+ /* the data sheet says it might be necessary to clear the status
+ twice after an abort */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+ my_wait(dev,SAA7146_I2C_DELAY);
+ }
+
+ /* if any error is still present, a fatal error has occured ... */
+ status = saa7146_i2c_status(dev);
+ if ( dev->i2c_bitrate != status ) {
+ DEB_I2C(("fatal error. status:0x%08x\n",status));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* this functions writes out the data-byte 'dword' to the i2c-device.
+ it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
+ failed badly (e.g. address error) */
+int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword)
+{
+ int i = 0;
+ u32 status = 0, mc2 = 0;
+
+ DEB_I2C(("before: 0x%08x\n",*dword));
+
+ /* write out i2c-command */
+ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
+ saa7146_write(dev, I2C_TRANSFER, *dword);
+ saa7146_write(dev, MC2, (MASK_00 | MASK_16));
+
+ /* do not poll for i2c-status before upload is complete */
+ for (i = 5; i > 0; i--) {
+ mc2 = (saa7146_read(dev, MC2) & 0x1);
+ if( 0 != mc2 )
+ break;
+ my_wait(dev,SAA7146_I2C_DELAY);
+ }
+ if (0 == i) {
+ DEB_I2C(("timeout error. #1\n"));
+ return -1;
+ }
+
+ /* wait until busy flag becomes inactive or we time out */
+ for (i = 5; i > 0; i--) {
+ status = saa7146_i2c_status(dev);
+ /* check busy flag */
+ if ( 0 == (status & SAA7146_I2C_BUSY))
+ break;
+ /* check error flag */
+ if ( 0 != (status & SAA7146_I2C_ERR)) {
+ break;
+ }
+ my_wait(dev,SAA7146_I2C_DELAY);
+ }
+ if (0 == i) {
+ DEB_I2C(("timeout error. #2\n"));
+ return -1;
+ }
+
+ /* give a detailed status report */
+ if ( 0 != (status & SAA7146_I2C_ERR)) {
+
+ if( 0 != (status & SAA7146_I2C_SPERR) ) {
+ DEB_I2C(("error due to invalid start/stop condition.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_DTERR) ) {
+ DEB_I2C(("error in data transmission.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_DRERR) ) {
+ DEB_I2C(("error when receiving data.\n"));
+ }
+ if( 0 != (status & SAA7146_I2C_AL) ) {
+ DEB_I2C(("error because arbitration lost.\n"));
+ }
+
+ /* we handle address-errors here */
+ if( 0 != (status & SAA7146_I2C_APERR) ) {
+ DEB_I2C(("error in address phase.\n"));
+ return -2;
+ }
+ return -1;
+ }
+
+ /* read back data, just in case we were reading ... */
+ *dword = saa7146_read(dev, I2C_TRANSFER);
+
+ DEB_I2C(("after: 0x%08x\n",*dword));
+
+ saa7146_i2c_status(dev);
+
+ return 0;
+}
+
+int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg msgs[], int num, int retries)
+{
+ int i = 0, result = 0, count = 0;
+ u32* buffer = dev->i2c_mem;
+ int err = 0;
+
+ if (down_interruptible (&dev->i2c_lock))
+ return -ERESTARTSYS;
+
+ for(i=0;i<num;i++) {
+ DEB_I2C(("msg:%d/%d\n",i+1,num));
+ }
+
+ /* prepare the message(s), get number of u32s to transfer */
+ count = saa7146_i2c_msg_prepare(msgs, num, buffer);
+ if ( 0 > count ) {
+ err = -1;
+ goto out;
+ }
+
+ /* loop through number of retries ... */
+ for(; retries >= 0; retries--) {
+
+ /* reset the i2c-device if necessary */
+ result = saa7146_i2c_reset(dev);
+ if ( 0 > result ) {
+ DEB_I2C(("could not reset i2c-device.\n"));
+ err = -1;
+ goto out;
+ }
+
+ /* write out the u32s one after another */
+ for(i = 0; i < count; i++) {
+
+ result = saa7146_i2c_writeout(dev, &buffer[i] );
+ if ( 0 != result) {
+ /* if address-error occured, don´t retry */
+ if ( -2 == result ) {
+ err = -1;
+ goto out;
+ }
+ DEB_I2C(("error while sending message(s). starting again.\n"));
+ break;
+ }
+ }
+
+ /* see if an error occured & the last retry failed */
+ if( (0 != result) && (0 == retries) ) {
+ DEB_I2C(("could not transfer i2c-message(s).\n"));
+ err = -1;
+ goto out;
+ }
+
+ if( 0 == result )
+ break;
+ }
+
+ /* if any things had to be read, get the results */
+ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
+ DEB_I2C(("could not cleanup i2c-message.\n"));
+ err = -1;
+ goto out;
+ }
+
+ saa7146_i2c_status(dev);
+
+ /* another bug in revision 0: the i2c-registers get uploaded randomly by other
+ uploads, so we better clear them out before continueing */
+ if( 0 == dev->revision ) {
+ u32 zero = 0;
+ if( 0 != saa7146_i2c_writeout(dev, &zero)) {
+ INFO(("revision 0 error. this should never happen.\n"));
+ }
+ }
+
+ /* return the number of delivered messages */
+ err = num;
+ DEB_I2C(("transmission successful. (msg:%d).\n",err));
+out:
+ up(&dev->i2c_lock);
+ return err;
+}
+
+/* utility functions */
+int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg msg[], int num)
+{
+ struct saa7146_dev* dev = (struct saa7146_dev*)adapter->data;
+
+ DEB_I2C(("adapter: '%s'.\n", adapter->name));
+
+ /* use helper function to transfer data */
+ return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
+}
+
+/* these are just for completeness */
+int saa7146_i2c_reg(struct i2c_client *client)
+{
+ return 0;
+}
+
+int saa7146_i2c_unreg(struct i2c_client *client)
+{
+ return 0;
+}
+
+/* fixme
+void saa7146_i2c_inc_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+void saa7146_i2c_dec_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+*/
+
+/*****************************************************************************/
+/* i2c-adapter helper functions */
+
+/* exported algorithm data */
+struct i2c_algorithm saa7146_algo = {
+ "saa7146 i2c algorithm",
+ I2C_ALGO_SAA7146,
+ saa7146_i2c_xfer,
+ NULL,
+ NULL, /* slave_xmit */
+ NULL, /* slave_recv */
+ NULL, /* ioctl */
+ saa7146_i2c_func, /* functionality */
+};
+
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev)
+{
+ /* fixme: this should be adjusted by the extension */
+ dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_480;
+
+ saa7146_i2c_reset(dev);
+
+ memset(dev->i2c_adapter,0,sizeof(struct i2c_adapter));
+ strcpy(dev->i2c_adapter->name, dev->name);
+
+ dev->i2c_adapter->data = dev;
+ dev->i2c_adapter->algo = &saa7146_algo;
+ dev->i2c_adapter->algo_data = NULL;
+ dev->i2c_adapter->id = I2C_ALGO_SAA7146;
+
+/* fixme
+ dev->i2c_adapter->inc_use = saa7146_i2c_inc_use;
+ dev->i2c_adapter->dec_use = saa7146_i2c_dec_use;
+*/
+ dev->i2c_adapter->client_register = saa7146_i2c_reg;
+ dev->i2c_adapter->client_unregister = saa7146_i2c_unreg;
+
+ dev->i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
+ dev->i2c_adapter->retries = SAA7146_I2C_RETRIES;
+
+ return 0;
+}
diff --git a/linux/drivers/media/common/saa7146_vbi.c b/linux/drivers/media/common/saa7146_vbi.c
new file mode 100644
index 000000000..4a8528295
--- /dev/null
+++ b/linux/drivers/media/common/saa7146_vbi.c
@@ -0,0 +1,440 @@
+#include "saa7146.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "saa7146"
+#endif
+
+static int vbi_pixel_to_capture = 720 * 2;
+
+int vbi_workaround(struct saa7146_dev *dev)
+{
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ int i, index;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ DEB_VBI(("dev:%p",dev));
+
+ /* once again, a bug in the saa7146: the brs acquisition
+ is buggy and especially the BXO-counter does not work
+ as specified. there is this workaround, but please
+ don't let me explain it. ;-) */
+
+ if(0 != dev->ext->vbi) {
+ dev->ext->vbi(dev);
+ }
+
+ cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
+ if (NULL == cpu)
+ return -ENOMEM;
+
+ /* setup some basic programming, just for the workaround */
+ saa7146_write(dev, BASE_EVEN3, dma_addr);
+ saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
+ saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
+ saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
+ saa7146_write(dev, BASE_PAGE3, 0x0);
+ saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+
+ /* we have to do the workaround two times to be sure that
+ everything is ok */
+ for(i = 0; i < 2; i++) {
+
+ /* indicate to the irq handler that we do the workaround */
+ saa7146_write(dev, MC2, MASK_31|MASK_15);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
+ saa7146_write(dev, MC2, MASK_04|MASK_20);
+
+ index = 0;
+
+ /* load brs-control register */
+ dev->rps1[index++] = CMD_WR_REG | (1 << 8) | (BRS_CTRL/4);
+ /* BXO = 1h, BRS to outbound */
+ dev->rps1[index++]=0xc000008c;
+ /* wait for vbi_a */
+ dev->rps1[index++] = CMD_PAUSE | MASK_10;
+ /* upload brs */
+ dev->rps1[index++] = CMD_UPLOAD | MASK_08;
+ /* load brs-control register */
+ dev->rps1[index++] = CMD_WR_REG | (1 << 8) | (BRS_CTRL/4);
+ /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
+ dev->rps1[index++] = ((1728-(vbi_pixel_to_capture)) << 7) | MASK_19;
+ /* wait for brs_done */
+ dev->rps1[index++] = CMD_PAUSE | MASK_08;
+ /* upload brs */
+ dev->rps1[index++] = CMD_UPLOAD | MASK_08;
+ /* load video-dma3 NumLines3 and NumBytes3 */
+ dev->rps1[index++] = CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4);
+ /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
+ dev->rps1[index++]= (2 << 16) | (vbi_pixel_to_capture);
+ /* load brs-control register */
+ dev->rps1[index++] = CMD_WR_REG | (1 << 8) | (BRS_CTRL/4);
+ /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
+ dev->rps1[index++] = (540 << 7) | (5 << 19); // 5 == vbi_start
+ /* wait for brs_done */
+ dev->rps1[index++] = CMD_PAUSE | MASK_08;
+ /* upload brs and video-dma3*/
+ dev->rps1[index++] = CMD_UPLOAD | MASK_08 | MASK_04;
+ /* load mc2 register: enable dma3 */
+ dev->rps1[index++] = CMD_WR_REG | (1 << 8) | (MC1/4);
+ dev->rps1[index++] = MASK_20 | MASK_04;
+ /* generate interrupt */
+ dev->rps1[index++] = CMD_INTERRUPT;
+ /* stop rps1 */
+ dev->rps1[index++] = CMD_STOP;
+
+ /* enable rps1 irqs */
+ IER_ENABLE(dev,MASK_28);
+
+ /* prepare to wait to be woken up by the irq-handler */
+ add_wait_queue(&dev->vbi_wq, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ /* start rps1 to enable workaround */
+ saa7146_write(dev, RPS_ADDR1, virt_to_bus(&dev->rps1[ 0]));
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ schedule();
+
+ DEB_VBI(("brs bug workaround %d/1.\n",i));
+
+ remove_wait_queue(&dev->vbi_wq, &wait);
+ current->state = TASK_RUNNING;
+
+ /* disable rps1 irqs */
+ IER_DISABLE(dev,MASK_28);
+
+ /* stop video-dma3 */
+ saa7146_write(dev, MC1, MASK_20);
+
+ if(signal_pending(current)) {
+
+ DEB_VBI(("aborted.\n"));
+
+ /* stop rps1 for sure */
+ saa7146_write(dev, MC1, MASK_29);
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return -EINTR;
+ }
+ }
+
+ pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
+ return 0;
+}
+
+void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
+{
+ struct saa7146_video_dma vdma3;
+
+ int count = 0;
+ unsigned long e_wait = dev->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
+ unsigned long o_wait = dev->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+
+/*
+ vdma3.base_even = (u32)dev->ov_fb.base+2048*70;
+ vdma3.base_odd = (u32)dev->ov_fb.base;
+ vdma3.prot_addr = (u32)dev->ov_fb.base+2048*164;
+ vdma3.pitch = 2048;
+ vdma3.base_page = 0;
+ vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
+*/
+ vdma3.base_even = buf->pt[2].offset;
+ vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
+ vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
+ vdma3.pitch = vbi_pixel_to_capture;
+ vdma3.base_page = buf->pt[2].dma | ME1;
+ vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
+ saa7146_write_out_dma(dev, 3, &vdma3);
+
+ /* write beginning of rps-program */
+ count = 0;
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
+
+ /* we don't wait here for the first field anymore. this is different from the video
+ capture and might cause that the first buffer is only half filled (with only
+ one field). but since this is some sort of streaming data, this is not that negative.
+ but by doing this, we can use the whole engine from video-buf.c... */
+
+/*
+ dev->rps1[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait;
+ dev->rps1[ count++ ] = CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait;
+*/
+ /* set bit 1 */
+ dev->rps1[ count++ ] = CMD_WR_REG | (1 << 8) | (MC2/4);
+ dev->rps1[ count++ ] = MASK_28 | MASK_12;
+
+ /* turn on video-dma3 */
+ dev->rps1[ count++ ] = CMD_WR_REG_MASK | (MC1/4);
+ dev->rps1[ count++ ] = MASK_04 | MASK_20; /* => mask */
+ dev->rps1[ count++ ] = MASK_04 | MASK_20; /* => values */
+
+ /* wait for o_fid_a/b / e_fid_a/b toggle */
+ dev->rps1[ count++ ] = CMD_PAUSE | o_wait;
+ dev->rps1[ count++ ] = CMD_PAUSE | e_wait;
+
+ /* generate interrupt */
+ dev->rps1[ count++ ] = CMD_INTERRUPT;
+
+ /* stop */
+ dev->rps1[ count++ ] = CMD_STOP;
+
+ /* enable rps1 irqs */
+ IER_ENABLE(dev, MASK_28);
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR1, virt_to_bus(&dev->rps1[ 0]));
+
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+}
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ buf->vb.state = STATE_ACTIVE;
+
+ DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
+ saa7146_set_vbi_capture(dev,buf,next);
+
+ mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ int err = 0;
+ int lines, llength, size;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+ size = lines * llength;
+
+ DEB_VBI(("vb:%p\n",vb));
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_VBI(("size mismatch.\n"));
+ return -EINVAL;
+ }
+
+ if (buf->vb.size != size)
+ saa7146_dma_free(dev,buf);
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+
+ err = videobuf_iolock(dev->pci,&buf->vb);
+ if (err)
+ goto oops;
+ saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen);
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_VBI(("error out.\n"));
+ saa7146_dma_free(dev,buf);
+
+ return err;
+}
+static int
+buffer_setup(struct file *file, int *count, int *size)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ int llength,lines;
+
+ lines = 16 * 2 ; /* 2 fields */
+ llength = vbi_pixel_to_capture;
+
+ *size = lines * llength;
+ *count = 2;
+
+ DEB_VBI(("count:%d, size:%d\n",*count,*size));
+
+ return 0;
+}
+
+static void buffer_queue(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI(("vb:%p\n",vb));
+ saa7146_buffer_queue(dev,&dev->vbi_q,buf);
+}
+
+static void buffer_release(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_VBI(("vb:%p\n",vb));
+ saa7146_dma_free(dev,buf);
+}
+
+struct videobuf_queue_ops vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+void vbi_stop(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ unsigned long flags;
+ DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps1 */
+ saa7146_write(dev, MC1, MASK_29);
+
+ /* disable rps1 irqs */
+ IER_DISABLE(dev, MASK_28);
+
+ /* shut down dma 3 transfers */
+ saa7146_write(dev, MC1, MASK_20);
+
+ dev->vbi_streaming = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void vbi_read_timeout(unsigned long data)
+{
+ struct saa7146_fh *fh = (struct saa7146_fh *)data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
+
+ vbi_stop(fh);
+}
+
+void vbi_init(struct saa7146_dev *dev)
+{
+ INIT_LIST_HEAD(&dev->vbi_q.queue);
+
+ init_timer(&dev->vbi_q.timeout);
+ dev->vbi_q.timeout.function = saa7146_buffer_timeout;
+ dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q);
+ dev->vbi_q.dev = dev;
+
+ init_waitqueue_head(&dev->vbi_wq);
+}
+
+void vbi_open(struct saa7146_dev *dev, struct saa7146_fh *fh)
+{
+ memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt));
+
+ fh->vbi_fmt.sampling_rate = 27000000;
+ fh->vbi_fmt.offset = 248; /* todo */
+ fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture;
+ fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ fh->vbi_fmt.start[0] = 5;
+ fh->vbi_fmt.count[0] = 16;
+ fh->vbi_fmt.start[1] = 312;
+ fh->vbi_fmt.count[1] = 16;
+
+ videobuf_queue_init(&fh->vbi_q, &vbi_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ sizeof(struct saa7146_buf));
+ init_MUTEX(&fh->vbi_q.lock);
+
+ init_timer(&fh->vbi_read_timeout);
+ fh->vbi_read_timeout.function = vbi_read_timeout;
+ fh->vbi_read_timeout.data = (unsigned long)fh;
+
+ vbi_workaround(dev);
+}
+
+void vbi_close(struct saa7146_dev *dev, struct saa7146_fh *fh, struct file *file)
+{
+ if( fh == dev->vbi_streaming ) {
+ vbi_stop(fh);
+ }
+}
+
+void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
+{
+ spin_lock(&dev->slock);
+
+ if (dev->vbi_q.curr) {
+ DEB_VBI(("dev:%p, curr:%p\n",dev,dev->vbi_q.curr));
+ /* this must be += 2, one count for each field */
+ dev->vbi_fieldcount+=2;
+ dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
+ saa7146_buffer_finish(dev,&dev->vbi_q,STATE_DONE);
+ } else {
+ DEB_VBI(("dev:%p\n",dev));
+ }
+ saa7146_buffer_next(dev,&dev->vbi_q,1);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t vbi_read(struct file *file, char *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ ssize_t ret = 0;
+
+ DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
+
+ if( NULL == dev->vbi_streaming ) {
+ // fixme: check if dma3 is available
+ // fixme: activate vbi engine here if necessary. (really?)
+ dev->vbi_streaming = fh;
+ }
+
+ if( fh != dev->vbi_streaming ) {
+ DEB_VBI(("open %p is already using vbi capture.",dev->vbi_streaming));
+ return -EBUSY;
+ }
+
+ mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
+ ret = videobuf_read_stream(file, &fh->vbi_q, data, count, ppos, 1);
+/*
+ printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
+ printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
+ printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
+ printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
+ printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
+ printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
+ printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
+*/
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_vbi_uops = {
+ .init = vbi_init,
+ .open = vbi_open,
+ .release = vbi_close,
+ .irq_done = vbi_irq_done,
+ .read = vbi_read,
+};
diff --git a/linux/drivers/media/common/saa7146_video.c b/linux/drivers/media/common/saa7146_video.c
new file mode 100644
index 000000000..b92f623eb
--- /dev/null
+++ b/linux/drivers/media/common/saa7146_video.c
@@ -0,0 +1,1297 @@
+#include "saa7146.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "saa7146"
+#endif
+
+int memory = 32;
+
+#ifdef MODULE
+MODULE_PARM(memory,"i");
+MODULE_PARM_DESC(memory, "maximum memory usage for capture buffers (default: 32Mb)");
+#endif
+
+/* format descriptions for capture and preview */
+struct saa7146_format formats[] =
+{
+ {"RGB-8 (3-3-2)", V4L2_PIX_FMT_RGB332, RGB08_COMPOSED, 8, 0},
+ {"RGB-16 (5-6-5)", V4L2_PIX_FMT_RGB565, RGB16_COMPOSED, 16, 0},
+ {"RGB-24 (B-G-R)", V4L2_PIX_FMT_BGR24, RGB24_COMPOSED, 24, 0},
+ {"RGB-32 (B-G-R)", V4L2_PIX_FMT_BGR32, RGB32_COMPOSED, 32, 0},
+ {"Greyscale-8", V4L2_PIX_FMT_GREY, Y8, 8, 0},
+ {"YUV 4:2:2 planar (Y-Cb-Cr)", V4L2_PIX_FMT_YUV422P, YUV422_DECOMPOSED, 16, 0},
+ {"YUV 4:2:0 planar (Y-Cb-Cr)", V4L2_PIX_FMT_YVU420, YUV420_DECOMPOSED, 12, 0},
+ {"YUV 4:2:2 (U-Y-V-Y)", V4L2_PIX_FMT_UYVY, YUV422_COMPOSED, 16, 0},
+ /* V4L2_PIX_FMT_YUYV must always be the last format in the list. the saa7146 (without "a")
+ is not capable of on-the-fly "byte swapping" (see below), so it does not support this
+ format... */
+ {"YUV 4:2:2 (Y-U-Y-V)", V4L2_PIX_FMT_YUYV, YUV422_COMPOSED, 16, SAA7146_TWO_BYTE_SWAP},
+};
+
+/* this is only true for saa7146a. for the saa7146 (without "a") this is NUM_CAPPVFMT-1 */
+int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
+
+struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i, j = NUM_FORMATS;
+
+ if( 0 == dev->revision ) {
+ j = NUM_FORMATS-1;
+ }
+
+ for (i = 0; i < j; i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc));
+ return NULL;
+}
+
+static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ f->fmt.pix = fh->video_fmt;
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ f->fmt.win = fh->ov.win;
+ return 0;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ {
+ if( 0 == BOARD_CAN_DO_VBI(dev)) {
+ DEB_D(("this device does not support vbi.\n"));
+ return -ENODEV;
+ }
+ f->fmt.vbi = fh->vbi_fmt;
+ return 0;
+ }
+ default:
+ DEB_D(("invalid format type '%d'.\n",f->type));
+ return -EINVAL;
+ }
+}
+
+static int try_win(struct saa7146_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ if (NULL == dev->ov_fb.base) {
+ DEB_D(("no fb base set.\n"));
+ return -EINVAL;
+ }
+ if (NULL == dev->ov_fmt) {
+ DEB_D(("no fb fmt set.\n"));
+ return -EINVAL;
+ }
+ if (win->w.width < 64 || win->w.height < 64) {
+ DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height));
+ return -EINVAL;
+ }
+ if (win->clipcount > 16) {
+ DEB_D(("clipcount too big.\n"));
+ return -EINVAL;
+ }
+
+ field = win->field;
+ maxw = dev->standard->h_max_out;
+ maxh = dev->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default: {
+ DEB_D(("no known field mode '%d'.\n",field));
+ return -EINVAL;
+ }
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+
+ return 0;
+}
+
+static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+
+ fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat);
+ if (NULL == fmt) {
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+ maxw = dev->standard->h_max_out;
+ maxh = dev->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default: {
+ DEB_D(("no known field mode '%d'.\n",field));
+ return -EINVAL;
+ }
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.sizeimage =
+ (f->fmt.pix.width * f->fmt.pix.height * fmt->depth)/8;
+ return 0;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+ err = try_win(dev,&f->fmt.win);
+ if (0 != err) {
+ return err;
+ }
+ return 0;
+ default:
+ DEB_EE(("unknown format type '%d'\n",f->type));
+ return -EINVAL;
+ }
+}
+
+static int start_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ int err = 0;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ /* check if we have overlay informations */
+ if( NULL == fh->ov.fh ) {
+ DEB_D(("not overlay data available. try S_FMT first.\n"));
+ return -EAGAIN;
+ }
+
+ /* check if overlay is running */
+ if( 0 != dev->ov_data ) {
+ if( fh != dev->ov_data->fh ) {
+ DEB_D(("overlay is running in another open.\n"));
+ return -EAGAIN;
+ }
+ DEB_D(("overlay is already active.\n"));
+ return 0;
+ }
+
+ if( 0 != dev->streaming ) {
+ DEB_D(("streaming capture is active.\n"));
+ return -EBUSY;
+ }
+
+ err = try_win(dev,&fh->ov.win);
+ if (0 != err) {
+ return err;
+ }
+
+ dev->ov_data = &fh->ov;
+
+ DEB_D(("%dx%d+%d+%d %s field=%s\n",
+ fh->ov.win.w.width,fh->ov.win.w.height,
+ fh->ov.win.w.left,fh->ov.win.w.top,
+ dev->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
+
+ saa7146_set_overlay(dev, fh, 1);
+
+ return 0;
+}
+
+static int stop_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ DEB_EE(("saa7146.o: stop_preview()\n"));
+
+ /* check if overlay is running */
+ if( 0 == dev->ov_data ) {
+ DEB_D(("overlay is not active.\n"));
+ return 0;
+ }
+
+ if( fh != dev->ov_data->fh ) {
+ DEB_D(("overlay is active, but for another open.\n"));
+ return -EBUSY;
+ }
+
+ saa7146_set_overlay(dev, fh, 0);
+ dev->ov_data = NULL;
+
+ return 0;
+}
+
+static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = fh->dev;
+
+ unsigned long flags;
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+ if( fh == dev->streaming ) {
+ DEB_EE(("streaming capture is active"));
+ return -EAGAIN;
+ }
+ err = try_fmt(fh,f);
+ if (0 != err)
+ return err;
+ fh->video_fmt = f->fmt.pix;
+ DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat));
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+ err = try_win(dev,&f->fmt.win);
+ if (0 != err)
+ return err;
+ down(&dev->lock);
+ fh->ov.win = f->fmt.win;
+ fh->ov.nclips = f->fmt.win.clipcount;
+ if (fh->ov.nclips > 16)
+ fh->ov.nclips = 16;
+ if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) {
+ up(&dev->lock);
+ return -EFAULT;
+ }
+
+ /* fh->ov.fh is used to indicate that we have valid overlay informations, too */
+ fh->ov.fh = fh;
+
+ /* check if we have an active overlay */
+ if( dev->ov_data != NULL ) {
+ if( fh == dev->ov_data->fh) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(fh);
+ start_preview(fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ }
+ up(&dev->lock);
+ return 0;
+ default:
+ DEB_D(("unknown format type '%d'\n",f->type));
+ return -EINVAL;
+ }
+}
+
+/********************************************************************************/
+/* device controls */
+
+static struct v4l2_queryctrl controls[] = {
+ {
+ id: V4L2_CID_BRIGHTNESS,
+ name: "Brightness",
+ minimum: 0,
+ maximum: 255,
+ step: 1,
+ default_value: 128,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ },{
+ id: V4L2_CID_CONTRAST,
+ name: "Contrast",
+ minimum: 0,
+ maximum: 127,
+ step: 1,
+ default_value: 64,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ },{
+ id: V4L2_CID_SATURATION,
+ name: "Saturation",
+ minimum: 0,
+ maximum: 127,
+ step: 1,
+ default_value: 64,
+ type: V4L2_CTRL_TYPE_INTEGER,
+ },{
+ id: V4L2_CID_VFLIP,
+ name: "Vertical flip",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ id: V4L2_CID_HFLIP,
+ name: "Horizontal flip",
+ minimum: 0,
+ maximum: 1,
+ type: V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);
+
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
+
+static struct v4l2_queryctrl* ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < NUM_CONTROLS; i++)
+ if (controls[i].id == id)
+ return controls+i;
+ return NULL;
+}
+
+static int get_control(struct saa7146_fh *fh, struct v4l2_control *c)
+{
+ struct saa7146_dev *dev = fh->dev;
+ const struct v4l2_queryctrl* ctrl;
+ u32 value = 0;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0xff & (value >> 24);
+ break;
+ case V4L2_CID_CONTRAST:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0x7f & (value >> 16);
+ break;
+ case V4L2_CID_SATURATION:
+ value = saa7146_read(dev, BCS_CTRL);
+ c->value = 0x7f & (value >> 0);
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = dev->vflip;
+ break;
+ case V4L2_CID_HFLIP:
+ c->value = dev->hflip;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
+{
+ struct saa7146_dev *dev = fh->dev;
+ const struct v4l2_queryctrl* ctrl;
+ unsigned long flags;
+ int restart_overlay = 0;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl) {
+ DEB_D(("unknown control %d\n",c->id));
+ return -EINVAL;
+ }
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (c->value < ctrl->minimum)
+ c->value = ctrl->minimum;
+ if (c->value > ctrl->maximum)
+ c->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */;
+ };
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0x00ffffff;
+ value |= (c->value << 24);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0xff00ffff;
+ value |= (c->value << 16);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ u32 value = saa7146_read(dev, BCS_CTRL);
+ value &= 0xffffff00;
+ value |= (c->value << 0);
+ saa7146_write(dev, BCS_CTRL, value);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+ break;
+ }
+ case V4L2_CID_HFLIP:
+ /* fixme: we can supfhrt changing VFLIP and HFLIP here... */
+ if( 0 != dev->streaming ) {
+ DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+ return -EINVAL;
+ }
+ dev->hflip = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_VFLIP:
+ if( 0 != dev->streaming ) {
+ DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+ return -EINVAL;
+ }
+ dev->vflip = c->value;
+ restart_overlay = 1;
+ break;
+ default: {
+ return -EINVAL;
+ }
+ }
+ if( 0 != restart_overlay ) {
+ if( 0 != dev->ov_data ) {
+ if( fh == dev->ov_data->fh ) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(fh);
+ start_preview(fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ }
+ }
+ return 0;
+}
+
+/********************************************************************************/
+/* common pagetable functions */
+
+int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct pci_dev *pci = dev->pci;
+ struct scatterlist *list = buf->vb.dma.sglist;
+ int length = buf->vb.dma.sglen;
+ struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_EE(("dev:%p, buf:%p\n",dev,buf));
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ u32 *ptr1, *ptr2, *ptr3;
+ u32 fill;
+
+ int size = ((buf->fmt->width*buf->fmt->height*sfmt->depth)/8)/2;
+ int i,p,m1,m2,m3,o1,o2;
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ /* walk all pages, copy all page addresses to ptr1 */
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < list->length; p++, ptr1++) {
+ *ptr1 = sg_dma_address(list) - list->offset;
+ }
+ }
+/*
+ ptr1 = pt1->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+*/
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE-1)/PAGE_SIZE)-1;
+ m2 = ((size+(size/2)+PAGE_SIZE-1)/PAGE_SIZE)-1;
+ m3 = ((2*size+PAGE_SIZE-1)/PAGE_SIZE)-1;
+
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/2))%PAGE_SIZE;
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = buf->vb.dma.sglist->offset;
+ pt2->offset = pt1->offset+o1;
+ pt3->offset = pt1->offset+o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for(i = m1; i <= m2 ; i++, ptr2++) {
+ *ptr2 = ptr1[i];
+ }
+ fill = *(ptr2-1);
+ for(;i<1024;i++,ptr2++) {
+ *ptr2 = fill;
+ }
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for(i = m2; i <= m3; i++,ptr3++) {
+ *ptr3 = ptr1[i];
+ }
+ fill = *(ptr3-1);
+ for(;i<1024;i++,ptr3++) {
+ *ptr3 = fill;
+ }
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu+m1;
+ fill = pt1->cpu[m1];
+ for(i=m1;i<1024;i++,ptr1++) {
+ *ptr1 = fill;
+ }
+/*
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
+ }
+*/
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+ saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+int video_begin(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ unsigned long flags;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ if( fh == dev->streaming ) {
+ DEB_S(("already capturing.\n"));
+ return 0;
+ }
+ if( dev->streaming != 0 ) {
+ DEB_S(("already capturing, but in another open.\n"));
+ return -EBUSY;
+ }
+
+ /* fixme: check for planar formats here, if we will interfere with
+ vbi capture for example */
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* clear out beginning of streaming bit */
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ IER_ENABLE(dev, MASK_27);
+
+ dev->streaming = fh;
+ spin_unlock_irqrestore(&dev->slock,flags);
+ return 0;
+}
+
+int video_end(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ unsigned long flags;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+
+ if( dev->streaming != fh ) {
+ DEB_S(("not capturing.\n"));
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ IER_DISABLE(dev, MASK_27);
+
+ // fixme: only used formats here!
+ /* fixme: look at planar formats here, especially at the
+ shutdown of planar formats! */
+
+ /* shut down all used video dma transfers */
+ /* fixme: what about the budget-dvb cards? they use
+ video-dma3, but video_end should not get called anyway ...*/
+ saa7146_write(dev, MC1, 0x00700000);
+
+ dev->streaming = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ return 0;
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others). userspace
+ * copying is done already, arg is a kernel fhinter.
+ */
+
+int video_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ unsigned long flags;
+ int err = 0, result = 0, ee = 0;
+
+ struct saa7146_use_ops *ops;
+ struct videobuf_queue *q;
+
+ /* check if extension handles the command */
+ for(ee = 0; dev->ext->ioctls[ee].flags != 0; ee++) {
+ if( cmd == dev->ext->ioctls[ee].cmd )
+ break;
+ }
+
+ if( 0 != (dev->ext->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
+ DEB_D(("extension handles ioctl exclusive.\n"));
+ result = dev->ext->ioctl(dev, cmd, arg);
+ return result;
+ }
+ if( 0 != (dev->ext->ioctls[ee].flags & SAA7146_BEFORE) ) {
+ DEB_D(("extension handles ioctl before.\n"));
+ result = dev->ext->ioctl(dev, cmd, arg);
+ if( -EAGAIN != result ) {
+ return result;
+ }
+ }
+
+ /* fixme: add handle "after" case (is it still needed?) */
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ ops = &saa7146_video_uops;
+ q = &fh->video_q;
+ break;
+ }
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ ops = &saa7146_vbi_uops;
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ return 0;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ memset(cap,0,sizeof(*cap));
+
+ DEB_EE(("VIDIOC_QUERYCAP\n"));
+
+ strcpy(cap->driver, "saa7146 v4l2");
+ strncpy(cap->card, dev->ext->name, sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",dev->pci->slot_name);
+ cap->version = SAA7146_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if( BOARD_CAN_DO_VBI(dev) != 0) {
+ cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
+ }
+ cap->capabilities |= dev->ext->capabilities;
+ return 0;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+
+ DEB_EE(("VIDIOC_G_FBUF\n"));
+
+ *fb = dev->ov_fb;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ return 0;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ struct saa7146_format *fmt;
+
+ DEB_EE(("VIDIOC_S_FBUF\n"));
+
+/*
+ if(!capable(CAP_SYS_ADMIN)) { // && !capable(CAP_SYS_RAWIO)) {
+ DEB_D(("VIDIOC_S_FBUF: not CAP_SYS_ADMIN or CAP_SYS_RAWIO.\n"));
+ return -EPERM;
+ }
+*/
+ if( 0 != dev->ov_data ) {
+ DEB_D(("VIDIOC_S_FBUF: overlay is active.\n"));
+ return -EPERM;
+ }
+
+ /* check args */
+ fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
+ if (NULL == fmt) {
+ return -EINVAL;
+ }
+
+ /* ok, accept it */
+ dev->ov_fb = *fb;
+ dev->ov_fmt = fmt;
+ if (0 == dev->ov_fb.fmt.bytesperline)
+ dev->ov_fb.fmt.bytesperline =
+ dev->ov_fb.fmt.width*fmt->depth/8;
+ return 0;
+ }
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+ int index;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ index = f->index;
+ if (index < 0 || index >= NUM_FORMATS) {
+ return -EINVAL;
+ }
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ strncpy(f->description,formats[index].name,31);
+ f->pixelformat = formats[index].pixelformat;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_ENUMSTD: type:%d, index:%d\n",f->type,f->index));
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ const struct v4l2_queryctrl *ctrl;
+ struct v4l2_queryctrl *c = arg;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+
+ ctrl = ctrl_by_id(c->id);
+ if( NULL == ctrl ) {
+ c->flags = V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+ }
+
+ DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
+ *c = *ctrl;
+ return 0;
+ }
+ case VIDIOC_G_CTRL: {
+ DEB_EE(("VIDIOC_G_CTRL\n"));
+ return get_control(fh,arg);
+ }
+ case VIDIOC_S_CTRL:
+ {
+ DEB_EE(("VIDIOC_S_CTRL\n"));
+ down(&dev->lock);
+ err = set_control(fh,arg);
+ up(&dev->lock);
+ return err;
+ }
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_streamparm *parm = arg;
+ if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
+ return -EINVAL;
+ }
+ memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
+ parm->parm.capture.readbuffers = 1;
+ // fixme: only for PAL!
+ parm->parm.capture.timeperframe.numerator = 1;
+ parm->parm.capture.timeperframe.denominator = 25;
+ return 0;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_G_FMT\n"));
+ return g_fmt(fh,f);
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_S_FMT\n"));
+ return s_fmt(fh,f);
+ }
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *f = arg;
+ DEB_EE(("VIDIOC_TRY_FMT\n"));
+ return try_fmt(fh,f);
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+ DEB_EE(("VIDIOC_G_STD\n"));
+ *id = dev->standard->id;
+ return 0;
+ }
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ if (e->index < 0 )
+ return -EINVAL;
+ if( e->index < dev->ext->num_stds ) {
+ DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index));
+ return v4l2_video_std_construct(e, dev->ext->stds[e->index].id, dev->ext->stds[e->index].name);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *id = arg;
+ int i;
+
+ int restart_overlay = 0;
+ int found = 0;
+
+ struct saa7146_fh *ov_fh = NULL;
+
+ if( 0 != dev->streaming ) {
+ return -EBUSY;
+ }
+
+ down(&dev->lock);
+
+ if( dev->ov_data != NULL ) {
+ ov_fh = dev->ov_data->fh;
+ stop_preview(ov_fh);
+ restart_overlay = 1;
+ }
+
+ for(i = 0; i < dev->ext->num_stds; i++)
+ if (*id & dev->ext->stds[i].id)
+ break;
+ if (i != dev->ext->num_stds) {
+ dev->standard = &dev->ext->stds[i];
+ if( NULL != dev->ext->std_callback )
+ dev->ext->std_callback(dev, dev->standard);
+ found = 1;
+ }
+
+ if( 0 != restart_overlay ) {
+ start_preview(ov_fh);
+ }
+ up(&dev->lock);
+
+ if( 0 == found ) {
+ DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",dev->standard->name));
+ return 0;
+ }
+ case VIDIOC_OVERLAY:
+ {
+ int on = *(int *)arg;
+ int err = 0;
+
+ if( NULL == dev->ov_fmt ) {
+ DEB_D(("VIDIOC_OVERLAY: no framebuffer informations. call S_FBUF first!\n"));
+ return -EAGAIN;
+ }
+
+ DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
+ if( 0 != on ) {
+ if( dev->ov_data != NULL ) {
+ if( fh != dev->ov_data->fh) {
+ return -EAGAIN;
+ }
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+ err = start_preview(fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ } else {
+ if( dev->ov_data != NULL ) {
+ if( fh != dev->ov_data->fh) {
+ return -EAGAIN;
+ }
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+ err = stop_preview(fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ return err;
+ }
+ case VIDIOC_REQBUFS: {
+ DEB_D(("VIDIOC_REQBUFS \n"));
+ return videobuf_reqbufs(file,q,arg);
+ }
+ case VIDIOC_QUERYBUF: {
+ DEB_D(("VIDIOC_QUERYBUF \n"));
+ return videobuf_querybuf(q,arg);
+ }
+ case VIDIOC_QBUF: {
+ DEB_D(("VIDIOC_QBUF \n"));
+ return videobuf_qbuf(file,q,arg);
+ }
+ case VIDIOC_DQBUF: {
+ DEB_D(("VIDIOC_DQBUF \n"));
+ return videobuf_dqbuf(file,q,arg);
+ }
+ case VIDIOC_STREAMON: {
+ DEB_D(("VIDIOC_STREAMON \n"));
+ if( 0 != ops->capture_begin ) {
+ if( 0 != (err = ops->capture_begin(fh))) {
+ return err;
+ }
+ }
+ err = videobuf_streamon(file,q);
+ return err;
+ }
+ case VIDIOC_STREAMOFF: {
+ DEB_D(("VIDIOC_STREAMOFF \n"));
+ if( 0 != ops->capture_end ) {
+ ops->capture_end(fh);
+ }
+ err = videobuf_streamoff(file,q);
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *mbuf = arg;
+ struct videobuf_queue *q;
+ int i;
+
+ /* fixme: number of capture buffers and sizes for v4l apps */
+ int gbuffers = 2;
+ int gbufsize = 768*576*4;
+
+ DEB_D(("VIDIOCGMBUF \n"));
+
+ q = &fh->video_q;
+ down(&q->lock);
+ err = videobuf_mmap_setup(file,q,gbuffers,gbufsize);
+ if (err < 0) {
+ up(&q->lock);
+ return err;
+ }
+ memset(mbuf,0,sizeof(*mbuf));
+ mbuf->frames = gbuffers;
+ mbuf->size = gbuffers * gbufsize;
+ for (i = 0; i < gbuffers; i++)
+ mbuf->offsets[i] = i * gbufsize;
+ up(&q->lock);
+ return 0;
+ }
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ video_do_ioctl);
+ }
+ return 0;
+#ifdef axa
+ not supfhrted:
+ VIDIOC_RESERVED
+ VIDIOC_S_PARM
+ VIDIOC_G_OUTPUT
+ VIDIOC_S_OUTPUT
+ VIDIOC_CROPCAP
+ VIDIOC_G_CROP
+ VIDIOC_S_CROP
+ VIDIOC_G_JPEGCOMP
+ VIDIOC_S_JPEGCOMP
+ VIDIOC_ENUMOUTPUT
+ VIDIOC_G_AUDOUT
+ VIDIOC_S_AUDOUT
+
+ VIDIOC_QUERYMENU
+ VIDIOC_G_MODULATOR
+ VIDIOC_S_MODULATOR
+ VIDIOC_QUERYSTD
+ /* --- control ioctls ---------------------------------------- */
+#endif
+}
+
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate(struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ buf->vb.state = STATE_ACTIVE;
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+ int size,err = 0;
+
+ /* sanity checks */
+ if (fh->video_fmt.width < 64 ||
+ fh->video_fmt.height < 64 ||
+ fh->video_fmt.width > dev->standard->h_max_out ||
+ fh->video_fmt.height > dev->standard->v_max_out) {
+ DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height));
+ return -EINVAL;
+ }
+
+ size = fh->video_fmt.sizeimage;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_D(("size mismatch.\n"));
+ return -EINVAL;
+ }
+
+ DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field]));
+ if (buf->vb.width != fh->video_fmt.width ||
+ buf->vb.height != fh->video_fmt.height ||
+ buf->vb.size != size ||
+ buf->vb.field != fh->video_fmt.field ||
+ buf->fmt != &fh->video_fmt) {
+ saa7146_dma_free(dev,buf);
+ }
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ struct saa7146_format *sfmt;
+
+ buf->vb.width = fh->video_fmt.width;
+ buf->vb.height = fh->video_fmt.height;
+ buf->vb.size = size;
+ buf->fmt = &fh->video_fmt;
+ buf->vb.field = fh->video_fmt.field;
+
+ sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ err = videobuf_iolock(dev->pci,&buf->vb);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build(dev,buf);
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_D(("error out.\n"));
+ saa7146_dma_free(dev,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct file *file, int *count, int *size)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
+ *count = MAX_SAA7146_CAPTURE_BUFFERS;
+
+ *size = fh->video_fmt.sizeimage;
+
+ /* check if we exceed the "memory" parameter */
+ if( (*count * *size) > (memory*1048576) ) {
+ *count = (memory*1048576) / *size;
+ }
+
+ DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size));
+
+ return 0;
+}
+
+static void buffer_queue(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP(("vbuf:%p\n",vb));
+ saa7146_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+}
+
+static void buffer_release(struct file *file, struct videobuf_buffer *vb)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP(("vbuf:%p\n",vb));
+ saa7146_dma_free(dev,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************************************************************************/
+/* file operations */
+
+struct saa7146_standard standard[] = {
+ { "PAL-BG", V4L2_STD_PAL_BG, SAA7146_PAL_VALUES },
+};
+
+void video_init(struct saa7146_dev *dev)
+{
+ INIT_LIST_HEAD(&dev->video_q.queue);
+ dev->video_q.timeout.function = saa7146_buffer_timeout;
+ dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
+ dev->video_q.dev = dev;
+
+ /* set some default values */
+ dev->standard = &standard[0];
+}
+
+void video_open(struct saa7146_dev *dev, struct saa7146_fh *fh)
+{
+ struct saa7146_format *sfmt;
+
+ fh->video_fmt.width = 384;
+ fh->video_fmt.height = 288;
+ fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24;
+ fh->video_fmt.field = V4L2_FIELD_ANY;
+ sfmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8;
+
+ videobuf_queue_init(&fh->video_q, &video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ sizeof(struct saa7146_buf));
+
+ init_MUTEX(&fh->video_q.lock);
+}
+
+void video_close(struct saa7146_dev *dev, struct saa7146_fh *fh, struct file *file)
+{
+ unsigned long flags;
+
+ if( 0 != dev->ov_data ) {
+ if( fh == dev->ov_data->fh ) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ }
+
+ if( fh == dev->streaming ) {
+ video_end(fh);
+ }
+
+ videobuf_queue_cancel(file,&fh->video_q);
+}
+
+void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_dmaqueue *q = &dev->video_q;
+
+ spin_lock(&dev->slock);
+ DEB_CAP(("called.\n"));
+
+ /* only finish the buffer if we have one... */
+ if( NULL != q->curr ) {
+ saa7146_buffer_finish(dev,q,STATE_DONE);
+ }
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t video_read(struct file *file, char *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ ssize_t ret = 0;
+
+ DEB_EE(("called.\n"));
+
+ if( 0 != video_begin(fh)) {
+ return -EAGAIN;
+ }
+ ret = videobuf_read_one(file,&fh->video_q , data, count, ppos);
+ video_end(fh);
+
+ /* restart overlay if it was active before */
+ if( 0 != dev->ov_data) {
+ start_preview(dev->ov_data->fh);
+ }
+
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .open = video_open,
+ .release = video_close,
+ .irq_done = video_irq_done,
+ .read = video_read,
+ .capture_begin = video_begin,
+ .capture_end = video_end,
+};