From 6cdee00ee3d3e4712b11f25190c17cd2a9afd603 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 2 Oct 2007 00:17:59 -0500 Subject: [PATCH] UBUNTU: Add ivtv-fb and saa717x modules OriginalAuthor: IVTV Developers OriginalLocation: www.ivtvdriver.org Bug: 147886 Ignore: no Normal ivtv modules have been merged with mainline kernel, but ivtv-fb and saa717x have not yet been merged with mainline. These are taken from ivtv-fb v1.02 Signed-off-by: Mario Limonciello --- ubuntu/Makefile | 1 + ubuntu/media/ivtv/Makefile | 9 + ubuntu/media/ivtv/driver/ivtv-driver.h | 870 ++++++++++++++ ubuntu/media/ivtv/driver/ivtv-fb.c | 1197 ++++++++++++++++++++ ubuntu/media/ivtv/driver/ivtv-fb.h | 34 + ubuntu/media/ivtv/driver/ivtv-mailbox.h | 25 + ubuntu/media/ivtv/driver/ivtv-svnrelease.h | 1 + ubuntu/media/ivtv/driver/ivtv-udma.h | 43 + ubuntu/media/ivtv/driver/ivtv-version.h | 29 + ubuntu/media/ivtv/driver/media/ivtv.h | 65 ++ ubuntu/media/ivtv/i2c-drivers/saa717x.c | 1677 ++++++++++++++++++++++++++++ 11 files changed, 3951 insertions(+), 0 deletions(-) create mode 100644 ubuntu/media/ivtv/Makefile create mode 100644 ubuntu/media/ivtv/driver/ivtv-driver.h create mode 100644 ubuntu/media/ivtv/driver/ivtv-fb.c create mode 100644 ubuntu/media/ivtv/driver/ivtv-fb.h create mode 100644 ubuntu/media/ivtv/driver/ivtv-mailbox.h create mode 100644 ubuntu/media/ivtv/driver/ivtv-svnrelease.h create mode 100644 ubuntu/media/ivtv/driver/ivtv-udma.h create mode 100644 ubuntu/media/ivtv/driver/ivtv-version.h create mode 100644 ubuntu/media/ivtv/driver/media/ivtv.h create mode 100644 ubuntu/media/ivtv/i2c-drivers/saa717x.c diff --git a/ubuntu/Makefile b/ubuntu/Makefile index 84e73cb..52d7e86 100644 --- a/ubuntu/Makefile +++ b/ubuntu/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_USB_ISIGHT) += media/isight/ obj-$(CONFIG_LIRC_DEV) += media/lirc/ obj-$(CONFIG_SND_HDA_INTEL) += media/snd-hda-intel/ obj-$(CONFIG_DRM_PSB) += media/drm-poulsbo/ +obj-$(CONFIG_IVTV_FB) += media/ivtv/ obj-$(CONFIG_NET_IPG) += net/ipg/ obj-$(CONFIG_E1000_ICH9) += net/e1000-ich9/ diff --git a/ubuntu/media/ivtv/Makefile b/ubuntu/media/ivtv/Makefile new file mode 100644 index 0000000..4b0d62f --- /dev/null +++ b/ubuntu/media/ivtv/Makefile @@ -0,0 +1,9 @@ +CONFIG_VIDEO_ADV_DEBUG=y +CPPFLAGS += -I$(M)/../i2c-drivers -I$(M) -I$(M)/../driver + +obj-m += driver/ivtv-fb.o +obj-m += i2c-drivers/saa717x.o + +ifeq ($(CONFIG_VIDEO_ADV_DEBUG),y) +EXTRA_CFLAGS += -DCONFIG_VIDEO_ADV_DEBUG=1 +endif diff --git a/ubuntu/media/ivtv/driver/ivtv-driver.h b/ubuntu/media/ivtv/driver/ivtv-driver.h new file mode 100644 index 0000000..e6e56f1 --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-driver.h @@ -0,0 +1,870 @@ +/* + ivtv driver internal defines and structures + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IVTV_DRIVER_H +#define IVTV_DRIVER_H + +/* Internal header for ivtv project: + * Driver for the cx23415/6 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* #define HAVE_XC3028 1 */ + +#include + +#define IVTV_ENCODER_OFFSET 0x00000000 +#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_DECODER_OFFSET 0x01000000 +#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_REG_OFFSET 0x02000000 +#define IVTV_REG_SIZE 0x00010000 + +/* Buffers on hardware offsets */ +#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */ +#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ + +/* Offset to filter table in firmware */ +#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8 +#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358 + +extern const u32 yuv_offset[4]; + +/* Maximum ivtv driver instances. + Based on 6 PVR500s each with two PVR15s... + TODO: make this dynamic. I believe it is only a global in order to support + ivtv-fb. There must be a better way to do that. */ +#define IVTV_MAX_CARDS 12 + +/* Supported cards */ +#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ +#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */ +#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two + PVR150s on one PCI board) */ +#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */ +#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */ +#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160 + cx23415 based, but does not have tv-out */ +#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */ +#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */ +#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */ +#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */ +#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */ +#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */ +#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */ +#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */ +#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */ +#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */ +#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */ +#ifdef HAVE_XC3028 +#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */ +#define IVTV_CARD_LAST 18 +#else +#define IVTV_CARD_LAST 17 +#endif + +/* Variants of existing cards but with the same PCI IDs. The driver + detects these based on other device information. + These cards must always come last. + New cards must be inserted above, and the indices of the cards below + must be adjusted accordingly. */ + +/* PVR-350 V1 (uses saa7114) */ +#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1) +/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2) +#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3) + +#define IVTV_ENC_STREAM_TYPE_MPG 0 +#define IVTV_ENC_STREAM_TYPE_YUV 1 +#define IVTV_ENC_STREAM_TYPE_VBI 2 +#define IVTV_ENC_STREAM_TYPE_PCM 3 +#define IVTV_ENC_STREAM_TYPE_RAD 4 +#define IVTV_DEC_STREAM_TYPE_MPG 5 +#define IVTV_DEC_STREAM_TYPE_VBI 6 +#define IVTV_DEC_STREAM_TYPE_VOUT 7 +#define IVTV_DEC_STREAM_TYPE_YUV 8 +#define IVTV_MAX_STREAMS 9 + +#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ +#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ +#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ +#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ + +#define IVTV_ENC_MEM_START 0x00000000 +#define IVTV_DEC_MEM_START 0x01000000 + +/* system vendor and device IDs */ +#define PCI_VENDOR_ID_ICOMP 0x4444 +#define PCI_DEVICE_ID_IVTV15 0x0803 +#define PCI_DEVICE_ID_IVTV16 0x0016 + +/* subsystem vendor ID */ +#define IVTV_PCI_ID_HAUPPAUGE 0x0070 +#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270 +#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070 +#define IVTV_PCI_ID_ADAPTEC 0x9005 +#define IVTV_PCI_ID_AVERMEDIA 0x1461 +#define IVTV_PCI_ID_YUAN1 0x12ab +#define IVTV_PCI_ID_YUAN2 0xff01 +#define IVTV_PCI_ID_YUAN3 0xffab +#define IVTV_PCI_ID_YUAN4 0xfbab +#define IVTV_PCI_ID_DIAMONDMM 0xff92 +#define IVTV_PCI_ID_IODATA 0x10fc +#define IVTV_PCI_ID_MELCO 0x1154 +#define IVTV_PCI_ID_GOTVIEW1 0xffac +#define IVTV_PCI_ID_GOTVIEW2 0xffad + +/* Decoder Buffer hardware size on Chip */ +#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */ +#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */ + +/* ======================================================================== */ +/* ========================== START USER SETTABLE DMA VARIABLES =========== */ +/* ======================================================================== */ + +#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */ + +/* DMA Buffers, Default size in MB allocated */ +#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4 +#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2 +#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1 +#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1 +#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1 +#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1 +#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1 + +/* ======================================================================== */ +/* ========================== END USER SETTABLE DMA VARIABLES ============= */ +/* ======================================================================== */ + +/* Decoder Status Register */ +#define IVTV_DMA_ERR_LIST 0x00000010 +#define IVTV_DMA_ERR_WRITE 0x00000008 +#define IVTV_DMA_ERR_READ 0x00000004 +#define IVTV_DMA_SUCCESS_WRITE 0x00000002 +#define IVTV_DMA_SUCCESS_READ 0x00000001 +#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ) +#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE) +#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ) + +/* DMA Registers */ +#define IVTV_REG_DMAXFER (0x0000) +#define IVTV_REG_DMASTATUS (0x0004) +#define IVTV_REG_DECDMAADDR (0x0008) +#define IVTV_REG_ENCDMAADDR (0x000c) +#define IVTV_REG_DMACONTROL (0x0010) +#define IVTV_REG_IRQSTATUS (0x0040) +#define IVTV_REG_IRQMASK (0x0048) + +/* Setup Registers */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8) +#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC) +#define IVTV_REG_VDM (0x2800) +#define IVTV_REG_AO (0x2D00) +#define IVTV_REG_BYTEFLUSH (0x2D24) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +#define IVTV_IRQ_ENC_START_CAP (0x1 << 31) +#define IVTV_IRQ_ENC_EOS (0x1 << 30) +#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) +#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) +#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) +#define IVTV_IRQ_ENC_PIO_COMPLETE (0x1 << 25) +#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) +#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) +#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) +#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) +#define IVTV_IRQ_DMA_ERR (0x1 << 18) +#define IVTV_IRQ_DMA_WRITE (0x1 << 17) +#define IVTV_IRQ_DMA_READ (0x1 << 16) +#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) + +/* IRQ Masks */ +#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|\ + IVTV_IRQ_DMA_READ|IVTV_IRQ_ENC_PIO_COMPLETE) + +#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) +#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 + +/* debugging */ + +#define IVTV_DBGFLG_WARN (1 << 0) +#define IVTV_DBGFLG_INFO (1 << 1) +#define IVTV_DBGFLG_API (1 << 2) +#define IVTV_DBGFLG_DMA (1 << 3) +#define IVTV_DBGFLG_IOCTL (1 << 4) +#define IVTV_DBGFLG_I2C (1 << 5) +#define IVTV_DBGFLG_IRQ (1 << 6) +#define IVTV_DBGFLG_DEC (1 << 7) +#define IVTV_DBGFLG_YUV (1 << 8) + +/* NOTE: extra space before comma in 'itv->num , ## args' is required for + gcc-2.95, otherwise it won't compile. */ +#define IVTV_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args) +#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +#define IVTV_FB_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) +#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +/* Standard kernel messages */ +#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args) + +/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ +#define MPEG_FRAME_TYPE_IFRAME 1 +#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 +#define MPEG_FRAME_TYPE_ALL 7 + +/* output modes (cx23415 only) */ +#define OUT_NONE 0 +#define OUT_MPG 1 +#define OUT_YUV 2 +#define OUT_UDMA_YUV 3 +#define OUT_PASSTHROUGH 4 + +#define IVTV_MAX_PGM_INDEX (400) + +extern int ivtv_debug; + + +struct ivtv_options { + int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */ + int cardtype; /* force card type on load */ + int tuner; /* set tuner on load */ + int radio; /* enable/disable radio */ + int newi2c; /* New I2C algorithm */ +}; + +#define IVTV_MBOX_DMA_START 6 +#define IVTV_MBOX_DMA_END 8 +#define IVTV_MBOX_DMA 9 +#define IVTV_MBOX_FIELD_DISPLAYED 8 + +/* ivtv-specific mailbox template */ +struct ivtv_mailbox { + u32 flags; + u32 cmd; + u32 retval; + u32 timeout; + u32 data[CX2341X_MBOX_MAX_DATA]; +}; + +struct ivtv_api_cache { + unsigned long last_jiffies; /* when last command was issued */ + u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */ +}; + +struct ivtv_mailbox_data { + volatile struct ivtv_mailbox __iomem *mbox; + /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes. + If the bit is set, then the corresponding mailbox is in use by the driver. */ + unsigned long busy; + u8 max_mbox; +}; + +/* per-buffer bit flags */ +#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ + +/* per-stream, s_flags */ +#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */ +#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */ +#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */ + +#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */ +#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ +#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ +#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */ +#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ +#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ + +#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */ +#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */ + +/* per-ivtv, i_flags */ +#define IVTV_F_I_DMA 0 /* DMA in progress */ +#define IVTV_F_I_UDMA 1 /* UDMA in progress */ +#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */ +#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */ +#define IVTV_F_I_EOS 4 /* End of encoder stream reached */ +#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */ +#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */ +#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */ +#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */ +#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */ +#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */ +#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */ +#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ +#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ +#define IVTV_F_I_HAVE_WORK 15 /* Used in the interrupt handler: there is work to be done */ +#define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */ +#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */ +#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */ +#define IVTV_F_I_PIO 19 /* PIO in progress */ + +/* Event notifications */ +#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ +#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */ +#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */ +#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */ + +/* Scatter-Gather array element, used in DMA transfers */ +struct ivtv_SG_element { + u32 src; + u32 dst; + u32 size; +}; + +struct ivtv_user_dma { + struct mutex lock; + int page_count; + struct page *map[IVTV_DMA_SG_OSD_ENT]; + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT]; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT]; +}; + +struct ivtv_dma_page_info { + unsigned long uaddr; + unsigned long first; + unsigned long last; + unsigned int offset; + unsigned int tail; + int page_count; +}; + +struct ivtv_buffer { + struct list_head list; + dma_addr_t dma_handle; + unsigned long b_flags; + char *buf; + + u32 bytesused; + u32 readpos; +}; + +struct ivtv_queue { + struct list_head list; + u32 buffers; + u32 length; + u32 bytesused; +}; + +struct ivtv; /* forward reference */ + +struct ivtv_stream { + /* These first four fields are always set, even if the stream + is not actually created. */ + struct video_device *v4l2dev; /* NULL when stream not created */ + struct ivtv *itv; /* for ease of use */ + const char *name; /* name of the stream */ + int type; /* stream type */ + + u32 id; + spinlock_t qlock; /* locks access to the queues */ + unsigned long s_flags; /* status flags, see above */ + int dma; /* can be PCI_DMA_TODEVICE, + PCI_DMA_FROMDEVICE or + PCI_DMA_NONE */ + u32 dma_offset; + u32 dma_backup; + u64 dma_pts; + + int subtype; + wait_queue_head_t waitq; + u32 dma_last_offset; + + /* Buffer Stats */ + u32 buffers; + u32 buf_size; + u32 buffers_stolen; + + /* Buffer Queues */ + struct ivtv_queue q_free; /* free buffers */ + struct ivtv_queue q_full; /* full buffers */ + struct ivtv_queue q_io; /* waiting for I/O */ + struct ivtv_queue q_dma; /* waiting for DMA */ + struct ivtv_queue q_predma; /* waiting for DMA */ + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element *SGarray; + struct ivtv_SG_element *PIOarray; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist *SGlist; +}; + +struct ivtv_open_id { + u32 open_id; + int type; + enum v4l2_priority prio; + struct ivtv *itv; +}; + +#define IVTV_YUV_UPDATE_HORIZONTAL 0x01 +#define IVTV_YUV_UPDATE_VERTICAL 0x02 + +struct yuv_frame_info +{ + u32 update; + int src_x; + int src_y; + unsigned int src_w; + unsigned int src_h; + int dst_x; + int dst_y; + unsigned int dst_w; + unsigned int dst_h; + int pan_x; + int pan_y; + u32 vis_w; + u32 vis_h; + u32 interlaced_y; + u32 interlaced_uv; + int tru_x; + u32 tru_w; + u32 tru_h; + u32 offset_y; +}; + +#define IVTV_YUV_MODE_INTERLACED 0x00 +#define IVTV_YUV_MODE_PROGRESSIVE 0x01 +#define IVTV_YUV_MODE_AUTO 0x02 +#define IVTV_YUV_MODE_MASK 0x03 + +#define IVTV_YUV_SYNC_EVEN 0x00 +#define IVTV_YUV_SYNC_ODD 0x04 +#define IVTV_YUV_SYNC_MASK 0x04 + +struct yuv_playback_info +{ + u32 reg_2834; + u32 reg_2838; + u32 reg_283c; + u32 reg_2840; + u32 reg_2844; + u32 reg_2848; + u32 reg_2854; + u32 reg_285c; + u32 reg_2864; + + u32 reg_2870; + u32 reg_2874; + u32 reg_2890; + u32 reg_2898; + u32 reg_289c; + + u32 reg_2918; + u32 reg_291c; + u32 reg_2920; + u32 reg_2924; + u32 reg_2928; + u32 reg_292c; + u32 reg_2930; + + u32 reg_2934; + + u32 reg_2938; + u32 reg_293c; + u32 reg_2940; + u32 reg_2944; + u32 reg_2948; + u32 reg_294c; + u32 reg_2950; + u32 reg_2954; + u32 reg_2958; + u32 reg_295c; + u32 reg_2960; + u32 reg_2964; + u32 reg_2968; + u32 reg_296c; + + u32 reg_2970; + + int v_filter_1; + int v_filter_2; + int h_filter; + + u32 osd_x_offset; + u32 osd_y_offset; + + u32 osd_x_pan; + u32 osd_y_pan; + + u32 osd_vis_w; + u32 osd_vis_h; + + int decode_height; + + int frame_interlaced; + int frame_interlaced_last; + + int lace_mode; + int lace_threshold; + int lace_sync_field; + + atomic_t next_dma_frame; + atomic_t next_fill_frame; + + u32 yuv_forced_update; + int update_frame; + struct yuv_frame_info new_frame_info[4]; + struct yuv_frame_info old_frame_info; + struct yuv_frame_info old_frame_info_args; + + void *blanking_ptr; + dma_addr_t blanking_dmaptr; +}; + +#define IVTV_VBI_FRAMES 32 + +/* VBI data */ +struct vbi_info { + u32 dec_start; + u32 enc_start, enc_size; + int fpi; + u32 frame; + u32 dma_offset; + u8 cc_data_odd[256]; + u8 cc_data_even[256]; + int cc_pos; + u8 cc_no_update; + u8 vps[5]; + u8 vps_found; + int wss; + u8 wss_found; + u8 wss_no_update; + u32 raw_decoder_line_size; + u8 raw_decoder_sav_odd_field; + u8 raw_decoder_sav_even_field; + u32 sliced_decoder_line_size; + u8 sliced_decoder_sav_odd_field; + u8 sliced_decoder_sav_even_field; + struct v4l2_format in; + /* convenience pointer to sliced struct in vbi_in union */ + struct v4l2_sliced_vbi_format *sliced_in; + u32 service_set_in; + u32 service_set_out; + int insert_mpeg; + + /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + One for /dev/vbi0 and one for /dev/vbi8 */ + struct v4l2_sliced_vbi_data sliced_data[36]; + struct v4l2_sliced_vbi_data sliced_dec_data[36]; + + /* Buffer for VBI data inserted into MPEG stream. + The first byte is a dummy byte that's never used. + The next 16 bytes contain the MPEG header for the VBI data, + the remainder is the actual VBI data. + The max size accepted by the MPEG VBI reinsertion turns out + to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, + where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is + a single line header byte and 2 * 18 is the number of VBI lines per frame. + + However, it seems that the data must be 1K aligned, so we have to + pad the data until the 1 or 2 K boundary. + + This pointer array will allocate 2049 bytes to store each VBI frame. */ + u8 *sliced_mpeg_data[IVTV_VBI_FRAMES]; + u32 sliced_mpeg_size[IVTV_VBI_FRAMES]; + struct ivtv_buffer sliced_mpeg_buf; + u32 inserted_frame; + + u32 start[2], count; + u32 raw_size; + u32 sliced_size; +}; + +/* forward declaration of struct defined in ivtv-cards.h */ +struct ivtv_card; + +/* Struct to hold info about ivtv cards */ +struct ivtv { + int num; /* board number, -1 during init! */ + char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ + struct pci_dev *dev; /* PCI device */ + const struct ivtv_card *card; /* card information */ + const char *card_name; /* full name of the card */ + u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */ + u8 is_50hz; + u8 is_60hz; + u8 is_out_50hz; + u8 is_out_60hz; + u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */ + u8 nof_inputs; /* number of video inputs */ + u8 nof_audio_inputs; /* number of audio inputs */ + u32 v4l2_cap; /* V4L2 capabilities of card */ + u32 hw_flags; /* Hardware description of the board */ + + /* controlling Video decoder function */ + int (*video_dec_func)(struct ivtv *, unsigned int, void *); + + struct ivtv_options options; /* User options */ + int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */ + struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */ + int speed; + u8 speed_mute_audio; + unsigned long i_flags; /* global ivtv flags */ + atomic_t capturing; /* count number of active capture streams */ + atomic_t decoding; /* count number of active decoding streams */ + u32 irq_rr_idx; /* Round-robin stream index */ + int cur_dma_stream; /* index of stream doing DMA */ + int cur_pio_stream; /* index of stream doing PIO */ + u32 dma_data_req_offset; + u32 dma_data_req_size; + int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */ + spinlock_t lock; /* lock access to this struct */ + int search_pack_header; + + spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + + /* User based DMA for OSD */ + struct ivtv_user_dma udma; + + int open_id; /* incremented each time an open occurs, used as unique ID. + starts at 1, so 0 can be used as uninitialized value + in the stream->id. */ + + u32 base_addr; + u32 irqmask; + + struct v4l2_prio_state prio; + struct workqueue_struct *irq_work_queues; + struct work_struct irq_work_queue; + struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */ + + struct vbi_info vbi; + + struct ivtv_mailbox_data enc_mbox; + struct ivtv_mailbox_data dec_mbox; + struct ivtv_api_cache api_cache[256]; /* Cached API Commands */ + + u8 card_rev; + volatile void __iomem *enc_mem, *dec_mem, *reg_mem; + + u32 pgm_info_offset; + u32 pgm_info_num; + u32 pgm_info_write_idx; + u32 pgm_info_read_idx; + struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; + + u64 mpg_data_received; + u64 vbi_data_inserted; + + wait_queue_head_t cap_w; + /* when the next decoder event arrives this queue is woken up */ + wait_queue_head_t event_waitq; + /* when the next decoder vsync arrives this queue is woken up */ + wait_queue_head_t vsync_waitq; + /* when the current DMA is finished this queue is woken up */ + wait_queue_head_t dma_waitq; + + /* OSD support */ + unsigned long osd_video_pbase; + int osd_global_alpha_state; /* 0=off : 1=on */ + int osd_local_alpha_state; /* 0=off : 1=on */ + int osd_color_key_state; /* 0=off : 1=on */ + u8 osd_global_alpha; /* Current global alpha */ + u32 osd_color_key; /* Current color key */ + u32 osd_pixelformat; /* Current pixel format */ + struct v4l2_rect osd_rect; /* Current OSD position and size */ + struct v4l2_rect main_rect; /* Current Main window position and size */ + + u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */ + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + struct mutex i2c_bus_lock; + int i2c_state; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* v4l2 and User settings */ + + /* codec settings */ + struct cx2341x_mpeg_params params; + u32 audio_input; + u32 active_input; + u32 active_output; + v4l2_std_id std; + v4l2_std_id std_out; + v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ + u8 audio_stereo_mode; + u8 audio_bilingual_mode; + + /* dualwatch */ + unsigned long dualwatch_jiffies; + u16 dualwatch_stereo_mode; + + /* Digitizer type */ + int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + + u32 lastVsyncFrame; + + struct yuv_playback_info yuv_info; + struct osd_info *osd_info; +}; + +/* Globals */ +extern struct ivtv *ivtv_cards[]; +extern int ivtv_cards_active; +extern int ivtv_first_minor; +extern spinlock_t ivtv_cards_lock; + +/*==============Prototypes==================*/ + +/* Hardware/IRQ */ +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask); +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask); + +/* try to set output mode, return current mode. */ +int ivtv_set_output_mode(struct ivtv *itv, int mode); + +/* return current output stream based on current mode */ +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv); + +/* Return non-zero if a signal is pending */ +int ivtv_sleep_timeout(int timeout, int intr); + +/* Wait on queue, returns -EINTR if interrupted */ +int ivtv_waitq(wait_queue_head_t *waitq); + +/* Read Hauppauge eeprom */ +struct tveeprom; /* forward reference */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); + +/* This is a PCI post thing, where if the pci register is not read, then + the write doesn't always take effect right away. By reading back the + register any pending PCI writes will be performed (in order), and so + you can be sure that the writes are guaranteed to be done. + + Rarely needed, only in some timing sensitive cases. + Apparently if this is not done some motherboards seem + to kill the firmware and get into the broken state until computer is + rebooted. */ +#define write_sync(val, reg) \ + do { writel(val, reg); readl(reg); } while (0) + +#define read_reg(reg) readl(itv->reg_mem + (reg)) +#define write_reg(val, reg) writel(val, itv->reg_mem + (reg)) +#define write_reg_sync(val, reg) \ + do { write_reg(val, reg); read_reg(reg); } while (0) + +#define read_enc(addr) readl(itv->enc_mem + (u32)(addr)) +#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr)) +#define write_enc_sync(val, addr) \ + do { write_enc(val, addr); read_enc(addr); } while (0) + +#define read_dec(addr) readl(itv->dec_mem + (u32)(addr)) +#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr)) +#define write_dec_sync(val, addr) \ + do { write_dec(val, addr); read_dec(addr); } while (0) + +#endif /* IVTV_DRIVER_H */ diff --git a/ubuntu/media/ivtv/driver/ivtv-fb.c b/ubuntu/media/ivtv/driver/ivtv-fb.c new file mode 100644 index 0000000..bf373d1 --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-fb.c @@ -0,0 +1,1197 @@ +/* + On Screen Display cx23415 Framebuffer driver + + This module presents the cx23415 OSD (onscreen display) framebuffer memory + as a standard Linux /dev/fb style framebuffer device. The framebuffer has + support for 8,16 & 32 bpp packed pixel formats with alpha channel. In 16bpp + mode, there is a choice of a three color depths (12, 15 or 16 bits), but no + local alpha. The colorspace is selectable between rgb & yuv. + Depending on the TV standard configured in the ivtv module at load time, + the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. + Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) + or 59.94 (NTSC) + + Copyright (c) 2003 Matt T. Yourst + + Derived from drivers/video/vesafb.c + Portions (c) 1998 Gerd Knorr + + 2.6 kernel port: + Copyright (C) 2004 Matthias Badaire + + Copyright (C) 2004 Chris Kennedy + + Copyright (C) 2006 Ian Armstrong + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif /* CONFIG_MTRR */ + +#include "ivtv-driver.h" +#include "ivtv-udma.h" +#include "ivtv-mailbox.h" +#include "ivtv-fb.h" + +/* card parameters */ +static int ivtv_fb_card_id; +static int osd_laced; +static int osd_compat; +static int osd_depth; +static int osd_upper; +static int osd_left; +static int osd_yres; +static int osd_xres; + +module_param(ivtv_fb_card_id, int, 0444); +module_param(osd_laced, bool, 0444); +module_param(osd_compat, bool, 0444); +module_param(osd_depth, int, 0444); +module_param(osd_upper, int, 0444); +module_param(osd_left, int, 0444); +module_param(osd_yres, int, 0444); +module_param(osd_xres, int, 0444); + +MODULE_PARM_DESC(ivtv_fb_card_id, + "ID number of ivtv card to use as framebuffer device (0-11)"); + +MODULE_PARM_DESC(osd_compat, + "Compatibility mode - Display size is locked (use for old X drivers)\n" + "\t\t\t0=off\n" + "\t\t\t1=on\n" + "\t\t\tdefault off"); + +/* Why upper, left, xres, yres, depth, laced ? To match terminology used + by fbset. + Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ + +MODULE_PARM_DESC(osd_laced, + "Interlaced mode\n" + "\t\t\t0=off\n" + "\t\t\t1=on\n" + "\t\t\tdefault off"); + +MODULE_PARM_DESC(osd_depth, + "Bits per pixel - 8,16,32\n" + "\t\t\tdefault 8"); + +MODULE_PARM_DESC(osd_upper, + "Vertical start position\n" + "\t\t\tdefault 0 (Centered)"); + +MODULE_PARM_DESC(osd_left, + "Horizontal start position\n" + "\t\t\tdefault 0 (Centered)"); + +MODULE_PARM_DESC(osd_yres, + "Display height\n" + "\t\t\tdefault 480 (PAL)\n" + "\t\t\t 400 (NTSC)"); + +MODULE_PARM_DESC(osd_xres, + "Display width\n" + "\t\t\tdefault 640"); + +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_MTRR +static int mtrr = 1; +#endif /* CONFIG_MTRR */ + +#define IVTV_OSD_MAX_WIDTH 720 +#define IVTV_OSD_MAX_HEIGHT 576 + +#define IVTV_OSD_BPP_8 0x00 +#define IVTV_OSD_BPP_16_444 0x03 +#define IVTV_OSD_BPP_16_555 0x02 +#define IVTV_OSD_BPP_16_565 0x01 +#define IVTV_OSD_BPP_32 0x04 + +struct osd_info { + /* Physical base address */ + unsigned long video_pbase; + /* Relative base address (relative to start of decoder memory) */ + u32 video_rbase; + /* Mapped base address */ + volatile char __iomem *video_vbase; + /* Buffer size */ + u32 video_buffer_size; + +#ifdef CONFIG_MTRR + /* video_base rounded down as required by hardware MTRRs */ + unsigned long fb_start_aligned_physaddr; + /* video_base rounded up as required by hardware MTRRs */ + unsigned long fb_end_aligned_physaddr; +#endif + + /* Store the buffer offset */ + int set_osd_coords_x; + int set_osd_coords_y; + + /* Current dimensions (NOT VISIBLE SIZE!) */ + int display_width; + int display_height; + int display_byte_stride; + + /* Current bits per pixel */ + int bits_per_pixel; + int bytes_per_pixel; + + /* Frame buffer stuff */ + struct fb_info ivtvfb_info; + struct fb_var_screeninfo ivtvfb_defined; + struct fb_fix_screeninfo ivtvfb_fix; +}; + +struct ivtv_osd_coords { + unsigned long offset; + unsigned long max_offset; + int pixel_stride; + int lines; + int x; + int y; +}; + +/* --------------------------------------------------------------------- */ + +/* ivtv API calls for framebuffer related support */ + +static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase, + u32 *fblength) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + int rc; + + rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); + *fbbase = data[0]; + *fblength = data[1]; + return rc; +} + +static int ivtv_fb_get_osd_coords(struct ivtv *itv, + struct ivtv_osd_coords *osd) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); + + osd->offset = data[0] - itv->osd_info->video_rbase; + osd->max_offset = itv->osd_info->display_width * itv->osd_info->display_height * 4; + osd->pixel_stride = data[1]; + osd->lines = data[2]; + osd->x = data[3]; + osd->y = data[4]; + return 0; +} + +static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) +{ + itv->osd_info->display_width = osd->pixel_stride; + itv->osd_info->display_byte_stride = osd->pixel_stride * itv->osd_info->bytes_per_pixel; + itv->osd_info->set_osd_coords_x += osd->x; + itv->osd_info->set_osd_coords_y = osd->y; + + return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, + osd->offset + itv->osd_info->video_rbase, + osd->pixel_stride, + osd->lines, osd->x, osd->y); +} + +static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) +{ + + int osd_height_limit = itv->is_50hz ? 576 : 480; + + /* Only fail if resolution too high, otherwise fudge the start coords. */ + if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) + return -EINVAL; + + /* Ensure we don't exceed display limits */ + if (ivtv_window->top + ivtv_window->height > osd_height_limit) { + IVTV_FB_DEBUG_INFO("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d,%d)\n", + ivtv_window->top, ivtv_window->height); + ivtv_window->top = osd_height_limit - ivtv_window->height; + } + + if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { + IVTV_FB_DEBUG_INFO("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d,%d)\n", + ivtv_window->left, ivtv_window->width); + ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; + } + + /* Set the OSD origin */ + write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); + + /* How much to display */ + write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); + + /* Pass this info back the yuv handler */ + itv->yuv_info.osd_vis_w = ivtv_window->width; + itv->yuv_info.osd_vis_h = ivtv_window->height; + itv->yuv_info.osd_x_offset = ivtv_window->left; + itv->yuv_info.osd_y_offset = ivtv_window->top; + + return 0; +} + +static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv, + unsigned long ivtv_dest_addr, void __user *userbuf, + int size_in_bytes) +{ + DEFINE_WAIT(wait); + int ret = 0; + int got_sig = 0; + + mutex_lock(&itv->udma.lock); + /* Map User DMA */ + if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { + mutex_unlock(&itv->udma.lock); + IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, " + "Error with get_user_pages: %d bytes, %d pages returned\n", + size_in_bytes, itv->udma.page_count); + + /* get_user_pages must have failed completely */ + return -EIO; + } + + IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", + size_in_bytes, itv->udma.page_count); + + ivtv_udma_prepare(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + /* if no UDMA is pending and no UDMA is in progress, then the DMA + is finished */ + while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + /* don't interrupt if the DMA is in progress but break off + a still pending DMA. */ + got_sig = signal_pending(current); + if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + + /* Unmap Last DMA Xfer */ + ivtv_udma_unmap(itv); + mutex_unlock(&itv->udma.lock); + if (got_sig) { + IVTV_DEBUG_INFO("User stopped OSD\n"); + return -EINTR; + } + + return ret; +} + +static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source, unsigned long dest_offset, int count) +{ + DEFINE_WAIT(wait); + + /* Nothing to do */ + if (count == 0) { + IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n"); + return -EINVAL; + } + + /* Check Total FB Size */ + if ((dest_offset + count) > itv->osd_info->video_buffer_size) { + IVTV_FB_WARN( + "ivtv_fb_prep_frame: Overflowing the framebuffer %ld, " + "only %d available\n", + (dest_offset + count), itv->osd_info->video_buffer_size); + return -E2BIG; + } + + /* Not fatal, but will have undesirable results */ + if ((unsigned long)source & 3) + IVTV_FB_WARN ("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",(unsigned long)source); + + if (dest_offset & 3) + IVTV_FB_WARN ("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n",dest_offset); + + if (count & 3) + IVTV_FB_WARN ("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n",count); + + /* Check Source */ + if (!access_ok(VERIFY_READ, source + dest_offset, count)) { + IVTV_FB_WARN( + "Invalid userspace pointer!!! 0x%08lx\n", + (unsigned long)source); + + IVTV_FB_DEBUG_WARN( + "access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n", + dest_offset, (unsigned long)source, + count); + return -EINVAL; + } + + /* OSD Address to send DMA to */ + dest_offset += IVTV_DEC_MEM_START + itv->osd_info->video_rbase; + + /* Fill Buffers */ + return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count); +} + +static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + DEFINE_WAIT(wait); + struct ivtv *itv = (struct ivtv *)info->par; + int rc=0; + + switch (cmd) { + case FBIOGET_VBLANK: { + struct fb_vblank vblank; + u32 trace; + + vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | + FB_VBLANK_HAVE_VSYNC; + trace = read_reg (0x028c0) >> 16; + if (itv->is_50hz && trace > 312) trace -= 312; + else if (itv->is_60hz && trace > 262) trace -= 262; + if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING; + vblank.count = itv->lastVsyncFrame; + vblank.vcount = trace; + vblank.hcount = 0; + if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) + return -EFAULT; + return 0; + } + + case FBIO_WAITFORVSYNC: { + prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); + if (!schedule_timeout(HZ/20)) rc = -ETIMEDOUT; + finish_wait (&itv->vsync_waitq, &wait); + return rc; + } + + case IVTVFB_IOC_DMA_FRAME: { + struct ivtvfb_dma_frame args; + + IVTV_FB_DEBUG_IOCTL("IVTVFB_IOC_DMA_FRAME\n"); + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) + return -EFAULT; + + return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); + } + + default: + IVTV_FB_ERR("Unknown IOCTL %08x\n",cmd); + return -EINVAL; + } + return 0; +} + +/* Framebuffer device handling */ + +static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) +{ + + struct ivtv_osd_coords ivtv_osd; + struct v4l2_rect ivtv_window; + + IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n"); + + /* Select color space */ + if (var->nonstd) /* YUV */ + write_reg (read_reg(0x02a00) | 0x0002000,0x02a00); + else /* RGB */ + write_reg (read_reg(0x02a00) & ~0x0002000,0x02a00); + + /* Set the color mode + Although rare, occasionally things go wrong. The extra mode + change seems to help... */ + + switch (var->bits_per_pixel) { + case 8: + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_8); + break; + case 32: + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_32); + break; + case 16: + switch (var->green.length) { + case 4: + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_444); + break; + case 5: + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_555); + break; + case 6: + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); + ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_565); + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); + } + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); + } + + itv->osd_info->bits_per_pixel = var->bits_per_pixel; + itv->osd_info->bytes_per_pixel = var->bits_per_pixel / 8; + + /* Set the flicker filter */ + switch (var->vmode & FB_VMODE_MASK) { + case FB_VMODE_NONINTERLACED: /* Filter on */ + ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); + break; + case FB_VMODE_INTERLACED: /* Filter off */ + ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); + break; + default: + IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); + } + + /* Read the current osd info */ + ivtv_fb_get_osd_coords(itv, &ivtv_osd); + + /* Now set the OSD to the size we want */ + ivtv_osd.pixel_stride = var->xres_virtual; + ivtv_osd.lines = var->yres_virtual; + ivtv_osd.x = 0; + ivtv_osd.y = 0; + ivtv_fb_set_osd_coords(itv, &ivtv_osd); + + /* Can't seem to find the right API combo for this. + Use another function which does what we need through direct register access. */ + ivtv_window.width = var->xres; + ivtv_window.height = var->yres; + + /* Minimum margin cannot be 0, as X won't allow such a mode */ + if (!var->upper_margin) var->upper_margin ++; + if (!var->left_margin) var->left_margin ++; + ivtv_window.top = var->upper_margin - 1; + ivtv_window.left = var->left_margin - 1; + + ivtv_fb_set_display_window(itv, &ivtv_window); + + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + + IVTV_FB_INFO("=== Display mode change ===\n"); + IVTV_FB_INFO("Display size %dx%d (%dx%d Virtual) @ %dbpp\n", + var->xres, + var->yres, + var->xres_virtual, + var->yres_virtual, + var->bits_per_pixel); + + IVTV_FB_INFO("Display position %d,%d\n", + var->left_margin, + var->upper_margin); + + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) { + IVTV_FB_INFO("Display filter : on\n"); + } + else { + IVTV_FB_INFO("Display filter : off\n"); + } + + if (var->nonstd) { + IVTV_FB_INFO("Color space : YUV\n"); + } + else { + IVTV_FB_INFO("Color space : RGB\n"); + } + + return 0; +} + +static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) +{ + IVTV_FB_DEBUG_INFO ("ivtvfb_get_fix\n"); + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "cx23415 TV out"); + fix->smem_start = itv->osd_info->video_pbase; + fix->smem_len = itv->osd_info->video_buffer_size; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = (itv->osd_info->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->line_length = itv->osd_info->display_byte_stride; + fix->accel = FB_ACCEL_NONE; + return 0; +} + +/* Check the requested display mode, returning -EINVAL if we can't + handle it. */ + +static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) +{ + int osd_height_limit; + u32 pixclock, hlimit, vlimit; + + IVTV_FB_DEBUG_INFO ("ivtvfb_check_var\n"); + + /* Set base references for mode calcs. */ + if (itv->is_50hz) { + pixclock = 84316; + hlimit = 776; + vlimit = 591; + osd_height_limit = 576; + } + else { + pixclock = 83926; + hlimit = 776; + vlimit = 495; + osd_height_limit = 480; + } + + /* Check the bits per pixel */ + if (osd_compat) { + if (var->bits_per_pixel != 32) { + IVTV_FB_DEBUG_WARN ("Invalid colour mode: %d\n",var->bits_per_pixel); + return -EINVAL; + } + } + + if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + } + else if (var->bits_per_pixel == 16) { + /* To find out the true mode, check green length */ + switch (var->green.length) { + case 4: + var->transp.offset = 0; + var->transp.length = 0; + var->red.offset = 8; + var->red.length = 4; + var->green.offset = 4; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + break; + case 5: + var->transp.offset = 0; + var->transp.length = 0; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + break; + default: + var->transp.offset = 0; + var->transp.length = 0; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + break; + } + } + else { + IVTV_FB_DEBUG_WARN ("Invalid colour mode: %d\n",var->bits_per_pixel); + return -EINVAL; + } + + /* Check the resolution */ + if (osd_compat) { + if (var->xres != itv->osd_info->ivtvfb_defined.xres || var->yres != itv->osd_info->ivtvfb_defined.yres || + var->xres_virtual != itv->osd_info->ivtvfb_defined.xres_virtual || var->yres_virtual != + itv->osd_info->ivtvfb_defined.yres_virtual) { + IVTV_FB_DEBUG_WARN ("Invalid resolution: %d x %d (%d x %d Virtual)\n", + var->xres,var->yres, var->xres_virtual,var->yres_virtual); + return -EINVAL; + } + } + else { + if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit ) { + IVTV_FB_DEBUG_WARN ("Invalid resolution: %d x %d\n", + var->xres,var->yres); + return -EINVAL; + } + + /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ + if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || + var->xres_virtual * var->yres_virtual * (var->bits_per_pixel/8) > itv->osd_info->video_buffer_size || + var->xres_virtual < var->xres || + var->yres_virtual < var->yres) { + IVTV_FB_DEBUG_WARN ("Invalid virtual resolution: %d x %d\n", + var->xres_virtual, var->yres_virtual); + return -EINVAL; + } + } + + /* Some extra checks if in 8 bit mode */ + if (var->bits_per_pixel == 8) { + /* Width must be a multiple of 4 */ + if (var->xres & 3) { + IVTV_FB_DEBUG_WARN ("Invalid resolution for 8bpp: %d\n", var->xres); + return -EINVAL; + } + if (var->xres_virtual & 3) { + IVTV_FB_DEBUG_WARN ("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); + return -EINVAL; + } + } + else if (var->bits_per_pixel == 16) { + /* Width must be a multiple of 2 */ + if (var->xres & 1) { + IVTV_FB_DEBUG_WARN ("Invalid resolution for 16bpp: %d\n", var->xres); + return -EINVAL; + } + if (var->xres_virtual & 1) { + IVTV_FB_DEBUG_WARN ("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); + return -EINVAL; + } + } + + /* Now check the offsets */ + if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { + IVTV_FB_DEBUG_WARN ("Invalid offset: %d (%d) %d (%d)\n",var->xoffset,var->xres_virtual, + var->yoffset,var->yres_virtual); + return -EINVAL; + } + + /* Check pixel format */ + if (var->nonstd > 1) { + IVTV_FB_DEBUG_WARN ("Invalid nonstd % d\n",var->nonstd); + return -EINVAL; + } + + /* Check video mode */ + if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && + ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { + IVTV_FB_DEBUG_WARN ("Invalid video mode: %d\n",var->vmode & FB_VMODE_MASK); + return -EINVAL; + } + + /* Check the left & upper margins + If the margins are too large, just center the screen + (enforcing margins causes too many problems) */ + + if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) { + var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); + } + if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) { + var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2); + } + + /* Maintain overall 'size' for a constant refresh rate */ + var->right_margin = hlimit - var->left_margin - var->xres; + var->lower_margin = vlimit - var->upper_margin - var->yres; + + /* Fixed sync times */ + var->hsync_len = 24; + var->vsync_len = 2; + + /* Non-interlaced / interlaced mode is used to switch the OSD filter + on or off. Adjust the clock timings to maintain a constant + vertical refresh rate. */ + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) + var->pixclock = pixclock / 2; + else + var->pixclock = pixclock; + + IVTV_FB_DEBUG_INFO ("ivtvfb_check_var - Parameters validated\n"); + + IVTV_FB_INFO("=== Validated display mode ===\n"); + IVTV_FB_INFO("Display size %dx%d (%dx%d Virtual) @ %dbpp\n", + var->xres, + var->yres, + var->xres_virtual, + var->yres_virtual, + var->bits_per_pixel); + + IVTV_FB_INFO("Display position %d,%d\n", + var->left_margin, + var->upper_margin); + + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) { + IVTV_FB_INFO("Display filter : on\n"); + } + else { + IVTV_FB_INFO("Display filter : off\n"); + } + + if (var->nonstd) { + IVTV_FB_INFO("Color space : YUV\n"); + } + else { + IVTV_FB_INFO("Color space : RGB\n"); + } + return 0; +} + +static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct ivtv *itv = (struct ivtv *) info->par; + IVTV_FB_DEBUG_INFO ("ivtvfb_check_var\n"); + return _ivtvfb_check_var (var,itv); +} + +static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 osd_pan_index; + struct ivtv *itv = (struct ivtv *) info->par; + + osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8; + write_reg (osd_pan_index,0x02A0C); + + /* Pass this info back the yuv handler */ + itv->yuv_info.osd_x_pan = var->xoffset; + itv->yuv_info.osd_y_pan = var->yoffset; + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + return 0; +} + +static int ivtvfb_set_par(struct fb_info *info) +{ + int rc = 0; + struct ivtv *itv = (struct ivtv *) info->par; + + IVTV_FB_DEBUG_INFO ("ivtvfb_set_par\n"); + + rc = ivtvfb_set_var(itv, &info->var); + ivtvfb_pan_display(&info->var, info); + ivtvfb_get_fix (itv, &info->fix); + return rc; +} + +static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 color, *palette; + struct ivtv *itv = (struct ivtv *) info->par; + + if (regno >= info->cmap.len) + return -EINVAL; + + color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); + if (info->var.bits_per_pixel <= 8) { + write_reg(regno, 0x02a30); + write_reg(color, 0x02a34); + } + else { + if (regno >= 16) + return -EINVAL; + + palette = info->pseudo_palette; + if (info->var.bits_per_pixel == 16) { + switch (info->var.green.length) { + case 4: + color = ((red & 0xf000) >> 4) | + ((green & 0xf000) >> 8) | + ((blue & 0xf000) >> 12); + break; + case 5: + color = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 6: + color = (red & 0xf800 ) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + } + } + palette[regno] = color; + } + + return 0; +} + +/* We don't really support blanking. All this does is enable or + disable the OSD. */ +static int ivtvfb_blank(int blank_mode, struct fb_info *info) +{ + struct ivtv *itv = (struct ivtv *)info->par; + + IVTV_FB_DEBUG_INFO ("Set blanking mode : %d\n",blank_mode); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); + break; + case FB_BLANK_NORMAL: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); + break; + } + return 0; +} + +static struct fb_ops ivtvfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = ivtvfb_check_var, + .fb_set_par = ivtvfb_set_par, + .fb_setcolreg = ivtvfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = NULL, + .fb_ioctl = ivtvfb_ioctl, + .fb_pan_display = ivtvfb_pan_display, + .fb_blank = ivtvfb_blank, +}; + +/* Initialization */ + + +/* Setup our initial video mode */ +static int ivtvfb_init_vidmode(struct ivtv *itv) +{ + int max_height; + struct v4l2_rect start_window; + + /* Color mode */ + + if (osd_compat) osd_depth = 32; + if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8; + itv->osd_info->bits_per_pixel = osd_depth; + itv->osd_info->bytes_per_pixel = itv->osd_info->bits_per_pixel / 8; + + /* Horizontal size & position */ + + if (osd_xres > 720) osd_xres = 720; + + /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ + if (osd_depth == 8) + osd_xres &= ~3; + else if (osd_depth == 16) + osd_xres &= ~1; + + if (osd_xres) + start_window.width = osd_xres; + else + start_window.width = osd_compat ? 720: 640; + + /* Check horizontal start (osd_left). */ + if (osd_left && osd_left + start_window.width > 721) { + IVTV_FB_ERR ("Invalid osd_left - assuming default\n"); + osd_left = 0; + } + + /* Hardware coords start at 0, user coords start at 1. */ + osd_left --; + + start_window.left = + osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); + + itv->osd_info->display_byte_stride = + start_window.width * itv->osd_info->bytes_per_pixel; + + /* Vertical size & position */ + + max_height = itv->is_50hz ? 576 : 480; + + if ( osd_yres > max_height) osd_yres = max_height; + + if (osd_yres) + start_window.height = osd_yres; + else { + if (itv->is_50hz) + start_window.height = osd_compat ? max_height : 480; + else + start_window.height = osd_compat ? max_height : 400; + } + + /* Check vertical start (osd_upper). */ + if (osd_upper + start_window.height > max_height + 1) { + IVTV_FB_ERR ("Invalid osd_upper - assuming default\n"); + osd_upper = 0; + } + + /* Hardware coords start at 0, user coords start at 1. */ + osd_upper --; + + start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); + + itv->osd_info->display_width = start_window.width; + itv->osd_info->display_height = start_window.height; + + /* Generate a valid fb_var_screeninfo */ + + itv->osd_info->ivtvfb_defined.xres = itv->osd_info->display_width; + itv->osd_info->ivtvfb_defined.yres = itv->osd_info->display_height; + itv->osd_info->ivtvfb_defined.xres_virtual = itv->osd_info->display_width; + itv->osd_info->ivtvfb_defined.yres_virtual = itv->osd_info->display_height; + itv->osd_info->ivtvfb_defined.bits_per_pixel = itv->osd_info->bits_per_pixel; + itv->osd_info->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); + itv->osd_info->ivtvfb_defined.left_margin = start_window.left + 1; + itv->osd_info->ivtvfb_defined.upper_margin = start_window.top + 1; + itv->osd_info->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; + itv->osd_info->ivtvfb_defined.nonstd = 0; + + /* We've filled in the most data, let the usual mode check + routine fill in the rest. */ + _ivtvfb_check_var (&itv->osd_info->ivtvfb_defined,itv); + + /* Generate valid fb_fix_screeninfo */ + + ivtvfb_get_fix(itv,&itv->osd_info->ivtvfb_fix); + + /* Generate valid fb_info */ + + itv->osd_info->ivtvfb_info.node = -1; + itv->osd_info->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT; + itv->osd_info->ivtvfb_info.fbops = &ivtvfb_ops; + itv->osd_info->ivtvfb_info.par = itv; + itv->osd_info->ivtvfb_info.var = itv->osd_info->ivtvfb_defined; + itv->osd_info->ivtvfb_info.fix = itv->osd_info->ivtvfb_fix; + itv->osd_info->ivtvfb_info.screen_base = (u8 __iomem *)itv->osd_info->video_vbase; + itv->osd_info->ivtvfb_info.fbops = &ivtvfb_ops; + + /* Supply some monitor specs. Bogus values will do for now */ + itv->osd_info->ivtvfb_info.monspecs.hfmin = 8000; + itv->osd_info->ivtvfb_info.monspecs.hfmax = 70000; + itv->osd_info->ivtvfb_info.monspecs.vfmin = 10; + itv->osd_info->ivtvfb_info.monspecs.vfmax = 100; + + /* Allocate color map */ + if (fb_alloc_cmap(&itv->osd_info->ivtvfb_info.cmap, 256, 1)) { + IVTV_FB_ERR ("abort, unable to alloc cmap\n"); + return -ENOMEM; + } + + /* Allocate the pseudo palette */ + itv->osd_info->ivtvfb_info.pseudo_palette = kmalloc(sizeof (u32) * 16, GFP_KERNEL); + + if (!itv->osd_info->ivtvfb_info.pseudo_palette) { + IVTV_FB_ERR ("abort, unable to alloc pseudo pallete\n"); + return -ENOMEM; + } + + return 0; +} + +/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ + +static int ivtvfb_init_io(struct ivtv *itv) +{ + ivtv_fb_get_framebuffer(itv, &itv->osd_info->video_rbase, &itv->osd_info->video_buffer_size); + + /* The osd buffer size depends on the number of video buffers allocated + on the PVR350 itself. For now we'll hardcode the smallest osd buffer + size to prevent any overlap. */ + itv->osd_info->video_buffer_size = 1704960; + + itv->osd_info->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; + itv->osd_info->video_vbase = itv->dec_mem + itv->osd_info->video_rbase; + + if (!itv->osd_info->video_vbase) { + IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", + itv->osd_info->video_buffer_size, itv->osd_info->video_pbase); + return -EIO; + } + + IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", + itv->osd_info->video_pbase, itv->osd_info->video_vbase, + itv->osd_info->video_buffer_size / 1024); + +#ifdef CONFIG_MTRR + if (mtrr) { + /* Find the largest power of two that maps the whole buffer */ + int size_shift = 31; + + while (!(itv->osd_info->video_buffer_size & (1 << size_shift))) { + size_shift--; + } + size_shift++; + itv->osd_info->fb_start_aligned_physaddr = itv->osd_info->video_pbase & ~((1 << size_shift) - 1); + itv->osd_info->fb_end_aligned_physaddr = itv->osd_info->video_pbase + itv->osd_info->video_buffer_size; + itv->osd_info->fb_end_aligned_physaddr += (1 << size_shift) - 1; + itv->osd_info->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); + if (mtrr_add(itv->osd_info->fb_start_aligned_physaddr, + itv->osd_info->fb_end_aligned_physaddr - itv->osd_info->fb_start_aligned_physaddr, + MTRR_TYPE_WRCOMB, 1) < 0) { + IVTV_FB_ERR("warning: mtrr_add() failed to add write combining region 0x%08x-0x%08x\n", + (unsigned int)itv->osd_info->fb_start_aligned_physaddr, + (unsigned int)itv->osd_info->fb_end_aligned_physaddr); + } + } +#endif /* CONFIG_MTRR */ + + /* Blank the entire osd. */ + memset_io(itv->osd_info->video_vbase, 0, itv->osd_info->video_buffer_size); + + return 0; +} + +/* Release any memory we've grabbed & remove mtrr entry */ +static void ivtvfb_release_buffers (struct ivtv *itv) +{ + /* Release cmap */ + if (itv->osd_info->ivtvfb_info.cmap.len); + fb_dealloc_cmap(&itv->osd_info->ivtvfb_info.cmap); + + /* Release pseudo palette */ + if (itv->osd_info->ivtvfb_info.pseudo_palette) + kfree(itv->osd_info->ivtvfb_info.pseudo_palette); + +#ifdef CONFIG_MTRR + mtrr_del(-1, itv->osd_info->fb_start_aligned_physaddr, + (itv->osd_info->fb_end_aligned_physaddr - itv->osd_info->fb_start_aligned_physaddr)); +#endif /* CONFIG_MTRR */ + + kfree(itv->osd_info); + itv->osd_info = NULL; +} + +/* Initialize the specified card */ + +static int ivtvfb_init_card (struct ivtv *itv) +{ + int rc; + + if (itv->osd_info) { + printk(KERN_ERR "ivtv-fb: Card %d already initialised\n", + ivtv_fb_card_id); + return -EBUSY; + } + + itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC); + if (itv->osd_info == 0) { + printk(KERN_ERR "ivtv-fb: Failed to allocate memory for osd_info\n"); + return -ENOMEM; + } + + /* Find & setup the OSD buffer */ + if ((rc = ivtvfb_init_io (itv))) + return rc; + + /* Set the startup video mode information */ + if ((rc = ivtvfb_init_vidmode (itv))) { + ivtvfb_release_buffers(itv); + return rc; + } + + /* Register the framebuffer */ + if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { + ivtvfb_release_buffers(itv); + return -EINVAL; + } + + itv->osd_video_pbase = itv->osd_info->video_pbase; + + /* Set the card to the requested mode */ + ivtvfb_set_par(&itv->osd_info->ivtvfb_info); + + /* Set color 0 to black */ + write_reg(0, 0x02a30); + write_reg(0, 0x02a34); + + /* Enable the osd */ + ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); + + /* Note if we're running in compatibility mode */ + if (osd_compat) + IVTV_FB_INFO ("Running in compatibility mode. Display resize & mode change disabled\n"); + + /* Allocate DMA */ + ivtv_udma_alloc(itv); + return 0; + +} + +static int __init ivtvfb_init(void) +{ + struct ivtv *itv; + int i, registered = 0; + + if ((ivtv_fb_card_id < 0) || (ivtv_fb_card_id >= ivtv_cards_active)) { + printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: 0-%d)\n", + ivtv_cards_active - 1); + return -EINVAL; + } + + /* Locate & initialise all cards supporting an OSD. */ + if (ivtv_fb_card_id == 0) { + for (i = 0; i < ivtv_cards_active; i++) { + itv = ivtv_cards[i]; + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (ivtvfb_init_card (itv) == 0) { + IVTV_FB_INFO ("Framebuffer registered on ivtv card id %d\n", i); + registered ++; + } + } + } + if (!registered) { + printk (KERN_ERR "ivtv-fb: no cards found"); + return -ENODEV; + } + return 0; + } + + itv = ivtv_cards[ivtv_fb_card_id]; + if (!itv || !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + printk(KERN_ERR "ivtv-fb: Specified card (id %d) is either not present or does not support TV out\n", + ivtv_fb_card_id); + return -ENODEV; + } + return (ivtvfb_init_card (itv)); +} + +static void ivtvfb_cleanup(void) +{ + struct ivtv *itv; + int i; + + printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n"); + + for ( i = 0; i < ivtv_cards_active; i++) { + itv = ivtv_cards[i]; + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) { + IVTV_FB_DEBUG_WARN ("Unregister framebuffer %d\n",i); + ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); + unregister_framebuffer(&itv->osd_info->ivtvfb_info); + ivtvfb_release_buffers(itv); + } + itv->osd_video_pbase = 0; + } +} + +module_init(ivtvfb_init); +module_exit(ivtvfb_cleanup); diff --git a/ubuntu/media/ivtv/driver/ivtv-fb.h b/ubuntu/media/ivtv/driver/ivtv-fb.h new file mode 100644 index 0000000..8e24cde --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-fb.h @@ -0,0 +1,34 @@ +/* + On Screen Display cx23415 Framebuffer driver + + Copyright (C) 2006 Ian Armstrong + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_IVTV_FB_H +#define _LINUX_IVTV_FB_H + +/* Framebuffer external API */ + +struct ivtvfb_dma_frame { + void __user *source; + unsigned long dest_offset; + int count; +}; + +#define IVTVFB_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtvfb_dma_frame) + +#endif diff --git a/ubuntu/media/ivtv/driver/ivtv-mailbox.h b/ubuntu/media/ivtv/driver/ivtv-mailbox.h new file mode 100644 index 0000000..79b8aec --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-mailbox.h @@ -0,0 +1,25 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]); +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); diff --git a/ubuntu/media/ivtv/driver/ivtv-svnrelease.h b/ubuntu/media/ivtv/driver/ivtv-svnrelease.h new file mode 100644 index 0000000..fcb681f --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-svnrelease.h @@ -0,0 +1 @@ +#define IVTV_DRIVER_VERSION_COMMENT "(tagged release)" diff --git a/ubuntu/media/ivtv/driver/ivtv-udma.h b/ubuntu/media/ivtv/driver/ivtv-udma.h new file mode 100644 index 0000000..e131bcc --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-udma.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2006-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* User DMA functions */ +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size); +int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset); +void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split); +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes); +void ivtv_udma_unmap(struct ivtv *itv); +void ivtv_udma_free(struct ivtv *itv); +void ivtv_udma_alloc(struct ivtv *itv); +void ivtv_udma_prepare(struct ivtv *itv); +void ivtv_udma_start(struct ivtv *itv); + +static inline void ivtv_udma_sync_for_device(struct ivtv *itv) +{ + pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} + +static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) +{ + pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} diff --git a/ubuntu/media/ivtv/driver/ivtv-version.h b/ubuntu/media/ivtv/driver/ivtv-version.h new file mode 100644 index 0000000..a73cbae --- /dev/null +++ b/ubuntu/media/ivtv/driver/ivtv-version.h @@ -0,0 +1,29 @@ +/* + ivtv driver version information + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Obtain IVTV_DRIVER_VERSION_COMMENT from here */ +#include "ivtv-svnversion.h" + +#define IVTV_DRIVER_NAME "ivtv" +#define IVTV_DRIVER_VERSION_MAJOR 1 +#define IVTV_DRIVER_VERSION_MINOR 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 2 + +#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) " " IVTV_DRIVER_VERSION_COMMENT +#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/ubuntu/media/ivtv/driver/media/ivtv.h b/ubuntu/media/ivtv/driver/media/ivtv.h new file mode 100644 index 0000000..412b48e --- /dev/null +++ b/ubuntu/media/ivtv/driver/media/ivtv.h @@ -0,0 +1,65 @@ +/* + Public ivtv API header + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_IVTV_H +#define _LINUX_IVTV_H + +/* ivtv knows several distinct output modes: MPEG streaming, + YUV streaming, YUV updates through user DMA and the passthrough + mode. + + In order to clearly tell the driver that we are in user DMA + YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL + first (althrough if you don't then the first time + DMA_FRAME is called the mode switch is done automatically). + + When you close the file handle the user DMA mode is exited again. + + While in one mode, you cannot use another mode (EBUSY is returned). + + All this means that if you want to change the YUV interlacing + for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME + with y_source == NULL before you can set the correct format using + VIDIOC_S_FMT. + + Eventually all this should be replaced with a proper V4L2 API, + but for now we have to do it this way. */ + +struct ivtv_dma_frame { + enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */ + __u32 pixelformat; /* 0 == same as destination */ + void __user *y_source; /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT, + then just switch to user DMA YUV output mode */ + void __user *uv_source; /* Unused for RGB pixelformats */ + struct v4l2_rect src; + struct v4l2_rect dst; + __u32 src_width; + __u32 src_height; +}; + +#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame) + +/* These are the VBI types as they appear in the embedded VBI private packets. */ +#define IVTV_SLICED_TYPE_TELETEXT_B (1) +#define IVTV_SLICED_TYPE_CAPTION_525 (4) +#define IVTV_SLICED_TYPE_WSS_625 (5) +#define IVTV_SLICED_TYPE_VPS (7) + +#endif /* _LINUX_IVTV_H */ diff --git a/ubuntu/media/ivtv/i2c-drivers/saa717x.c b/ubuntu/media/ivtv/i2c-drivers/saa717x.c new file mode 100644 index 0000000..3e94120 --- /dev/null +++ b/ubuntu/media/ivtv/i2c-drivers/saa717x.c @@ -0,0 +1,1677 @@ +/* + * saa717x - Philips SAA717xHL video decoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on saa7111 driver by Dave Perks + * Based on saa7115 driver by Maxim Yevtyushkin + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * Changes by Kevin Thayer + * - changed to saa7115. (2/17/2003) + * + * Changes by Ohta Kyuma + * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) + * + * Changes by T.Adachi (tadachi@tadachi-net.com) + * - support audio, video scaler etc, and ckecked the initialize sequence. + * + * 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. + */ + +/* + * NOTE: + * I wrote the comment to the place of a register value. But, these are + * referenced datasheet of SAA7134. Because we were not able to + * obtain the datasheet of saa 7173/4. It seems the functions of almost + * registers are the same as SAA7133. But, the correctness of the contents + * is not guaranteed. + * + * We implemented the initialization sequences that were based on the + * result of the capture of I2C bus on the Windows driver. Therefore, we + * are checked only it on the environment of MPG600GR(NTSC) w/uPD64031A+uPD64083. + * --tadachi + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA717x video decoder driver"); +MODULE_AUTHOR("Kevin Thayer, K. Ohta"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +module_param(debug, int, 0644); +#else +MODULE_PARM(debug, "i"); +#endif +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +struct saa717x_state { + v4l2_std_id std; + int input; + int enable; + int radio; + int bright; + int contrast; + int hue; + int sat; + int playback; + int audio; + // Add 20041101 K.Ohta + int tuner_audio_mode; + int audio_main_mute; + int audio_main_vol_r; + int audio_main_vol_l; + u16 audio_main_bass; + u16 audio_main_treble; + u16 audio_main_volume; + u16 audio_main_balance; + int audio_input; +}; + +/* ----------------------------------------------------------------------- */ + +// for audio mode +#define TUNER_AUDIO_MONO 0 // LL +#define TUNER_AUDIO_STEREO 1 // LR +#define TUNER_AUDIO_LANG1 2 // LL +#define TUNER_AUDIO_LANG2 3 // RR + +#define SAA_7114_NTSC_HOFFSET (6) +#define SAA_7114_NTSC_VOFFSET (10) +#define SAA_7114_NTSC_WIDTH (704) +#define SAA_7114_NTSC_HEIGHT (480) /* was 250*/ + +#define SAA_7114_SECAM_HSYNC_START (-17) +#define SAA_7114_SECAM_HSYNC_STOP (-32) + +#define SAA_7114_SECAM_HOFFSET (2) +#define SAA_7114_SECAM_VOFFSET (10) +#define SAA_7114_SECAM_WIDTH (720) +#define SAA_7114_SECAM_HEIGHT (300) + +#define SAA_7114_PAL_HSYNC_START (-17) +#define SAA_7114_PAL_HSYNC_STOP (-32) + +#define SAA_7114_PAL_HOFFSET (2) +#define SAA_7114_PAL_VOFFSET (10) +#define SAA_7114_PAL_WIDTH (720) +#define SAA_7114_PAL_HEIGHT (300) + +#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040 +#define SAA_7114_VERTICAL_LUMA_OFFSET 0 + +#define LOBYTE(x) ((unsigned char)((x) & 0xff)) +#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff)) +#define LOWORD(x) ((unsigned short int)((x) & 0xffff)) +#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff)) + + +/* ----------------------------------------------------------------------- */ +/* + * Now,This routine and value is larger than 0xff, + * happend problems cause of invalid endian,please fix! 20060928 K.Ohta + */ + +static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) +{ + struct i2c_adapter *adap = client->adapter; + //int fw_addr = (reg >= 0x404 && reg <= 0x4b8); // Fix Below 'cos register area is more limited. + int fw_addr = ((reg == 0x454) || ((reg >= 0x464) && (reg <= 0x478)) || (reg == 0x480) || (reg == 0x488)); + unsigned char mm1[6]; + struct i2c_msg msg; + + msg.flags = 0; + msg.addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + + if (fw_addr) { /* Fix2 20061006 K.Ohta */ + mm1[4] = (value >> 16) & 0xff; // Apply data-tables. + mm1[3] = (value >> 8) & 0xff;// Apply data-tables. + mm1[2] = value & 0xff;// Apply data-tables. + } else { + mm1[2] = value & 0xff; + } + //msg.len = fw_addr ? 6 : 3; + msg.len = fw_addr ? 5 : 3; // Long Registers have *only* three bytes! + msg.buf = mm1; + v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value); + return i2c_transfer(adap, &msg, 1) == 1; +} + +static void saa717x_write_regs(struct i2c_client *client, u32 *data) +{ + int i=0; // Because imcompatible effect with gcc4.1.1 @ amd64 + while ((data[i+0] != 0) || (data[i+1] != 0)) { // Fix because writing register address 0x0000 while initialization 20061107 + saa717x_write(client, data[i+0], data[i+1]); + i += 2; + } +} + +static u32 saa717x_read(struct i2c_client *client, u32 reg) +{ + struct i2c_adapter *adap=client->adapter; + int fw_addr = ((reg >= 0x404 && reg <= 0x4b8) || (reg ==0x528)); + unsigned char mm1[2]; + unsigned char mm2[4] = { 0, 0, 0, 0 }; + struct i2c_msg msgs[2]; + u32 value; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + msgs[0].len = 2; + msgs[0].buf = mm1; + //msgs[1].len = fw_addr ? 4 : 1; + msgs[1].len = fw_addr ? 3 : 1; // Multibyte Registers contains *only 3bytes */ + msgs[1].buf = mm2; + i2c_transfer(adap, msgs, 2); + + if (fw_addr) { /* Fix2 20061006 K.Ohta */ + value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff)>> 16); + } else { + value = mm2[0] & 0xff; + } + + v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value); + + return value; +} + +/* ----------------------------------------------------------------------- */ +/* + * Now,This table and value is larger than 0xff, + * happend problems cause of invalid endian,please fix! 20060928 K.Ohta + */ + +/***** initialize @ detect_client *****/ +static u32 reg_init_initialize[] = +{ + /* from linux driver */ + 0x101, 0x008, // Increment delay + + 0x103, 0x000, // Analog input control 2 + 0x104, 0x090, // Analog input control 3 + 0x105, 0x090, // Analog input control 4 + 0x106, 0x0eb, // Horizontal sync start + 0x107, 0x0e0, // Horizontal sync stop + 0x109, 0x055, // Luminance control + + 0x10f, 0x02a, // Chroma gain control + 0x110, 0x000, // Chroma control 2 + + 0x114, 0x045, // analog/ADC + + 0x118, 0x040, // RAW data gain + 0x119, 0x080, // RAW data offset + + 0x044, 0x000, // VBI horizontal input window start (L) TASK A + 0x045, 0x000, // VBI horizontal input window start (H) TASK A + 0x046, 0x0cf, // VBI horizontal input window stop (L) TASK A + 0x047, 0x002, // VBI horizontal input window stop (H) TASK A + + 0x049, 0x000, // VBI vertical input window start (H) TASK A + + 0x04c, 0x0d0, // VBI horizontal output length (L) TASK A + 0x04d, 0x002, // VBI horizontal output length (H) TASK A + + 0x064, 0x080, // Lumina brightness TASK A + 0x065, 0x040, // Luminance contrast TASK A + 0x066, 0x040, // Chroma saturation TASK A + // 067H: Reserved + 0x068, 0x000, // VBI horizontal scaling increment (L) TASK A + 0x069, 0x004, // VBI horizontal scaling increment (H) TASK A + 0x06a, 0x000, // VBI phase offset TASK A + + 0x06e, 0x000, // Horizontal phase offset Luma TASK A + 0x06f, 0x000, // Horizontal phase offset Chroma TASK A + + 0x072, 0x000, // Vertical filter mode TASK A + + 0x084, 0x000, // VBI horizontal input window start (L) TAKS B + 0x085, 0x000, // VBI horizontal input window start (H) TAKS B + 0x086, 0x0cf, // VBI horizontal input window stop (L) TAKS B + 0x087, 0x002, // VBI horizontal input window stop (H) TAKS B + + 0x089, 0x000, // VBI vertical input window start (H) TAKS B + + 0x08c, 0x0d0, // VBI horizontal output length (L) TASK B + 0x08d, 0x002, // VBI horizontal output length (H) TASK B + + 0x0a4, 0x080, // Lumina brightness TASK B + 0x0a5, 0x040, // Luminance contrast TASK B + 0x0a6, 0x040, // Chroma saturation TASK B + // 0A7H reserved + 0x0a8, 0x000, // VBI horizontal scaling increment (L) TASK B + 0x0a9, 0x004, // VBI horizontal scaling increment (H) TASK B + 0x0aa, 0x000, // VBI phase offset TASK B + + 0x0ae, 0x000, // Horizontal phase offset Luma TASK B + 0x0af, 0x000, //Horizontal phase offset Chroma TASK B + + 0x0b2, 0x000, // Vertical filter mode TASK B + + 0x00c, 0x000, // Start point GREEN path + 0x00d, 0x000, // Start point BLUE path + 0x00e, 0x000, // Start point RED path + + 0x010, 0x010, // GREEN path gamma curve --- + 0x011, 0x020, + 0x012, 0x030, + 0x013, 0x040, + 0x014, 0x050, + 0x015, 0x060, + 0x016, 0x070, + 0x017, 0x080, + 0x018, 0x090, + 0x019, 0x0a0, + 0x01a, 0x0b0, + 0x01b, 0x0c0, + 0x01c, 0x0d0, + 0x01d, 0x0e0, + 0x01e, 0x0f0, + 0x01f, 0x0ff, // --- GREEN path gamma curve + + 0x020, 0x010, // BLUE path gamma curve --- + 0x021, 0x020, + 0x022, 0x030, + 0x023, 0x040, + 0x024, 0x050, + 0x025, 0x060, + 0x026, 0x070, + 0x027, 0x080, + 0x028, 0x090, + 0x029, 0x0a0, + 0x02a, 0x0b0, + 0x02b, 0x0c0, + 0x02c, 0x0d0, + 0x02d, 0x0e0, + 0x02e, 0x0f0, + 0x02f, 0x0ff, // --- BLUE path gamma curve + + 0x030, 0x010, // RED path gamma curve --- + 0x031, 0x020, + 0x032, 0x030, + 0x033, 0x040, + 0x034, 0x050, + 0x035, 0x060, + 0x036, 0x070, + 0x037, 0x080, + 0x038, 0x090, + 0x039, 0x0a0, + 0x03a, 0x0b0, + 0x03b, 0x0c0, + 0x03c, 0x0d0, + 0x03d, 0x0e0, + 0x03e, 0x0f0, + 0x03f, 0x0ff, // --- RED path gamma curve + + 0x109, 0x085, // Luminance control + + /**** from app start ****/ + 0x584, 0x000, // AGC gain control + 0x585, 0x000, // Program count + 0x586, 0x003, // Status reset + 0x588, 0x0ff, // Number of audio samples (L) + 0x589, 0x00f, // Number of audio samples (M) + 0x58a, 0x000, // Number of audio samples (H) + 0x58b, 0x000, // Audio select + 0x58c, 0x010, // Audio channel assign1 + 0x58d, 0x032, // Audio channel assign2 + 0x58e, 0x054, // Audio channel assign3 + 0x58f, 0x023, // Audio format + 0x590, 0x000, // SIF control + + 0x595, 0x000, // ?? + 0x596, 0x000, // ?? + 0x597, 0x000, // ?? + + 0x464, 0x00, // Digital input crossbar1 + + 0x46c, 0xbbbb10, // Digital output selection1-3 + 0x470, 0x101010, // Digital output selection4-6 + + 0x478, 0x00, // Sound feature control + + 0x474, 0x18, // Softmute control + + 0x454, 0x0425b9, // Sound Easy programming(reset) + 0x454, 0x042539, // Sound Easy programming(reset) + + + /**** common setting( of DVD play, including scaler commands) ****/ + 0x042, 0x003, // Data path configuration for VBI (TASK A) + + 0x082, 0x003, // Data path configuration for VBI (TASK B) + + 0x108, 0x0f8, // Sync control + 0x2a9, 0x0fd, // ??? + 0x102, 0x089, // select video input "mode 9" + 0x111, 0x000, // Mode/delay control + + 0x10e, 0x00a, // Chroma control 1 + + 0x594, 0x002, // SIF, analog I/O select + + 0x454, 0x0425b9, // Sound + 0x454, 0x042539, + + 0x111, 0x000, + 0x10e, 0x00a, + 0x464, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x0ff, 0x0ff, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x0ff, 0x0ff, + 0x108, 0x0f8, + 0x111, 0x000, + 0x10e, 0x00a, + 0x2a9, 0x0fd, + 0x464, 0x001, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x0a6, + 0x108, 0x0f8, + 0x042, 0x003, + 0x082, 0x003, + 0x454, 0x0425b9, + 0x454, 0x042539, + 0x193, 0x000, + 0x193, 0x0a6, + 0x464, 0x000, + + 0, 0 +}; + +/***** tuner *****/ +static u32 reg_init_tuner_input[] = { + 0x108, 0x0f8, // sync control? + 0x111, 0x000, // Mode/delay control + 0x10e, 0x00a, // Chroma control 1 + 0, 0 +}; +/***** composite *****/ +static u32 reg_init_composite_input[] = { + 0x108, 0x0e8, // Sync control + 0x111, 0x000, // Mode/delay control + 0x10e, 0x04a, // Chroma control1 + 0, 0 +}; + +/***** s-video *****/ +static u32 reg_init_svideo_input[] = { + 0x108, 0x0e8, // Sync control + 0x111, 0x000, // Mode/delay control + 0x10e, 0x04a, // Chroma control1 + 0, 0 +}; + +static u32 reg_set_audio_template[4][2] = +{ + { // for MONO + // tadachi 6/29 DMA audio output select? + // Register 0x46c + // 7-4:DMA2,3-0:DMA1 ch. DMA4,DMA3 DMA2,DMA1 + // 0:MAIN left, 1:MAIN right + // 2:AUX1 left, 3:AUX1 right + // 4:AUX2 left, 5:AUX2 right + // 6:DPL left, 7:DPL right + // 8:DPL center, 9:DPL surround + // A:monitor output, B:digital sience + 0xbbbb00, + + // tadachi 6/29 DAC and I2S output select? + // Register 0x470 + // 7-4:DAC right ch. 3-0:DAC left ch. + // I2S1 right,left I2S2 right,left + 0x00, + }, + { // for STEREO + 0xbbbb10, 0x101010, + }, + { // for LANG1 + 0xbbbb00, 0x00, + }, + { // for LANG2/SAP + 0xbbbb11, 0x111111, + } +}; + + +/* ===== get detected audio flags (from saa7134 driver) ===== */ +static void get_inf_dev_status(struct i2c_client *client, + int *dual_flag, + int *stereo_flag) +{ + u32 reg_data3; + + static char *stdres[0x20] = { + [0x00] = "no standard detected", + [0x01] = "B/G (in progress)", + [0x02] = "D/K (in progress)", + [0x03] = "M (in progress)", + + [0x04] = "B/G A2", + [0x05] = "B/G NICAM", + [0x06] = "D/K A2 (1)", + [0x07] = "D/K A2 (2)", + [0x08] = "D/K A2 (3)", + [0x09] = "D/K NICAM", + [0x0a] = "L NICAM", + [0x0b] = "I NICAM", + + [0x0c] = "M Korea", + [0x0d] = "M BTSC ", + [0x0e] = "M EIAJ", + + [0x0f] = "FM radio / IF 10.7 / 50 deemp", + [0x10] = "FM radio / IF 10.7 / 75 deemp", + [0x11] = "FM radio / IF sel / 50 deemp", + [0x12] = "FM radio / IF sel / 75 deemp", + + [0x13 ... 0x1e ] = "unknown", + [0x1f] = "??? [in progress]", + }; + + + *dual_flag = *stereo_flag = 0; + + // (demdec status: 0x528) + + // read current status + reg_data3 = saa717x_read(client, 0x0528); + + v4l_dbg(1, debug, client,"tvaudio thread status: 0x%x [%s%s%s]\n", + reg_data3, stdres[reg_data3 & 0x1f], + (reg_data3 & 0x000020) ? ",stereo" : "", + (reg_data3 & 0x000040) ? ",dual" : ""); + v4l_dbg(1, debug, client,"detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", + (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", + (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", + (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", + + (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", + (reg_data3 & 0x001000) ? " SAP carrier " : "", + (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", + (reg_data3 & 0x004000) ? " SAP noise mute " : "", + (reg_data3 & 0x008000) ? " VDSP " : "", + + (reg_data3 & 0x010000) ? " NICST " : "", + (reg_data3 & 0x020000) ? " NICDU " : "", + (reg_data3 & 0x040000) ? " NICAM muted " : "", + (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", + + (reg_data3 & 0x100000) ? " init done " : ""); + + if (((reg_data3 & 0x000020) != 0)|| + ((reg_data3 & 0x000200)) != 0) { + v4l_dbg(1, debug, client,"ST!!!\n"); + *stereo_flag = 1; + } + + if (((reg_data3 & 0x000040) != 0)|| + ((reg_data3 & 0x000100) != 0)) { + v4l_dbg(1, debug, client,"DUAL!!!\n"); + *dual_flag = 1; + } +} + +/* ===== regs write to set audio mode ===== */ +static int set_audio_mode(struct i2c_client *client, int audio_mode) +{ + v4l_dbg(1, debug, client,"writing registers to set audio mode by set %d\n", + audio_mode); + + saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]); + return 0; +} + +/* ===== write regs to video output level (bright,contrast,hue,sat) ===== */ +static int set_video_output_level_regs(struct i2c_client *client, + struct saa717x_state *decoder) +{ + // brightness ffh(bright)-80h(ITU level)-00h(dark) + saa717x_write(client, 0x10a, decoder->bright); + + // contrast 7fh(max:1.984)-44h(ITU)-40h(1.0)- + // 0h (luminance off) 40:i2c dump + // c0h (-1.0 inverse chrominance) + // 80h (-2.0 inverse chrominance) + saa717x_write(client, 0x10b, decoder->contrast); + + // saturation? 7fh(max)-40h(ITU)-0h(color off) + // c0h (-1.0 inverse chrominance) + // 80h (-2.0 inverse chrominance) + saa717x_write(client, 0x10c, decoder->sat); + + // color hue(phase) control + // 7fh(+178.6)-0h(:0 normal)-80h(-180.0) + saa717x_write(client, 0x10d, decoder->hue); + + return 0; +} + +/* ===== write regs to set audio volume, bass and treble ===== */ +static int set_audio_regs(struct i2c_client *client, + struct saa717x_state *decoder) +{ + u8 mute = 0xac; /* -84 dB */ + u32 val; + unsigned int work_l, work_r; + + // set SIF analog I/O select + saa717x_write(client, 0x0594,decoder->audio_input); + v4l_dbg(1, debug, client, "set audio input %d\n", + decoder->audio_input); + + // normalize ( 65535 to 0 -> 24 to -40 (not -84)) + work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; + work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; + decoder->audio_main_vol_l = (long)work_l*(24-(-40))/65535-40; + decoder->audio_main_vol_r = (long)work_r*(24-(-40))/65535-40; + + // set main volume + // main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) + // def:0dB->6dB(MPG600GR) + // if mute is on, set mute + if (decoder->audio_main_mute) { + val = mute | (mute << 8); + } else { + val = (u8)decoder->audio_main_vol_l | + ((u8)decoder->audio_main_vol_r << 8); + } + + saa717x_write(client, 0x480, val); + + // bass and treble; go to another function + // set bass and treble + val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); + saa717x_write(client, 0x488, val); + return 0; +} + +/********** scaling staff ***********/ +static void set_h_prescale(struct i2c_client *client, + int task, int prescale) +{ + static const struct { + int xpsc; + int xacl; + int xc2_1; + int xdcg; + int vpfy; + } vals[] = { + /* XPSC XACL XC2_1 XDCG VPFY */ + { 1, 0, 0, 0, 0 }, + { 2, 2, 1, 2, 2 }, + { 3, 4, 1, 3, 2 }, + { 4, 8, 1, 4, 2 }, + { 5, 8, 1, 4, 2 }, + { 6, 8, 1, 4, 3 }, + { 7, 8, 1, 4, 3 }, + { 8, 15, 0, 4, 3 }, + { 9, 15, 0, 4, 3 }, + { 10, 16, 1, 5, 3 }, + }; + static const int count = ARRAY_SIZE(vals); + int i,task_shift; + + task_shift = task*0x40; + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + // horizonal prescaling + saa717x_write (client, 0x60+task_shift, vals[i].xpsc); + // accumulation length + saa717x_write (client, 0x61+task_shift, vals[i].xacl); + // level control + saa717x_write (client, 0x62+task_shift, + (vals[i].xc2_1 << 3) | (vals[i].xdcg)); + //FIR prefilter control + saa717x_write (client, 0x63+task_shift, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +/********** scaling staff ***********/ +static void set_v_scale(struct i2c_client *client, int task, int yscale) +{ + int task_shift; + + task_shift = task*0x40; + // Vertical scaling ratio (LOW) + saa717x_write (client, 0x70+task_shift, yscale & 0xff); + // Vertical scaling ratio (HI) + saa717x_write (client, 0x71+task_shift, yscale >> 8); +} + +static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +{ + /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */ + return 0; +} + +static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa717x_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + return -ERANGE; + } + + state->bright = ctrl->value; + v4l_dbg(1, debug, client,"bright:%d\n", state->bright); + saa717x_write(client, 0x10a, state->bright); + break; + + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + return -ERANGE; + } + + state->contrast = ctrl->value; + v4l_dbg(1, debug, client,"contrast:%d\n", state->contrast); + saa717x_write(client, 0x10b, state->contrast); + break; + + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + return -ERANGE; + } + + state->sat = ctrl->value; + v4l_dbg(1, debug, client,"sat:%d\n", state->sat); + saa717x_write(client, 0x10c, state->sat); + break; + + case V4L2_CID_HUE: + if (ctrl->value < -127 || ctrl->value > 127) { + v4l_err(client, "invalid hue setting %d\n", ctrl->value); + return -ERANGE; + } + + state->hue = ctrl->value; + v4l_dbg(1, debug, client,"hue:%d\n", state->hue); + saa717x_write(client, 0x10d, state->hue); + break; + + case V4L2_CID_AUDIO_MUTE: + state->audio_main_mute = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_VOLUME: + state->audio_main_volume = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_BALANCE: + state->audio_main_balance = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_TREBLE: + state->audio_main_treble = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_BASS: + state->audio_main_bass = ctrl->value; + set_audio_regs(client, state); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa717x_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->bright; + break; + + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + + case V4L2_CID_SATURATION: + ctrl->value = state->sat; + break; + + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->audio_main_mute; + break; + + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->audio_main_volume; + break; + + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = state->audio_main_balance; + break; + + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = state->audio_main_treble; + break; + + case V4L2_CID_AUDIO_BASS: + ctrl->value = state->audio_main_bass; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static struct v4l2_queryctrl saa717x_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 58880, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BASS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + }, { + .id = V4L2_CID_AUDIO_TREBLE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + }, +}; + +static int saa717x_set_video_input(struct i2c_client *client,struct saa717x_state *decoder,int inp) +{ + int is_tuner = inp & 0x80; /* tuner input flag */ + + inp &= 0x7f; + + v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp); + /* inputs from 0-9 are available*/ + /* saa717x have mode0-mode9 but mode5 is reserved. */ + if (inp < 0 || inp > 9 || inp == 5) { + return -EINVAL; + } + + if (decoder->input != inp) { + int input_line = inp; + + decoder->input = input_line; + v4l_dbg(1, debug, client, "now setting %s input %d\n", + input_line >= 6 ? "S-Video" : "Composite", + input_line); + + /* select mode */ + saa717x_write(client, 0x102, + (saa717x_read(client,0x102) & 0xf0)| + input_line); + + /* bypass chrominance trap for modes 6..9 */ + saa717x_write(client, 0x109, + (saa717x_read(client,0x109) & 0x7f)| + (input_line < 6 ? 0x0 : 0x80)); + + // change audio_mode + if (is_tuner) { + // tuner + set_audio_mode(client, decoder->tuner_audio_mode); + } else { + // set force toSTEREO mode if Composite or S-Video were choose + set_audio_mode(client, TUNER_AUDIO_STEREO); + } + // change initialize procedure (Composite/S-Video) + if (is_tuner) + saa717x_write_regs(client, reg_init_tuner_input); + else if (input_line >= 6) + saa717x_write_regs(client, reg_init_svideo_input); + else + saa717x_write_regs(client, reg_init_composite_input); + } + + return 0; +} + +static int saa717x_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct saa717x_state *decoder = i2c_get_clientdata(client); + + v4l_dbg(1, debug, client,"IOCTL: %08x\n",cmd); + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return saa717x_set_audio_clock_freq(client, *(u32 *)arg); + + case VIDIOC_G_CTRL: + return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_S_CTRL: + return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) + if (qc->id && qc->id == saa717x_qctrl[i].id) { + memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(client, reg->reg); + break; + } + + case VIDIOC_DBG_S_REGISTER: + { + struct v4l2_register *reg = arg; + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(client, addr, val); + break; + } +#endif + + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = (struct v4l2_format *)arg; + struct v4l2_pix_format *pix; + int prescale, h_scale, v_scale; + + pix = &(fmt->fmt.pix); + v4l_dbg(1, debug, client, "decoder set size\n"); + + // FIXME need better bounds checking here + if ( (pix->width < 1) || (pix->width > 1440)) + return -EINVAL; + if ( (pix->height < 1) || (pix->height > 960)) + return -EINVAL; + + // scaling setting + // NTSC and interlace only ***FIXME!! + prescale = SAA_7114_NTSC_WIDTH / pix->width; + if (prescale == 0) prescale = 1; + h_scale = 1024 * SAA_7114_NTSC_WIDTH / prescale / pix->width; + v_scale = 512 * 2 /* interlace */ * SAA_7114_NTSC_HEIGHT / pix->height; + + // *** Horizonal prescaling etc *** + set_h_prescale(client,0,prescale); + set_h_prescale(client,1,prescale); + + // *** Horizonal scaling increment *** + // TASK A + saa717x_write (client, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write (client, 0x6D, (u8)((h_scale >> 8)&0xFF)); + // TASK B + saa717x_write (client, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write (client, 0xAD, (u8)((h_scale >> 8)&0xFF)); + + // *** Vertical prescaling etc *** + set_v_scale(client,0,v_scale); + set_v_scale(client,1,v_scale); + + // *** set video output size *** + // *** video number of pixels at output *** + // TASK A + saa717x_write (client, 0x5C, (u8)(pix->width & 0xFF)); + saa717x_write (client, 0x5D, (u8)((pix->width >> 8)&0xFF)); + // TASK B + saa717x_write (client, 0x9C, (u8)(pix->width & 0xFF)); + saa717x_write (client, 0x9D, (u8)((pix->width >> 8)&0xFF)); + + // *** video number of lines at output *** + // TASK A + saa717x_write (client, 0x5E, (u8)(pix->height & 0xFF)); + saa717x_write (client, 0x5F, (u8)((pix->height >> 8)&0xFF)); + // TASK B + saa717x_write (client, 0x9E, (u8)(pix->height & 0xFF)); + saa717x_write (client, 0x9F, (u8)((pix->height >> 8)&0xFF)); + break; + } + + case AUDC_SET_RADIO: + decoder->radio = 1; + break; + + case VIDIOC_S_STD: + { + v4l2_std_id std = *(v4l2_std_id *) arg; + + v4l_dbg(1, debug, client, "decoder set norm "); + v4l_dbg(1, debug, client, "(not yet implementd)\n"); + + decoder->radio = 0; + decoder->std = std; + break; + } + + case VIDIOC_INT_G_AUDIO_ROUTING: + { + struct v4l2_routing *route = arg; + + route->input = decoder->audio_input; + route->output = 0; + break; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: + { + struct v4l2_routing *route = arg; + + if (route->input < 3) { // FIXME! --tadachi + decoder->audio_input = route->input; + v4l_dbg(1, debug, client, + "set decoder audio input to %d\n",decoder->audio_input); + set_audio_regs(client,decoder); + break; + } + return -ERANGE; + } + + case VIDIOC_INT_S_VIDEO_ROUTING: /* Rename from VIDIOC_S_INPUT,another arg. 20060706 K.Ohta Pls. Merge it! (-_-# */ + { + struct v4l2_routing *route = arg; + int inp = route->input; + + return saa717x_set_video_input(client,decoder,inp); + } + + case VIDIOC_STREAMON: + { + v4l_dbg(1, debug, client, "decoder enable output\n"); + decoder->enable = 1; + saa717x_write(client,0x193,0xa6); + break; + } + + case VIDIOC_STREAMOFF: + { + v4l_dbg(1, debug, client, "decoder disable output\n"); + decoder->enable = 0; + saa717x_write(client,0x193,0x26); // right? FIXME!--tadachi + break; + } + + // change audio mode + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + int audio_mode; + char *mes[4]={ + "MONO","STEREO","LANG1","LANG2/SAP" + }; + + audio_mode = V4L2_TUNER_MODE_STEREO; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + audio_mode = TUNER_AUDIO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + audio_mode = TUNER_AUDIO_STEREO; + break; + case V4L2_TUNER_MODE_LANG2:// LANG2=SAP in videodev2.h + audio_mode = TUNER_AUDIO_LANG2; + break; + case V4L2_TUNER_MODE_LANG1: + audio_mode = TUNER_AUDIO_LANG1; + break; + } + + v4l_dbg(1, debug, client, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + // The registers are not changed here. + // See DECODER_ENABLE_OUTPUT section. + set_audio_mode(client, decoder->tuner_audio_mode); + break; + } + + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + int dual_f, stereo_f; + + if (decoder->radio) + break; + get_inf_dev_status(client,&dual_f,&stereo_f); + + v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n",stereo_f,dual_f); + + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==MONO\n"); + } + + /* stereo */ + if (stereo_f == 1) { + if ((vt->audmode == V4L2_TUNER_MODE_STEREO) + || (vt->audmode == V4L2_TUNER_MODE_LANG1)) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l_dbg(1, debug, client, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n"); + } + } + + /* dual*/ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==DUAL2\n"); + } + } + break; + } + + case VIDIOC_LOG_STATUS: + /* not yet implemented */ + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + + +/* i2c implementation */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { 0x42 >> 1, 0x42 >> 1, I2C_CLIENT_END }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) */ + +I2C_CLIENT_INSMOD; + +struct i2c_driver i2c_driver; + + +/* ----------------------------------------------------------------------- */ +static int saa717x_detect_client (struct i2c_adapter *adapter, + int address, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + unsigned short flags, +#endif + int kind) +{ + struct i2c_client *client; + struct saa717x_state *decoder; + u8 id = 0; + char *p = ""; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + client->flags = I2C_CLIENT_ALLOW_USE; +#endif + snprintf(client->name, sizeof(client->name) - 1, "saa717x"); + + if (saa717x_write(client, 0x5a4, 0xfe) && + saa717x_write(client, 0x5a5, 0x0f) && + saa717x_write(client, 0x5a6, 0x00) && + saa717x_write(client, 0x5a7, 0x01)) { + id = saa717x_read(client, 0x5a0); + } + if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { + v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id); + kfree(client); + return 0; + } + if (id == 0xc2) p = "saa7173"; + else if (id == 0x32) p = "saa7174A"; + else if (id == 0x6c) p = "saa7174HL"; + else p = "saa7171"; + v4l_info(client, "%s found @ 0x%x (%s)\n", p, address << 1, adapter->name); + + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + i2c_set_clientdata(client, decoder); + + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + decoder->std = V4L2_STD_NTSC; + decoder->input = -1; + decoder->enable = 1; + + // tune these parameters + decoder->bright = 0x80; + decoder->contrast = 0x44; + decoder->sat = 0x40; + decoder->hue = 0x00; + + // FIXME!! + decoder->playback = 0; // initially capture mode used + decoder->audio = 1; /* DECODER_AUDIO_48_KHZ; */ + + decoder->audio_input = 2; //FIXME!! + + decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; + // set volume, bass and treble + decoder->audio_main_vol_l = 6; + decoder->audio_main_vol_r = 6; + decoder->audio_main_bass = 0; + decoder->audio_main_treble = 0; + decoder->audio_main_mute = 0; + decoder->audio_main_balance = 32768; + // normalize (24 to -40 (not -84) -> 65535 to 0) + decoder->audio_main_volume = (decoder->audio_main_vol_r + 41) * 65535/(24-(-40)); + + v4l_dbg(1, debug, client, "writing init values\n"); + + // FIXME!! + saa717x_write_regs(client, reg_init_initialize); + set_video_output_level_regs(client, decoder); + // set bass,treble to 0db 20041101 K.Ohta + decoder->audio_main_bass = 0; + decoder->audio_main_treble = 0; + set_audio_regs(client, decoder); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2*HZ); + + i2c_attach_client(client); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int +saa717x_attach_adapter (struct i2c_adapter *adapter) +{ +#ifdef I2C_CLASS_TV_ANALOG + if (adapter->class & I2C_CLASS_TV_ANALOG) +#else + if (adapter->id == I2C_HW_B_BT848) +#endif + return i2c_probe(adapter, &addr_data, &saa717x_detect_client); + return 0; +} + +static int +saa717x_detach_client (struct i2c_client *client) +{ + struct saa717x *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +struct i2c_driver i2c_driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .owner = THIS_MODULE, +#endif +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .name = "saa717x", + .flags = I2C_DF_NOTIFY, +#else + .driver = { + .name = "saa717x", + }, +#endif + .id = I2C_DRIVERID_SAA717X, + .attach_adapter = saa717x_attach_adapter, + .detach_client = saa717x_detach_client, + .command = saa717x_command, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +EXPORT_NO_SYMBOLS; +#endif + +static int __init saa717x_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit saa717x_exit_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(saa717x_init_module); +module_exit(saa717x_exit_module); -- 1.5.2.5