14751 lines
437 KiB
Diff
14751 lines
437 KiB
Diff
Hans Verkuil (2):
|
|
V4L/DVB (10982): cx231xx: fix compile warning
|
|
V4L/DVB (10989): cx25840: cx23885 detection was broken
|
|
|
|
Mauro Carvalho Chehab (12):
|
|
V4L/DVB (10953): cx25840: Fix CodingStyle errors introduced by the last patch
|
|
V4L/DVB (10955): cx231xx: CodingStyle automatic fixes with Lindent
|
|
V4L/DVB (10956): cx231xx: First series of manual CodingStyle fixes
|
|
V4L/DVB (10957a): cx231xx: Fix compilation breakage
|
|
V4L/DVB (11130): cx231xx: fix an inverted logic at vidioc_streamoff
|
|
V4L/DVB (11131): cx231xx: avoid trying to access unfilled dev struct
|
|
V4L/DVB (11132): cx231xx: usb probe cleanups
|
|
V4L/DVB (11133): cx231xx: don't print pcb config debug messages by default
|
|
V4L/DVB (11134): cx231xx: dmesg cleanup
|
|
V4L/DVB (11135): cx231xx: use usb_make_path() for bus_info
|
|
V4L/DVB (11250): cx231xx: Fix Kconfig help items
|
|
Merge branch 'next' of ../pending into Fedora
|
|
|
|
Sri Deevi (7):
|
|
V4L/DVB (10952): cx25840: prepare it to be used by cx231xx module
|
|
V4L/DVB (10954): Add cx231xx USB driver
|
|
V4L/DVB (10957): cx231xx: Fix CodingStyle
|
|
V4L/DVB (10958): cx231xx: some additional CodingStyle and minor fixes
|
|
V4L/DVB (11038): Fix the issue with audio module & correction of Names
|
|
V4L/DVB (11128): cx231xx: convert the calls to subdev format
|
|
V4L/DVB (11129): cx231xx: Use generic names for each device block
|
|
|
|
Uri Shkolnik (2):
|
|
V4L/DVB (11239): sdio: add cards ids for sms (Siano Mobile Silicon) MDTV receivers
|
|
V4L/DVB (11240): siano: add high level SDIO interface driver for SMS based cards
|
|
|
|
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
|
|
new file mode 100644
|
|
index 0000000..31ba8c5
|
|
--- /dev/null
|
|
+++ b/drivers/media/dvb/siano/smssdio.c
|
|
@@ -0,0 +1,356 @@
|
|
+/*
|
|
+ * smssdio.c - Siano 1xxx SDIO interface driver
|
|
+ *
|
|
+ * Copyright 2008 Pierre Ossman
|
|
+ *
|
|
+ * Based on code by Siano Mobile Silicon, Inc.,
|
|
+ * Copyright (C) 2006-2008, Uri Shkolnik
|
|
+ *
|
|
+ * 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 hardware is a bit odd in that all transfers should be done
|
|
+ * to/from the SMSSDIO_DATA register, yet the "increase address" bit
|
|
+ * always needs to be set.
|
|
+ *
|
|
+ * Also, buffers from the card are always aligned to 128 byte
|
|
+ * boundaries.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * General cleanup notes:
|
|
+ *
|
|
+ * - only typedefs should be name *_t
|
|
+ *
|
|
+ * - use ERR_PTR and friends for smscore_register_device()
|
|
+ *
|
|
+ * - smscore_getbuffer should zero fields
|
|
+ *
|
|
+ * Fix stop command
|
|
+ */
|
|
+
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/firmware.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mmc/card.h>
|
|
+#include <linux/mmc/sdio_func.h>
|
|
+#include <linux/mmc/sdio_ids.h>
|
|
+
|
|
+#include "smscoreapi.h"
|
|
+#include "sms-cards.h"
|
|
+
|
|
+/* Registers */
|
|
+
|
|
+#define SMSSDIO_DATA 0x00
|
|
+#define SMSSDIO_INT 0x04
|
|
+
|
|
+static const struct sdio_device_id smssdio_ids[] = {
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
|
|
+ .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
|
|
+ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
|
|
+ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
|
|
+ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
|
|
+ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
|
+ { /* end: all zeroes */ },
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(sdio, smssdio_ids);
|
|
+
|
|
+struct smssdio_device {
|
|
+ struct sdio_func *func;
|
|
+
|
|
+ struct smscore_device_t *coredev;
|
|
+
|
|
+ struct smscore_buffer_t *split_cb;
|
|
+};
|
|
+
|
|
+/*******************************************************************/
|
|
+/* Siano core callbacks */
|
|
+/*******************************************************************/
|
|
+
|
|
+static int smssdio_sendrequest(void *context, void *buffer, size_t size)
|
|
+{
|
|
+ int ret;
|
|
+ struct smssdio_device *smsdev;
|
|
+
|
|
+ smsdev = context;
|
|
+
|
|
+ sdio_claim_host(smsdev->func);
|
|
+
|
|
+ while (size >= smsdev->func->cur_blksize) {
|
|
+ ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ buffer += smsdev->func->cur_blksize;
|
|
+ size -= smsdev->func->cur_blksize;
|
|
+ }
|
|
+
|
|
+ if (size) {
|
|
+ ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
|
|
+ buffer, size);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ sdio_release_host(smsdev->func);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*******************************************************************/
|
|
+/* SDIO callbacks */
|
|
+/*******************************************************************/
|
|
+
|
|
+static void smssdio_interrupt(struct sdio_func *func)
|
|
+{
|
|
+ int ret, isr;
|
|
+
|
|
+ struct smssdio_device *smsdev;
|
|
+ struct smscore_buffer_t *cb;
|
|
+ struct SmsMsgHdr_ST *hdr;
|
|
+ size_t size;
|
|
+
|
|
+ smsdev = sdio_get_drvdata(func);
|
|
+
|
|
+ /*
|
|
+ * The interrupt register has no defined meaning. It is just
|
|
+ * a way of turning of the level triggered interrupt.
|
|
+ */
|
|
+ isr = sdio_readb(func, SMSSDIO_INT, &ret);
|
|
+ if (ret) {
|
|
+ dev_err(&smsdev->func->dev,
|
|
+ "Unable to read interrupt register!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (smsdev->split_cb == NULL) {
|
|
+ cb = smscore_getbuffer(smsdev->coredev);
|
|
+ if (!cb) {
|
|
+ dev_err(&smsdev->func->dev,
|
|
+ "Unable to allocate data buffer!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
|
|
+ if (ret) {
|
|
+ dev_err(&smsdev->func->dev,
|
|
+ "Error %d reading initial block!\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hdr = cb->p;
|
|
+
|
|
+ if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
|
|
+ smsdev->split_cb = cb;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ size = hdr->msgLength - smsdev->func->cur_blksize;
|
|
+ } else {
|
|
+ cb = smsdev->split_cb;
|
|
+ hdr = cb->p;
|
|
+
|
|
+ size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
|
|
+
|
|
+ smsdev->split_cb = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdr->msgLength > smsdev->func->cur_blksize) {
|
|
+ void *buffer;
|
|
+
|
|
+ size = ALIGN(size, 128);
|
|
+ buffer = cb->p + hdr->msgLength;
|
|
+
|
|
+ BUG_ON(smsdev->func->cur_blksize != 128);
|
|
+
|
|
+ /*
|
|
+ * First attempt to transfer all of it in one go...
|
|
+ */
|
|
+ ret = sdio_read_blocks(smsdev->func, buffer,
|
|
+ SMSSDIO_DATA, size / 128);
|
|
+ if (ret && ret != -EINVAL) {
|
|
+ smscore_putbuffer(smsdev->coredev, cb);
|
|
+ dev_err(&smsdev->func->dev,
|
|
+ "Error %d reading data from card!\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * ..then fall back to one block at a time if that is
|
|
+ * not possible...
|
|
+ *
|
|
+ * (we have to do this manually because of the
|
|
+ * problem with the "increase address" bit)
|
|
+ */
|
|
+ if (ret == -EINVAL) {
|
|
+ while (size) {
|
|
+ ret = sdio_read_blocks(smsdev->func,
|
|
+ buffer, SMSSDIO_DATA, 1);
|
|
+ if (ret) {
|
|
+ smscore_putbuffer(smsdev->coredev, cb);
|
|
+ dev_err(&smsdev->func->dev,
|
|
+ "Error %d reading "
|
|
+ "data from card!\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ buffer += smsdev->func->cur_blksize;
|
|
+ if (size > smsdev->func->cur_blksize)
|
|
+ size -= smsdev->func->cur_blksize;
|
|
+ else
|
|
+ size = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cb->size = hdr->msgLength;
|
|
+ cb->offset = 0;
|
|
+
|
|
+ smscore_onresponse(smsdev->coredev, cb);
|
|
+}
|
|
+
|
|
+static int smssdio_probe(struct sdio_func *func,
|
|
+ const struct sdio_device_id *id)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ int board_id;
|
|
+ struct smssdio_device *smsdev;
|
|
+ struct smsdevice_params_t params;
|
|
+
|
|
+ board_id = id->driver_data;
|
|
+
|
|
+ smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
|
|
+ if (!smsdev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ smsdev->func = func;
|
|
+
|
|
+ memset(¶ms, 0, sizeof(struct smsdevice_params_t));
|
|
+
|
|
+ params.device = &func->dev;
|
|
+ params.buffer_size = 0x5000; /* ?? */
|
|
+ params.num_buffers = 22; /* ?? */
|
|
+ params.context = smsdev;
|
|
+
|
|
+ snprintf(params.devpath, sizeof(params.devpath),
|
|
+ "sdio\\%s", sdio_func_id(func));
|
|
+
|
|
+ params.sendrequest_handler = smssdio_sendrequest;
|
|
+
|
|
+ params.device_type = sms_get_board(board_id)->type;
|
|
+
|
|
+ if (params.device_type != SMS_STELLAR)
|
|
+ params.flags |= SMS_DEVICE_FAMILY2;
|
|
+ else {
|
|
+ /*
|
|
+ * FIXME: Stellar needs special handling...
|
|
+ */
|
|
+ ret = -ENODEV;
|
|
+ goto free;
|
|
+ }
|
|
+
|
|
+ ret = smscore_register_device(¶ms, &smsdev->coredev);
|
|
+ if (ret < 0)
|
|
+ goto free;
|
|
+
|
|
+ smscore_set_board_id(smsdev->coredev, board_id);
|
|
+
|
|
+ sdio_claim_host(func);
|
|
+
|
|
+ ret = sdio_enable_func(func);
|
|
+ if (ret)
|
|
+ goto release;
|
|
+
|
|
+ ret = sdio_set_block_size(func, 128);
|
|
+ if (ret)
|
|
+ goto disable;
|
|
+
|
|
+ ret = sdio_claim_irq(func, smssdio_interrupt);
|
|
+ if (ret)
|
|
+ goto disable;
|
|
+
|
|
+ sdio_set_drvdata(func, smsdev);
|
|
+
|
|
+ sdio_release_host(func);
|
|
+
|
|
+ ret = smscore_start_device(smsdev->coredev);
|
|
+ if (ret < 0)
|
|
+ goto reclaim;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+reclaim:
|
|
+ sdio_claim_host(func);
|
|
+ sdio_release_irq(func);
|
|
+disable:
|
|
+ sdio_disable_func(func);
|
|
+release:
|
|
+ sdio_release_host(func);
|
|
+ smscore_unregister_device(smsdev->coredev);
|
|
+free:
|
|
+ kfree(smsdev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void smssdio_remove(struct sdio_func *func)
|
|
+{
|
|
+ struct smssdio_device *smsdev;
|
|
+
|
|
+ smsdev = sdio_get_drvdata(func);
|
|
+
|
|
+ /* FIXME: racy! */
|
|
+ if (smsdev->split_cb)
|
|
+ smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
|
|
+
|
|
+ smscore_unregister_device(smsdev->coredev);
|
|
+
|
|
+ sdio_claim_host(func);
|
|
+ sdio_release_irq(func);
|
|
+ sdio_disable_func(func);
|
|
+ sdio_release_host(func);
|
|
+
|
|
+ kfree(smsdev);
|
|
+}
|
|
+
|
|
+static struct sdio_driver smssdio_driver = {
|
|
+ .name = "smssdio",
|
|
+ .id_table = smssdio_ids,
|
|
+ .probe = smssdio_probe,
|
|
+ .remove = smssdio_remove,
|
|
+};
|
|
+
|
|
+/*******************************************************************/
|
|
+/* Module functions */
|
|
+/*******************************************************************/
|
|
+
|
|
+int smssdio_register(void)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
|
|
+ printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
|
|
+
|
|
+ ret = sdio_register_driver(&smssdio_driver);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void smssdio_unregister(void)
|
|
+{
|
|
+ sdio_unregister_driver(&smssdio_driver);
|
|
+}
|
|
+
|
|
+MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
|
|
+MODULE_AUTHOR("Pierre Ossman");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
|
|
index 76bad58..58abbe3 100644
|
|
--- a/drivers/media/video/Kconfig
|
|
+++ b/drivers/media/video/Kconfig
|
|
@@ -795,6 +795,8 @@ source "drivers/media/video/hdpvr/Kconfig"
|
|
|
|
source "drivers/media/video/em28xx/Kconfig"
|
|
|
|
+source "drivers/media/video/cx231xx/Kconfig"
|
|
+
|
|
source "drivers/media/video/usbvision/Kconfig"
|
|
|
|
source "drivers/media/video/usbvideo/Kconfig"
|
|
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
|
|
index b904674..08765d8 100644
|
|
--- a/drivers/media/video/Makefile
|
|
+++ b/drivers/media/video/Makefile
|
|
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
|
|
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
|
|
obj-$(CONFIG_VIDEO_CX88) += cx88/
|
|
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
|
|
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
|
|
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
|
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
|
|
obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
|
|
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
|
|
new file mode 100644
|
|
index 0000000..9115654
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/Kconfig
|
|
@@ -0,0 +1,35 @@
|
|
+config VIDEO_CX231XX
|
|
+ tristate "Conexant cx231xx USB video capture support"
|
|
+ depends on VIDEO_DEV && I2C && INPUT
|
|
+ select VIDEO_TUNER
|
|
+ select VIDEO_TVEEPROM
|
|
+ select VIDEO_IR
|
|
+ select VIDEOBUF_VMALLOC
|
|
+ select VIDEO_CX25840
|
|
+ select VIDEO_CX231XX_ALSA
|
|
+
|
|
+ ---help---
|
|
+ This is a video4linux driver for Conexant 231xx USB based TV cards.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called cx231xx
|
|
+
|
|
+config VIDEO_CX231XX_ALSA
|
|
+ tristate "Conexant Cx231xx ALSA audio module"
|
|
+ depends on VIDEO_CX231XX && SND
|
|
+ select SND_PCM
|
|
+
|
|
+ ---help---
|
|
+ This is an ALSA driver for Cx231xx USB based TV cards.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called cx231xx-alsa
|
|
+
|
|
+config VIDEO_CX231XX_DVB
|
|
+ tristate "DVB/ATSC Support for Cx231xx based TV cards"
|
|
+ depends on VIDEO_CX231XX && DVB_CORE
|
|
+ select VIDEOBUF_DVB
|
|
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
|
|
+ ---help---
|
|
+ This adds support for DVB cards based on the
|
|
+ Conexant cx231xx chips.
|
|
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
|
|
new file mode 100644
|
|
index 0000000..755dd0c
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/Makefile
|
|
@@ -0,0 +1,14 @@
|
|
+cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \
|
|
+ cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o
|
|
+
|
|
+cx231xx-alsa-objs := cx231xx-audio.o
|
|
+
|
|
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
|
|
+obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
|
|
+obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
|
|
+
|
|
+EXTRA_CFLAGS += -Idrivers/media/video
|
|
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
|
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
|
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
|
+
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c
|
|
new file mode 100644
|
|
index 0000000..9ab0628
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-audio.c
|
|
@@ -0,0 +1,585 @@
|
|
+/*
|
|
+ * Conexant Cx231xx audio extension
|
|
+ *
|
|
+ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ * Based on em28xx driver
|
|
+ *
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/sound.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/soundcard.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/module.h>
|
|
+#include <sound/core.h>
|
|
+#include <sound/pcm.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/info.h>
|
|
+#include <sound/initval.h>
|
|
+#include <sound/control.h>
|
|
+#include <media/v4l2-common.h>
|
|
+#include "cx231xx.h"
|
|
+
|
|
+static int debug;
|
|
+module_param(debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "activates debug info");
|
|
+
|
|
+#define dprintk(fmt, arg...) do { \
|
|
+ if (debug) \
|
|
+ printk(KERN_INFO "cx231xx-audio %s: " fmt, \
|
|
+ __func__, ##arg); \
|
|
+ } while (0)
|
|
+
|
|
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
|
+
|
|
+static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ dprintk("Stopping isoc\n");
|
|
+
|
|
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
|
|
+ if (dev->adev.urb[i]) {
|
|
+ if (!irqs_disabled())
|
|
+ usb_kill_urb(dev->adev.urb[i]);
|
|
+ else
|
|
+ usb_unlink_urb(dev->adev.urb[i]);
|
|
+
|
|
+ usb_free_urb(dev->adev.urb[i]);
|
|
+ dev->adev.urb[i] = NULL;
|
|
+
|
|
+ kfree(dev->adev.transfer_buffer[i]);
|
|
+ dev->adev.transfer_buffer[i] = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void cx231xx_audio_isocirq(struct urb *urb)
|
|
+{
|
|
+ struct cx231xx *dev = urb->context;
|
|
+ int i;
|
|
+ unsigned int oldptr;
|
|
+ int period_elapsed = 0;
|
|
+ int status;
|
|
+ unsigned char *cp;
|
|
+ unsigned int stride;
|
|
+ struct snd_pcm_substream *substream;
|
|
+ struct snd_pcm_runtime *runtime;
|
|
+
|
|
+ switch (urb->status) {
|
|
+ case 0: /* success */
|
|
+ case -ETIMEDOUT: /* NAK */
|
|
+ break;
|
|
+ case -ECONNRESET: /* kill */
|
|
+ case -ENOENT:
|
|
+ case -ESHUTDOWN:
|
|
+ return;
|
|
+ default: /* error */
|
|
+ dprintk("urb completition error %d.\n", urb->status);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (dev->adev.capture_pcm_substream) {
|
|
+ substream = dev->adev.capture_pcm_substream;
|
|
+ runtime = substream->runtime;
|
|
+ stride = runtime->frame_bits >> 3;
|
|
+
|
|
+ for (i = 0; i < urb->number_of_packets; i++) {
|
|
+ int length = urb->iso_frame_desc[i].actual_length /
|
|
+ stride;
|
|
+ cp = (unsigned char *)urb->transfer_buffer +
|
|
+ urb->iso_frame_desc[i].offset;
|
|
+
|
|
+ if (!length)
|
|
+ continue;
|
|
+
|
|
+ oldptr = dev->adev.hwptr_done_capture;
|
|
+ if (oldptr + length >= runtime->buffer_size) {
|
|
+ unsigned int cnt;
|
|
+
|
|
+ cnt = runtime->buffer_size - oldptr;
|
|
+ memcpy(runtime->dma_area + oldptr * stride, cp,
|
|
+ cnt * stride);
|
|
+ memcpy(runtime->dma_area, cp + cnt * stride,
|
|
+ length * stride - cnt * stride);
|
|
+ } else {
|
|
+ memcpy(runtime->dma_area + oldptr * stride, cp,
|
|
+ length * stride);
|
|
+ }
|
|
+
|
|
+ snd_pcm_stream_lock(substream);
|
|
+
|
|
+ dev->adev.hwptr_done_capture += length;
|
|
+ if (dev->adev.hwptr_done_capture >=
|
|
+ runtime->buffer_size)
|
|
+ dev->adev.hwptr_done_capture -=
|
|
+ runtime->buffer_size;
|
|
+
|
|
+ dev->adev.capture_transfer_done += length;
|
|
+ if (dev->adev.capture_transfer_done >=
|
|
+ runtime->period_size) {
|
|
+ dev->adev.capture_transfer_done -=
|
|
+ runtime->period_size;
|
|
+ period_elapsed = 1;
|
|
+ }
|
|
+ snd_pcm_stream_unlock(substream);
|
|
+ }
|
|
+ if (period_elapsed)
|
|
+ snd_pcm_period_elapsed(substream);
|
|
+ }
|
|
+ urb->status = 0;
|
|
+
|
|
+ status = usb_submit_urb(urb, GFP_ATOMIC);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
|
|
+ status);
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int cx231xx_init_audio_isoc(struct cx231xx *dev)
|
|
+{
|
|
+ int i, errCode;
|
|
+ int sb_size;
|
|
+
|
|
+ cx231xx_info("%s: Starting AUDIO transfers\n", __func__);
|
|
+
|
|
+ sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
|
|
+
|
|
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
|
|
+ struct urb *urb;
|
|
+ int j, k;
|
|
+
|
|
+ dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
|
|
+ if (!dev->adev.transfer_buffer[i])
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
|
|
+ urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
|
|
+ if (!urb) {
|
|
+ cx231xx_errdev("usb_alloc_urb failed!\n");
|
|
+ for (j = 0; j < i; j++) {
|
|
+ usb_free_urb(dev->adev.urb[j]);
|
|
+ kfree(dev->adev.transfer_buffer[j]);
|
|
+ }
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ urb->dev = dev->udev;
|
|
+ urb->context = dev;
|
|
+ urb->pipe = usb_rcvisocpipe(dev->udev,
|
|
+ dev->adev.end_point_addr);
|
|
+ urb->transfer_flags = URB_ISO_ASAP;
|
|
+ urb->transfer_buffer = dev->adev.transfer_buffer[i];
|
|
+ urb->interval = 1;
|
|
+ urb->complete = cx231xx_audio_isocirq;
|
|
+ urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS;
|
|
+ urb->transfer_buffer_length = sb_size;
|
|
+
|
|
+ for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS;
|
|
+ j++, k += dev->adev.max_pkt_size) {
|
|
+ urb->iso_frame_desc[j].offset = k;
|
|
+ urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
|
|
+ }
|
|
+ dev->adev.urb[i] = urb;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
|
|
+ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_isoc_audio_deinit(dev);
|
|
+ return errCode;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return errCode;
|
|
+}
|
|
+
|
|
+static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg)
|
|
+{
|
|
+ dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
|
|
+ "stop" : "start");
|
|
+
|
|
+ switch (cmd) {
|
|
+ case CX231XX_CAPTURE_STREAM_EN:
|
|
+ if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
|
|
+ dev->adev.capture_stream = STREAM_ON;
|
|
+ cx231xx_init_audio_isoc(dev);
|
|
+ } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
|
|
+ dev->adev.capture_stream = STREAM_OFF;
|
|
+ cx231xx_isoc_audio_deinit(dev);
|
|
+ } else {
|
|
+ cx231xx_errdev("An underrun very likely occurred. "
|
|
+ "Ignoring it.\n");
|
|
+ }
|
|
+ return 0;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
|
|
+ size_t size)
|
|
+{
|
|
+ struct snd_pcm_runtime *runtime = subs->runtime;
|
|
+
|
|
+ dprintk("Allocating vbuffer\n");
|
|
+ if (runtime->dma_area) {
|
|
+ if (runtime->dma_bytes > size)
|
|
+ return 0;
|
|
+
|
|
+ vfree(runtime->dma_area);
|
|
+ }
|
|
+ runtime->dma_area = vmalloc(size);
|
|
+ if (!runtime->dma_area)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ runtime->dma_bytes = size;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
|
|
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
+ SNDRV_PCM_INFO_MMAP |
|
|
+ SNDRV_PCM_INFO_INTERLEAVED |
|
|
+ SNDRV_PCM_INFO_MMAP_VALID,
|
|
+
|
|
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
|
|
+
|
|
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
|
|
+
|
|
+ .rate_min = 48000,
|
|
+ .rate_max = 48000,
|
|
+ .channels_min = 2,
|
|
+ .channels_max = 2,
|
|
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
|
|
+ .period_bytes_min = 64, /* 12544/2, */
|
|
+ .period_bytes_max = 12544,
|
|
+ .periods_min = 2,
|
|
+ .periods_max = 98, /* 12544, */
|
|
+};
|
|
+
|
|
+static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
+ int ret = 0;
|
|
+
|
|
+ dprintk("opening device and trying to acquire exclusive lock\n");
|
|
+
|
|
+ if (!dev) {
|
|
+ cx231xx_errdev("BUG: cx231xx can't find device struct."
|
|
+ " Can't proceed with open\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ /* Sets volume, mute, etc */
|
|
+ dev->mute = 0;
|
|
+
|
|
+ /* set alternate setting for audio interface */
|
|
+ /* 1 - 48000 samples per sec */
|
|
+ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
|
|
+ if (ret < 0) {
|
|
+ cx231xx_errdev("failed to set alternate setting !\n");
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* inform hardware to start streaming */
|
|
+ ret = cx231xx_capture_start(dev, 1, Audio);
|
|
+
|
|
+ runtime->hw = snd_cx231xx_hw_capture;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ dev->adev.users++;
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
|
+ dev->adev.capture_pcm_substream = substream;
|
|
+ runtime->private_data = dev;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ int ret;
|
|
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
|
+
|
|
+ dprintk("closing device\n");
|
|
+
|
|
+ /* set alternate setting for audio interface */
|
|
+ /* 1 - 48000 samples per sec */
|
|
+ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
|
|
+ if (ret < 0) {
|
|
+ cx231xx_errdev("failed to set alternate setting !\n");
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* inform hardware to start streaming */
|
|
+ ret = cx231xx_capture_start(dev, 0, Audio);
|
|
+
|
|
+ dev->mute = 1;
|
|
+ mutex_lock(&dev->lock);
|
|
+ dev->adev.users--;
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
|
|
+ dprintk("audio users: %d\n", dev->adev.users);
|
|
+ dprintk("disabling audio stream!\n");
|
|
+ dev->adev.shutdown = 0;
|
|
+ dprintk("released lock\n");
|
|
+ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
|
|
+ struct snd_pcm_hw_params *hw_params)
|
|
+{
|
|
+ unsigned int channels, rate, format;
|
|
+ int ret;
|
|
+
|
|
+ dprintk("Setting capture parameters\n");
|
|
+
|
|
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
|
|
+ params_buffer_bytes(hw_params));
|
|
+ format = params_format(hw_params);
|
|
+ rate = params_rate(hw_params);
|
|
+ channels = params_channels(hw_params);
|
|
+
|
|
+ /* TODO: set up cx231xx audio chip to deliver the correct audio format,
|
|
+ current default is 48000hz multiplexed => 96000hz mono
|
|
+ which shouldn't matter since analogue TV only supports mono */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
|
+
|
|
+ dprintk("Stop capture, if needed\n");
|
|
+
|
|
+ if (dev->adev.capture_stream == STREAM_ON)
|
|
+ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
|
|
+ int cmd)
|
|
+{
|
|
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
|
|
+ int retval;
|
|
+
|
|
+ dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
|
|
+ "start" : "stop");
|
|
+
|
|
+ spin_lock(&dev->adev.slock);
|
|
+ switch (cmd) {
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
+ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN,
|
|
+ CX231XX_START_AUDIO);
|
|
+ retval = 0;
|
|
+ break;
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
+ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
|
|
+ retval = 0;
|
|
+ break;
|
|
+ default:
|
|
+ retval = -EINVAL;
|
|
+ }
|
|
+
|
|
+ spin_unlock(&dev->adev.slock);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
|
|
+ *substream)
|
|
+{
|
|
+ struct cx231xx *dev;
|
|
+ unsigned long flags;
|
|
+ snd_pcm_uframes_t hwptr_done;
|
|
+
|
|
+ dev = snd_pcm_substream_chip(substream);
|
|
+
|
|
+ spin_lock_irqsave(&dev->adev.slock, flags);
|
|
+ hwptr_done = dev->adev.hwptr_done_capture;
|
|
+ spin_unlock_irqrestore(&dev->adev.slock, flags);
|
|
+
|
|
+ return hwptr_done;
|
|
+}
|
|
+
|
|
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
|
|
+ unsigned long offset)
|
|
+{
|
|
+ void *pageptr = subs->runtime->dma_area + offset;
|
|
+
|
|
+ return vmalloc_to_page(pageptr);
|
|
+}
|
|
+
|
|
+static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
|
|
+ .open = snd_cx231xx_capture_open,
|
|
+ .close = snd_cx231xx_pcm_close,
|
|
+ .ioctl = snd_pcm_lib_ioctl,
|
|
+ .hw_params = snd_cx231xx_hw_capture_params,
|
|
+ .hw_free = snd_cx231xx_hw_capture_free,
|
|
+ .prepare = snd_cx231xx_prepare,
|
|
+ .trigger = snd_cx231xx_capture_trigger,
|
|
+ .pointer = snd_cx231xx_capture_pointer,
|
|
+ .page = snd_pcm_get_vmalloc_page,
|
|
+};
|
|
+
|
|
+static int cx231xx_audio_init(struct cx231xx *dev)
|
|
+{
|
|
+ struct cx231xx_audio *adev = &dev->adev;
|
|
+ struct snd_pcm *pcm;
|
|
+ struct snd_card *card;
|
|
+ static int devnr;
|
|
+ int err;
|
|
+ struct usb_interface *uif;
|
|
+ int i, isoc_pipe = 0;
|
|
+
|
|
+ if (dev->has_alsa_audio != 1) {
|
|
+ /* This device does not support the extension (in this case
|
|
+ the device is expecting the snd-usb-audio module or
|
|
+ doesn't have analog audio support at all) */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cx231xx_info("cx231xx-audio.c: probing for cx231xx "
|
|
+ "non standard usbaudio\n");
|
|
+
|
|
+ card = snd_card_new(index[devnr], "Cx231xx Audio", THIS_MODULE, 0);
|
|
+ if (card == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ spin_lock_init(&adev->slock);
|
|
+ err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
|
|
+ if (err < 0) {
|
|
+ snd_card_free(card);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
|
+ &snd_cx231xx_pcm_capture);
|
|
+ pcm->info_flags = 0;
|
|
+ pcm->private_data = dev;
|
|
+ strcpy(pcm->name, "Conexant cx231xx Capture");
|
|
+ strcpy(card->driver, "Conexant cx231xx Audio");
|
|
+ strcpy(card->shortname, "Cx231xx Audio");
|
|
+ strcpy(card->longname, "Conexant cx231xx Audio");
|
|
+
|
|
+ err = snd_card_register(card);
|
|
+ if (err < 0) {
|
|
+ snd_card_free(card);
|
|
+ return err;
|
|
+ }
|
|
+ adev->sndcard = card;
|
|
+ adev->udev = dev->udev;
|
|
+
|
|
+ /* compute alternate max packet sizes for Audio */
|
|
+ uif =
|
|
+ dev->udev->actconfig->interface[dev->current_pcb_config.
|
|
+ hs_config_info[0].interface_info.
|
|
+ audio_index + 1];
|
|
+
|
|
+ adev->end_point_addr =
|
|
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
|
|
+ bEndpointAddress);
|
|
+
|
|
+ adev->num_alt = uif->num_altsetting;
|
|
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
|
|
+ adev->end_point_addr, adev->num_alt);
|
|
+ adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
|
|
+
|
|
+ if (adev->alt_max_pkt_size == NULL) {
|
|
+ cx231xx_errdev("out of memory!\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < adev->num_alt; i++) {
|
|
+ u16 tmp =
|
|
+ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
|
|
+ wMaxPacketSize);
|
|
+ adev->alt_max_pkt_size[i] =
|
|
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
|
|
+ adev->alt_max_pkt_size[i]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int cx231xx_audio_fini(struct cx231xx *dev)
|
|
+{
|
|
+ if (dev == NULL)
|
|
+ return 0;
|
|
+
|
|
+ if (dev->has_alsa_audio != 1) {
|
|
+ /* This device does not support the extension (in this case
|
|
+ the device is expecting the snd-usb-audio module or
|
|
+ doesn't have analog audio support at all) */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (dev->adev.sndcard) {
|
|
+ snd_card_free(dev->adev.sndcard);
|
|
+ kfree(dev->adev.alt_max_pkt_size);
|
|
+ dev->adev.sndcard = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct cx231xx_ops audio_ops = {
|
|
+ .id = CX231XX_AUDIO,
|
|
+ .name = "Cx231xx Audio Extension",
|
|
+ .init = cx231xx_audio_init,
|
|
+ .fini = cx231xx_audio_fini,
|
|
+};
|
|
+
|
|
+static int __init cx231xx_alsa_register(void)
|
|
+{
|
|
+ return cx231xx_register_extension(&audio_ops);
|
|
+}
|
|
+
|
|
+static void __exit cx231xx_alsa_unregister(void)
|
|
+{
|
|
+ cx231xx_unregister_extension(&audio_ops);
|
|
+}
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
|
|
+MODULE_DESCRIPTION("Cx231xx Audio driver");
|
|
+
|
|
+module_init(cx231xx_alsa_register);
|
|
+module_exit(cx231xx_alsa_unregister);
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
|
|
new file mode 100644
|
|
index 0000000..1be3881
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
|
|
@@ -0,0 +1,2581 @@
|
|
+/*
|
|
+ cx231xx_avcore.c - driver for Conexant Cx23100/101/102
|
|
+ USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+
|
|
+ This program contains the specific code to control the avdecoder chip and
|
|
+ other related usb control functions for cx231xx based chipset.
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/bitmap.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/mutex.h>
|
|
+
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+
|
|
+/******************************************************************************
|
|
+ -: BLOCK ARRANGEMENT :-
|
|
+ I2S block ----------------------|
|
|
+ [I2S audio] |
|
|
+ |
|
|
+ Analog Front End --> Direct IF -|-> Cx25840 --> Audio
|
|
+ [video & audio] | [Audio]
|
|
+ |
|
|
+ |-> Cx25840 --> Video
|
|
+ [Video]
|
|
+
|
|
+*******************************************************************************/
|
|
+
|
|
+/******************************************************************************
|
|
+ * A F E - B L O C K C O N T R O L functions *
|
|
+ * [ANALOG FRONT END] *
|
|
+ ******************************************************************************/
|
|
+static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
|
|
+{
|
|
+ return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
+ saddr, 2, data, 1);
|
|
+}
|
|
+
|
|
+static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
|
|
+{
|
|
+ int status;
|
|
+ u32 temp = 0;
|
|
+
|
|
+ status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
+ saddr, 2, &temp, 1);
|
|
+ *data = (u8) temp;
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count)
|
|
+{
|
|
+ int status = 0;
|
|
+ u8 temp = 0;
|
|
+ u8 afe_power_status = 0;
|
|
+ int i = 0;
|
|
+
|
|
+ /* super block initialize */
|
|
+ temp = (u8) (ref_count & 0xff);
|
|
+ status = afe_write_byte(dev, SUP_BLK_TUNE2, temp);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ temp = (u8) ((ref_count & 0x300) >> 8);
|
|
+ temp |= 0x40;
|
|
+ status = afe_write_byte(dev, SUP_BLK_TUNE1, temp);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ /* enable pll */
|
|
+ while (afe_power_status != 0x18) {
|
|
+ status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18);
|
|
+ if (status < 0) {
|
|
+ cx231xx_info(
|
|
+ ": Init Super Block failed in send cmd\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status);
|
|
+ afe_power_status &= 0xff;
|
|
+ if (status < 0) {
|
|
+ cx231xx_info(
|
|
+ ": Init Super Block failed in receive cmd\n");
|
|
+ break;
|
|
+ }
|
|
+ i++;
|
|
+ if (i == 10) {
|
|
+ cx231xx_info(
|
|
+ ": Init Super Block force break in loop !!!!\n");
|
|
+ status = -1;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ /* start tuning filter */
|
|
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ msleep(5);
|
|
+
|
|
+ /* exit tuning */
|
|
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_init_channels(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* power up all 3 channels, clear pd_buffer */
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00);
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00);
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00);
|
|
+
|
|
+ /* Enable quantizer calibration */
|
|
+ status = afe_write_byte(dev, ADC_COM_QUANT, 0x02);
|
|
+
|
|
+ /* channel initialize, force modulator (fb) reset */
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17);
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17);
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17);
|
|
+
|
|
+ /* start quantilizer calibration */
|
|
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10);
|
|
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10);
|
|
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10);
|
|
+ msleep(5);
|
|
+
|
|
+ /* exit modulator (fb) reset */
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07);
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07);
|
|
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07);
|
|
+
|
|
+ /* enable the pre_clamp in each channel for single-ended input */
|
|
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0);
|
|
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0);
|
|
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0);
|
|
+
|
|
+ /* use diode instead of resistor, so set term_en to 0, res_en to 0 */
|
|
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
|
|
+ ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00);
|
|
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
|
|
+ ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00);
|
|
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
|
|
+ ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00);
|
|
+
|
|
+ /* dynamic element matching off */
|
|
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03);
|
|
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03);
|
|
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev)
|
|
+{
|
|
+ u8 c_value = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value);
|
|
+ c_value &= (~(0x50));
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*
|
|
+ The Analog Front End in Cx231xx has 3 channels. These
|
|
+ channels are used to share between different inputs
|
|
+ like tuner, s-video and composite inputs.
|
|
+
|
|
+ channel 1 ----- pin 1 to pin4(in reg is 1-4)
|
|
+ channel 2 ----- pin 5 to pin8(in reg is 5-8)
|
|
+ channel 3 ----- pin 9 to pin 12(in reg is 9-11)
|
|
+*/
|
|
+int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux)
|
|
+{
|
|
+ u8 ch1_setting = (u8) input_mux;
|
|
+ u8 ch2_setting = (u8) (input_mux >> 8);
|
|
+ u8 ch3_setting = (u8) (input_mux >> 16);
|
|
+ int status = 0;
|
|
+ u8 value = 0;
|
|
+
|
|
+ if (ch1_setting != 0) {
|
|
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &value);
|
|
+ value &= (!INPUT_SEL_MASK);
|
|
+ value |= (ch1_setting - 1) << 4;
|
|
+ value &= 0xff;
|
|
+ status = afe_write_byte(dev, ADC_INPUT_CH1, value);
|
|
+ }
|
|
+
|
|
+ if (ch2_setting != 0) {
|
|
+ status = afe_read_byte(dev, ADC_INPUT_CH2, &value);
|
|
+ value &= (!INPUT_SEL_MASK);
|
|
+ value |= (ch2_setting - 1) << 4;
|
|
+ value &= 0xff;
|
|
+ status = afe_write_byte(dev, ADC_INPUT_CH2, value);
|
|
+ }
|
|
+
|
|
+ /* For ch3_setting, the value to put in the register is
|
|
+ 7 less than the input number */
|
|
+ if (ch3_setting != 0) {
|
|
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
|
|
+ value &= (!INPUT_SEL_MASK);
|
|
+ value |= (ch3_setting - 1) << 4;
|
|
+ value &= 0xff;
|
|
+ status = afe_write_byte(dev, ADC_INPUT_CH3, value);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /*
|
|
+ * FIXME: We need to implement the AFE code for LOW IF and for HI IF.
|
|
+ * Currently, only baseband works.
|
|
+ */
|
|
+
|
|
+ switch (mode) {
|
|
+ case AFE_MODE_LOW_IF:
|
|
+ /* SetupAFEforLowIF(); */
|
|
+ break;
|
|
+ case AFE_MODE_BASEBAND:
|
|
+ status = cx231xx_afe_setup_AFE_for_baseband(dev);
|
|
+ break;
|
|
+ case AFE_MODE_EU_HI_IF:
|
|
+ /* SetupAFEforEuHiIF(); */
|
|
+ break;
|
|
+ case AFE_MODE_US_HI_IF:
|
|
+ /* SetupAFEforUsHiIF(); */
|
|
+ break;
|
|
+ case AFE_MODE_JAPAN_HI_IF:
|
|
+ /* SetupAFEforJapanHiIF(); */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ((mode != dev->afe_mode) &&
|
|
+ (dev->video_input == CX231XX_VMUX_TELEVISION))
|
|
+ status = cx231xx_afe_adjust_ref_count(dev,
|
|
+ CX231XX_VMUX_TELEVISION);
|
|
+
|
|
+ dev->afe_mode = mode;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_update_power_control(struct cx231xx *dev,
|
|
+ enum AV_MODE avmode)
|
|
+{
|
|
+ u8 afe_power_status = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ switch (dev->model) {
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
|
|
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL)) {
|
|
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL);
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ if (status < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x00);
|
|
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x70);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x70);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x70);
|
|
+
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
|
|
+ FLD_PWRDN_PD_BIAS |
|
|
+ FLD_PWRDN_PD_TUNECK;
|
|
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ afe_power_status);
|
|
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
|
|
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL)) {
|
|
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL);
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ if (status < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x00);
|
|
+ } else {
|
|
+ cx231xx_info("Invalid AV mode input\n");
|
|
+ status = -1;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
|
|
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL)) {
|
|
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL);
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ if (status < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x40);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x40);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x00);
|
|
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
|
|
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x70);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x70);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x70);
|
|
+
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
|
|
+ FLD_PWRDN_PD_BIAS |
|
|
+ FLD_PWRDN_PD_TUNECK;
|
|
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ afe_power_status);
|
|
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
|
|
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL)) {
|
|
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
|
|
+ FLD_PWRDN_TUNING_BIAS |
|
|
+ FLD_PWRDN_ENABLE_PLL);
|
|
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
|
|
+ &afe_power_status);
|
|
+ if (status < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
|
|
+ 0x00);
|
|
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
|
|
+ 0x40);
|
|
+ } else {
|
|
+ cx231xx_info("Invalid AV mode input\n");
|
|
+ status = -1;
|
|
+ }
|
|
+ } /* switch */
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input)
|
|
+{
|
|
+ u8 input_mode = 0;
|
|
+ u8 ntf_mode = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ dev->video_input = video_input;
|
|
+
|
|
+ if (video_input == CX231XX_VMUX_TELEVISION) {
|
|
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode);
|
|
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3,
|
|
+ &ntf_mode);
|
|
+ } else {
|
|
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode);
|
|
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1,
|
|
+ &ntf_mode);
|
|
+ }
|
|
+
|
|
+ input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1);
|
|
+
|
|
+ switch (input_mode) {
|
|
+ case SINGLE_ENDED:
|
|
+ dev->afe_ref_count = 0x23C;
|
|
+ break;
|
|
+ case LOW_IF:
|
|
+ dev->afe_ref_count = 0x24C;
|
|
+ break;
|
|
+ case EU_IF:
|
|
+ dev->afe_ref_count = 0x258;
|
|
+ break;
|
|
+ case US_IF:
|
|
+ dev->afe_ref_count = 0x260;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * V I D E O / A U D I O D E C O D E R C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
|
|
+{
|
|
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ saddr, 2, data, 1);
|
|
+}
|
|
+
|
|
+static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
|
|
+{
|
|
+ int status;
|
|
+ u32 temp = 0;
|
|
+
|
|
+ status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ saddr, 2, &temp, 1);
|
|
+ *data = (u8) temp;
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data)
|
|
+{
|
|
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ saddr, 2, data, 4);
|
|
+}
|
|
+
|
|
+static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data)
|
|
+{
|
|
+ return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ saddr, 2, data, 4);
|
|
+}
|
|
+
|
|
+int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ switch (INPUT(input)->type) {
|
|
+ case CX231XX_VMUX_COMPOSITE1:
|
|
+ case CX231XX_VMUX_SVIDEO:
|
|
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
|
|
+ (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) {
|
|
+ /* External AV */
|
|
+ status = cx231xx_set_power_mode(dev,
|
|
+ POLARIS_AVMODE_ENXTERNAL_AV);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: set_power_mode : Failed to"
|
|
+ " set Power - errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+ status = cx231xx_set_decoder_video_input(dev,
|
|
+ INPUT(input)->type,
|
|
+ INPUT(input)->vmux);
|
|
+ break;
|
|
+ case CX231XX_VMUX_TELEVISION:
|
|
+ case CX231XX_VMUX_CABLE:
|
|
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
|
|
+ (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) {
|
|
+ /* Tuner */
|
|
+ status = cx231xx_set_power_mode(dev,
|
|
+ POLARIS_AVMODE_ANALOGT_TV);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: set_power_mode:Failed"
|
|
+ " to set Power - errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+ status = cx231xx_set_decoder_video_input(dev,
|
|
+ CX231XX_VMUX_COMPOSITE1,
|
|
+ INPUT(input)->vmux);
|
|
+ break;
|
|
+ default:
|
|
+ cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
|
|
+ __func__, INPUT(input)->type);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* save the selection */
|
|
+ dev->video_input = input;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_set_decoder_video_input(struct cx231xx *dev,
|
|
+ u8 pin_type, u8 input)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 value = 0;
|
|
+
|
|
+ if (pin_type != dev->video_input) {
|
|
+ status = cx231xx_afe_adjust_ref_count(dev, pin_type);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: adjust_ref_count :Failed to set"
|
|
+ "AFE input mux - errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* call afe block to set video inputs */
|
|
+ status = cx231xx_afe_set_input_mux(dev, input);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: set_input_mux :Failed to set"
|
|
+ " AFE input mux - errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ switch (pin_type) {
|
|
+ case CX231XX_VMUX_COMPOSITE1:
|
|
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
|
|
+ value |= (0 << 13) | (1 << 4);
|
|
+ value &= ~(1 << 5);
|
|
+
|
|
+ /* set [24:23] [22:15] to 0 */
|
|
+ value &= (~(0x1ff8000));
|
|
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
|
|
+ value |= 0x1000000;
|
|
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
|
|
+
|
|
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
|
|
+ value |= (1 << 7);
|
|
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
|
|
+
|
|
+ /* Set vip 1.1 output mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ OUT_CTRL1,
|
|
+ FLD_OUT_MODE,
|
|
+ OUT_MODE_VIP11);
|
|
+
|
|
+ /* Tell DIF object to go to baseband mode */
|
|
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
|
|
+ " mode- errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* Read the DFE_CTRL1 register */
|
|
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
|
|
+
|
|
+ /* enable the VBI_GATE_EN */
|
|
+ value |= FLD_VBI_GATE_EN;
|
|
+
|
|
+ /* Enable the auto-VGA enable */
|
|
+ value |= FLD_VGA_AUTO_EN;
|
|
+
|
|
+ /* Write it back */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
|
|
+
|
|
+ /* Disable auto config of registers */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_ACFG_DIS,
|
|
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
|
|
+
|
|
+ /* Set CVBS input mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_INPUT_MODE,
|
|
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0));
|
|
+ break;
|
|
+ case CX231XX_VMUX_SVIDEO:
|
|
+ /* Disable the use of DIF */
|
|
+
|
|
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
|
|
+
|
|
+ /* set [24:23] [22:15] to 0 */
|
|
+ value &= (~(0x1ff8000));
|
|
+ /* set FUNC_MODE[24:23] = 2
|
|
+ IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */
|
|
+ value |= 0x1000010;
|
|
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
|
|
+
|
|
+ /* Tell DIF object to go to baseband mode */
|
|
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
|
|
+ " mode- errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* Read the DFE_CTRL1 register */
|
|
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
|
|
+
|
|
+ /* enable the VBI_GATE_EN */
|
|
+ value |= FLD_VBI_GATE_EN;
|
|
+
|
|
+ /* Enable the auto-VGA enable */
|
|
+ value |= FLD_VGA_AUTO_EN;
|
|
+
|
|
+ /* Write it back */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
|
|
+
|
|
+ /* Disable auto config of registers */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_ACFG_DIS,
|
|
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
|
|
+
|
|
+ /* Set YC input mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL,
|
|
+ FLD_INPUT_MODE,
|
|
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1));
|
|
+
|
|
+ /* Chroma to ADC2 */
|
|
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
|
|
+ value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */
|
|
+
|
|
+ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8)
|
|
+ This sets them to use video
|
|
+ rather than audio. Only one of the two will be in use. */
|
|
+ value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3);
|
|
+
|
|
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
|
|
+
|
|
+ status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND);
|
|
+ break;
|
|
+ case CX231XX_VMUX_TELEVISION:
|
|
+ case CX231XX_VMUX_CABLE:
|
|
+ default:
|
|
+ switch (dev->model) {
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+ /* Disable the use of DIF */
|
|
+
|
|
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
|
|
+ value |= (0 << 13) | (1 << 4);
|
|
+ value &= ~(1 << 5);
|
|
+
|
|
+ /* set [24:23] [22:15] to 0 */
|
|
+ value &= (~(0x1FF8000));
|
|
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
|
|
+ value |= 0x1000000;
|
|
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
|
|
+
|
|
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
|
|
+ value |= (1 << 7);
|
|
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
|
|
+
|
|
+ /* Set vip 1.1 output mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ OUT_CTRL1, FLD_OUT_MODE,
|
|
+ OUT_MODE_VIP11);
|
|
+
|
|
+ /* Tell DIF object to go to baseband mode */
|
|
+ status = cx231xx_dif_set_standard(dev,
|
|
+ DIF_USE_BASEBAND);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
|
|
+ " mode- errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* Read the DFE_CTRL1 register */
|
|
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
|
|
+
|
|
+ /* enable the VBI_GATE_EN */
|
|
+ value |= FLD_VBI_GATE_EN;
|
|
+
|
|
+ /* Enable the auto-VGA enable */
|
|
+ value |= FLD_VGA_AUTO_EN;
|
|
+
|
|
+ /* Write it back */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
|
|
+
|
|
+ /* Disable auto config of registers */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_ACFG_DIS,
|
|
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
|
|
+
|
|
+ /* Set CVBS input mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_INPUT_MODE,
|
|
+ cx231xx_set_field(FLD_INPUT_MODE,
|
|
+ INPUT_MODE_CVBS_0));
|
|
+ break;
|
|
+ default:
|
|
+ /* Enable the DIF for the tuner */
|
|
+
|
|
+ /* Reinitialize the DIF */
|
|
+ status = cx231xx_dif_set_standard(dev, dev->norm);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
|
|
+ " mode- errCode [%d]!\n",
|
|
+ __func__, status);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /* Make sure bypass is cleared */
|
|
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value);
|
|
+
|
|
+ /* Clear the bypass bit */
|
|
+ value &= ~FLD_DIF_DIF_BYPASS;
|
|
+
|
|
+ /* Enable the use of the DIF block */
|
|
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL, value);
|
|
+
|
|
+ /* Read the DFE_CTRL1 register */
|
|
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
|
|
+
|
|
+ /* Disable the VBI_GATE_EN */
|
|
+ value &= ~FLD_VBI_GATE_EN;
|
|
+
|
|
+ /* Enable the auto-VGA enable, AGC, and
|
|
+ set the skip count to 2 */
|
|
+ value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000;
|
|
+
|
|
+ /* Write it back */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
|
|
+
|
|
+ /* Wait until AGC locks up */
|
|
+ msleep(1);
|
|
+
|
|
+ /* Disable the auto-VGA enable AGC */
|
|
+ value &= ~(FLD_VGA_AUTO_EN);
|
|
+
|
|
+ /* Write it back */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
|
|
+
|
|
+ /* Enable Polaris B0 AGC output */
|
|
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
|
|
+ value |= (FLD_OEF_AGC_RF) |
|
|
+ (FLD_OEF_AGC_IFVGA) |
|
|
+ (FLD_OEF_AGC_IF);
|
|
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
|
|
+
|
|
+ /* Set vip 1.1 output mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ OUT_CTRL1, FLD_OUT_MODE,
|
|
+ OUT_MODE_VIP11);
|
|
+
|
|
+ /* Disable auto config of registers */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_ACFG_DIS,
|
|
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
|
|
+
|
|
+ /* Set CVBS input mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ MODE_CTRL, FLD_INPUT_MODE,
|
|
+ cx231xx_set_field(FLD_INPUT_MODE,
|
|
+ INPUT_MODE_CVBS_0));
|
|
+
|
|
+ /* Set some bits in AFE_CTRL so that channel 2 or 3
|
|
+ * is ready to receive audio */
|
|
+ /* Clear clamp for channels 2 and 3 (bit 16-17) */
|
|
+ /* Clear droop comp (bit 19-20) */
|
|
+ /* Set VGA_SEL (for audio control) (bit 7-8) */
|
|
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
|
|
+
|
|
+ value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2;
|
|
+
|
|
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
|
|
+ break;
|
|
+
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Set raw VBI mode */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ OUT_CTRL1, FLD_VBIHACTRAW_EN,
|
|
+ cx231xx_set_field(FLD_VBIHACTRAW_EN, 1));
|
|
+
|
|
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
|
|
+ if (value & 0x02) {
|
|
+ value |= (1 << 19);
|
|
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Handle any video-mode specific overrides that are different
|
|
+ * on a per video standards basis after touching the MODE_CTRL
|
|
+ * register which resets many values for autodetect
|
|
+ */
|
|
+int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ cx231xx_info("do_mode_ctrl_overrides : 0x%x\n",
|
|
+ (unsigned int)dev->norm);
|
|
+
|
|
+ /* Change the DFE_CTRL3 bp_percent to fix flagging */
|
|
+ status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280);
|
|
+
|
|
+ if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) {
|
|
+ cx231xx_info("do_mode_ctrl_overrides NTSC\n");
|
|
+
|
|
+ /* Move the close caption lines out of active video,
|
|
+ adjust the active video start point */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ VERT_TIM_CTRL,
|
|
+ FLD_VBLANK_CNT, 0x18);
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ VERT_TIM_CTRL,
|
|
+ FLD_VACTIVE_CNT,
|
|
+ 0x1E6000);
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ VERT_TIM_CTRL,
|
|
+ FLD_V656BLANK_CNT,
|
|
+ 0x1E000000);
|
|
+
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ HORIZ_TIM_CTRL,
|
|
+ FLD_HBLANK_CNT,
|
|
+ cx231xx_set_field
|
|
+ (FLD_HBLANK_CNT, 0x79));
|
|
+ } else if (dev->norm & V4L2_STD_SECAM) {
|
|
+ cx231xx_info("do_mode_ctrl_overrides SECAM\n");
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ VERT_TIM_CTRL,
|
|
+ FLD_VBLANK_CNT, 0x24);
|
|
+ /* Adjust the active video horizontal start point */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ HORIZ_TIM_CTRL,
|
|
+ FLD_HBLANK_CNT,
|
|
+ cx231xx_set_field
|
|
+ (FLD_HBLANK_CNT, 0x85));
|
|
+ } else {
|
|
+ cx231xx_info("do_mode_ctrl_overrides PAL\n");
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ VERT_TIM_CTRL,
|
|
+ FLD_VBLANK_CNT, 0x24);
|
|
+ /* Adjust the active video horizontal start point */
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ HORIZ_TIM_CTRL,
|
|
+ FLD_HBLANK_CNT,
|
|
+ cx231xx_set_field
|
|
+ (FLD_HBLANK_CNT, 0x85));
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_set_audio_input(struct cx231xx *dev, u8 input)
|
|
+{
|
|
+ int status = 0;
|
|
+ enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE;
|
|
+
|
|
+ switch (INPUT(input)->amux) {
|
|
+ case CX231XX_AMUX_VIDEO:
|
|
+ ainput = AUDIO_INPUT_TUNER_TV;
|
|
+ break;
|
|
+ case CX231XX_AMUX_LINE_IN:
|
|
+ status = cx231xx_i2s_blk_set_audio_input(dev, input);
|
|
+ ainput = AUDIO_INPUT_LINE;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ status = cx231xx_set_audio_decoder_input(dev, ainput);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
|
|
+ enum AUDIO_INPUT audio_input)
|
|
+{
|
|
+ u32 dwval;
|
|
+ int status;
|
|
+ u8 gen_ctrl;
|
|
+ u32 value = 0;
|
|
+
|
|
+ /* Put it in soft reset */
|
|
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
|
|
+ gen_ctrl |= 1;
|
|
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
|
|
+
|
|
+ switch (audio_input) {
|
|
+ case AUDIO_INPUT_LINE:
|
|
+ /* setup AUD_IO control from Merlin paralle output */
|
|
+ value = cx231xx_set_field(FLD_AUD_CHAN1_SRC,
|
|
+ AUD_CHAN_SRC_PARALLEL);
|
|
+ status = vid_blk_write_word(dev, AUD_IO_CTRL, value);
|
|
+
|
|
+ /* setup input to Merlin, SRC2 connect to AC97
|
|
+ bypass upsample-by-2, slave mode, sony mode, left justify
|
|
+ adr 091c, dat 01000000 */
|
|
+ status = vid_blk_read_word(dev, AC97_CTL, &dwval);
|
|
+
|
|
+ status = vid_blk_write_word(dev, AC97_CTL,
|
|
+ (dwval | FLD_AC97_UP2X_BYPASS));
|
|
+
|
|
+ /* select the parallel1 and SRC3 */
|
|
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
|
|
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) |
|
|
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) |
|
|
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0));
|
|
+
|
|
+ /* unmute all, AC97 in, independence mode
|
|
+ adr 08d0, data 0x00063073 */
|
|
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073);
|
|
+
|
|
+ /* set AVC maximum threshold, adr 08d4, dat ffff0024 */
|
|
+ status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval);
|
|
+ status = vid_blk_write_word(dev, PATH1_VOL_CTL,
|
|
+ (dwval | FLD_PATH1_AVC_THRESHOLD));
|
|
+
|
|
+ /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */
|
|
+ status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval);
|
|
+ status = vid_blk_write_word(dev, PATH1_SC_CTL,
|
|
+ (dwval | FLD_PATH1_SC_THRESHOLD));
|
|
+ break;
|
|
+
|
|
+ case AUDIO_INPUT_TUNER_TV:
|
|
+ default:
|
|
+
|
|
+ /* Setup SRC sources and clocks */
|
|
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
|
|
+ cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) |
|
|
+ cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) |
|
|
+ cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) |
|
|
+ cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) |
|
|
+ cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) |
|
|
+ cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) |
|
|
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) |
|
|
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) |
|
|
+ cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) |
|
|
+ cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) |
|
|
+ cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) |
|
|
+ cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) |
|
|
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01));
|
|
+
|
|
+ /* Setup the AUD_IO control */
|
|
+ status = vid_blk_write_word(dev, AUD_IO_CTRL,
|
|
+ cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) |
|
|
+ cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) |
|
|
+ cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) |
|
|
+ cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) |
|
|
+ cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03));
|
|
+
|
|
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870);
|
|
+
|
|
+ /* setAudioStandard(_audio_standard); */
|
|
+
|
|
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870);
|
|
+ switch (dev->model) {
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+ status = cx231xx_read_modify_write_i2c_dword(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ CHIP_CTRL,
|
|
+ FLD_SIF_EN,
|
|
+ cx231xx_set_field(FLD_SIF_EN, 1));
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AUDIO_INPUT_TUNER_FM:
|
|
+ /* use SIF for FM radio
|
|
+ setupFM();
|
|
+ setAudioStandard(_audio_standard);
|
|
+ */
|
|
+ break;
|
|
+
|
|
+ case AUDIO_INPUT_MUTE:
|
|
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Take it out of soft reset */
|
|
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
|
|
+ gen_ctrl &= ~1;
|
|
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* Set resolution of the video */
|
|
+int cx231xx_resolution_set(struct cx231xx *dev)
|
|
+{
|
|
+ int width, height;
|
|
+ u32 hscale, vscale;
|
|
+ int status = 0;
|
|
+
|
|
+ width = dev->width;
|
|
+ height = dev->height;
|
|
+
|
|
+ get_scale(dev, width, height, &hscale, &vscale);
|
|
+
|
|
+ /* set horzontal scale */
|
|
+ status = vid_blk_write_word(dev, HSCALE_CTRL, hscale);
|
|
+
|
|
+ /* set vertical scale */
|
|
+ status = vid_blk_write_word(dev, VSCALE_CTRL, vscale);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * C H I P Specific C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+int cx231xx_init_ctrl_pin_status(struct cx231xx *dev)
|
|
+{
|
|
+ u32 value;
|
|
+ int status = 0;
|
|
+
|
|
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
|
|
+ value |= (~dev->board.ctl_pin_status_mask);
|
|
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
|
|
+ u8 analog_or_digital)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* first set the direction to output */
|
|
+ status = cx231xx_set_gpio_direction(dev,
|
|
+ dev->board.
|
|
+ agc_analog_digital_select_gpio, 1);
|
|
+
|
|
+ /* 0 - demod ; 1 - Analog mode */
|
|
+ status = cx231xx_set_gpio_value(dev,
|
|
+ dev->board.agc_analog_digital_select_gpio,
|
|
+ analog_or_digital);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex)
|
|
+{
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+ int status = 0;
|
|
+
|
|
+ cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex);
|
|
+
|
|
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ if (I2CIndex == I2C_1) {
|
|
+ if (value[0] & I2C_DEMOD_EN) {
|
|
+ value[0] &= ~I2C_DEMOD_EN;
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ }
|
|
+ } else {
|
|
+ if (!(value[0] & I2C_DEMOD_EN)) {
|
|
+ value[0] |= I2C_DEMOD_EN;
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * D I F - B L O C K C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
|
|
+ u32 function_mode, u32 standard)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ if (mode == V4L2_TUNER_RADIO) {
|
|
+ /* C2HH */
|
|
+ /* lo if big signal */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
|
|
+ /* FUNC_MODE = DIF */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode);
|
|
+ /* IF_MODE */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF);
|
|
+ /* no inv */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
|
|
+ } else if (standard != DIF_USE_BASEBAND) {
|
|
+ if (standard & V4L2_STD_MN) {
|
|
+ /* lo if big signal */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
|
|
+ /* FUNC_MODE = DIF */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
|
|
+ function_mode);
|
|
+ /* IF_MODE */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb);
|
|
+ /* no inv */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
|
|
+ /* 0x124, AUD_CHAN1_SRC = 0x3 */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AUD_IO_CTRL, 0, 31, 0x00000003);
|
|
+ } else if ((standard == V4L2_STD_PAL_I) |
|
|
+ (standard & V4L2_STD_SECAM)) {
|
|
+ /* C2HH setup */
|
|
+ /* lo if big signal */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
|
|
+ /* FUNC_MODE = DIF */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
|
|
+ function_mode);
|
|
+ /* IF_MODE */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF);
|
|
+ /* no inv */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
|
|
+ } else {
|
|
+ /* default PAL BG */
|
|
+ /* C2HH setup */
|
|
+ /* lo if big signal */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
|
|
+ /* FUNC_MODE = DIF */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
|
|
+ function_mode);
|
|
+ /* IF_MODE */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE);
|
|
+ /* no inv */
|
|
+ status = cx231xx_reg_mask_write(dev,
|
|
+ VID_BLK_I2C_ADDRESS, 32,
|
|
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 dif_misc_ctrl_value = 0;
|
|
+ u32 func_mode = 0;
|
|
+
|
|
+ cx231xx_info("%s: setStandard to %x\n", __func__, standard);
|
|
+
|
|
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value);
|
|
+ if (standard != DIF_USE_BASEBAND)
|
|
+ dev->norm = standard;
|
|
+
|
|
+ switch (dev->model) {
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+ func_mode = 0x03;
|
|
+ break;
|
|
+ default:
|
|
+ func_mode = 0x01;
|
|
+ }
|
|
+
|
|
+ status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
|
|
+ func_mode, standard);
|
|
+
|
|
+ if (standard == DIF_USE_BASEBAND) { /* base band */
|
|
+ /* There is a different SRC_PHASE_INC value
|
|
+ for baseband vs. DIF */
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83);
|
|
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
|
|
+ &dif_misc_ctrl_value);
|
|
+ dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS;
|
|
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL,
|
|
+ dif_misc_ctrl_value);
|
|
+ } else if (standard & V4L2_STD_PAL_D) {
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
|
|
+ 0x26001700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_RF_CURRENT, 0, 31,
|
|
+ 0x00002660);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VIDEO_AGC_CTRL, 0, 31,
|
|
+ 0x72500800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VID_AUD_OVERRIDE, 0, 31,
|
|
+ 0x27000100);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_COMP_FLT_CTRL, 0, 31,
|
|
+ 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_PHASE_INC, 0, 31,
|
|
+ 0x1befbf06);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_GAIN_CONTROL, 0, 31,
|
|
+ 0x000035e8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a023F11;
|
|
+ } else if (standard & V4L2_STD_PAL_I) {
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
|
|
+ 0x26001700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_RF_CURRENT, 0, 31,
|
|
+ 0x00002660);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VIDEO_AGC_CTRL, 0, 31,
|
|
+ 0x72500800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VID_AUD_OVERRIDE, 0, 31,
|
|
+ 0x27000100);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_COMP_FLT_CTRL, 0, 31,
|
|
+ 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_PHASE_INC, 0, 31,
|
|
+ 0x1befbf06);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_GAIN_CONTROL, 0, 31,
|
|
+ 0x000035e8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a033F11;
|
|
+ } else if (standard & V4L2_STD_PAL_M) {
|
|
+ /* improved Low Frequency Phase Noise */
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
|
|
+ 0x26001700);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
|
|
+ 0x00002660);
|
|
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
|
|
+ 0x72500800);
|
|
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
|
|
+ 0x27000100);
|
|
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d);
|
|
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
|
|
+ 0x009f50c1);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
|
|
+ 0x1befbf06);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
|
|
+ 0x000035e8);
|
|
+ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
|
|
+ 0x00000000);
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3A0A3F10;
|
|
+ } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
|
|
+ /* improved Low Frequency Phase Noise */
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
|
|
+ 0x26001700);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
|
|
+ 0x00002660);
|
|
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
|
|
+ 0x72500800);
|
|
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
|
|
+ 0x27000100);
|
|
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL,
|
|
+ 0x012c405d);
|
|
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
|
|
+ 0x009f50c1);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
|
|
+ 0x1befbf06);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
|
|
+ 0x000035e8);
|
|
+ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
|
|
+ 0x00000000);
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value = 0x3A093F10;
|
|
+ } else if (standard &
|
|
+ (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G |
|
|
+ V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) {
|
|
+
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
|
|
+ 0x26001700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_RF_CURRENT, 0, 31,
|
|
+ 0x00002660);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VID_AUD_OVERRIDE, 0, 31,
|
|
+ 0x27000100);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_COMP_FLT_CTRL, 0, 31,
|
|
+ 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_PHASE_INC, 0, 31,
|
|
+ 0x1befbf06);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_GAIN_CONTROL, 0, 31,
|
|
+ 0x000035e8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VIDEO_AGC_CTRL, 0, 31,
|
|
+ 0xf4000000);
|
|
+
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a023F11;
|
|
+ } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) {
|
|
+ /* Is it SECAM_L1? */
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
|
|
+ 0x26001700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_RF_CURRENT, 0, 31,
|
|
+ 0x00002660);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VID_AUD_OVERRIDE, 0, 31,
|
|
+ 0x27000100);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_COMP_FLT_CTRL, 0, 31,
|
|
+ 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_PHASE_INC, 0, 31,
|
|
+ 0x1befbf06);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_GAIN_CONTROL, 0, 31,
|
|
+ 0x000035e8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VIDEO_AGC_CTRL, 0, 31,
|
|
+ 0xf2560000);
|
|
+
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a023F11;
|
|
+
|
|
+ } else if (standard & V4L2_STD_NTSC_M) {
|
|
+ /* V4L2_STD_NTSC_M (75 IRE Setup) Or
|
|
+ V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */
|
|
+
|
|
+ /* For NTSC the centre frequency of video coming out of
|
|
+ sidewinder is around 7.1MHz or 3.6MHz depending on the
|
|
+ spectral inversion. so for a non spectrally inverted channel
|
|
+ the pll freq word is 0x03420c49
|
|
+ */
|
|
+
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A);
|
|
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
|
|
+ 0x26001700);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
|
|
+ 0x00002660);
|
|
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
|
|
+ 0x04000800);
|
|
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
|
|
+ 0x27000100);
|
|
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f);
|
|
+
|
|
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
|
|
+ 0x009f50c1);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
|
|
+ 0x1befbf06);
|
|
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
|
|
+ 0x000035e8);
|
|
+
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT,
|
|
+ 0xC2262600);
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600);
|
|
+
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a003F10;
|
|
+ } else {
|
|
+ /* default PAL BG */
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
|
|
+ 0x26001700);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AGC_RF_CURRENT, 0, 31,
|
|
+ 0x00002660);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VIDEO_AGC_CTRL, 0, 31,
|
|
+ 0x72500800);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_VID_AUD_OVERRIDE, 0, 31,
|
|
+ 0x27000100);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_COMP_FLT_CTRL, 0, 31,
|
|
+ 0x00A653A8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_PHASE_INC, 0, 31,
|
|
+ 0x1befbf06);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_SRC_GAIN_CONTROL, 0, 31,
|
|
+ 0x000035e8);
|
|
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
|
|
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
|
|
+ /* Save the Spec Inversion value */
|
|
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
|
|
+ dif_misc_ctrl_value |= 0x3a013F11;
|
|
+ }
|
|
+
|
|
+ /* The AGC values should be the same for all standards,
|
|
+ AUD_SRC_SEL[19] should always be disabled */
|
|
+ dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL;
|
|
+
|
|
+ /* It is still possible to get Set Standard calls even when we
|
|
+ are in FM mode.
|
|
+ This is done to override the value for FM. */
|
|
+ if (dev->active_mode == V4L2_TUNER_RADIO)
|
|
+ dif_misc_ctrl_value = 0x7a080000;
|
|
+
|
|
+ /* Write the calculated value for misc ontrol register */
|
|
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_tuner_pre_channel_change(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 dwval;
|
|
+
|
|
+ /* Set the RF and IF k_agc values to 3 */
|
|
+ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
|
|
+ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
|
|
+ dwval |= 0x33000000;
|
|
+
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_tuner_post_channel_change(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 dwval;
|
|
+
|
|
+ /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for
|
|
+ * SECAM L/B/D standards */
|
|
+ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
|
|
+ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
|
|
+
|
|
+ if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B |
|
|
+ V4L2_STD_SECAM_D))
|
|
+ dwval |= 0x88000000;
|
|
+ else
|
|
+ dwval |= 0x44000000;
|
|
+
|
|
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * I 2 S - B L O C K C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+int cx231xx_i2s_blk_initialize(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 value;
|
|
+
|
|
+ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL1, 1, &value, 1);
|
|
+ /* enables clock to delta-sigma and decimation filter */
|
|
+ value |= 0x80;
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL1, 1, value, 1);
|
|
+ /* power up all channel */
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL2, 1, 0x00, 1);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
|
|
+ enum AV_MODE avmode)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 value = 0;
|
|
+
|
|
+ if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) {
|
|
+ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL2, 1, &value, 1);
|
|
+ value |= 0xfe;
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL2, 1, value, 1);
|
|
+ } else {
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL2, 1, 0x00, 1);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* set i2s_blk for audio input types */
|
|
+int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ switch (audio_input) {
|
|
+ case CX231XX_AMUX_LINE_IN:
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL2, 1, 0x00, 1);
|
|
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ CH_PWR_CTRL1, 1, 0x80, 1);
|
|
+ break;
|
|
+ case CX231XX_AMUX_VIDEO:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dev->ctl_ainput = audio_input;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * P O W E R C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
|
|
+{
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+ u32 tmp = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ if (dev->power_mode != mode)
|
|
+ dev->power_mode = mode;
|
|
+ else {
|
|
+ cx231xx_info(" setPowerMode::mode = %d, No Change req.\n",
|
|
+ mode);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cx231xx_info(" setPowerMode::mode = %d\n", mode);
|
|
+
|
|
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
|
|
+ 4);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ tmp = *((u32 *) value);
|
|
+
|
|
+ switch (mode) {
|
|
+ case POLARIS_AVMODE_ENXTERNAL_AV:
|
|
+
|
|
+ tmp &= (~PWR_MODE_MASK);
|
|
+
|
|
+ tmp |= PWR_AV_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+
|
|
+ tmp |= PWR_ISO_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status =
|
|
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
|
|
+ value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+
|
|
+ tmp |= POLARIS_AVMODE_ENXTERNAL_AV;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+
|
|
+ /* reset state of xceive tuner */
|
|
+ dev->xc_fw_load_done = 0;
|
|
+ break;
|
|
+
|
|
+ case POLARIS_AVMODE_ANALOGT_TV:
|
|
+
|
|
+ tmp &= (~PWR_DEMOD_EN);
|
|
+ tmp |= (I2C_DEMOD_EN);
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+
|
|
+ if (!(tmp & PWR_TUNER_EN)) {
|
|
+ tmp |= (PWR_TUNER_EN);
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ if (!(tmp & PWR_AV_EN)) {
|
|
+ tmp |= PWR_AV_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+ if (!(tmp & PWR_ISO_EN)) {
|
|
+ tmp |= PWR_ISO_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) {
|
|
+ tmp |= POLARIS_AVMODE_ANALOGT_TV;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
|
|
+ (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
|
|
+ /* tuner path to channel 1 from port 3 */
|
|
+ cx231xx_enable_i2c_for_tuner(dev, I2C_3);
|
|
+
|
|
+ if (dev->cx231xx_reset_analog_tuner)
|
|
+ dev->cx231xx_reset_analog_tuner(dev);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case POLARIS_AVMODE_DIGITAL:
|
|
+ if (!(tmp & PWR_TUNER_EN)) {
|
|
+ tmp |= (PWR_TUNER_EN);
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+ if (!(tmp & PWR_AV_EN)) {
|
|
+ tmp |= PWR_AV_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+ if (!(tmp & PWR_ISO_EN)) {
|
|
+ tmp |= PWR_ISO_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+
|
|
+ if (!(tmp & PWR_DEMOD_EN)) {
|
|
+ tmp |= PWR_DEMOD_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
|
|
+ (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
|
|
+ /* tuner path to channel 1 from port 3 */
|
|
+ cx231xx_enable_i2c_for_tuner(dev, I2C_3);
|
|
+
|
|
+ if (dev->cx231xx_reset_analog_tuner)
|
|
+ dev->cx231xx_reset_analog_tuner(dev);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+
|
|
+ /* For power saving, only enable Pwr_resetout_n
|
|
+ when digital TV is selected. */
|
|
+ if (mode == POLARIS_AVMODE_DIGITAL) {
|
|
+ tmp |= PWR_RESETOUT_EN;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
|
|
+ PWR_CTL_EN, value, 4);
|
|
+ msleep(PWR_SLEEP_INTERVAL);
|
|
+ }
|
|
+
|
|
+ /* update power control for afe */
|
|
+ status = cx231xx_afe_update_power_control(dev, mode);
|
|
+
|
|
+ /* update power control for i2s_blk */
|
|
+ status = cx231xx_i2s_blk_update_power_control(dev, mode);
|
|
+
|
|
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
|
|
+ 4);
|
|
+ cx231xx_info(" The data of PWR_CTL_EN register 0x74"
|
|
+ "=0x%0x,0x%0x,0x%0x,0x%0x\n",
|
|
+ value[0], value[1], value[2], value[3]);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_power_suspend(struct cx231xx *dev)
|
|
+{
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+ u32 tmp = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
|
|
+ value, 4);
|
|
+ if (status > 0)
|
|
+ return status;
|
|
+
|
|
+ tmp = *((u32 *) value);
|
|
+ tmp &= (~PWR_MODE_MASK);
|
|
+
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
|
|
+ value, 4);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/******************************************************************************
|
|
+ * S T R E A M C O N T R O L functions *
|
|
+ ******************************************************************************/
|
|
+int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask)
|
|
+{
|
|
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
|
|
+ u32 tmp = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask);
|
|
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
|
|
+ value, 4);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ tmp = *((u32 *) value);
|
|
+ tmp |= ep_mask;
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
|
|
+ value, 4);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask)
|
|
+{
|
|
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
|
|
+ u32 tmp = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask);
|
|
+ status =
|
|
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ tmp = *((u32 *) value);
|
|
+ tmp &= (~ep_mask);
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+
|
|
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
|
|
+ value, 4);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ if (dev->udev->speed == USB_SPEED_HIGH) {
|
|
+ switch (media_type) {
|
|
+ case 81: /* audio */
|
|
+ cx231xx_info("%s: Audio enter HANC\n", __func__);
|
|
+ status =
|
|
+ cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
|
|
+ break;
|
|
+
|
|
+ case 2: /* vbi */
|
|
+ cx231xx_info("%s: set vanc registers\n", __func__);
|
|
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
|
|
+ break;
|
|
+
|
|
+ case 3: /* sliced cc */
|
|
+ cx231xx_info("%s: set hanc registers\n", __func__);
|
|
+ status =
|
|
+ cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
|
|
+ break;
|
|
+
|
|
+ case 0: /* video */
|
|
+ cx231xx_info("%s: set video registers\n", __func__);
|
|
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
|
|
+ break;
|
|
+
|
|
+ case 4: /* ts1 */
|
|
+ cx231xx_info("%s: set ts1 registers\n", __func__);
|
|
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
|
|
+ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
|
|
+ break;
|
|
+ case 6: /* ts1 parallel mode */
|
|
+ cx231xx_info("%s: set ts1 parrallel mode registers\n",
|
|
+ __func__);
|
|
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
|
|
+ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
|
|
+{
|
|
+ int rc;
|
|
+ u32 ep_mask = -1;
|
|
+ struct pcb_config *pcb_config;
|
|
+
|
|
+ /* get EP for media type */
|
|
+ pcb_config = (struct pcb_config *)&dev->current_pcb_config;
|
|
+
|
|
+ if (pcb_config->config_num == 1) {
|
|
+ switch (media_type) {
|
|
+ case 0: /* Video */
|
|
+ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
|
|
+ break;
|
|
+ case 1: /* Audio */
|
|
+ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
|
|
+ break;
|
|
+ case 2: /* Vbi */
|
|
+ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
|
|
+ break;
|
|
+ case 3: /* Sliced_cc */
|
|
+ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
|
|
+ break;
|
|
+ case 4: /* ts1 */
|
|
+ case 6: /* ts1 parallel mode */
|
|
+ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
|
|
+ break;
|
|
+ case 5: /* ts2 */
|
|
+ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ } else if (pcb_config->config_num > 1) {
|
|
+ switch (media_type) {
|
|
+ case 0: /* Video */
|
|
+ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
|
|
+ break;
|
|
+ case 1: /* Audio */
|
|
+ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
|
|
+ break;
|
|
+ case 2: /* Vbi */
|
|
+ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
|
|
+ break;
|
|
+ case 3: /* Sliced_cc */
|
|
+ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
|
|
+ break;
|
|
+ case 4: /* ts1 */
|
|
+ case 6: /* ts1 parallel mode */
|
|
+ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
|
|
+ break;
|
|
+ case 5: /* ts2 */
|
|
+ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if (start) {
|
|
+ rc = cx231xx_initialize_stream_xfer(dev, media_type);
|
|
+
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ /* enable video capture */
|
|
+ if (ep_mask > 0)
|
|
+ rc = cx231xx_start_stream(dev, ep_mask);
|
|
+ } else {
|
|
+ /* disable video capture */
|
|
+ if (ep_mask > 0)
|
|
+ rc = cx231xx_stop_stream(dev, ep_mask);
|
|
+ }
|
|
+
|
|
+ if (dev->mode == CX231XX_ANALOG_MODE)
|
|
+ ;/* do any in Analog mode */
|
|
+ else
|
|
+ ;/* do any in digital mode */
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_capture_start);
|
|
+
|
|
+/*****************************************************************************
|
|
+* G P I O B I T control functions *
|
|
+******************************************************************************/
|
|
+int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*
|
|
+* cx231xx_set_gpio_direction
|
|
+* Sets the direction of the GPIO pin to input or output
|
|
+*
|
|
+* Parameters :
|
|
+* pin_number : The GPIO Pin number to program the direction for
|
|
+* from 0 to 31
|
|
+* pin_value : The Direction of the GPIO Pin under reference.
|
|
+* 0 = Input direction
|
|
+* 1 = Output direction
|
|
+*/
|
|
+int cx231xx_set_gpio_direction(struct cx231xx *dev,
|
|
+ int pin_number, int pin_value)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 value = 0;
|
|
+
|
|
+ /* Check for valid pin_number - if 32 , bail out */
|
|
+ if (pin_number >= 32)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* input */
|
|
+ if (pin_value == 0)
|
|
+ value = dev->gpio_dir & (~(1 << pin_number)); /* clear */
|
|
+ else
|
|
+ value = dev->gpio_dir | (1 << pin_number);
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val);
|
|
+
|
|
+ /* cache the value for future */
|
|
+ dev->gpio_dir = value;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*
|
|
+* cx231xx_set_gpio_value
|
|
+* Sets the value of the GPIO pin to Logic high or low. The Pin under
|
|
+* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!!
|
|
+*
|
|
+* Parameters :
|
|
+* pin_number : The GPIO Pin number to program the direction for
|
|
+* pin_value : The value of the GPIO Pin under reference.
|
|
+* 0 = set it to 0
|
|
+* 1 = set it to 1
|
|
+*/
|
|
+int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 value = 0;
|
|
+
|
|
+ /* Check for valid pin_number - if 0xFF , bail out */
|
|
+ if (pin_number >= 32)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* first do a sanity check - if the Pin is not output, make it output */
|
|
+ if ((dev->gpio_dir & (1 << pin_number)) == 0x00) {
|
|
+ /* It was in input mode */
|
|
+ value = dev->gpio_dir | (1 << pin_number);
|
|
+ dev->gpio_dir = value;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *) &dev->gpio_val);
|
|
+ value = 0;
|
|
+ }
|
|
+
|
|
+ if (pin_value == 0)
|
|
+ value = dev->gpio_val & (~(1 << pin_number));
|
|
+ else
|
|
+ value = dev->gpio_val | (1 << pin_number);
|
|
+
|
|
+ /* store the value */
|
|
+ dev->gpio_val = value;
|
|
+
|
|
+ /* toggle bit0 of GP_IO */
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+* G P I O I2C related functions *
|
|
+******************************************************************************/
|
|
+int cx231xx_gpio_i2c_start(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* set SCL to output 1 ; set SDA to output 1 */
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set SCL to output 1; set SDA to output 0 */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set SCL to output 0; set SDA to output 0 */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_end(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* set SCL to output 0; set SDA to output 0 */
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
|
|
+
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set SCL to output 1; set SDA to output 0 */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set SCL to input ,release SCL cable control
|
|
+ set SDA to input ,release SDA cable control */
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+
|
|
+ status =
|
|
+ cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+ if (status < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data)
|
|
+{
|
|
+ int status = 0;
|
|
+ u8 i;
|
|
+
|
|
+ /* set SCL to output ; set SDA to output */
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
|
|
+
|
|
+ for (i = 0; i < 8; i++) {
|
|
+ if (((data << i) & 0x80) == 0) {
|
|
+ /* set SCL to output 0; set SDA to output 0 */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL to output 1; set SDA to output 0 */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL to output 0; set SDA to output 0 */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+ } else {
|
|
+ /* set SCL to output 0; set SDA to output 1 */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL to output 1; set SDA to output 1 */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL to output 0; set SDA to output 1 */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+ }
|
|
+ }
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 * buf)
|
|
+{
|
|
+ u8 value = 0;
|
|
+ int status = 0;
|
|
+ u32 gpio_logic_value = 0;
|
|
+ u8 i;
|
|
+
|
|
+ /* read byte */
|
|
+ for (i = 0; i < 8; i++) { /* send write I2c addr */
|
|
+
|
|
+ /* set SCL to output 0; set SDA to input */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL to output 1; set SDA to input */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* get SDA data bit */
|
|
+ gpio_logic_value = dev->gpio_val;
|
|
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+ if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0)
|
|
+ value |= (1 << (8 - i - 1));
|
|
+
|
|
+ dev->gpio_val = gpio_logic_value;
|
|
+ }
|
|
+
|
|
+ /* set SCL to output 0,finish the read latest SCL signal.
|
|
+ !!!set SDA to input, never to modify SDA direction at
|
|
+ the same times */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* store the value */
|
|
+ *buf = value & 0xff;
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 gpio_logic_value = 0;
|
|
+ int nCnt = 10;
|
|
+ int nInit = nCnt;
|
|
+
|
|
+ /* clock stretch; set SCL to input; set SDA to input;
|
|
+ get SCL value till SCL = 1 */
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+
|
|
+ gpio_logic_value = dev->gpio_val;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ do {
|
|
+ msleep(2);
|
|
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
|
|
+ (u8 *)&dev->gpio_val);
|
|
+ nCnt--;
|
|
+ } while (((dev->gpio_val &
|
|
+ (1 << dev->board.tuner_scl_gpio)) == 0) &&
|
|
+ (nCnt > 0));
|
|
+
|
|
+ if (nCnt == 0)
|
|
+ cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
|
|
+ nInit * 10);
|
|
+
|
|
+ /* readAck
|
|
+ throuth clock stretch ,slave has given a SCL signal,
|
|
+ so the SDA data can be directly read. */
|
|
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) {
|
|
+ dev->gpio_val = gpio_logic_value;
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ status = 0;
|
|
+ } else {
|
|
+ dev->gpio_val = gpio_logic_value;
|
|
+ dev->gpio_val |= (1 << dev->board.tuner_sda_gpio);
|
|
+ }
|
|
+
|
|
+ /* read SDA end, set the SCL to output 0, after this operation,
|
|
+ SDA direction can be changed. */
|
|
+ dev->gpio_val = gpio_logic_value;
|
|
+ dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio);
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* set SDA to ouput */
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL = 0 (output); set SDA = 0 (output) */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL = 1 (output); set SDA = 0 (output) */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SCL = 0 (output); set SDA = 0 (output) */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set SDA to input,and then the slave will read data from SDA. */
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ /* set scl to output ; set sda to input */
|
|
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
|
|
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set scl to output 0; set sda to input */
|
|
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ /* set scl to output 1; set sda to input */
|
|
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
|
|
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+* G P I O I2C related functions *
|
|
+******************************************************************************/
|
|
+/* cx231xx_gpio_i2c_read
|
|
+ * Function to read data from gpio based I2C interface
|
|
+ */
|
|
+int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
|
|
+{
|
|
+ int status = 0;
|
|
+ int i = 0;
|
|
+
|
|
+ /* get the lock */
|
|
+ mutex_lock(&dev->gpio_i2c_lock);
|
|
+
|
|
+ /* start */
|
|
+ status = cx231xx_gpio_i2c_start(dev);
|
|
+
|
|
+ /* write dev_addr */
|
|
+ status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1);
|
|
+
|
|
+ /* readAck */
|
|
+ status = cx231xx_gpio_i2c_read_ack(dev);
|
|
+
|
|
+ /* read data */
|
|
+ for (i = 0; i < len; i++) {
|
|
+ /* read data */
|
|
+ buf[i] = 0;
|
|
+ status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]);
|
|
+
|
|
+ if ((i + 1) != len) {
|
|
+ /* only do write ack if we more length */
|
|
+ status = cx231xx_gpio_i2c_write_ack(dev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* write NAK - inform reads are complete */
|
|
+ status = cx231xx_gpio_i2c_write_nak(dev);
|
|
+
|
|
+ /* write end */
|
|
+ status = cx231xx_gpio_i2c_end(dev);
|
|
+
|
|
+ /* release the lock */
|
|
+ mutex_unlock(&dev->gpio_i2c_lock);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* cx231xx_gpio_i2c_write
|
|
+ * Function to write data to gpio based I2C interface
|
|
+ */
|
|
+int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
|
|
+{
|
|
+ int status = 0;
|
|
+ int i = 0;
|
|
+
|
|
+ /* get the lock */
|
|
+ mutex_lock(&dev->gpio_i2c_lock);
|
|
+
|
|
+ /* start */
|
|
+ status = cx231xx_gpio_i2c_start(dev);
|
|
+
|
|
+ /* write dev_addr */
|
|
+ status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1);
|
|
+
|
|
+ /* read Ack */
|
|
+ status = cx231xx_gpio_i2c_read_ack(dev);
|
|
+
|
|
+ for (i = 0; i < len; i++) {
|
|
+ /* Write data */
|
|
+ status = cx231xx_gpio_i2c_write_byte(dev, buf[i]);
|
|
+
|
|
+ /* read Ack */
|
|
+ status = cx231xx_gpio_i2c_read_ack(dev);
|
|
+ }
|
|
+
|
|
+ /* write End */
|
|
+ status = cx231xx_gpio_i2c_end(dev);
|
|
+
|
|
+ /* release the lock */
|
|
+ mutex_unlock(&dev->gpio_i2c_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
|
|
new file mode 100644
|
|
index 0000000..d2f2091
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
|
|
@@ -0,0 +1,919 @@
|
|
+/*
|
|
+ cx231xx-cards.c - driver for Conexant Cx23100/101/102
|
|
+ USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/usb.h>
|
|
+#include <media/tuner.h>
|
|
+#include <media/tveeprom.h>
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+
|
|
+#include <media/cx25840.h>
|
|
+#include "xc5000.h"
|
|
+
|
|
+#include "cx231xx.h"
|
|
+
|
|
+static int tuner = -1;
|
|
+module_param(tuner, int, 0444);
|
|
+MODULE_PARM_DESC(tuner, "tuner type");
|
|
+
|
|
+static unsigned int disable_ir;
|
|
+module_param(disable_ir, int, 0444);
|
|
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
|
|
+
|
|
+/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
|
|
+static unsigned long cx231xx_devused;
|
|
+
|
|
+/*
|
|
+ * Reset sequences for analog/digital modes
|
|
+ */
|
|
+
|
|
+static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
|
|
+ {0x03, 0x01, 10},
|
|
+ {0x03, 0x00, 30},
|
|
+ {0x03, 0x01, 10},
|
|
+ {-1, -1, -1},
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Board definitions
|
|
+ */
|
|
+struct cx231xx_board cx231xx_boards[] = {
|
|
+ [CX231XX_BOARD_UNKNOWN] = {
|
|
+ .name = "Unknown CX231xx video grabber",
|
|
+ .tuner_type = TUNER_ABSENT,
|
|
+ .input = {{
|
|
+ .type = CX231XX_VMUX_TELEVISION,
|
|
+ .vmux = CX231XX_VIN_3_1,
|
|
+ .amux = CX231XX_AMUX_VIDEO,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_COMPOSITE1,
|
|
+ .vmux = CX231XX_VIN_2_1,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_SVIDEO,
|
|
+ .vmux = CX231XX_VIN_1_1 |
|
|
+ (CX231XX_VIN_1_2 << 8) |
|
|
+ CX25840_SVIDEO_ON,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ [CX231XX_BOARD_CNXT_RDE_250] = {
|
|
+ .name = "Conexant Hybrid TV - RDE250",
|
|
+ .tuner_type = TUNER_XC5000,
|
|
+ .tuner_addr = 0x61,
|
|
+ .tuner_gpio = RDE250_XCV_TUNER,
|
|
+ .tuner_sif_gpio = 0x05,
|
|
+ .tuner_scl_gpio = 0x1a,
|
|
+ .tuner_sda_gpio = 0x1b,
|
|
+ .decoder = CX231XX_AVDECODER,
|
|
+ .demod_xfer_mode = 0,
|
|
+ .ctl_pin_status_mask = 0xFFFFFFC4,
|
|
+ .agc_analog_digital_select_gpio = 0x0c,
|
|
+ .gpio_pin_status_mask = 0x4001000,
|
|
+ .tuner_i2c_master = 1,
|
|
+ .demod_i2c_master = 2,
|
|
+ .has_dvb = 1,
|
|
+ .demod_addr = 0x02,
|
|
+ .norm = V4L2_STD_PAL,
|
|
+
|
|
+ .input = {{
|
|
+ .type = CX231XX_VMUX_TELEVISION,
|
|
+ .vmux = CX231XX_VIN_3_1,
|
|
+ .amux = CX231XX_AMUX_VIDEO,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_COMPOSITE1,
|
|
+ .vmux = CX231XX_VIN_2_1,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_SVIDEO,
|
|
+ .vmux = CX231XX_VIN_1_1 |
|
|
+ (CX231XX_VIN_1_2 << 8) |
|
|
+ CX25840_SVIDEO_ON,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+
|
|
+ [CX231XX_BOARD_CNXT_RDU_250] = {
|
|
+ .name = "Conexant Hybrid TV - RDU250",
|
|
+ .tuner_type = TUNER_XC5000,
|
|
+ .tuner_addr = 0x61,
|
|
+ .tuner_gpio = RDE250_XCV_TUNER,
|
|
+ .tuner_sif_gpio = 0x05,
|
|
+ .tuner_scl_gpio = 0x1a,
|
|
+ .tuner_sda_gpio = 0x1b,
|
|
+ .decoder = CX231XX_AVDECODER,
|
|
+ .demod_xfer_mode = 0,
|
|
+ .ctl_pin_status_mask = 0xFFFFFFC4,
|
|
+ .agc_analog_digital_select_gpio = 0x0c,
|
|
+ .gpio_pin_status_mask = 0x4001000,
|
|
+ .tuner_i2c_master = 1,
|
|
+ .demod_i2c_master = 2,
|
|
+ .has_dvb = 1,
|
|
+ .demod_addr = 0x32,
|
|
+ .norm = V4L2_STD_NTSC,
|
|
+
|
|
+ .input = {{
|
|
+ .type = CX231XX_VMUX_TELEVISION,
|
|
+ .vmux = CX231XX_VIN_3_1,
|
|
+ .amux = CX231XX_AMUX_VIDEO,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_COMPOSITE1,
|
|
+ .vmux = CX231XX_VIN_2_1,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }, {
|
|
+ .type = CX231XX_VMUX_SVIDEO,
|
|
+ .vmux = CX231XX_VIN_1_1 |
|
|
+ (CX231XX_VIN_1_2 << 8) |
|
|
+ CX25840_SVIDEO_ON,
|
|
+ .amux = CX231XX_AMUX_LINE_IN,
|
|
+ .gpio = 0,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+};
|
|
+const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
|
|
+
|
|
+/* table of devices that work with this driver */
|
|
+struct usb_device_id cx231xx_id_table[] = {
|
|
+ {USB_DEVICE(0x0572, 0x5A3C),
|
|
+ .driver_info = CX231XX_BOARD_UNKNOWN},
|
|
+ {USB_DEVICE(0x0572, 0x58A2),
|
|
+ .driver_info = CX231XX_BOARD_CNXT_RDE_250},
|
|
+ {USB_DEVICE(0x0572, 0x58A1),
|
|
+ .driver_info = CX231XX_BOARD_CNXT_RDU_250},
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
|
|
+
|
|
+/* cx231xx_tuner_callback
|
|
+ * will be used to reset XC5000 tuner using GPIO pin
|
|
+ */
|
|
+
|
|
+int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
|
|
+{
|
|
+ int rc = 0;
|
|
+ struct cx231xx *dev = ptr;
|
|
+
|
|
+ if (dev->tuner_type == TUNER_XC5000) {
|
|
+ if (command == XC5000_TUNER_RESET) {
|
|
+ cx231xx_info
|
|
+ ("Tuner CB: RESET: cmd %d : tuner type %d \n",
|
|
+ command, dev->tuner_type);
|
|
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
|
|
+ 1);
|
|
+ msleep(10);
|
|
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
|
|
+ 0);
|
|
+ msleep(330);
|
|
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
|
|
+ 1);
|
|
+ msleep(10);
|
|
+ }
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
|
|
+
|
|
+static inline void cx231xx_set_model(struct cx231xx *dev)
|
|
+{
|
|
+ memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
|
|
+}
|
|
+
|
|
+/* Since cx231xx_pre_card_setup() requires a proper dev->model,
|
|
+ * this won't work for boards with generic PCI IDs
|
|
+ */
|
|
+void cx231xx_pre_card_setup(struct cx231xx *dev)
|
|
+{
|
|
+
|
|
+ cx231xx_set_model(dev);
|
|
+
|
|
+ cx231xx_info("Identified as %s (card=%d)\n",
|
|
+ dev->board.name, dev->model);
|
|
+
|
|
+ /* set the direction for GPIO pins */
|
|
+ cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
|
|
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
|
|
+ cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
|
|
+
|
|
+ /* request some modules if any required */
|
|
+
|
|
+ /* reset the Tuner */
|
|
+ cx231xx_gpio_set(dev, dev->board.tuner_gpio);
|
|
+
|
|
+ /* set the mode to Analog mode initially */
|
|
+ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
|
|
+
|
|
+ /* Unlock device */
|
|
+ /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
|
|
+
|
|
+}
|
|
+
|
|
+static void cx231xx_config_tuner(struct cx231xx *dev)
|
|
+{
|
|
+ struct tuner_setup tun_setup;
|
|
+ struct v4l2_frequency f;
|
|
+
|
|
+ if (dev->tuner_type == TUNER_ABSENT)
|
|
+ return;
|
|
+
|
|
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
|
|
+ tun_setup.type = dev->tuner_type;
|
|
+ tun_setup.addr = dev->tuner_addr;
|
|
+ tun_setup.tuner_callback = cx231xx_tuner_callback;
|
|
+
|
|
+ tuner_call(dev, tuner, s_type_addr, &tun_setup);
|
|
+
|
|
+#if 0
|
|
+ if (tun_setup.type == TUNER_XC5000) {
|
|
+ static struct xc2028_ctrl ctrl = {
|
|
+ .fname = XC5000_DEFAULT_FIRMWARE,
|
|
+ .max_len = 64,
|
|
+ .demod = 0;
|
|
+ };
|
|
+ struct v4l2_priv_tun_config cfg = {
|
|
+ .tuner = dev->tuner_type,
|
|
+ .priv = &ctrl,
|
|
+ };
|
|
+ tuner_call(dev, tuner, s_config, &cfg);
|
|
+ }
|
|
+#endif
|
|
+ /* configure tuner */
|
|
+ f.tuner = 0;
|
|
+ f.type = V4L2_TUNER_ANALOG_TV;
|
|
+ f.frequency = 9076; /* just a magic number */
|
|
+ dev->ctl_freq = f.frequency;
|
|
+ call_all(dev, tuner, s_frequency, &f);
|
|
+
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------- */
|
|
+void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir)
|
|
+{
|
|
+ if (disable_ir) {
|
|
+ ir->get_key = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* detect & configure */
|
|
+ switch (dev->model) {
|
|
+
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+ break;
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void cx231xx_card_setup(struct cx231xx *dev)
|
|
+{
|
|
+
|
|
+ cx231xx_set_model(dev);
|
|
+
|
|
+ dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
|
|
+ if (cx231xx_boards[dev->model].tuner_addr)
|
|
+ dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
|
|
+
|
|
+ /* request some modules */
|
|
+ if (dev->board.decoder == CX231XX_AVDECODER) {
|
|
+ dev->sd_cx25840 =
|
|
+ v4l2_i2c_new_subdev(&dev->i2c_bus[0].i2c_adap,
|
|
+ "cx25840", "cx25840", 0x88 >> 1);
|
|
+ if (dev->sd_cx25840 == NULL)
|
|
+ cx231xx_info("cx25840 subdev registration failure\n");
|
|
+ cx25840_call(dev, core, init, 0);
|
|
+
|
|
+ }
|
|
+
|
|
+ if (dev->board.tuner_type != TUNER_ABSENT) {
|
|
+ dev->sd_tuner =
|
|
+ v4l2_i2c_new_subdev(&dev->i2c_bus[1].i2c_adap,
|
|
+ "tuner", "tuner", 0xc2 >> 1);
|
|
+ if (dev->sd_tuner == NULL)
|
|
+ cx231xx_info("tuner subdev registration failure\n");
|
|
+
|
|
+ cx231xx_config_tuner(dev);
|
|
+ }
|
|
+
|
|
+ cx231xx_config_tuner(dev);
|
|
+
|
|
+#if 0
|
|
+ /* TBD IR will be added later */
|
|
+ cx231xx_ir_init(dev);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_config()
|
|
+ * inits registers with sane defaults
|
|
+ */
|
|
+int cx231xx_config(struct cx231xx *dev)
|
|
+{
|
|
+ /* TBD need to add cx231xx specific code */
|
|
+ dev->mute = 1; /* maybe not the right place... */
|
|
+ dev->volume = 0x1f;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_config_i2c()
|
|
+ * configure i2c attached devices
|
|
+ */
|
|
+void cx231xx_config_i2c(struct cx231xx *dev)
|
|
+{
|
|
+ struct v4l2_routing route;
|
|
+
|
|
+ route.input = INPUT(dev->video_input)->vmux;
|
|
+ route.output = 0;
|
|
+
|
|
+ call_all(dev, video, s_stream, 1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_realease_resources()
|
|
+ * unregisters the v4l2,i2c and usb devices
|
|
+ * called when the device gets disconected or at module unload
|
|
+*/
|
|
+void cx231xx_release_resources(struct cx231xx *dev)
|
|
+{
|
|
+
|
|
+#if 0 /* TBD IR related */
|
|
+ if (dev->ir)
|
|
+ cx231xx_ir_fini(dev);
|
|
+#endif
|
|
+
|
|
+ cx231xx_release_analog_resources(dev);
|
|
+
|
|
+ cx231xx_remove_from_devlist(dev);
|
|
+
|
|
+ cx231xx_dev_uninit(dev);
|
|
+
|
|
+ usb_put_dev(dev->udev);
|
|
+
|
|
+ /* Mark device as unused */
|
|
+ cx231xx_devused &= ~(1 << dev->devno);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_init_dev()
|
|
+ * allocates and inits the device structs, registers i2c bus and v4l device
|
|
+ */
|
|
+static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
|
|
+ int minor)
|
|
+{
|
|
+ struct cx231xx *dev = *devhandle;
|
|
+ int retval = -ENOMEM;
|
|
+ int errCode;
|
|
+ unsigned int maxh, maxw;
|
|
+
|
|
+ dev->udev = udev;
|
|
+ mutex_init(&dev->lock);
|
|
+ mutex_init(&dev->ctrl_urb_lock);
|
|
+ mutex_init(&dev->gpio_i2c_lock);
|
|
+
|
|
+ spin_lock_init(&dev->video_mode.slock);
|
|
+ spin_lock_init(&dev->vbi_mode.slock);
|
|
+ spin_lock_init(&dev->sliced_cc_mode.slock);
|
|
+
|
|
+ init_waitqueue_head(&dev->open);
|
|
+ init_waitqueue_head(&dev->wait_frame);
|
|
+ init_waitqueue_head(&dev->wait_stream);
|
|
+
|
|
+ dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
|
|
+ dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
|
|
+ dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
|
|
+ dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
|
|
+ dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
|
|
+
|
|
+ /* Query cx231xx to find what pcb config it is related to */
|
|
+ initialize_cx231xx(dev);
|
|
+
|
|
+ /* Cx231xx pre card setup */
|
|
+ cx231xx_pre_card_setup(dev);
|
|
+
|
|
+ errCode = cx231xx_config(dev);
|
|
+ if (errCode) {
|
|
+ cx231xx_errdev("error configuring device\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* set default norm */
|
|
+ dev->norm = dev->board.norm;
|
|
+
|
|
+ /* register i2c bus */
|
|
+ errCode = cx231xx_dev_init(dev);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* Do board specific init */
|
|
+ cx231xx_card_setup(dev);
|
|
+
|
|
+ /* configure the device */
|
|
+ cx231xx_config_i2c(dev);
|
|
+
|
|
+ maxw = norm_maxw(dev);
|
|
+ maxh = norm_maxh(dev);
|
|
+
|
|
+ /* set default image size */
|
|
+ dev->width = maxw;
|
|
+ dev->height = maxh;
|
|
+ dev->interlaced = 0;
|
|
+ dev->hscale = 0;
|
|
+ dev->vscale = 0;
|
|
+ dev->video_input = 0;
|
|
+
|
|
+ errCode = cx231xx_config(dev);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* init video dma queues */
|
|
+ INIT_LIST_HEAD(&dev->video_mode.vidq.active);
|
|
+ INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
|
|
+
|
|
+ /* init vbi dma queues */
|
|
+ INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
|
|
+ INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
|
|
+
|
|
+ /* Reset other chips required if they are tied up with GPIO pins */
|
|
+
|
|
+ cx231xx_add_into_devlist(dev);
|
|
+
|
|
+ retval = cx231xx_register_analog_devices(dev);
|
|
+ if (retval < 0) {
|
|
+ cx231xx_release_resources(dev);
|
|
+ goto fail_reg_devices;
|
|
+ }
|
|
+
|
|
+ cx231xx_init_extension(dev);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+fail_reg_devices:
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+#if defined(CONFIG_MODULES) && defined(MODULE)
|
|
+static void request_module_async(struct work_struct *work)
|
|
+{
|
|
+ struct cx231xx *dev = container_of(work,
|
|
+ struct cx231xx, request_module_wk);
|
|
+
|
|
+ if (dev->has_alsa_audio)
|
|
+ request_module("cx231xx-alsa");
|
|
+
|
|
+ if (dev->board.has_dvb)
|
|
+ request_module("cx231xx-dvb");
|
|
+
|
|
+}
|
|
+
|
|
+static void request_modules(struct cx231xx *dev)
|
|
+{
|
|
+ INIT_WORK(&dev->request_module_wk, request_module_async);
|
|
+ schedule_work(&dev->request_module_wk);
|
|
+}
|
|
+#else
|
|
+#define request_modules(dev)
|
|
+#endif /* CONFIG_MODULES */
|
|
+
|
|
+/*
|
|
+ * cx231xx_usb_probe()
|
|
+ * checks for supported devices
|
|
+ */
|
|
+static int cx231xx_usb_probe(struct usb_interface *interface,
|
|
+ const struct usb_device_id *id)
|
|
+{
|
|
+ struct usb_device *udev;
|
|
+ struct usb_interface *uif;
|
|
+ struct cx231xx *dev = NULL;
|
|
+ int retval = -ENODEV;
|
|
+ int nr = 0, ifnum;
|
|
+ int i, isoc_pipe = 0;
|
|
+ char *speed;
|
|
+ char descr[255] = "";
|
|
+ struct usb_interface *lif = NULL;
|
|
+ int skip_interface = 0;
|
|
+ struct usb_interface_assoc_descriptor *assoc_desc;
|
|
+
|
|
+ udev = usb_get_dev(interface_to_usbdev(interface));
|
|
+ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
|
+
|
|
+ if (!ifnum) {
|
|
+ /*
|
|
+ * Interface number 0 - IR interface
|
|
+ */
|
|
+ /* Check to see next free device and mark as used */
|
|
+ nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
|
|
+ cx231xx_devused |= 1 << nr;
|
|
+
|
|
+ if (nr >= CX231XX_MAXBOARDS) {
|
|
+ cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n",
|
|
+ CX231XX_MAXBOARDS);
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* allocate memory for our device state and initialize it */
|
|
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
+ if (dev == NULL) {
|
|
+ cx231xx_err(DRIVER_NAME ": out of memory!\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ snprintf(dev->name, 29, "cx231xx #%d", nr);
|
|
+ dev->devno = nr;
|
|
+ dev->model = id->driver_info;
|
|
+ dev->video_mode.alt = -1;
|
|
+ dev->interface_count++;
|
|
+
|
|
+ /* reset gpio dir and value */
|
|
+ dev->gpio_dir = 0;
|
|
+ dev->gpio_val = 0;
|
|
+ dev->xc_fw_load_done = 0;
|
|
+ dev->has_alsa_audio = 1;
|
|
+ dev->power_mode = -1;
|
|
+
|
|
+ /* 0 - vbi ; 1 -sliced cc mode */
|
|
+ dev->vbi_or_sliced_cc_mode = 0;
|
|
+
|
|
+ /* get maximum no.of IAD interfaces */
|
|
+ assoc_desc = udev->actconfig->intf_assoc[0];
|
|
+ dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
|
|
+
|
|
+ /* init CIR module TBD */
|
|
+
|
|
+ /* store the current interface */
|
|
+ lif = interface;
|
|
+
|
|
+ switch (udev->speed) {
|
|
+ case USB_SPEED_LOW:
|
|
+ speed = "1.5";
|
|
+ break;
|
|
+ case USB_SPEED_UNKNOWN:
|
|
+ case USB_SPEED_FULL:
|
|
+ speed = "12";
|
|
+ break;
|
|
+ case USB_SPEED_HIGH:
|
|
+ speed = "480";
|
|
+ break;
|
|
+ default:
|
|
+ speed = "unknown";
|
|
+ }
|
|
+
|
|
+ if (udev->manufacturer)
|
|
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
|
|
+
|
|
+ if (udev->product) {
|
|
+ if (*descr)
|
|
+ strlcat(descr, " ", sizeof(descr));
|
|
+ strlcat(descr, udev->product, sizeof(descr));
|
|
+ }
|
|
+ if (*descr)
|
|
+ strlcat(descr, " ", sizeof(descr));
|
|
+
|
|
+ cx231xx_info("New device %s@ %s Mbps "
|
|
+ "(%04x:%04x) with %d interfaces\n",
|
|
+ descr,
|
|
+ speed,
|
|
+ le16_to_cpu(udev->descriptor.idVendor),
|
|
+ le16_to_cpu(udev->descriptor.idProduct),
|
|
+ dev->max_iad_interface_count);
|
|
+ } else {
|
|
+ /* Get dev structure first */
|
|
+ dev = usb_get_intfdata(udev->actconfig->interface[0]);
|
|
+ if (dev == NULL) {
|
|
+ cx231xx_err(DRIVER_NAME ": out of first interface!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ /* store the interface 0 back */
|
|
+ lif = udev->actconfig->interface[0];
|
|
+
|
|
+ /* increment interface count */
|
|
+ dev->interface_count++;
|
|
+
|
|
+ /* get device number */
|
|
+ nr = dev->devno;
|
|
+
|
|
+ /*
|
|
+ * set skip interface, for all interfaces but
|
|
+ * interface 1 and the last one
|
|
+ */
|
|
+ if ((ifnum != 1) && ((dev->interface_count - 1)
|
|
+ != dev->max_iad_interface_count))
|
|
+ skip_interface = 1;
|
|
+
|
|
+ if (ifnum == 1) {
|
|
+ assoc_desc = udev->actconfig->intf_assoc[0];
|
|
+ if (assoc_desc->bFirstInterface != ifnum) {
|
|
+ cx231xx_err(DRIVER_NAME ": Not found "
|
|
+ "matching IAD interface\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (skip_interface)
|
|
+ return -ENODEV;
|
|
+
|
|
+ cx231xx_info("registering interface %d\n", ifnum);
|
|
+
|
|
+ /* save our data pointer in this interface device */
|
|
+ usb_set_intfdata(lif, dev);
|
|
+
|
|
+ if ((dev->interface_count - 1) != dev->max_iad_interface_count)
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * AV device initialization - only done at the last interface
|
|
+ */
|
|
+
|
|
+ /* Create v4l2 device */
|
|
+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
|
|
+ "%s-%03d", "cx231xx", nr);
|
|
+ retval = v4l2_device_register(&udev->dev, &dev->v4l2_dev);
|
|
+ if (retval) {
|
|
+ cx231xx_errdev("v4l2_device_register failed\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ kfree(dev);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* allocate device struct */
|
|
+ retval = cx231xx_init_dev(&dev, udev, nr);
|
|
+ if (retval) {
|
|
+ cx231xx_devused &= ~(1 << dev->devno);
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ kfree(dev);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ /* compute alternate max packet sizes for video */
|
|
+ uif = udev->actconfig->interface[dev->current_pcb_config.
|
|
+ hs_config_info[0].interface_info.video_index + 1];
|
|
+
|
|
+ dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
|
|
+ endpoint[isoc_pipe].desc.bEndpointAddress);
|
|
+
|
|
+ dev->video_mode.num_alt = uif->num_altsetting;
|
|
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
|
|
+ dev->video_mode.end_point_addr,
|
|
+ dev->video_mode.num_alt);
|
|
+ dev->video_mode.alt_max_pkt_size =
|
|
+ kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
|
|
+
|
|
+ if (dev->video_mode.alt_max_pkt_size == NULL) {
|
|
+ cx231xx_errdev("out of memory!\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ kfree(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev->video_mode.num_alt; i++) {
|
|
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
|
|
+ desc.wMaxPacketSize);
|
|
+ dev->video_mode.alt_max_pkt_size[i] =
|
|
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
|
|
+ dev->video_mode.alt_max_pkt_size[i]);
|
|
+ }
|
|
+
|
|
+ /* compute alternate max packet sizes for vbi */
|
|
+ uif = udev->actconfig->interface[dev->current_pcb_config.
|
|
+ hs_config_info[0].interface_info.
|
|
+ vanc_index + 1];
|
|
+
|
|
+ dev->vbi_mode.end_point_addr =
|
|
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
|
|
+ bEndpointAddress);
|
|
+
|
|
+ dev->vbi_mode.num_alt = uif->num_altsetting;
|
|
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
|
|
+ dev->vbi_mode.end_point_addr,
|
|
+ dev->vbi_mode.num_alt);
|
|
+ dev->vbi_mode.alt_max_pkt_size =
|
|
+ kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
|
|
+
|
|
+ if (dev->vbi_mode.alt_max_pkt_size == NULL) {
|
|
+ cx231xx_errdev("out of memory!\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ kfree(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev->vbi_mode.num_alt; i++) {
|
|
+ u16 tmp =
|
|
+ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
|
|
+ desc.wMaxPacketSize);
|
|
+ dev->vbi_mode.alt_max_pkt_size[i] =
|
|
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
|
|
+ dev->vbi_mode.alt_max_pkt_size[i]);
|
|
+ }
|
|
+
|
|
+ /* compute alternate max packet sizes for sliced CC */
|
|
+ uif = udev->actconfig->interface[dev->current_pcb_config.
|
|
+ hs_config_info[0].interface_info.
|
|
+ hanc_index + 1];
|
|
+
|
|
+ dev->sliced_cc_mode.end_point_addr =
|
|
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
|
|
+ bEndpointAddress);
|
|
+
|
|
+ dev->sliced_cc_mode.num_alt = uif->num_altsetting;
|
|
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
|
|
+ dev->sliced_cc_mode.end_point_addr,
|
|
+ dev->sliced_cc_mode.num_alt);
|
|
+ dev->sliced_cc_mode.alt_max_pkt_size =
|
|
+ kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
|
|
+
|
|
+ if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
|
|
+ cx231xx_errdev("out of memory!\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ kfree(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
|
|
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
|
|
+ desc.wMaxPacketSize);
|
|
+ dev->sliced_cc_mode.alt_max_pkt_size[i] =
|
|
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
|
|
+ dev->sliced_cc_mode.alt_max_pkt_size[i]);
|
|
+ }
|
|
+
|
|
+ if (dev->current_pcb_config.ts1_source != 0xff) {
|
|
+ /* compute alternate max packet sizes for TS1 */
|
|
+ uif = udev->actconfig->interface[dev->current_pcb_config.
|
|
+ hs_config_info[0].
|
|
+ interface_info.
|
|
+ ts1_index + 1];
|
|
+
|
|
+ dev->ts1_mode.end_point_addr =
|
|
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
|
|
+ desc.bEndpointAddress);
|
|
+
|
|
+ dev->ts1_mode.num_alt = uif->num_altsetting;
|
|
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
|
|
+ dev->ts1_mode.end_point_addr,
|
|
+ dev->ts1_mode.num_alt);
|
|
+ dev->ts1_mode.alt_max_pkt_size =
|
|
+ kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
|
|
+
|
|
+ if (dev->ts1_mode.alt_max_pkt_size == NULL) {
|
|
+ cx231xx_errdev("out of memory!\n");
|
|
+ cx231xx_devused &= ~(1 << nr);
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+ kfree(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < dev->ts1_mode.num_alt; i++) {
|
|
+ u16 tmp = le16_to_cpu(uif->altsetting[i].
|
|
+ endpoint[isoc_pipe].desc.
|
|
+ wMaxPacketSize);
|
|
+ dev->ts1_mode.alt_max_pkt_size[i] =
|
|
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
|
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
|
|
+ dev->ts1_mode.alt_max_pkt_size[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* load other modules required */
|
|
+ request_modules(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_usb_disconnect()
|
|
+ * called when the device gets diconencted
|
|
+ * video device will be unregistered on v4l2_close in case it is still open
|
|
+ */
|
|
+static void cx231xx_usb_disconnect(struct usb_interface *interface)
|
|
+{
|
|
+ struct cx231xx *dev;
|
|
+
|
|
+ dev = usb_get_intfdata(interface);
|
|
+ usb_set_intfdata(interface, NULL);
|
|
+
|
|
+ if (!dev)
|
|
+ return;
|
|
+
|
|
+ if (!dev->udev)
|
|
+ return;
|
|
+
|
|
+ /* delete v4l2 device */
|
|
+ v4l2_device_unregister(&dev->v4l2_dev);
|
|
+
|
|
+ /* wait until all current v4l2 io is finished then deallocate
|
|
+ resources */
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ wake_up_interruptible_all(&dev->open);
|
|
+
|
|
+ if (dev->users) {
|
|
+ cx231xx_warn
|
|
+ ("device /dev/video%d is open! Deregistration and memory "
|
|
+ "deallocation are deferred on close.\n", dev->vdev->num);
|
|
+
|
|
+ dev->state |= DEV_MISCONFIGURED;
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+ dev->state |= DEV_DISCONNECTED;
|
|
+ wake_up_interruptible(&dev->wait_frame);
|
|
+ wake_up_interruptible(&dev->wait_stream);
|
|
+ } else {
|
|
+ dev->state |= DEV_DISCONNECTED;
|
|
+ cx231xx_release_resources(dev);
|
|
+ }
|
|
+
|
|
+ cx231xx_close_extension(dev);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (!dev->users) {
|
|
+ kfree(dev->video_mode.alt_max_pkt_size);
|
|
+ kfree(dev->vbi_mode.alt_max_pkt_size);
|
|
+ kfree(dev->sliced_cc_mode.alt_max_pkt_size);
|
|
+ kfree(dev->ts1_mode.alt_max_pkt_size);
|
|
+ kfree(dev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct usb_driver cx231xx_usb_driver = {
|
|
+ .name = "cx231xx",
|
|
+ .probe = cx231xx_usb_probe,
|
|
+ .disconnect = cx231xx_usb_disconnect,
|
|
+ .id_table = cx231xx_id_table,
|
|
+};
|
|
+
|
|
+static int __init cx231xx_module_init(void)
|
|
+{
|
|
+ int result;
|
|
+
|
|
+ printk(KERN_INFO DRIVER_NAME " v4l2 driver loaded.\n");
|
|
+
|
|
+ /* register this driver with the USB subsystem */
|
|
+ result = usb_register(&cx231xx_usb_driver);
|
|
+ if (result)
|
|
+ cx231xx_err(DRIVER_NAME
|
|
+ " usb_register failed. Error number %d.\n", result);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static void __exit cx231xx_module_exit(void)
|
|
+{
|
|
+ /* deregister this driver with the USB subsystem */
|
|
+ usb_deregister(&cx231xx_usb_driver);
|
|
+}
|
|
+
|
|
+module_init(cx231xx_module_init);
|
|
+module_exit(cx231xx_module_exit);
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
|
|
new file mode 100644
|
|
index 0000000..a6f398a
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
|
|
@@ -0,0 +1,494 @@
|
|
+/*
|
|
+ cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
|
|
+ video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+
|
|
+ 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.
|
|
+ */
|
|
+
|
|
+#ifndef _POLARIS_REG_H_
|
|
+#define _POLARIS_REG_H_
|
|
+
|
|
+#define BOARD_CFG_STAT 0x0
|
|
+#define TS_MODE_REG 0x4
|
|
+#define TS1_CFG_REG 0x8
|
|
+#define TS1_LENGTH_REG 0xc
|
|
+#define TS2_CFG_REG 0x10
|
|
+#define TS2_LENGTH_REG 0x14
|
|
+#define EP_MODE_SET 0x18
|
|
+#define CIR_PWR_PTN1 0x1c
|
|
+#define CIR_PWR_PTN2 0x20
|
|
+#define CIR_PWR_PTN3 0x24
|
|
+#define CIR_PWR_MASK0 0x28
|
|
+#define CIR_PWR_MASK1 0x2c
|
|
+#define CIR_PWR_MASK2 0x30
|
|
+#define CIR_GAIN 0x34
|
|
+#define CIR_CAR_REG 0x38
|
|
+#define CIR_OT_CFG1 0x40
|
|
+#define CIR_OT_CFG2 0x44
|
|
+#define PWR_CTL_EN 0x74
|
|
+
|
|
+/* Polaris Endpoints capture mask for register EP_MODE_SET */
|
|
+#define ENABLE_EP1 0x01 /* Bit[0]=1 */
|
|
+#define ENABLE_EP2 0x02 /* Bit[1]=1 */
|
|
+#define ENABLE_EP3 0x04 /* Bit[2]=1 */
|
|
+#define ENABLE_EP4 0x08 /* Bit[3]=1 */
|
|
+#define ENABLE_EP5 0x10 /* Bit[4]=1 */
|
|
+#define ENABLE_EP6 0x20 /* Bit[5]=1 */
|
|
+
|
|
+/* Bit definition for register PWR_CTL_EN */
|
|
+#define PWR_MODE_MASK 0x17f
|
|
+#define PWR_AV_EN 0x08 /* bit3 */
|
|
+#define PWR_ISO_EN 0x40 /* bit6 */
|
|
+#define PWR_AV_MODE 0x30 /* bit4,5 */
|
|
+#define PWR_TUNER_EN 0x04 /* bit2 */
|
|
+#define PWR_DEMOD_EN 0x02 /* bit1 */
|
|
+#define I2C_DEMOD_EN 0x01 /* bit0 */
|
|
+#define PWR_RESETOUT_EN 0x100 /* bit8 */
|
|
+
|
|
+enum AV_MODE{
|
|
+ POLARIS_AVMODE_DEFAULT = 0,
|
|
+ POLARIS_AVMODE_DIGITAL = 0x10,
|
|
+ POLARIS_AVMODE_ANALOGT_TV = 0x20,
|
|
+ POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
|
|
+
|
|
+};
|
|
+
|
|
+/* Colibri Registers */
|
|
+
|
|
+#define SINGLE_ENDED 0x0
|
|
+#define LOW_IF 0x4
|
|
+#define EU_IF 0x9
|
|
+#define US_IF 0xa
|
|
+
|
|
+#define SUP_BLK_TUNE1 0x00
|
|
+#define SUP_BLK_TUNE2 0x01
|
|
+#define SUP_BLK_TUNE3 0x02
|
|
+#define SUP_BLK_XTAL 0x03
|
|
+#define SUP_BLK_PLL1 0x04
|
|
+#define SUP_BLK_PLL2 0x05
|
|
+#define SUP_BLK_PLL3 0x06
|
|
+#define SUP_BLK_REF 0x07
|
|
+#define SUP_BLK_PWRDN 0x08
|
|
+#define SUP_BLK_TESTPAD 0x09
|
|
+#define ADC_COM_INT5_STAB_REF 0x0a
|
|
+#define ADC_COM_QUANT 0x0b
|
|
+#define ADC_COM_BIAS1 0x0c
|
|
+#define ADC_COM_BIAS2 0x0d
|
|
+#define ADC_COM_BIAS3 0x0e
|
|
+#define TESTBUS_CTRL 0x12
|
|
+
|
|
+#define FLD_PWRDN_TUNING_BIAS 0x10
|
|
+#define FLD_PWRDN_ENABLE_PLL 0x08
|
|
+#define FLD_PWRDN_PD_BANDGAP 0x04
|
|
+#define FLD_PWRDN_PD_BIAS 0x02
|
|
+#define FLD_PWRDN_PD_TUNECK 0x01
|
|
+
|
|
+
|
|
+#define ADC_STATUS_CH1 0x20
|
|
+#define ADC_STATUS_CH2 0x40
|
|
+#define ADC_STATUS_CH3 0x60
|
|
+
|
|
+#define ADC_STATUS2_CH1 0x21
|
|
+#define ADC_STATUS2_CH2 0x41
|
|
+#define ADC_STATUS2_CH3 0x61
|
|
+
|
|
+#define ADC_CAL_ATEST_CH1 0x22
|
|
+#define ADC_CAL_ATEST_CH2 0x42
|
|
+#define ADC_CAL_ATEST_CH3 0x62
|
|
+
|
|
+#define ADC_PWRDN_CLAMP_CH1 0x23
|
|
+#define ADC_PWRDN_CLAMP_CH2 0x43
|
|
+#define ADC_PWRDN_CLAMP_CH3 0x63
|
|
+
|
|
+#define ADC_CTRL_DAC23_CH1 0x24
|
|
+#define ADC_CTRL_DAC23_CH2 0x44
|
|
+#define ADC_CTRL_DAC23_CH3 0x64
|
|
+
|
|
+#define ADC_CTRL_DAC1_CH1 0x25
|
|
+#define ADC_CTRL_DAC1_CH2 0x45
|
|
+#define ADC_CTRL_DAC1_CH3 0x65
|
|
+
|
|
+#define ADC_DCSERVO_DEM_CH1 0x26
|
|
+#define ADC_DCSERVO_DEM_CH2 0x46
|
|
+#define ADC_DCSERVO_DEM_CH3 0x66
|
|
+
|
|
+#define ADC_FB_FRCRST_CH1 0x27
|
|
+#define ADC_FB_FRCRST_CH2 0x47
|
|
+#define ADC_FB_FRCRST_CH3 0x67
|
|
+
|
|
+#define ADC_INPUT_CH1 0x28
|
|
+#define ADC_INPUT_CH2 0x48
|
|
+#define ADC_INPUT_CH3 0x68
|
|
+#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
|
|
+
|
|
+#define ADC_NTF_PRECLMP_EN_CH1 0x29
|
|
+#define ADC_NTF_PRECLMP_EN_CH2 0x49
|
|
+#define ADC_NTF_PRECLMP_EN_CH3 0x69
|
|
+
|
|
+#define ADC_QGAIN_RES_TRM_CH1 0x2a
|
|
+#define ADC_QGAIN_RES_TRM_CH2 0x4a
|
|
+#define ADC_QGAIN_RES_TRM_CH3 0x6a
|
|
+
|
|
+#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
|
|
+#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
|
|
+#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
|
|
+
|
|
+#define TESTBUS_CTRL_CH1 0x32
|
|
+#define TESTBUS_CTRL_CH2 0x52
|
|
+#define TESTBUS_CTRL_CH3 0x72
|
|
+
|
|
+/******************************************************************************
|
|
+ * DIF registers *
|
|
+ ******************************************************************************/
|
|
+#define DIRECT_IF_REVB_BASE 0x00300
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_PLL_LOCK 0x80000000
|
|
+/* Reserved [30:29] */
|
|
+#define FLD_DIF_PLL_FREE_RUN 0x10000000
|
|
+#define FLD_DIF_PLL_FREQ 0x0fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_KD_PD 0xff000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_DIF_KDS_PD 0x000f0000
|
|
+#define FLD_DIF_KI_PD 0x0000ff00
|
|
+/* Reserved [7:4] */
|
|
+#define FLD_DIF_KIS_PD 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_KD_FD 0xff000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_DIF_KDS_FD 0x000f0000
|
|
+#define FLD_DIF_KI_FD 0x0000ff00
|
|
+#define FLD_DIF_SIG_PROP_SZ 0x000000f0
|
|
+#define FLD_DIF_KIS_FD 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_PLL_AGC_REF 0xfff00000
|
|
+#define FLD_DIF_PLL_AGC_KI 0x000f0000
|
|
+/* Reserved [15] */
|
|
+#define FLD_DIF_FREQ_LIMIT 0x00007000
|
|
+#define FLD_DIF_K_FD 0x00000f00
|
|
+#define FLD_DIF_DOWNSMPL_FD 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:16] */
|
|
+#define FLD_DIF_PLL_AGC_EN 0x00008000
|
|
+/* Reserved [14:12] */
|
|
+#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_K_AGC_RF 0xf0000000
|
|
+#define FLD_DIF_K_AGC_IF 0x0f000000
|
|
+#define FLD_DIF_K_AGC_INT 0x00f00000
|
|
+/* Reserved [19:12] */
|
|
+#define FLD_DIF_IF_REF 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_IF_MAX 0xff000000
|
|
+#define FLD_DIF_IF_MIN 0x00ff0000
|
|
+#define FLD_DIF_IF_AGC 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_INT_MAX 0xff000000
|
|
+#define FLD_DIF_INT_MIN 0x00ff0000
|
|
+#define FLD_DIF_INT_AGC 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_RF_MAX 0xff000000
|
|
+#define FLD_DIF_RF_MIN 0x00ff0000
|
|
+#define FLD_DIF_RF_AGC 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_IF_AGC_IN 0xffff0000
|
|
+#define FLD_DIF_INT_AGC_IN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:16] */
|
|
+#define FLD_DIF_RF_AGC_IN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_AFD 0xc0000000
|
|
+#define FLD_DIF_K_VID_AGC 0x30000000
|
|
+#define FLD_DIF_LINE_LENGTH 0x0fff0000
|
|
+#define FLD_DIF_AGC_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
|
|
+/* Reserved [30:30] */
|
|
+#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
|
|
+/* Reserved [23:17] */
|
|
+#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
|
|
+#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_LPF_FREQ 0xc0000000
|
|
+#define FLD_DIF_AV_PHASE_INC 0x3f000000
|
|
+#define FLD_DIF_AUDIO_FREQ 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:24] */
|
|
+#define FLD_DIF_IIR23_R2 0x00ff0000
|
|
+#define FLD_DIF_IIR23_R1 0x0000ff00
|
|
+#define FLD_DIF_IIR1_R1 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
|
|
+/*****************************************************************************/
|
|
+#define FLD_DIF_DIF_BYPASS 0x80000000
|
|
+#define FLD_DIF_FM_NYQ_GAIN 0x40000000
|
|
+#define FLD_DIF_RF_AGC_ENA 0x20000000
|
|
+#define FLD_DIF_INT_AGC_ENA 0x10000000
|
|
+#define FLD_DIF_IF_AGC_ENA 0x08000000
|
|
+#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
|
|
+#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
|
|
+#define FLD_DIF_RF_AGC_INV 0x01000000
|
|
+#define FLD_DIF_INT_AGC_INV 0x00800000
|
|
+#define FLD_DIF_IF_AGC_INV 0x00400000
|
|
+#define FLD_DIF_SPEC_INV 0x00200000
|
|
+#define FLD_DIF_AUD_FULL_BW 0x00100000
|
|
+#define FLD_DIF_AUD_SRC_SEL 0x00080000
|
|
+/* Reserved [18] */
|
|
+#define FLD_DIF_IF_FREQ 0x00030000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_TIP_OFFSET 0x00003f00
|
|
+/* Reserved [7:5] */
|
|
+#define FLD_DIF_DITHER_ENA 0x00000010
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_DIF_RF_IF_LOCK 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_DIF_PHASE_INC 0x1fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:16] */
|
|
+#define FLD_DIF_SRC_KI 0x0000ff00
|
|
+#define FLD_DIF_SRC_KD 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:19] */
|
|
+#define FLD_DIF_BPF_COEFF_0 0x00070000
|
|
+/* Reserved [15:4] */
|
|
+#define FLD_DIF_BPF_COEFF_1 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:22] */
|
|
+#define FLD_DIF_BPF_COEFF_2 0x003f0000
|
|
+/* Reserved [15:7] */
|
|
+#define FLD_DIF_BPF_COEFF_3 0x0000007f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:24] */
|
|
+#define FLD_DIF_BPF_COEFF_4 0x00ff0000
|
|
+/* Reserved [15:8] */
|
|
+#define FLD_DIF_BPF_COEFF_5 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:25] */
|
|
+#define FLD_DIF_BPF_COEFF_6 0x01ff0000
|
|
+/* Reserved [15:9] */
|
|
+#define FLD_DIF_BPF_COEFF_7 0x000001ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:26] */
|
|
+#define FLD_DIF_BPF_COEFF_8 0x03ff0000
|
|
+/* Reserved [15:10] */
|
|
+#define FLD_DIF_BPF_COEFF_9 0x000003ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:27] */
|
|
+#define FLD_DIF_BPF_COEFF_10 0x07ff0000
|
|
+/* Reserved [15:11] */
|
|
+#define FLD_DIF_BPF_COEFF_11 0x000007ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:27] */
|
|
+#define FLD_DIF_BPF_COEFF_12 0x07ff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_DIF_BPF_COEFF_13 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_DIF_BPF_COEFF_14 0x0fff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_DIF_BPF_COEFF_15 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_DIF_BPF_COEFF_16 0x1fff0000
|
|
+/* Reserved [15:13] */
|
|
+#define FLD_DIF_BPF_COEFF_17 0x00001fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_DIF_BPF_COEFF_18 0x1fff0000
|
|
+/* Reserved [15:13] */
|
|
+#define FLD_DIF_BPF_COEFF_19 0x00001fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_DIF_BPF_COEFF_20 0x1fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_21 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_22 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_23 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_24 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_25 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_26 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_27 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_28 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_29 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_30 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_31 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_32 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_33 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_34 0x3fff0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_DIF_BPF_COEFF_35 0x00003fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_DIF_BPF_COEFF_36 0x3fff0000
|
|
+/* Reserved [15:0] */
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:20] */
|
|
+#define FLD_DIF_RPT_VARIANCE 0x000fffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:8] */
|
|
+#define FLD_DIF_DIF_SOFT_RST 0x00000080
|
|
+#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
|
|
+#define FLD_DIF_AGC_RST_MSK 0x00000020
|
|
+#define FLD_DIF_CMP_RST_MSK 0x00000010
|
|
+#define FLD_DIF_AVS_RST_MSK 0x00000008
|
|
+#define FLD_DIF_NYQ_RST_MSK 0x00000004
|
|
+#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
|
|
+#define FLD_DIF_PLL_RST_MSK 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
|
|
+/*****************************************************************************/
|
|
+/* Reserved [31:25] */
|
|
+#define FLD_DIF_CTL_IP 0x01ffffff
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
|
|
new file mode 100644
|
|
index 0000000..0d333e6
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
|
|
@@ -0,0 +1,1200 @@
|
|
+/*
|
|
+ cx231xx-core.c - driver for Conexant Cx23100/101/102
|
|
+ USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <media/v4l2-common.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+#include "cx231xx-reg.h"
|
|
+
|
|
+/* #define ENABLE_DEBUG_ISOC_FRAMES */
|
|
+
|
|
+static unsigned int core_debug;
|
|
+module_param(core_debug, int, 0644);
|
|
+MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
|
|
+
|
|
+#define cx231xx_coredbg(fmt, arg...) do {\
|
|
+ if (core_debug) \
|
|
+ printk(KERN_INFO "%s %s :"fmt, \
|
|
+ dev->name, __func__ , ##arg); } while (0)
|
|
+
|
|
+static unsigned int reg_debug;
|
|
+module_param(reg_debug, int, 0644);
|
|
+MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
|
|
+
|
|
+#define cx231xx_regdbg(fmt, arg...) do {\
|
|
+ if (reg_debug) \
|
|
+ printk(KERN_INFO "%s %s :"fmt, \
|
|
+ dev->name, __func__ , ##arg); } while (0)
|
|
+
|
|
+static int alt = CX231XX_PINOUT;
|
|
+module_param(alt, int, 0644);
|
|
+MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
|
|
+
|
|
+#define cx231xx_isocdbg(fmt, arg...) do {\
|
|
+ if (core_debug) \
|
|
+ printk(KERN_INFO "%s %s :"fmt, \
|
|
+ dev->name, __func__ , ##arg); } while (0)
|
|
+
|
|
+/*****************************************************************
|
|
+* Device control list functions *
|
|
+******************************************************************/
|
|
+
|
|
+static LIST_HEAD(cx231xx_devlist);
|
|
+static DEFINE_MUTEX(cx231xx_devlist_mutex);
|
|
+
|
|
+struct cx231xx *cx231xx_get_device(int minor,
|
|
+ enum v4l2_buf_type *fh_type, int *has_radio)
|
|
+{
|
|
+ struct cx231xx *h, *dev = NULL;
|
|
+
|
|
+ *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+ *has_radio = 0;
|
|
+
|
|
+ mutex_lock(&cx231xx_devlist_mutex);
|
|
+ list_for_each_entry(h, &cx231xx_devlist, devlist) {
|
|
+ if (h->vdev->minor == minor)
|
|
+ dev = h;
|
|
+ if (h->vbi_dev->minor == minor) {
|
|
+ dev = h;
|
|
+ *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
|
|
+ }
|
|
+ if (h->radio_dev && h->radio_dev->minor == minor) {
|
|
+ dev = h;
|
|
+ *has_radio = 1;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&cx231xx_devlist_mutex);
|
|
+
|
|
+ return dev;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_realease_resources()
|
|
+ * unregisters the v4l2,i2c and usb devices
|
|
+ * called when the device gets disconected or at module unload
|
|
+*/
|
|
+void cx231xx_remove_from_devlist(struct cx231xx *dev)
|
|
+{
|
|
+ mutex_lock(&cx231xx_devlist_mutex);
|
|
+ list_del(&dev->devlist);
|
|
+ mutex_unlock(&cx231xx_devlist_mutex);
|
|
+};
|
|
+
|
|
+void cx231xx_add_into_devlist(struct cx231xx *dev)
|
|
+{
|
|
+ mutex_lock(&cx231xx_devlist_mutex);
|
|
+ list_add_tail(&dev->devlist, &cx231xx_devlist);
|
|
+ mutex_unlock(&cx231xx_devlist_mutex);
|
|
+};
|
|
+
|
|
+static LIST_HEAD(cx231xx_extension_devlist);
|
|
+static DEFINE_MUTEX(cx231xx_extension_devlist_lock);
|
|
+
|
|
+int cx231xx_register_extension(struct cx231xx_ops *ops)
|
|
+{
|
|
+ struct cx231xx *dev = NULL;
|
|
+
|
|
+ mutex_lock(&cx231xx_devlist_mutex);
|
|
+ mutex_lock(&cx231xx_extension_devlist_lock);
|
|
+ list_add_tail(&ops->next, &cx231xx_extension_devlist);
|
|
+ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
|
|
+ if (dev)
|
|
+ ops->init(dev);
|
|
+ }
|
|
+ printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
|
|
+ mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
+ mutex_unlock(&cx231xx_devlist_mutex);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(cx231xx_register_extension);
|
|
+
|
|
+void cx231xx_unregister_extension(struct cx231xx_ops *ops)
|
|
+{
|
|
+ struct cx231xx *dev = NULL;
|
|
+
|
|
+ mutex_lock(&cx231xx_devlist_mutex);
|
|
+ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
|
|
+ if (dev)
|
|
+ ops->fini(dev);
|
|
+ }
|
|
+
|
|
+ mutex_lock(&cx231xx_extension_devlist_lock);
|
|
+ printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
|
|
+ list_del(&ops->next);
|
|
+ mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
+ mutex_unlock(&cx231xx_devlist_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL(cx231xx_unregister_extension);
|
|
+
|
|
+void cx231xx_init_extension(struct cx231xx *dev)
|
|
+{
|
|
+ struct cx231xx_ops *ops = NULL;
|
|
+
|
|
+ mutex_lock(&cx231xx_extension_devlist_lock);
|
|
+ if (!list_empty(&cx231xx_extension_devlist)) {
|
|
+ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
|
|
+ if (ops->init)
|
|
+ ops->init(dev);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
+}
|
|
+
|
|
+void cx231xx_close_extension(struct cx231xx *dev)
|
|
+{
|
|
+ struct cx231xx_ops *ops = NULL;
|
|
+
|
|
+ mutex_lock(&cx231xx_extension_devlist_lock);
|
|
+ if (!list_empty(&cx231xx_extension_devlist)) {
|
|
+ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
|
|
+ if (ops->fini)
|
|
+ ops->fini(dev);
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&cx231xx_extension_devlist_lock);
|
|
+}
|
|
+
|
|
+/****************************************************************
|
|
+* U S B related functions *
|
|
+*****************************************************************/
|
|
+int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
|
|
+ struct cx231xx_i2c_xfer_data *req_data)
|
|
+{
|
|
+ int status = 0;
|
|
+ struct cx231xx *dev = i2c_bus->dev;
|
|
+ struct VENDOR_REQUEST_IN ven_req;
|
|
+
|
|
+ u8 saddr_len = 0;
|
|
+ u8 _i2c_period = 0;
|
|
+ u8 _i2c_nostop = 0;
|
|
+ u8 _i2c_reserve = 0;
|
|
+
|
|
+ /* Get the I2C period, nostop and reserve parameters */
|
|
+ _i2c_period = i2c_bus->i2c_period;
|
|
+ _i2c_nostop = i2c_bus->i2c_nostop;
|
|
+ _i2c_reserve = i2c_bus->i2c_reserve;
|
|
+
|
|
+ saddr_len = req_data->saddr_len;
|
|
+
|
|
+ /* Set wValue */
|
|
+ if (saddr_len == 1) /* need check saddr_len == 0 */
|
|
+ ven_req.wValue =
|
|
+ req_data->
|
|
+ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
|
|
+ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
|
|
+ else
|
|
+ ven_req.wValue =
|
|
+ req_data->
|
|
+ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
|
|
+ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
|
|
+
|
|
+ /* set channel number */
|
|
+ if (req_data->direction & I2C_M_RD) {
|
|
+ /* channel number, for read,spec required channel_num +4 */
|
|
+ ven_req.bRequest = i2c_bus->nr + 4;
|
|
+ } else
|
|
+ ven_req.bRequest = i2c_bus->nr; /* channel number, */
|
|
+
|
|
+ /* set index value */
|
|
+ switch (saddr_len) {
|
|
+ case 0:
|
|
+ ven_req.wIndex = 0; /* need check */
|
|
+ break;
|
|
+ case 1:
|
|
+ ven_req.wIndex = (req_data->saddr_dat & 0xff);
|
|
+ break;
|
|
+ case 2:
|
|
+ ven_req.wIndex = req_data->saddr_dat;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* set wLength value */
|
|
+ ven_req.wLength = req_data->buf_size;
|
|
+
|
|
+ /* set bData value */
|
|
+ ven_req.bData = 0;
|
|
+
|
|
+ /* set the direction */
|
|
+ if (req_data->direction) {
|
|
+ ven_req.direction = USB_DIR_IN;
|
|
+ memset(req_data->p_buffer, 0x00, ven_req.wLength);
|
|
+ } else
|
|
+ ven_req.direction = USB_DIR_OUT;
|
|
+
|
|
+ /* set the buffer for read / write */
|
|
+ ven_req.pBuff = req_data->p_buffer;
|
|
+
|
|
+
|
|
+ /* call common vendor command request */
|
|
+ status = cx231xx_send_vendor_cmd(dev, &ven_req);
|
|
+ if (status < 0) {
|
|
+ cx231xx_info
|
|
+ ("UsbInterface::sendCommand, failed with status -%d\n",
|
|
+ status);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_send_usb_command);
|
|
+
|
|
+/*
|
|
+ * cx231xx_read_ctrl_reg()
|
|
+ * reads data from the usb device specifying bRequest and wValue
|
|
+ */
|
|
+int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
|
|
+ char *buf, int len)
|
|
+{
|
|
+ u8 val = 0;
|
|
+ int ret;
|
|
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
|
|
+
|
|
+ if (dev->state & DEV_DISCONNECTED)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (len > URB_MAX_CTRL_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ val = ENABLE_ONE_BYTE;
|
|
+ break;
|
|
+ case 2:
|
|
+ val = ENABLE_TWE_BYTE;
|
|
+ break;
|
|
+ case 3:
|
|
+ val = ENABLE_THREE_BYTE;
|
|
+ break;
|
|
+ case 4:
|
|
+ val = ENABLE_FOUR_BYTE;
|
|
+ break;
|
|
+ default:
|
|
+ val = 0xFF; /* invalid option */
|
|
+ }
|
|
+
|
|
+ if (val == 0xFF)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (reg_debug) {
|
|
+ cx231xx_isocdbg("(pipe 0x%08x): "
|
|
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
|
|
+ pipe,
|
|
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ req, 0, val,
|
|
+ reg & 0xff, reg >> 8, len & 0xff, len >> 8);
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->ctrl_urb_lock);
|
|
+ ret = usb_control_msg(dev->udev, pipe, req,
|
|
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ val, reg, dev->urb_buf, len, HZ);
|
|
+ if (ret < 0) {
|
|
+ cx231xx_isocdbg(" failed!\n");
|
|
+ /* mutex_unlock(&dev->ctrl_urb_lock); */
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (len)
|
|
+ memcpy(buf, dev->urb_buf, len);
|
|
+
|
|
+ mutex_unlock(&dev->ctrl_urb_lock);
|
|
+
|
|
+ if (reg_debug) {
|
|
+ int byte;
|
|
+
|
|
+ cx231xx_isocdbg("<<<");
|
|
+ for (byte = 0; byte < len; byte++)
|
|
+ cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
|
|
+ cx231xx_isocdbg("\n");
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int cx231xx_send_vendor_cmd(struct cx231xx *dev,
|
|
+ struct VENDOR_REQUEST_IN *ven_req)
|
|
+{
|
|
+ int ret;
|
|
+ int pipe = 0;
|
|
+
|
|
+ if (dev->state & DEV_DISCONNECTED)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if ((ven_req->wLength > URB_MAX_CTRL_SIZE))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (ven_req->direction)
|
|
+ pipe = usb_rcvctrlpipe(dev->udev, 0);
|
|
+ else
|
|
+ pipe = usb_sndctrlpipe(dev->udev, 0);
|
|
+
|
|
+ if (reg_debug) {
|
|
+ int byte;
|
|
+
|
|
+ cx231xx_isocdbg("(pipe 0x%08x): "
|
|
+ "OUT: %02x %02x %02x %04x %04x %04x >>>",
|
|
+ pipe,
|
|
+ ven_req->
|
|
+ direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ ven_req->bRequest, 0, ven_req->wValue,
|
|
+ ven_req->wIndex, ven_req->wLength);
|
|
+
|
|
+ for (byte = 0; byte < ven_req->wLength; byte++)
|
|
+ cx231xx_isocdbg(" %02x",
|
|
+ (unsigned char)ven_req->pBuff[byte]);
|
|
+ cx231xx_isocdbg("\n");
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->ctrl_urb_lock);
|
|
+ ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest,
|
|
+ ven_req->
|
|
+ direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ ven_req->wValue, ven_req->wIndex, ven_req->pBuff,
|
|
+ ven_req->wLength, HZ);
|
|
+ mutex_unlock(&dev->ctrl_urb_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_write_ctrl_reg()
|
|
+ * sends data to the usb device, specifying bRequest
|
|
+ */
|
|
+int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf,
|
|
+ int len)
|
|
+{
|
|
+ u8 val = 0;
|
|
+ int ret;
|
|
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
|
|
+
|
|
+ if (dev->state & DEV_DISCONNECTED)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
|
|
+ return -EINVAL;
|
|
+
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ val = ENABLE_ONE_BYTE;
|
|
+ break;
|
|
+ case 2:
|
|
+ val = ENABLE_TWE_BYTE;
|
|
+ break;
|
|
+ case 3:
|
|
+ val = ENABLE_THREE_BYTE;
|
|
+ break;
|
|
+ case 4:
|
|
+ val = ENABLE_FOUR_BYTE;
|
|
+ break;
|
|
+ default:
|
|
+ val = 0xFF; /* invalid option */
|
|
+ }
|
|
+
|
|
+ if (val == 0xFF)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (reg_debug) {
|
|
+ int byte;
|
|
+
|
|
+ cx231xx_isocdbg("(pipe 0x%08x): "
|
|
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
|
|
+ pipe,
|
|
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ req, 0, val, reg & 0xff,
|
|
+ reg >> 8, len & 0xff, len >> 8);
|
|
+
|
|
+ for (byte = 0; byte < len; byte++)
|
|
+ cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
|
|
+ cx231xx_isocdbg("\n");
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->ctrl_urb_lock);
|
|
+ memcpy(dev->urb_buf, buf, len);
|
|
+ ret = usb_control_msg(dev->udev, pipe, req,
|
|
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
+ val, reg, dev->urb_buf, len, HZ);
|
|
+ mutex_unlock(&dev->ctrl_urb_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/****************************************************************
|
|
+* USB Alternate Setting functions *
|
|
+*****************************************************************/
|
|
+
|
|
+int cx231xx_set_video_alternate(struct cx231xx *dev)
|
|
+{
|
|
+ int errCode, prev_alt = dev->video_mode.alt;
|
|
+ unsigned int min_pkt_size = dev->width * 2 + 4;
|
|
+ u32 usb_interface_index = 0;
|
|
+
|
|
+ /* When image size is bigger than a certain value,
|
|
+ the frame size should be increased, otherwise, only
|
|
+ green screen will be received.
|
|
+ */
|
|
+ if (dev->width * 2 * dev->height > 720 * 240 * 2)
|
|
+ min_pkt_size *= 2;
|
|
+
|
|
+ if (dev->width > 360) {
|
|
+ /* resolutions: 720,704,640 */
|
|
+ dev->video_mode.alt = 3;
|
|
+ } else if (dev->width > 180) {
|
|
+ /* resolutions: 360,352,320,240 */
|
|
+ dev->video_mode.alt = 2;
|
|
+ } else if (dev->width > 0) {
|
|
+ /* resolutions: 180,176,160,128,88 */
|
|
+ dev->video_mode.alt = 1;
|
|
+ } else {
|
|
+ /* Change to alt0 BULK to release USB bandwidth */
|
|
+ dev->video_mode.alt = 0;
|
|
+ }
|
|
+
|
|
+ /* Get the correct video interface Index */
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ video_index + 1;
|
|
+
|
|
+ if (dev->video_mode.alt != prev_alt) {
|
|
+ cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
|
|
+ min_pkt_size, dev->video_mode.alt);
|
|
+ dev->video_mode.max_pkt_size =
|
|
+ dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
|
|
+ cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
|
|
+ dev->video_mode.alt,
|
|
+ dev->video_mode.max_pkt_size);
|
|
+ cx231xx_info
|
|
+ (" setting alt %d with wMaxPktSize=%u , Interface = %d\n",
|
|
+ dev->video_mode.alt, dev->video_mode.max_pkt_size,
|
|
+ usb_interface_index);
|
|
+ errCode =
|
|
+ usb_set_interface(dev->udev, usb_interface_index,
|
|
+ dev->video_mode.alt);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("cannot change alt number to %d (error=%i)\n",
|
|
+ dev->video_mode.alt, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 usb_interface_index = 0;
|
|
+ u32 max_pkt_size = 0;
|
|
+
|
|
+ switch (index) {
|
|
+ case INDEX_TS1:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ ts1_index + 1;
|
|
+ dev->video_mode.alt = alt;
|
|
+ if (dev->ts1_mode.alt_max_pkt_size != NULL)
|
|
+ max_pkt_size = dev->ts1_mode.max_pkt_size =
|
|
+ dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt];
|
|
+ break;
|
|
+ case INDEX_TS2:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ ts2_index + 1;
|
|
+ break;
|
|
+ case INDEX_AUDIO:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ audio_index + 1;
|
|
+ dev->adev.alt = alt;
|
|
+ if (dev->adev.alt_max_pkt_size != NULL)
|
|
+ max_pkt_size = dev->adev.max_pkt_size =
|
|
+ dev->adev.alt_max_pkt_size[dev->adev.alt];
|
|
+ break;
|
|
+ case INDEX_VIDEO:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ video_index + 1;
|
|
+ dev->video_mode.alt = alt;
|
|
+ if (dev->video_mode.alt_max_pkt_size != NULL)
|
|
+ max_pkt_size = dev->video_mode.max_pkt_size =
|
|
+ dev->video_mode.alt_max_pkt_size[dev->video_mode.
|
|
+ alt];
|
|
+ break;
|
|
+ case INDEX_VANC:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ vanc_index + 1;
|
|
+ dev->vbi_mode.alt = alt;
|
|
+ if (dev->vbi_mode.alt_max_pkt_size != NULL)
|
|
+ max_pkt_size = dev->vbi_mode.max_pkt_size =
|
|
+ dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt];
|
|
+ break;
|
|
+ case INDEX_HANC:
|
|
+ usb_interface_index =
|
|
+ dev->current_pcb_config.hs_config_info[0].interface_info.
|
|
+ hanc_index + 1;
|
|
+ dev->sliced_cc_mode.alt = alt;
|
|
+ if (dev->sliced_cc_mode.alt_max_pkt_size != NULL)
|
|
+ max_pkt_size = dev->sliced_cc_mode.max_pkt_size =
|
|
+ dev->sliced_cc_mode.alt_max_pkt_size[dev->
|
|
+ sliced_cc_mode.
|
|
+ alt];
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (alt > 0 && max_pkt_size == 0) {
|
|
+ cx231xx_errdev
|
|
+ ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
|
|
+ usb_interface_index, alt);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ cx231xx_info
|
|
+ (" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n",
|
|
+ alt, max_pkt_size, usb_interface_index);
|
|
+
|
|
+ if (usb_interface_index > 0) {
|
|
+ status = usb_set_interface(dev->udev, usb_interface_index, alt);
|
|
+ if (status < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("can't change interface %d alt no. to %d (err=%i)\n",
|
|
+ usb_interface_index, alt, status);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting);
|
|
+
|
|
+int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio)
|
|
+{
|
|
+ int rc = 0;
|
|
+
|
|
+ if (!gpio)
|
|
+ return rc;
|
|
+
|
|
+ /* Send GPIO reset sequences specified at board entry */
|
|
+ while (gpio->sleep >= 0) {
|
|
+ rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if (gpio->sleep > 0)
|
|
+ msleep(gpio->sleep);
|
|
+
|
|
+ gpio++;
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
|
|
+{
|
|
+ if (dev->mode == set_mode)
|
|
+ return 0;
|
|
+
|
|
+ if (set_mode == CX231XX_SUSPEND) {
|
|
+ /* Set the chip in power saving mode */
|
|
+ dev->mode = set_mode;
|
|
+ }
|
|
+
|
|
+ /* Resource is locked */
|
|
+ if (dev->mode != CX231XX_SUSPEND)
|
|
+ return -EINVAL;
|
|
+
|
|
+ dev->mode = set_mode;
|
|
+
|
|
+ if (dev->mode == CX231XX_DIGITAL_MODE)
|
|
+ ;/* Set Digital power mode */
|
|
+ else
|
|
+ ;/* Set Analog Power mode */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_set_mode);
|
|
+
|
|
+/*****************************************************************
|
|
+* URB Streaming functions *
|
|
+******************************************************************/
|
|
+
|
|
+/*
|
|
+ * IRQ callback, called by URB callback
|
|
+ */
|
|
+static void cx231xx_irq_callback(struct urb *urb)
|
|
+{
|
|
+ struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
+ struct cx231xx_video_mode *vmode =
|
|
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
|
|
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
|
|
+ int rc, i;
|
|
+
|
|
+ switch (urb->status) {
|
|
+ case 0: /* success */
|
|
+ case -ETIMEDOUT: /* NAK */
|
|
+ break;
|
|
+ case -ECONNRESET: /* kill */
|
|
+ case -ENOENT:
|
|
+ case -ESHUTDOWN:
|
|
+ return;
|
|
+ default: /* error */
|
|
+ cx231xx_isocdbg("urb completition error %d.\n", urb->status);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Copy data from URB */
|
|
+ spin_lock(&dev->video_mode.slock);
|
|
+ rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
|
|
+ spin_unlock(&dev->video_mode.slock);
|
|
+
|
|
+ /* Reset urb buffers */
|
|
+ for (i = 0; i < urb->number_of_packets; i++) {
|
|
+ urb->iso_frame_desc[i].status = 0;
|
|
+ urb->iso_frame_desc[i].actual_length = 0;
|
|
+ }
|
|
+ urb->status = 0;
|
|
+
|
|
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
|
|
+ if (urb->status) {
|
|
+ cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
|
|
+ urb->status);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Stop and Deallocate URBs
|
|
+ */
|
|
+void cx231xx_uninit_isoc(struct cx231xx *dev)
|
|
+{
|
|
+ struct urb *urb;
|
|
+ int i;
|
|
+
|
|
+ cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n");
|
|
+
|
|
+ dev->video_mode.isoc_ctl.nfields = -1;
|
|
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
|
+ urb = dev->video_mode.isoc_ctl.urb[i];
|
|
+ if (urb) {
|
|
+ if (!irqs_disabled())
|
|
+ usb_kill_urb(urb);
|
|
+ else
|
|
+ usb_unlink_urb(urb);
|
|
+
|
|
+ if (dev->video_mode.isoc_ctl.transfer_buffer[i]) {
|
|
+ usb_buffer_free(dev->udev,
|
|
+ urb->transfer_buffer_length,
|
|
+ dev->video_mode.isoc_ctl.
|
|
+ transfer_buffer[i],
|
|
+ urb->transfer_dma);
|
|
+ }
|
|
+ usb_free_urb(urb);
|
|
+ dev->video_mode.isoc_ctl.urb[i] = NULL;
|
|
+ }
|
|
+ dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL;
|
|
+ }
|
|
+
|
|
+ kfree(dev->video_mode.isoc_ctl.urb);
|
|
+ kfree(dev->video_mode.isoc_ctl.transfer_buffer);
|
|
+
|
|
+ dev->video_mode.isoc_ctl.urb = NULL;
|
|
+ dev->video_mode.isoc_ctl.transfer_buffer = NULL;
|
|
+ dev->video_mode.isoc_ctl.num_bufs = 0;
|
|
+
|
|
+ cx231xx_capture_start(dev, 0, Raw_Video);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
|
|
+
|
|
+/*
|
|
+ * Allocate URBs and start IRQ
|
|
+ */
|
|
+int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
|
|
+ int num_bufs, int max_pkt_size,
|
|
+ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb))
|
|
+{
|
|
+ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
|
|
+ int i;
|
|
+ int sb_size, pipe;
|
|
+ struct urb *urb;
|
|
+ int j, k;
|
|
+ int rc;
|
|
+
|
|
+ cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n");
|
|
+
|
|
+ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
|
|
+
|
|
+ cx231xx_info("Setting Video mux to %d\n", dev->video_input);
|
|
+ video_mux(dev, dev->video_input);
|
|
+
|
|
+ /* De-allocates all pending stuff */
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+
|
|
+ dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
|
|
+ dev->video_mode.isoc_ctl.num_bufs = num_bufs;
|
|
+ dma_q->pos = 0;
|
|
+ dma_q->is_partial_line = 0;
|
|
+ dma_q->last_sav = 0;
|
|
+ dma_q->current_field = -1;
|
|
+ dma_q->field1_done = 0;
|
|
+ dma_q->lines_per_field = dev->height / 2;
|
|
+ dma_q->bytes_left_in_line = dev->width << 1;
|
|
+ dma_q->lines_completed = 0;
|
|
+ for (i = 0; i < 8; i++)
|
|
+ dma_q->partial_buf[i] = 0;
|
|
+
|
|
+ dev->video_mode.isoc_ctl.urb =
|
|
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
|
+ if (!dev->video_mode.isoc_ctl.urb) {
|
|
+ cx231xx_errdev("cannot alloc memory for usb buffers\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dev->video_mode.isoc_ctl.transfer_buffer =
|
|
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
|
+ if (!dev->video_mode.isoc_ctl.transfer_buffer) {
|
|
+ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
|
|
+ kfree(dev->video_mode.isoc_ctl.urb);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size;
|
|
+ dev->video_mode.isoc_ctl.buf = NULL;
|
|
+
|
|
+ sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size;
|
|
+
|
|
+ /* allocate urbs and transfer buffers */
|
|
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
|
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
|
|
+ if (!urb) {
|
|
+ cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i);
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dev->video_mode.isoc_ctl.urb[i] = urb;
|
|
+
|
|
+ dev->video_mode.isoc_ctl.transfer_buffer[i] =
|
|
+ usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL,
|
|
+ &urb->transfer_dma);
|
|
+ if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) {
|
|
+ cx231xx_err("unable to allocate %i bytes for transfer"
|
|
+ " buffer %i%s\n",
|
|
+ sb_size, i,
|
|
+ in_interrupt() ? " while in int" : "");
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size);
|
|
+
|
|
+ pipe =
|
|
+ usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr);
|
|
+
|
|
+ usb_fill_int_urb(urb, dev->udev, pipe,
|
|
+ dev->video_mode.isoc_ctl.transfer_buffer[i],
|
|
+ sb_size, cx231xx_irq_callback, dma_q, 1);
|
|
+
|
|
+ urb->number_of_packets = max_packets;
|
|
+ urb->transfer_flags = URB_ISO_ASAP;
|
|
+
|
|
+ k = 0;
|
|
+ for (j = 0; j < max_packets; j++) {
|
|
+ urb->iso_frame_desc[j].offset = k;
|
|
+ urb->iso_frame_desc[j].length =
|
|
+ dev->video_mode.isoc_ctl.max_pkt_size;
|
|
+ k += dev->video_mode.isoc_ctl.max_pkt_size;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ init_waitqueue_head(&dma_q->wq);
|
|
+
|
|
+ /* submit urbs and enables IRQ */
|
|
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
|
|
+ rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i],
|
|
+ GFP_ATOMIC);
|
|
+ if (rc) {
|
|
+ cx231xx_err("submit of urb %i failed (error=%i)\n", i,
|
|
+ rc);
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cx231xx_capture_start(dev, 1, Raw_Video);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_init_isoc);
|
|
+
|
|
+/*****************************************************************
|
|
+* Device Init/UnInit functions *
|
|
+******************************************************************/
|
|
+int cx231xx_dev_init(struct cx231xx *dev)
|
|
+{
|
|
+ int errCode = 0;
|
|
+
|
|
+ /* Initialize I2C bus */
|
|
+
|
|
+ /* External Master 1 Bus */
|
|
+ dev->i2c_bus[0].nr = 0;
|
|
+ dev->i2c_bus[0].dev = dev;
|
|
+ dev->i2c_bus[0].i2c_period = I2C_SPEED_1M; /* 1MHz */
|
|
+ dev->i2c_bus[0].i2c_nostop = 0;
|
|
+ dev->i2c_bus[0].i2c_reserve = 0;
|
|
+
|
|
+ /* External Master 2 Bus */
|
|
+ dev->i2c_bus[1].nr = 1;
|
|
+ dev->i2c_bus[1].dev = dev;
|
|
+ dev->i2c_bus[1].i2c_period = I2C_SPEED_1M; /* 1MHz */
|
|
+ dev->i2c_bus[1].i2c_nostop = 0;
|
|
+ dev->i2c_bus[1].i2c_reserve = 0;
|
|
+
|
|
+ /* Internal Master 3 Bus */
|
|
+ dev->i2c_bus[2].nr = 2;
|
|
+ dev->i2c_bus[2].dev = dev;
|
|
+ dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */
|
|
+ dev->i2c_bus[2].i2c_nostop = 0;
|
|
+ dev->i2c_bus[2].i2c_reserve = 0;
|
|
+
|
|
+ /* register I2C buses */
|
|
+ cx231xx_i2c_register(&dev->i2c_bus[0]);
|
|
+ cx231xx_i2c_register(&dev->i2c_bus[1]);
|
|
+ cx231xx_i2c_register(&dev->i2c_bus[2]);
|
|
+
|
|
+ /* init hardware */
|
|
+ /* Note : with out calling set power mode function,
|
|
+ afe can not be set up correctly */
|
|
+ errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: Failed to set Power - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* initialize Colibri block */
|
|
+ errCode = cx231xx_afe_init_super_block(dev, 0x23c);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: cx231xx_afe init super block - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+ errCode = cx231xx_afe_init_channels(dev);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: cx231xx_afe init channels - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* Set DIF in By pass mode */
|
|
+ errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* I2S block related functions */
|
|
+ errCode = cx231xx_i2s_blk_initialize(dev);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: cx231xx_i2s block initialize - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* init control pins */
|
|
+ errCode = cx231xx_init_ctrl_pin_status(dev);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* set AGC mode to Analog */
|
|
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
|
|
+ __func__, errCode);
|
|
+ return errCode;
|
|
+ }
|
|
+
|
|
+ /* set all alternate settings to zero initially */
|
|
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
|
|
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
|
+ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
|
|
+ if (dev->board.has_dvb)
|
|
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
|
|
+
|
|
+ /* set the I2C master port to 3 on channel 1 */
|
|
+ errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3);
|
|
+
|
|
+ return errCode;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_dev_init);
|
|
+
|
|
+void cx231xx_dev_uninit(struct cx231xx *dev)
|
|
+{
|
|
+ /* Un Initialize I2C bus */
|
|
+ cx231xx_i2c_unregister(&dev->i2c_bus[2]);
|
|
+ cx231xx_i2c_unregister(&dev->i2c_bus[1]);
|
|
+ cx231xx_i2c_unregister(&dev->i2c_bus[0]);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_dev_uninit);
|
|
+
|
|
+/*****************************************************************
|
|
+* G P I O related functions *
|
|
+******************************************************************/
|
|
+int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val,
|
|
+ u8 len, u8 request, u8 direction)
|
|
+{
|
|
+ int status = 0;
|
|
+ struct VENDOR_REQUEST_IN ven_req;
|
|
+
|
|
+ /* Set wValue */
|
|
+ ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff);
|
|
+
|
|
+ /* set request */
|
|
+ if (!request) {
|
|
+ if (direction)
|
|
+ ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */
|
|
+ else
|
|
+ ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */
|
|
+ } else {
|
|
+ if (direction)
|
|
+ ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */
|
|
+ else
|
|
+ ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */
|
|
+ }
|
|
+
|
|
+ /* set index value */
|
|
+ ven_req.wIndex = (u16) (gpio_bit & 0xffff);
|
|
+
|
|
+ /* set wLength value */
|
|
+ ven_req.wLength = len;
|
|
+
|
|
+ /* set bData value */
|
|
+ ven_req.bData = 0;
|
|
+
|
|
+ /* set the buffer for read / write */
|
|
+ ven_req.pBuff = gpio_val;
|
|
+
|
|
+ /* set the direction */
|
|
+ if (direction) {
|
|
+ ven_req.direction = USB_DIR_IN;
|
|
+ memset(ven_req.pBuff, 0x00, ven_req.wLength);
|
|
+ } else
|
|
+ ven_req.direction = USB_DIR_OUT;
|
|
+
|
|
+
|
|
+ /* call common vendor command request */
|
|
+ status = cx231xx_send_vendor_cmd(dev, &ven_req);
|
|
+ if (status < 0) {
|
|
+ cx231xx_info
|
|
+ ("UsbInterface::sendCommand, failed with status -%d\n",
|
|
+ status);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd);
|
|
+
|
|
+/*****************************************************************
|
|
+ * C O N T R O L - Register R E A D / W R I T E functions *
|
|
+ *****************************************************************/
|
|
+int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode)
|
|
+{
|
|
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
|
|
+ u32 tmp = 0;
|
|
+ int status = 0;
|
|
+
|
|
+ status =
|
|
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4);
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ tmp = *((u32 *) value);
|
|
+ tmp |= mode;
|
|
+
|
|
+ value[0] = (u8) tmp;
|
|
+ value[1] = (u8) (tmp >> 8);
|
|
+ value[2] = (u8) (tmp >> 16);
|
|
+ value[3] = (u8) (tmp >> 24);
|
|
+
|
|
+ status =
|
|
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*****************************************************************
|
|
+ * I 2 C Internal C O N T R O L functions *
|
|
+ *****************************************************************/
|
|
+int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
|
|
+ u8 saddr_len, u32 *data, u8 data_len)
|
|
+{
|
|
+ int status = 0;
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+
|
|
+ if (saddr_len == 0)
|
|
+ saddr = 0;
|
|
+ else if (saddr_len == 0)
|
|
+ saddr &= 0xff;
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = dev_addr >> 1;
|
|
+ req_data.direction = I2C_M_RD;
|
|
+ req_data.saddr_len = saddr_len;
|
|
+ req_data.saddr_dat = saddr;
|
|
+ req_data.buf_size = data_len;
|
|
+ req_data.p_buffer = (u8 *) value;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
|
|
+
|
|
+ if (status >= 0) {
|
|
+ /* Copy the data read back to main buffer */
|
|
+ if (data_len == 1)
|
|
+ *data = value[0];
|
|
+ else
|
|
+ *data =
|
|
+ value[0] | value[1] << 8 | value[2] << 16 | value[3]
|
|
+ << 24;
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
|
|
+ u8 saddr_len, u32 data, u8 data_len)
|
|
+{
|
|
+ int status = 0;
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+
|
|
+ value[0] = (u8) data;
|
|
+ value[1] = (u8) (data >> 8);
|
|
+ value[2] = (u8) (data >> 16);
|
|
+ value[3] = (u8) (data >> 24);
|
|
+
|
|
+ if (saddr_len == 0)
|
|
+ saddr = 0;
|
|
+ else if (saddr_len == 0)
|
|
+ saddr &= 0xff;
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = dev_addr >> 1;
|
|
+ req_data.direction = 0;
|
|
+ req_data.saddr_len = saddr_len;
|
|
+ req_data.saddr_dat = saddr;
|
|
+ req_data.buf_size = data_len;
|
|
+ req_data.p_buffer = value;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
|
|
+ u16 register_address, u8 bit_start, u8 bit_end,
|
|
+ u32 value)
|
|
+{
|
|
+ int status = 0;
|
|
+ u32 tmp;
|
|
+ u32 mask = 0;
|
|
+ int i;
|
|
+
|
|
+ if (bit_start > (size - 1) || bit_end > (size - 1))
|
|
+ return -1;
|
|
+
|
|
+ if (size == 8) {
|
|
+ status =
|
|
+ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
|
|
+ &tmp, 1);
|
|
+ } else {
|
|
+ status =
|
|
+ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
|
|
+ &tmp, 4);
|
|
+ }
|
|
+
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ mask = 1 << bit_end;
|
|
+ for (i = bit_end; i > bit_start && i > 0; i--)
|
|
+ mask = mask + (1 << (i - 1));
|
|
+
|
|
+ value <<= bit_start;
|
|
+
|
|
+ if (size == 8) {
|
|
+ tmp &= ~mask;
|
|
+ tmp |= value;
|
|
+ tmp &= 0xff;
|
|
+ status =
|
|
+ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
|
|
+ tmp, 1);
|
|
+ } else {
|
|
+ tmp &= ~mask;
|
|
+ tmp |= value;
|
|
+ status =
|
|
+ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
|
|
+ tmp, 4);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
|
|
+ u16 saddr, u32 mask, u32 value)
|
|
+{
|
|
+ u32 temp;
|
|
+ int status = 0;
|
|
+
|
|
+ status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4);
|
|
+
|
|
+ if (status < 0)
|
|
+ return status;
|
|
+
|
|
+ temp &= ~mask;
|
|
+ temp |= value;
|
|
+
|
|
+ status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+u32 cx231xx_set_field(u32 field_mask, u32 data)
|
|
+{
|
|
+ u32 temp;
|
|
+
|
|
+ for (temp = field_mask; (temp & 1) == 0; temp >>= 1)
|
|
+ data <<= 1;
|
|
+
|
|
+ return data;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c
|
|
new file mode 100644
|
|
index 0000000..c5082a4
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-dvb.c
|
|
@@ -0,0 +1,559 @@
|
|
+/*
|
|
+ DVB device driver for cx231xx
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/usb.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/videobuf-vmalloc.h>
|
|
+
|
|
+#include "xc5000.h"
|
|
+#include "dvb_dummy_fe.h"
|
|
+
|
|
+MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
|
|
+MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+static unsigned int debug;
|
|
+module_param(debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
|
|
+
|
|
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
+
|
|
+#define dprintk(level, fmt, arg...) do { \
|
|
+if (debug >= level) \
|
|
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
|
|
+} while (0)
|
|
+
|
|
+#define CX231XX_DVB_NUM_BUFS 5
|
|
+#define CX231XX_DVB_MAX_PACKETSIZE 564
|
|
+#define CX231XX_DVB_MAX_PACKETS 64
|
|
+
|
|
+struct cx231xx_dvb {
|
|
+ struct dvb_frontend *frontend;
|
|
+
|
|
+ /* feed count management */
|
|
+ struct mutex lock;
|
|
+ int nfeeds;
|
|
+
|
|
+ /* general boilerplate stuff */
|
|
+ struct dvb_adapter adapter;
|
|
+ struct dvb_demux demux;
|
|
+ struct dmxdev dmxdev;
|
|
+ struct dmx_frontend fe_hw;
|
|
+ struct dmx_frontend fe_mem;
|
|
+ struct dvb_net net;
|
|
+};
|
|
+
|
|
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
|
|
+{
|
|
+ char *errmsg = "Unknown";
|
|
+
|
|
+ switch (status) {
|
|
+ case -ENOENT:
|
|
+ errmsg = "unlinked synchronuously";
|
|
+ break;
|
|
+ case -ECONNRESET:
|
|
+ errmsg = "unlinked asynchronuously";
|
|
+ break;
|
|
+ case -ENOSR:
|
|
+ errmsg = "Buffer error (overrun)";
|
|
+ break;
|
|
+ case -EPIPE:
|
|
+ errmsg = "Stalled (device not responding)";
|
|
+ break;
|
|
+ case -EOVERFLOW:
|
|
+ errmsg = "Babble (bad cable?)";
|
|
+ break;
|
|
+ case -EPROTO:
|
|
+ errmsg = "Bit-stuff error (bad cable?)";
|
|
+ break;
|
|
+ case -EILSEQ:
|
|
+ errmsg = "CRC/Timeout (could be anything)";
|
|
+ break;
|
|
+ case -ETIME:
|
|
+ errmsg = "Device does not respond";
|
|
+ break;
|
|
+ }
|
|
+ if (packet < 0) {
|
|
+ dprintk(1, "URB status %d [%s].\n", status, errmsg);
|
|
+ } else {
|
|
+ dprintk(1, "URB packet %d, status %d [%s].\n",
|
|
+ packet, status, errmsg);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (!dev)
|
|
+ return 0;
|
|
+
|
|
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
|
|
+ return 0;
|
|
+
|
|
+ if (urb->status < 0) {
|
|
+ print_err_status(dev, -1, urb->status);
|
|
+ if (urb->status == -ENOENT)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < urb->number_of_packets; i++) {
|
|
+ int status = urb->iso_frame_desc[i].status;
|
|
+
|
|
+ if (status < 0) {
|
|
+ print_err_status(dev, i, status);
|
|
+ if (urb->iso_frame_desc[i].status != -EPROTO)
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
|
|
+ urb->iso_frame_desc[i].offset,
|
|
+ urb->iso_frame_desc[i].actual_length);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int start_streaming(struct cx231xx_dvb *dvb)
|
|
+{
|
|
+ int rc;
|
|
+ struct cx231xx *dev = dvb->adapter.priv;
|
|
+
|
|
+ usb_set_interface(dev->udev, 0, 1);
|
|
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
|
|
+ CX231XX_DVB_NUM_BUFS,
|
|
+ CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy);
|
|
+}
|
|
+
|
|
+static int stop_streaming(struct cx231xx_dvb *dvb)
|
|
+{
|
|
+ struct cx231xx *dev = dvb->adapter.priv;
|
|
+
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+
|
|
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int start_feed(struct dvb_demux_feed *feed)
|
|
+{
|
|
+ struct dvb_demux *demux = feed->demux;
|
|
+ struct cx231xx_dvb *dvb = demux->priv;
|
|
+ int rc, ret;
|
|
+
|
|
+ if (!demux->dmx.frontend)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&dvb->lock);
|
|
+ dvb->nfeeds++;
|
|
+ rc = dvb->nfeeds;
|
|
+
|
|
+ if (dvb->nfeeds == 1) {
|
|
+ ret = start_streaming(dvb);
|
|
+ if (ret < 0)
|
|
+ rc = ret;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dvb->lock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int stop_feed(struct dvb_demux_feed *feed)
|
|
+{
|
|
+ struct dvb_demux *demux = feed->demux;
|
|
+ struct cx231xx_dvb *dvb = demux->priv;
|
|
+ int err = 0;
|
|
+
|
|
+ mutex_lock(&dvb->lock);
|
|
+ dvb->nfeeds--;
|
|
+
|
|
+ if (0 == dvb->nfeeds)
|
|
+ err = stop_streaming(dvb);
|
|
+
|
|
+ mutex_unlock(&dvb->lock);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
|
|
+{
|
|
+ struct cx231xx *dev = fe->dvb->priv;
|
|
+
|
|
+ if (acquire)
|
|
+ return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
|
+ else
|
|
+ return cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static struct xc5000_config cnxt_rde250_tunerconfig = {
|
|
+ .i2c_address = 0x61,
|
|
+ .if_khz = 5380,
|
|
+};
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+#if 0
|
|
+static int attach_xc5000(u8 addr, struct cx231xx *dev)
|
|
+{
|
|
+
|
|
+ struct dvb_frontend *fe;
|
|
+ struct xc5000_config cfg;
|
|
+
|
|
+ memset(&cfg, 0, sizeof(cfg));
|
|
+ cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap;
|
|
+ cfg.i2c_addr = addr;
|
|
+
|
|
+ if (!dev->dvb->frontend) {
|
|
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
|
|
+ "Can't attach xc5000\n", dev->name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
|
|
+ if (!fe) {
|
|
+ printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
|
|
+ dvb_frontend_detach(dev->dvb->frontend);
|
|
+ dev->dvb->frontend = NULL;
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
+
|
|
+int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
|
|
+
|
|
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
|
|
+
|
|
+ if (dops->set_analog_params != NULL) {
|
|
+ struct analog_parameters params;
|
|
+
|
|
+ params.frequency = freq;
|
|
+ params.std = dev->norm;
|
|
+ params.mode = 0; /* 0- Air; 1 - cable */
|
|
+ /*params.audmode = ; */
|
|
+
|
|
+ /* Set the analog parameters to set the frequency */
|
|
+ cx231xx_info("Setting Frequency for XC5000\n");
|
|
+ dops->set_analog_params(dev->dvb->frontend, ¶ms);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+int cx231xx_reset_analog_tuner(struct cx231xx *dev)
|
|
+{
|
|
+ int status = 0;
|
|
+
|
|
+ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
|
|
+
|
|
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
|
|
+
|
|
+ if (dops->init != NULL && !dev->xc_fw_load_done) {
|
|
+
|
|
+ cx231xx_info("Reloading firmware for XC5000\n");
|
|
+ status = dops->init(dev->dvb->frontend);
|
|
+ if (status == 0) {
|
|
+ dev->xc_fw_load_done = 1;
|
|
+ cx231xx_info
|
|
+ ("XC5000 firmware download completed\n");
|
|
+ } else {
|
|
+ dev->xc_fw_load_done = 0;
|
|
+ cx231xx_info
|
|
+ ("XC5000 firmware download failed !!!\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static int register_dvb(struct cx231xx_dvb *dvb,
|
|
+ struct module *module,
|
|
+ struct cx231xx *dev, struct device *device)
|
|
+{
|
|
+ int result;
|
|
+
|
|
+ mutex_init(&dvb->lock);
|
|
+
|
|
+ /* register adapter */
|
|
+ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
|
|
+ adapter_nr);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING
|
|
+ "%s: dvb_register_adapter failed (errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_adapter;
|
|
+ }
|
|
+
|
|
+ /* Ensure all frontends negotiate bus access */
|
|
+ dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
|
|
+
|
|
+ dvb->adapter.priv = dev;
|
|
+
|
|
+ /* register frontend */
|
|
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING
|
|
+ "%s: dvb_register_frontend failed (errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_frontend;
|
|
+ }
|
|
+
|
|
+ /* register demux stuff */
|
|
+ dvb->demux.dmx.capabilities =
|
|
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
|
|
+ DMX_MEMORY_BASED_FILTERING;
|
|
+ dvb->demux.priv = dvb;
|
|
+ dvb->demux.filternum = 256;
|
|
+ dvb->demux.feednum = 256;
|
|
+ dvb->demux.start_feed = start_feed;
|
|
+ dvb->demux.stop_feed = stop_feed;
|
|
+
|
|
+ result = dvb_dmx_init(&dvb->demux);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_dmx;
|
|
+ }
|
|
+
|
|
+ dvb->dmxdev.filternum = 256;
|
|
+ dvb->dmxdev.demux = &dvb->demux.dmx;
|
|
+ dvb->dmxdev.capabilities = 0;
|
|
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_dmxdev;
|
|
+ }
|
|
+
|
|
+ dvb->fe_hw.source = DMX_FRONTEND_0;
|
|
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING
|
|
+ "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_fe_hw;
|
|
+ }
|
|
+
|
|
+ dvb->fe_mem.source = DMX_MEMORY_FE;
|
|
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING
|
|
+ "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
|
|
+ dev->name, result);
|
|
+ goto fail_fe_mem;
|
|
+ }
|
|
+
|
|
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
|
|
+ if (result < 0) {
|
|
+ printk(KERN_WARNING
|
|
+ "%s: connect_frontend failed (errno = %d)\n", dev->name,
|
|
+ result);
|
|
+ goto fail_fe_conn;
|
|
+ }
|
|
+
|
|
+ /* register network adapter */
|
|
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
|
|
+ return 0;
|
|
+
|
|
+fail_fe_conn:
|
|
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
|
|
+fail_fe_mem:
|
|
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
|
|
+fail_fe_hw:
|
|
+ dvb_dmxdev_release(&dvb->dmxdev);
|
|
+fail_dmxdev:
|
|
+ dvb_dmx_release(&dvb->demux);
|
|
+fail_dmx:
|
|
+ dvb_unregister_frontend(dvb->frontend);
|
|
+fail_frontend:
|
|
+ dvb_frontend_detach(dvb->frontend);
|
|
+ dvb_unregister_adapter(&dvb->adapter);
|
|
+fail_adapter:
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static void unregister_dvb(struct cx231xx_dvb *dvb)
|
|
+{
|
|
+ dvb_net_release(&dvb->net);
|
|
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
|
|
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
|
|
+ dvb_dmxdev_release(&dvb->dmxdev);
|
|
+ dvb_dmx_release(&dvb->demux);
|
|
+ dvb_unregister_frontend(dvb->frontend);
|
|
+ dvb_frontend_detach(dvb->frontend);
|
|
+ dvb_unregister_adapter(&dvb->adapter);
|
|
+}
|
|
+
|
|
+static int dvb_init(struct cx231xx *dev)
|
|
+{
|
|
+ int result = 0;
|
|
+ struct cx231xx_dvb *dvb;
|
|
+
|
|
+ if (!dev->board.has_dvb) {
|
|
+ /* This device does not support the extension */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
|
|
+
|
|
+ if (dvb == NULL) {
|
|
+ printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dev->dvb = dvb;
|
|
+ dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
|
|
+ dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
|
|
+
|
|
+ cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
|
|
+ /* init frontend */
|
|
+ switch (dev->model) {
|
|
+ case CX231XX_BOARD_CNXT_RDE_250:
|
|
+
|
|
+ /* dev->dvb->frontend = dvb_attach(s5h1411_attach,
|
|
+ &dvico_s5h1411_config,
|
|
+ &dev->i2c_bus[1].i2c_adap); */
|
|
+ dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
|
|
+
|
|
+ if (dev->dvb->frontend == NULL) {
|
|
+ printk(DRIVER_NAME
|
|
+ ": Failed to attach dummy front end\n");
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ /* define general-purpose callback pointer */
|
|
+ dvb->frontend->callback = cx231xx_tuner_callback;
|
|
+
|
|
+ if (dvb_attach(xc5000_attach, dev->dvb->frontend,
|
|
+ &dev->i2c_bus[1].i2c_adap,
|
|
+ &cnxt_rde250_tunerconfig) < 0) {
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ case CX231XX_BOARD_CNXT_RDU_250:
|
|
+
|
|
+ dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
|
|
+
|
|
+ if (dev->dvb->frontend == NULL) {
|
|
+ printk(DRIVER_NAME
|
|
+ ": Failed to attach dummy front end\n");
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ /* define general-purpose callback pointer */
|
|
+ dvb->frontend->callback = cx231xx_tuner_callback;
|
|
+
|
|
+ if (dvb_attach(xc5000_attach, dev->dvb->frontend,
|
|
+ &dev->i2c_bus[1].i2c_adap,
|
|
+ &cnxt_rde250_tunerconfig) < 0) {
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
|
|
+ " isn't supported yet\n", dev->name);
|
|
+ break;
|
|
+ }
|
|
+ if (NULL == dvb->frontend) {
|
|
+ printk(KERN_ERR
|
|
+ "%s/2: frontend initialization failed\n", dev->name);
|
|
+ result = -EINVAL;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ /* register everything */
|
|
+ result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
|
|
+
|
|
+ if (result < 0)
|
|
+ goto out_free;
|
|
+
|
|
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
+ printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
|
|
+ return 0;
|
|
+
|
|
+out_free:
|
|
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
+ kfree(dvb);
|
|
+ dev->dvb = NULL;
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static int dvb_fini(struct cx231xx *dev)
|
|
+{
|
|
+ if (!dev->board.has_dvb) {
|
|
+ /* This device does not support the extension */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (dev->dvb) {
|
|
+ unregister_dvb(dev->dvb);
|
|
+ dev->dvb = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct cx231xx_ops dvb_ops = {
|
|
+ .id = CX231XX_DVB,
|
|
+ .name = "Cx231xx dvb Extension",
|
|
+ .init = dvb_init,
|
|
+ .fini = dvb_fini,
|
|
+};
|
|
+
|
|
+static int __init cx231xx_dvb_register(void)
|
|
+{
|
|
+ return cx231xx_register_extension(&dvb_ops);
|
|
+}
|
|
+
|
|
+static void __exit cx231xx_dvb_unregister(void)
|
|
+{
|
|
+ cx231xx_unregister_extension(&dvb_ops);
|
|
+}
|
|
+
|
|
+module_init(cx231xx_dvb_register);
|
|
+module_exit(cx231xx_dvb_unregister);
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
|
|
new file mode 100644
|
|
index 0000000..b4a03d8
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
|
|
@@ -0,0 +1,555 @@
|
|
+/*
|
|
+ cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+ Based on Cx23885 driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/tuner.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+
|
|
+/* ----------------------------------------------------------- */
|
|
+
|
|
+static unsigned int i2c_scan;
|
|
+module_param(i2c_scan, int, 0444);
|
|
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
|
|
+
|
|
+static unsigned int i2c_debug;
|
|
+module_param(i2c_debug, int, 0644);
|
|
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
|
|
+
|
|
+#define dprintk1(lvl, fmt, args...) \
|
|
+do { \
|
|
+ if (i2c_debug >= lvl) { \
|
|
+ printk(fmt, ##args); \
|
|
+ } \
|
|
+} while (0)
|
|
+
|
|
+#define dprintk2(lvl, fmt, args...) \
|
|
+do { \
|
|
+ if (i2c_debug >= lvl) { \
|
|
+ printk(KERN_DEBUG "%s at %s: " fmt, \
|
|
+ dev->name, __func__ , ##args); \
|
|
+ } \
|
|
+} while (0)
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_send_bytes()
|
|
+ */
|
|
+int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
|
|
+ const struct i2c_msg *msg)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+ int status = 0;
|
|
+ u16 size = 0;
|
|
+ u8 loop = 0;
|
|
+ u8 saddr_len = 1;
|
|
+ u8 *buf_ptr = NULL;
|
|
+ u16 saddr = 0;
|
|
+ u8 need_gpio = 0;
|
|
+
|
|
+ if ((bus->nr == 1) && (msg->addr == 0x61)
|
|
+ && (dev->tuner_type == TUNER_XC5000)) {
|
|
+
|
|
+ size = msg->len;
|
|
+
|
|
+ if (size == 2) { /* register write sub addr */
|
|
+ /* Just writing sub address will cause problem
|
|
+ * to XC5000. So ignore the request */
|
|
+ return 0;
|
|
+ } else if (size == 4) { /* register write with sub addr */
|
|
+ if (msg->len >= 2)
|
|
+ saddr = msg->buf[0] << 8 | msg->buf[1];
|
|
+ else if (msg->len == 1)
|
|
+ saddr = msg->buf[0];
|
|
+
|
|
+ switch (saddr) {
|
|
+ case 0x0000: /* start tuner calibration mode */
|
|
+ need_gpio = 1;
|
|
+ /* FW Loading is done */
|
|
+ dev->xc_fw_load_done = 1;
|
|
+ break;
|
|
+ case 0x000D: /* Set signal source */
|
|
+ case 0x0001: /* Set TV standard - Video */
|
|
+ case 0x0002: /* Set TV standard - Audio */
|
|
+ case 0x0003: /* Set RF Frequency */
|
|
+ need_gpio = 1;
|
|
+ break;
|
|
+ default:
|
|
+ if (dev->xc_fw_load_done)
|
|
+ need_gpio = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (need_gpio) {
|
|
+ dprintk1(1,
|
|
+ "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
|
|
+ msg->addr, msg->len, saddr);
|
|
+
|
|
+ return dev->cx231xx_gpio_i2c_write(dev,
|
|
+ msg->addr,
|
|
+ msg->buf,
|
|
+ msg->len);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* special case for Xc5000 tuner case */
|
|
+ saddr_len = 1;
|
|
+
|
|
+ /* adjust the length to correct length */
|
|
+ size -= saddr_len;
|
|
+ buf_ptr = (u8 *) (msg->buf + 1);
|
|
+
|
|
+ do {
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg->addr;
|
|
+ req_data.direction = msg->flags;
|
|
+ req_data.saddr_len = saddr_len;
|
|
+ req_data.saddr_dat = msg->buf[0];
|
|
+ req_data.buf_size = size > 16 ? 16 : size;
|
|
+ req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
|
|
+
|
|
+ bus->i2c_nostop = (size > 16) ? 1 : 0;
|
|
+ bus->i2c_reserve = (loop == 0) ? 0 : 1;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+ loop++;
|
|
+
|
|
+ if (size >= 16)
|
|
+ size -= 16;
|
|
+ else
|
|
+ size = 0;
|
|
+
|
|
+ } while (size > 0);
|
|
+
|
|
+ bus->i2c_nostop = 0;
|
|
+ bus->i2c_reserve = 0;
|
|
+
|
|
+ } else { /* regular case */
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg->addr;
|
|
+ req_data.direction = msg->flags;
|
|
+ req_data.saddr_len = 0;
|
|
+ req_data.saddr_dat = 0;
|
|
+ req_data.buf_size = msg->len;
|
|
+ req_data.p_buffer = msg->buf;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+ }
|
|
+
|
|
+ return status < 0 ? status : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_recv_bytes()
|
|
+ * read a byte from the i2c device
|
|
+ */
|
|
+static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
|
|
+ const struct i2c_msg *msg)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+ int status = 0;
|
|
+ u16 saddr = 0;
|
|
+ u8 need_gpio = 0;
|
|
+
|
|
+ if ((bus->nr == 1) && (msg->addr == 0x61)
|
|
+ && dev->tuner_type == TUNER_XC5000) {
|
|
+
|
|
+ if (msg->len == 2)
|
|
+ saddr = msg->buf[0] << 8 | msg->buf[1];
|
|
+ else if (msg->len == 1)
|
|
+ saddr = msg->buf[0];
|
|
+
|
|
+ if (dev->xc_fw_load_done) {
|
|
+
|
|
+ switch (saddr) {
|
|
+ case 0x0009: /* BUSY check */
|
|
+ dprintk1(1,
|
|
+ "GPIO R E A D: Special case BUSY check \n");
|
|
+ /*Try read BUSY register, just set it to zero*/
|
|
+ msg->buf[0] = 0;
|
|
+ if (msg->len == 2)
|
|
+ msg->buf[1] = 0;
|
|
+ return 0;
|
|
+ case 0x0004: /* read Lock status */
|
|
+ need_gpio = 1;
|
|
+ break;
|
|
+
|
|
+ }
|
|
+
|
|
+ if (need_gpio) {
|
|
+ /* this is a special case to handle Xceive tuner
|
|
+ clock stretch issue with gpio based I2C */
|
|
+
|
|
+ dprintk1(1,
|
|
+ "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
|
|
+ msg->addr, msg->len,
|
|
+ msg->buf[0] << 8 | msg->buf[1]);
|
|
+
|
|
+ status =
|
|
+ dev->cx231xx_gpio_i2c_write(dev, msg->addr,
|
|
+ msg->buf,
|
|
+ msg->len);
|
|
+ status =
|
|
+ dev->cx231xx_gpio_i2c_read(dev, msg->addr,
|
|
+ msg->buf,
|
|
+ msg->len);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg->addr;
|
|
+ req_data.direction = msg->flags;
|
|
+ req_data.saddr_len = msg->len;
|
|
+ req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
|
|
+ req_data.buf_size = msg->len;
|
|
+ req_data.p_buffer = msg->buf;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+
|
|
+ } else {
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg->addr;
|
|
+ req_data.direction = msg->flags;
|
|
+ req_data.saddr_len = 0;
|
|
+ req_data.saddr_dat = 0;
|
|
+ req_data.buf_size = msg->len;
|
|
+ req_data.p_buffer = msg->buf;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+ }
|
|
+
|
|
+ return status < 0 ? status : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_recv_bytes_with_saddr()
|
|
+ * read a byte from the i2c device
|
|
+ */
|
|
+static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
|
|
+ const struct i2c_msg *msg1,
|
|
+ const struct i2c_msg *msg2)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+ int status = 0;
|
|
+ u16 saddr = 0;
|
|
+ u8 need_gpio = 0;
|
|
+
|
|
+ if (msg1->len == 2)
|
|
+ saddr = msg1->buf[0] << 8 | msg1->buf[1];
|
|
+ else if (msg1->len == 1)
|
|
+ saddr = msg1->buf[0];
|
|
+
|
|
+ if ((bus->nr == 1) && (msg2->addr == 0x61)
|
|
+ && dev->tuner_type == TUNER_XC5000) {
|
|
+
|
|
+ if ((msg2->len < 16)) {
|
|
+
|
|
+ dprintk1(1,
|
|
+ "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
|
|
+ msg2->addr, msg2->len, saddr, msg1->len);
|
|
+
|
|
+ switch (saddr) {
|
|
+ case 0x0008: /* read FW load status */
|
|
+ need_gpio = 1;
|
|
+ break;
|
|
+ case 0x0004: /* read Lock status */
|
|
+ need_gpio = 1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (need_gpio) {
|
|
+ status =
|
|
+ dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
|
|
+ msg1->buf,
|
|
+ msg1->len);
|
|
+ status =
|
|
+ dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
|
|
+ msg2->buf,
|
|
+ msg2->len);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg2->addr;
|
|
+ req_data.direction = msg2->flags;
|
|
+ req_data.saddr_len = msg1->len;
|
|
+ req_data.saddr_dat = saddr;
|
|
+ req_data.buf_size = msg2->len;
|
|
+ req_data.p_buffer = msg2->buf;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+
|
|
+ return status < 0 ? status : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_check_for_device()
|
|
+ * check if there is a i2c_device at the supplied address
|
|
+ */
|
|
+static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
|
|
+ const struct i2c_msg *msg)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+ struct cx231xx_i2c_xfer_data req_data;
|
|
+ int status = 0;
|
|
+
|
|
+ /* prepare xfer_data struct */
|
|
+ req_data.dev_addr = msg->addr;
|
|
+ req_data.direction = msg->flags;
|
|
+ req_data.saddr_len = 0;
|
|
+ req_data.saddr_dat = 0;
|
|
+ req_data.buf_size = 0;
|
|
+ req_data.p_buffer = NULL;
|
|
+
|
|
+ /* usb send command */
|
|
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
|
|
+
|
|
+ return status < 0 ? status : 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_xfer()
|
|
+ * the main i2c transfer function
|
|
+ */
|
|
+static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
|
|
+ struct i2c_msg msgs[], int num)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+ int addr, rc, i, byte;
|
|
+
|
|
+ if (num <= 0)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < num; i++) {
|
|
+
|
|
+ addr = msgs[i].addr >> 1;
|
|
+
|
|
+ dprintk2(2, "%s %s addr=%x len=%d:",
|
|
+ (msgs[i].flags & I2C_M_RD) ? "read" : "write",
|
|
+ i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
|
|
+ if (!msgs[i].len) {
|
|
+ /* no len: check only for device presence */
|
|
+ rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
|
|
+ if (rc < 0) {
|
|
+ dprintk2(2, " no device\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ } else if (msgs[i].flags & I2C_M_RD) {
|
|
+ /* read bytes */
|
|
+ rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
|
|
+ if (i2c_debug >= 2) {
|
|
+ for (byte = 0; byte < msgs[i].len; byte++)
|
|
+ printk(" %02x", msgs[i].buf[byte]);
|
|
+ }
|
|
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
|
|
+ msgs[i].addr == msgs[i + 1].addr
|
|
+ && (msgs[i].len <= 2) && (bus->nr < 2)) {
|
|
+ /* read bytes */
|
|
+ rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
|
|
+ &msgs[i],
|
|
+ &msgs[i + 1]);
|
|
+ if (i2c_debug >= 2) {
|
|
+ for (byte = 0; byte < msgs[i].len; byte++)
|
|
+ printk(" %02x", msgs[i].buf[byte]);
|
|
+ }
|
|
+ i++;
|
|
+ } else {
|
|
+ /* write bytes */
|
|
+ if (i2c_debug >= 2) {
|
|
+ for (byte = 0; byte < msgs[i].len; byte++)
|
|
+ printk(" %02x", msgs[i].buf[byte]);
|
|
+ }
|
|
+ rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
|
|
+ }
|
|
+ if (rc < 0)
|
|
+ goto err;
|
|
+ if (i2c_debug >= 2)
|
|
+ printk("\n");
|
|
+ }
|
|
+
|
|
+ return num;
|
|
+err:
|
|
+ dprintk2(2, " ERROR: %i\n", rc);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------- */
|
|
+
|
|
+/*
|
|
+ * functionality()
|
|
+ */
|
|
+static u32 functionality(struct i2c_adapter *adap)
|
|
+{
|
|
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * attach_inform()
|
|
+ * gets called when a device attaches to the i2c bus
|
|
+ * does some basic configuration
|
|
+ */
|
|
+static int attach_inform(struct i2c_client *client)
|
|
+{
|
|
+ struct cx231xx_i2c *bus = i2c_get_adapdata(client->adapter);
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+
|
|
+ switch (client->addr << 1) {
|
|
+ case 0x8e:
|
|
+ {
|
|
+ struct IR_i2c *ir = i2c_get_clientdata(client);
|
|
+ dprintk1(1, "attach_inform: IR detected (%s).\n",
|
|
+ ir->phys);
|
|
+ cx231xx_set_ir(dev, ir);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct i2c_algorithm cx231xx_algo = {
|
|
+ .master_xfer = cx231xx_i2c_xfer,
|
|
+ .functionality = functionality,
|
|
+};
|
|
+
|
|
+static struct i2c_adapter cx231xx_adap_template = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "cx231xx",
|
|
+ .id = I2C_HW_B_CX231XX,
|
|
+ .algo = &cx231xx_algo,
|
|
+ .client_register = attach_inform,
|
|
+};
|
|
+
|
|
+static struct i2c_client cx231xx_client_template = {
|
|
+ .name = "cx231xx internal",
|
|
+};
|
|
+
|
|
+/* ----------------------------------------------------------- */
|
|
+
|
|
+/*
|
|
+ * i2c_devs
|
|
+ * incomplete list of known devices
|
|
+ */
|
|
+static char *i2c_devs[128] = {
|
|
+ [0x60 >> 1] = "colibri",
|
|
+ [0x88 >> 1] = "hammerhead",
|
|
+ [0x8e >> 1] = "CIR",
|
|
+ [0x32 >> 1] = "GeminiIII",
|
|
+ [0x02 >> 1] = "Aquarius",
|
|
+ [0xa0 >> 1] = "eeprom",
|
|
+ [0xc0 >> 1] = "tuner/XC3028",
|
|
+ [0xc2 >> 1] = "tuner/XC5000",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * cx231xx_do_i2c_scan()
|
|
+ * check i2c address range for devices
|
|
+ */
|
|
+void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
|
|
+{
|
|
+ unsigned char buf;
|
|
+ int i, rc;
|
|
+
|
|
+ cx231xx_info(": Checking for I2C devices ..\n");
|
|
+ for (i = 0; i < 128; i++) {
|
|
+ c->addr = i;
|
|
+ rc = i2c_master_recv(c, &buf, 0);
|
|
+ if (rc < 0)
|
|
+ continue;
|
|
+ cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
|
|
+ dev->name, i << 1,
|
|
+ i2c_devs[i] ? i2c_devs[i] : "???");
|
|
+ }
|
|
+ cx231xx_info(": Completed Checking for I2C devices.\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_register()
|
|
+ * register i2c bus
|
|
+ */
|
|
+int cx231xx_i2c_register(struct cx231xx_i2c *bus)
|
|
+{
|
|
+ struct cx231xx *dev = bus->dev;
|
|
+
|
|
+ BUG_ON(!dev->cx231xx_send_usb_command);
|
|
+
|
|
+ memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
|
|
+ memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
|
|
+ memcpy(&bus->i2c_client, &cx231xx_client_template,
|
|
+ sizeof(bus->i2c_client));
|
|
+
|
|
+ bus->i2c_adap.dev.parent = &dev->udev->dev;
|
|
+
|
|
+ strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
|
|
+
|
|
+ bus->i2c_algo.data = bus;
|
|
+ bus->i2c_adap.algo_data = bus;
|
|
+ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
|
|
+ i2c_add_adapter(&bus->i2c_adap);
|
|
+
|
|
+ bus->i2c_client.adapter = &bus->i2c_adap;
|
|
+
|
|
+ if (0 == bus->i2c_rc) {
|
|
+ if (i2c_scan)
|
|
+ cx231xx_do_i2c_scan(dev, &bus->i2c_client);
|
|
+ } else
|
|
+ cx231xx_warn("%s: i2c bus %d register FAILED\n",
|
|
+ dev->name, bus->nr);
|
|
+
|
|
+ return bus->i2c_rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_i2c_unregister()
|
|
+ * unregister i2c_bus
|
|
+ */
|
|
+int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
|
|
+{
|
|
+ i2c_del_adapter(&bus->i2c_adap);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
|
|
new file mode 100644
|
|
index 0000000..97e304c
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-input.c
|
|
@@ -0,0 +1,246 @@
|
|
+/*
|
|
+ handle cx231xx IR remotes via linux kernel input layer.
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+
|
|
+ < This is a place holder for IR now.>
|
|
+
|
|
+ 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 <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/usb.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+
|
|
+static unsigned int ir_debug;
|
|
+module_param(ir_debug, int, 0644);
|
|
+MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
|
|
+
|
|
+#define i2cdprintk(fmt, arg...) \
|
|
+ if (ir_debug) { \
|
|
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
|
|
+ }
|
|
+
|
|
+#define dprintk(fmt, arg...) \
|
|
+ if (ir_debug) { \
|
|
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
|
|
+ }
|
|
+
|
|
+/**********************************************************
|
|
+ Polling structure used by cx231xx IR's
|
|
+ **********************************************************/
|
|
+
|
|
+struct cx231xx_ir_poll_result {
|
|
+ unsigned int toggle_bit:1;
|
|
+ unsigned int read_count:7;
|
|
+ u8 rc_address;
|
|
+ u8 rc_data[4];
|
|
+};
|
|
+
|
|
+struct cx231xx_IR {
|
|
+ struct cx231xx *dev;
|
|
+ struct input_dev *input;
|
|
+ struct ir_input_state ir;
|
|
+ char name[32];
|
|
+ char phys[32];
|
|
+
|
|
+ /* poll external decoder */
|
|
+ int polling;
|
|
+ struct work_struct work;
|
|
+ struct timer_list timer;
|
|
+ unsigned int last_toggle:1;
|
|
+ unsigned int last_readcount;
|
|
+ unsigned int repeat_interval;
|
|
+
|
|
+ int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
|
|
+};
|
|
+
|
|
+/**********************************************************
|
|
+ Polling code for cx231xx
|
|
+ **********************************************************/
|
|
+
|
|
+static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
|
|
+{
|
|
+ int result;
|
|
+ int do_sendkey = 0;
|
|
+ struct cx231xx_ir_poll_result poll_result;
|
|
+
|
|
+ /* read the registers containing the IR status */
|
|
+ result = ir->get_key(ir, &poll_result);
|
|
+ if (result < 0) {
|
|
+ dprintk("ir->get_key() failed %d\n", result);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
|
|
+ poll_result.toggle_bit, poll_result.read_count,
|
|
+ ir->last_readcount, poll_result.rc_data[0]);
|
|
+
|
|
+ if (ir->dev->chip_id == CHIP_ID_EM2874) {
|
|
+ /* The em2874 clears the readcount field every time the
|
|
+ register is read. The em2860/2880 datasheet says that it
|
|
+ is supposed to clear the readcount, but it doesn't. So with
|
|
+ the em2874, we are looking for a non-zero read count as
|
|
+ opposed to a readcount that is incrementing */
|
|
+ ir->last_readcount = 0;
|
|
+ }
|
|
+
|
|
+ if (poll_result.read_count == 0) {
|
|
+ /* The button has not been pressed since the last read */
|
|
+ } else if (ir->last_toggle != poll_result.toggle_bit) {
|
|
+ /* A button has been pressed */
|
|
+ dprintk("button has been pressed\n");
|
|
+ ir->last_toggle = poll_result.toggle_bit;
|
|
+ ir->repeat_interval = 0;
|
|
+ do_sendkey = 1;
|
|
+ } else if (poll_result.toggle_bit == ir->last_toggle &&
|
|
+ poll_result.read_count > 0 &&
|
|
+ poll_result.read_count != ir->last_readcount) {
|
|
+ /* The button is still being held down */
|
|
+ dprintk("button being held down\n");
|
|
+
|
|
+ /* Debouncer for first keypress */
|
|
+ if (ir->repeat_interval++ > 9) {
|
|
+ /* Start repeating after 1 second */
|
|
+ do_sendkey = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (do_sendkey) {
|
|
+ dprintk("sending keypress\n");
|
|
+ ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
|
|
+ poll_result.rc_data[0]);
|
|
+ ir_input_nokey(ir->input, &ir->ir);
|
|
+ }
|
|
+
|
|
+ ir->last_readcount = poll_result.read_count;
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void ir_timer(unsigned long data)
|
|
+{
|
|
+ struct cx231xx_IR *ir = (struct cx231xx_IR *)data;
|
|
+
|
|
+ schedule_work(&ir->work);
|
|
+}
|
|
+
|
|
+static void cx231xx_ir_work(struct work_struct *work)
|
|
+{
|
|
+ struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);
|
|
+
|
|
+ cx231xx_ir_handle_key(ir);
|
|
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
|
|
+}
|
|
+
|
|
+void cx231xx_ir_start(struct cx231xx_IR *ir)
|
|
+{
|
|
+ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
|
|
+ INIT_WORK(&ir->work, cx231xx_ir_work);
|
|
+ schedule_work(&ir->work);
|
|
+}
|
|
+
|
|
+static void cx231xx_ir_stop(struct cx231xx_IR *ir)
|
|
+{
|
|
+ del_timer_sync(&ir->timer);
|
|
+ flush_scheduled_work();
|
|
+}
|
|
+
|
|
+int cx231xx_ir_init(struct cx231xx *dev)
|
|
+{
|
|
+ struct cx231xx_IR *ir;
|
|
+ struct input_dev *input_dev;
|
|
+ u8 ir_config;
|
|
+ int err = -ENOMEM;
|
|
+
|
|
+ if (dev->board.ir_codes == NULL) {
|
|
+ /* No remote control support */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
|
|
+ input_dev = input_allocate_device();
|
|
+ if (!ir || !input_dev)
|
|
+ goto err_out_free;
|
|
+
|
|
+ ir->input = input_dev;
|
|
+
|
|
+ /* Setup the proper handler based on the chip */
|
|
+ switch (dev->chip_id) {
|
|
+ default:
|
|
+ printk("Unrecognized cx231xx chip id: IR not supported\n");
|
|
+ goto err_out_free;
|
|
+ }
|
|
+
|
|
+ /* This is how often we ask the chip for IR information */
|
|
+ ir->polling = 100; /* ms */
|
|
+
|
|
+ /* init input device */
|
|
+ snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
|
|
+
|
|
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
|
|
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
|
|
+
|
|
+ ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
|
|
+ input_dev->name = ir->name;
|
|
+ input_dev->phys = ir->phys;
|
|
+ input_dev->id.bustype = BUS_USB;
|
|
+ input_dev->id.version = 1;
|
|
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
|
|
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
|
|
+
|
|
+ input_dev->dev.parent = &dev->udev->dev;
|
|
+ /* record handles to ourself */
|
|
+ ir->dev = dev;
|
|
+ dev->ir = ir;
|
|
+
|
|
+ cx231xx_ir_start(ir);
|
|
+
|
|
+ /* all done */
|
|
+ err = input_register_device(ir->input);
|
|
+ if (err)
|
|
+ goto err_out_stop;
|
|
+
|
|
+ return 0;
|
|
+err_out_stop:
|
|
+ cx231xx_ir_stop(ir);
|
|
+ dev->ir = NULL;
|
|
+err_out_free:
|
|
+ input_free_device(input_dev);
|
|
+ kfree(ir);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+int cx231xx_ir_fini(struct cx231xx *dev)
|
|
+{
|
|
+ struct cx231xx_IR *ir = dev->ir;
|
|
+
|
|
+ /* skip detach on non attached boards */
|
|
+ if (!ir)
|
|
+ return 0;
|
|
+
|
|
+ cx231xx_ir_stop(ir);
|
|
+ input_unregister_device(ir->input);
|
|
+ kfree(ir);
|
|
+
|
|
+ /* done */
|
|
+ dev->ir = NULL;
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
|
|
new file mode 100644
|
|
index 0000000..7473c33
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
|
|
@@ -0,0 +1,795 @@
|
|
+/*
|
|
+ cx231xx-pcb-config.c - driver for Conexant
|
|
+ Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include "cx231xx.h"
|
|
+#include "cx231xx-conf-reg.h"
|
|
+
|
|
+static unsigned int pcb_debug;
|
|
+module_param(pcb_debug, int, 0644);
|
|
+MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
|
|
+
|
|
+/******************************************************************************/
|
|
+
|
|
+struct pcb_config cx231xx_Scenario[] = {
|
|
+ {
|
|
+ INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ MOD_DIGITAL, /* mode */
|
|
+ SOURCE_TS_BDA, /* ts1_source, digital tv only */
|
|
+ NOT_SUPPORTED, /* ts2_source */
|
|
+ NOT_SUPPORTED, /* analog source */
|
|
+
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ ,
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed config */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ MOD_DIGITAL, /* mode */
|
|
+ SOURCE_TS_BDA, /* ts1_source, digital tv only */
|
|
+ 0, /* ts2_source,need update from register */
|
|
+ NOT_SUPPORTED, /* analog source */
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ 2, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ 2, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_SELFPOWER_ANALOG_ONLY, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
|
|
+ NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
|
|
+ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
|
|
+ 0, /* analog source, need update */
|
|
+
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 1, /* AUDIO */
|
|
+ 2, /* VIDEO */
|
|
+ 3, /* VANC */
|
|
+ 4, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 1, /* AUDIO */
|
|
+ 2, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_SELFPOWER_DUAL, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ /* mode ,analog tv and digital path */
|
|
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
|
|
+ 0, /* ts1_source,will update in register */
|
|
+ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
|
|
+ 0, /* analog source need update */
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ 4, /* VANC */
|
|
+ 5, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_SELFPOWER_TRIPLE, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ /* mode ,analog tv and digital path */
|
|
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
|
|
+ 0, /* ts1_source, update in register */
|
|
+ 0, /* ts2_source,update in register */
|
|
+ 0, /* analog source, need update */
|
|
+
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ 2, /* TS2 index */
|
|
+ 3, /* AUDIO */
|
|
+ 4, /* VIDEO */
|
|
+ 5, /* VANC */
|
|
+ 6, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ 2, /* TS2 index */
|
|
+ 3, /* AUDIO */
|
|
+ 4, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_SELFPOWER_COMPRESSOR, /* index */
|
|
+ USB_SELF_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ /* mode ,analog tv AND DIGITAL path */
|
|
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
|
|
+ NOT_SUPPORTED, /* ts1_source, disable */
|
|
+ SOURCE_TS_BDA, /* ts2_source */
|
|
+ 0, /* analog source,need update */
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ 1, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ 4, /* VANC */
|
|
+ 5, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ 1, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+ {
|
|
+ INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
|
|
+ USB_BUS_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
|
|
+ SOURCE_TS_BDA, /* ts1_source, disable */
|
|
+ NOT_SUPPORTED, /* ts2_source */
|
|
+ NOT_SUPPORTED, /* analog source */
|
|
+
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index = 2 */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ /* full-speed */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index = 2 */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ NOT_SUPPORTED, /* AUDIO */
|
|
+ NOT_SUPPORTED, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {
|
|
+ INDEX_BUSPOWER_ANALOG_ONLY, /* index */
|
|
+ USB_BUS_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
|
|
+ NOT_SUPPORTED, /* ts1_source, disable */
|
|
+ NOT_SUPPORTED, /* ts2_source */
|
|
+ SOURCE_ANALOG, /* analog source--analog */
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 1, /* AUDIO */
|
|
+ 2, /* VIDEO */
|
|
+ 3, /* VANC */
|
|
+ 4, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ { /* full-speed */
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ NOT_SUPPORTED, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 1, /* AUDIO */
|
|
+ 2, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {
|
|
+ INDEX_BUSPOWER_DIF_ONLY, /* index */
|
|
+ USB_BUS_POWER, /* power_type */
|
|
+ 0, /* speed , not decide yet */
|
|
+ /* mode ,analog tv AND DIGITAL path */
|
|
+ MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
|
|
+ SOURCE_TS_BDA, /* ts1_source, disable */
|
|
+ NOT_SUPPORTED, /* ts2_source */
|
|
+ SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
|
|
+ 0, /* digital_index */
|
|
+ 0, /* analog index */
|
|
+ 0, /* dif_index */
|
|
+ 0, /* external_index */
|
|
+ 1, /* only one configuration */
|
|
+ {
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ 4, /* VANC */
|
|
+ 5, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ { /* full speed */
|
|
+ {
|
|
+ 0, /* config index */
|
|
+ {
|
|
+ 0, /* interrupt ep index */
|
|
+ 1, /* ts1 index */
|
|
+ NOT_SUPPORTED, /* TS2 index */
|
|
+ 2, /* AUDIO */
|
|
+ 3, /* VIDEO */
|
|
+ NOT_SUPPORTED, /* VANC */
|
|
+ NOT_SUPPORTED, /* HANC */
|
|
+ NOT_SUPPORTED /* ir_index */
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ ,
|
|
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
|
|
+ NOT_SUPPORTED}
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ ,
|
|
+
|
|
+};
|
|
+
|
|
+/*****************************************************************/
|
|
+
|
|
+u32 initialize_cx231xx(struct cx231xx *dev)
|
|
+{
|
|
+ u32 config_info = 0;
|
|
+ struct pcb_config *p_pcb_info;
|
|
+ u8 usb_speed = 1; /* from register,1--HS, 0--FS */
|
|
+ u8 data[4] = { 0, 0, 0, 0 };
|
|
+ u32 ts1_source = 0;
|
|
+ u32 ts2_source = 0;
|
|
+ u32 analog_source = 0;
|
|
+ u8 _current_scenario_idx = 0xff;
|
|
+
|
|
+ ts1_source = SOURCE_TS_BDA;
|
|
+ ts2_source = SOURCE_TS_BDA;
|
|
+
|
|
+ /* read board config register to find out which
|
|
+ pcb config it is related to */
|
|
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
|
|
+
|
|
+ config_info = *((u32 *) data);
|
|
+ usb_speed = (u8) (config_info & 0x1);
|
|
+
|
|
+ /* Verify this device belongs to Bus power or Self power device */
|
|
+ if (config_info & BUS_POWER) { /* bus-power */
|
|
+ switch (config_info & BUSPOWER_MASK) {
|
|
+ case TS1_PORT | BUS_POWER:
|
|
+ cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
|
|
+ usb_speed;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
|
|
+ _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | BUS_POWER:
|
|
+ cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
|
|
+ usb_speed;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
|
|
+ _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
|
|
+ cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
|
|
+ usb_speed;
|
|
+ p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
|
|
+ _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
|
|
+ break;
|
|
+ default:
|
|
+ cx231xx_info("bad config in buspower!!!!\n");
|
|
+ cx231xx_info("config_info=%x\n",
|
|
+ (config_info & BUSPOWER_MASK));
|
|
+ return 1;
|
|
+ }
|
|
+ } else { /* self-power */
|
|
+
|
|
+ switch (config_info & SELFPOWER_MASK) {
|
|
+ case TS1_PORT | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
|
|
+ usb_speed;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
|
|
+ break;
|
|
+ case TS1_TS2_PORT | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
|
|
+ usb_speed;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
|
|
+ ts2_source = ts2_source;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
|
|
+ usb_speed;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
|
|
+ analog_source = analog_source;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
|
|
+ usb_speed;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
|
|
+ ts1_source;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
|
|
+ analog_source;
|
|
+ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_DUAL;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
|
|
+ usb_speed;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
|
|
+ ts1_source;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
|
|
+ ts2_source;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
|
|
+ analog_source;
|
|
+ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
|
|
+ break;
|
|
+ case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
|
|
+ usb_speed;
|
|
+ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
|
|
+ analog_source = analog_source;
|
|
+ p_pcb_info =
|
|
+ &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
|
|
+ _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
|
|
+ break;
|
|
+ default:
|
|
+ cx231xx_info("bad senario!!!!!\n");
|
|
+ cx231xx_info("config_info=%x\n",
|
|
+ (config_info & SELFPOWER_MASK));
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev->current_scenario_idx = _current_scenario_idx;
|
|
+
|
|
+ memcpy(&dev->current_pcb_config, p_pcb_info,
|
|
+ sizeof(struct pcb_config));
|
|
+
|
|
+ if (pcb_debug) {
|
|
+ cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
|
|
+ cx231xx_info("scenario %d\n",
|
|
+ (dev->current_pcb_config.index) + 1);
|
|
+ cx231xx_info("type=%x\n", dev->current_pcb_config.type);
|
|
+ cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
|
|
+ cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
|
|
+ cx231xx_info("ts1_source=%x\n",
|
|
+ dev->current_pcb_config.ts1_source);
|
|
+ cx231xx_info("ts2_source=%x\n",
|
|
+ dev->current_pcb_config.ts2_source);
|
|
+ cx231xx_info("analog_source=%x\n",
|
|
+ dev->current_pcb_config.analog_source);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
|
|
new file mode 100644
|
|
index 0000000..f5e46e8
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
|
|
@@ -0,0 +1,231 @@
|
|
+/*
|
|
+ cx231xx-pcb-cfg.h - driver for Conexant
|
|
+ Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+
|
|
+ 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.
|
|
+ */
|
|
+
|
|
+#ifndef _PCB_CONFIG_H_
|
|
+#define _PCB_CONFIG_H_
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+/***************************************************************************
|
|
+ * Class Information *
|
|
+***************************************************************************/
|
|
+#define CLASS_DEFAULT 0xFF
|
|
+
|
|
+enum VENDOR_REQUEST_TYPE {
|
|
+ /* Set/Get I2C */
|
|
+ VRT_SET_I2C0 = 0x0,
|
|
+ VRT_SET_I2C1 = 0x1,
|
|
+ VRT_SET_I2C2 = 0x2,
|
|
+ VRT_GET_I2C0 = 0x4,
|
|
+ VRT_GET_I2C1 = 0x5,
|
|
+ VRT_GET_I2C2 = 0x6,
|
|
+
|
|
+ /* Set/Get GPIO */
|
|
+ VRT_SET_GPIO = 0x8,
|
|
+ VRT_GET_GPIO = 0x9,
|
|
+
|
|
+ /* Set/Get GPIE */
|
|
+ VRT_SET_GPIE = 0xA,
|
|
+ VRT_GET_GPIE = 0xB,
|
|
+
|
|
+ /* Set/Get Register Control/Status */
|
|
+ VRT_SET_REGISTER = 0xC,
|
|
+ VRT_GET_REGISTER = 0xD,
|
|
+
|
|
+ /* Get Extended Compat ID Descriptor */
|
|
+ VRT_GET_EXTCID_DESC = 0xFF,
|
|
+};
|
|
+
|
|
+enum BYTE_ENABLE_MASK {
|
|
+ ENABLE_ONE_BYTE = 0x1,
|
|
+ ENABLE_TWE_BYTE = 0x3,
|
|
+ ENABLE_THREE_BYTE = 0x7,
|
|
+ ENABLE_FOUR_BYTE = 0xF,
|
|
+};
|
|
+
|
|
+#define SPEED_MASK 0x1
|
|
+enum USB_SPEED{
|
|
+ FULL_SPEED = 0x0, /* 0: full speed */
|
|
+ HIGH_SPEED = 0x1 /* 1: high speed */
|
|
+};
|
|
+
|
|
+enum _true_false{
|
|
+ FALSE = 0,
|
|
+ TRUE = 1
|
|
+};
|
|
+
|
|
+#define TS_MASK 0x6
|
|
+enum TS_PORT{
|
|
+ NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
|
|
+ only offers Analog TV or Video */
|
|
+ TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
|
|
+ Digital or External Analog/Compressed source) */
|
|
+ TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
|
|
+ (Dual inputs from Digital and/or
|
|
+ External Analog/Compressed sources) */
|
|
+ TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
|
|
+ to external clock */
|
|
+ TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
|
|
+ TS2 Input (from Compressor) */
|
|
+};
|
|
+
|
|
+#define EAVP_MASK 0x8
|
|
+enum EAV_PRESENT{
|
|
+ NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
|
|
+ (no need for i2s blcok),
|
|
+ Analog Tuner must be present */
|
|
+ EXTERNAL_AV = 0x8 /* 1: External A/V inputs
|
|
+ present (requires i2s blk) */
|
|
+};
|
|
+
|
|
+#define ATM_MASK 0x30
|
|
+enum AT_MODE{
|
|
+ DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
|
|
+ BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
|
|
+ Sound-IF Signals present */
|
|
+ NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
|
|
+};
|
|
+
|
|
+#define PWR_SEL_MASK 0x40
|
|
+enum POWE_TYPE{
|
|
+ SELF_POWER = 0x0, /* 0: self power */
|
|
+ BUS_POWER = 0x40 /* 1: bus power */
|
|
+};
|
|
+
|
|
+enum USB_POWE_TYPE{
|
|
+ USB_SELF_POWER = 0,
|
|
+ USB_BUS_POWER
|
|
+};
|
|
+
|
|
+#define BO_0_MASK 0x80
|
|
+enum AVDEC_STATUS{
|
|
+ AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
|
|
+ AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
|
|
+};
|
|
+
|
|
+#define BO_1_MASK 0x100
|
|
+
|
|
+#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
|
|
+#define SELFPOWER_MASK 0x86
|
|
+
|
|
+/***************************************************************************/
|
|
+#define NOT_DECIDE_YET 0xFE
|
|
+#define NOT_SUPPORTED 0xFF
|
|
+
|
|
+/***************************************************************************
|
|
+ * for mod field use *
|
|
+***************************************************************************/
|
|
+#define MOD_DIGITAL 0x1
|
|
+#define MOD_ANALOG 0x2
|
|
+#define MOD_DIF 0x4
|
|
+#define MOD_EXTERNAL 0x8
|
|
+#define CAP_ALL_MOD 0x0f
|
|
+
|
|
+/***************************************************************************
|
|
+ * source define *
|
|
+***************************************************************************/
|
|
+#define SOURCE_DIGITAL 0x1
|
|
+#define SOURCE_ANALOG 0x2
|
|
+#define SOURCE_DIF 0x4
|
|
+#define SOURCE_EXTERNAL 0x8
|
|
+#define SOURCE_TS_BDA 0x10
|
|
+#define SOURCE_TS_ENCODE 0x20
|
|
+#define SOURCE_TS_EXTERNAL 0x40
|
|
+
|
|
+/***************************************************************************
|
|
+ * interface information define *
|
|
+***************************************************************************/
|
|
+struct INTERFACE_INFO {
|
|
+ u8 interrupt_index;
|
|
+ u8 ts1_index;
|
|
+ u8 ts2_index;
|
|
+ u8 audio_index;
|
|
+ u8 video_index;
|
|
+ u8 vanc_index; /* VBI */
|
|
+ u8 hanc_index; /* Sliced CC */
|
|
+ u8 ir_index;
|
|
+};
|
|
+
|
|
+enum INDEX_INTERFACE_INFO{
|
|
+ INDEX_INTERRUPT = 0x0,
|
|
+ INDEX_TS1,
|
|
+ INDEX_TS2,
|
|
+ INDEX_AUDIO,
|
|
+ INDEX_VIDEO,
|
|
+ INDEX_VANC,
|
|
+ INDEX_HANC,
|
|
+ INDEX_IR,
|
|
+};
|
|
+
|
|
+/***************************************************************************
|
|
+ * configuration information define *
|
|
+***************************************************************************/
|
|
+struct CONFIG_INFO {
|
|
+ u8 config_index;
|
|
+ struct INTERFACE_INFO interface_info;
|
|
+};
|
|
+
|
|
+struct pcb_config {
|
|
+ u8 index;
|
|
+ u8 type; /* bus power or self power,
|
|
+ self power--0, bus_power--1 */
|
|
+ u8 speed; /* usb speed, 2.0--1, 1.1--0 */
|
|
+ u8 mode; /* digital , anlog, dif or external A/V */
|
|
+ u32 ts1_source; /* three source -- BDA,External,encode */
|
|
+ u32 ts2_source;
|
|
+ u32 analog_source;
|
|
+ u8 digital_index; /* bus-power used */
|
|
+ u8 analog_index; /* bus-power used */
|
|
+ u8 dif_index; /* bus-power used */
|
|
+ u8 external_index; /* bus-power used */
|
|
+ u8 config_num; /* current config num, 0,1,2,
|
|
+ for self-power, always 0 */
|
|
+ struct CONFIG_INFO hs_config_info[3];
|
|
+ struct CONFIG_INFO fs_config_info[3];
|
|
+};
|
|
+
|
|
+enum INDEX_PCB_CONFIG{
|
|
+ INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
|
|
+ INDEX_SELFPOWER_DUAL_DIGITAL,
|
|
+ INDEX_SELFPOWER_ANALOG_ONLY,
|
|
+ INDEX_SELFPOWER_DUAL,
|
|
+ INDEX_SELFPOWER_TRIPLE,
|
|
+ INDEX_SELFPOWER_COMPRESSOR,
|
|
+ INDEX_BUSPOWER_DIGITAL_ONLY,
|
|
+ INDEX_BUSPOWER_ANALOG_ONLY,
|
|
+ INDEX_BUSPOWER_DIF_ONLY,
|
|
+ INDEX_BUSPOWER_EXTERNAL_ONLY,
|
|
+ INDEX_BUSPOWER_EXTERNAL_ANALOG,
|
|
+ INDEX_BUSPOWER_EXTERNAL_DIF,
|
|
+ INDEX_BUSPOWER_EXTERNAL_DIGITAL,
|
|
+ INDEX_BUSPOWER_DIGITAL_ANALOG,
|
|
+ INDEX_BUSPOWER_DIGITAL_DIF,
|
|
+ INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
|
|
+ INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
|
|
+};
|
|
+
|
|
+/***************************************************************************/
|
|
+struct cx231xx;
|
|
+
|
|
+u32 initialize_cx231xx(struct cx231xx *p_dev);
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-reg.h b/drivers/media/video/cx231xx/cx231xx-reg.h
|
|
new file mode 100644
|
|
index 0000000..750c5d3
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-reg.h
|
|
@@ -0,0 +1,1564 @@
|
|
+/*
|
|
+ cx231xx-reg.h - driver for Conexant Cx23100/101/102
|
|
+ USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+
|
|
+ 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.
|
|
+ */
|
|
+
|
|
+#ifndef _CX231XX_REG_H
|
|
+#define _CX231XX_REG_H
|
|
+
|
|
+/*****************************************************************************
|
|
+ * VBI codes *
|
|
+*****************************************************************************/
|
|
+
|
|
+#define SAV_ACTIVE_VIDEO_FIELD1 0x80
|
|
+#define EAV_ACTIVE_VIDEO_FIELD1 0x90
|
|
+
|
|
+#define SAV_ACTIVE_VIDEO_FIELD2 0xc0
|
|
+#define EAV_ACTIVE_VIDEO_FIELD2 0xd0
|
|
+
|
|
+#define SAV_VBLANK_FIELD1 0xa0
|
|
+#define EAV_VBLANK_FIELD1 0xb0
|
|
+
|
|
+#define SAV_VBLANK_FIELD2 0xe0
|
|
+#define EAV_VBLANK_FIELD2 0xf0
|
|
+
|
|
+#define SAV_VBI_FIELD1 0x20
|
|
+#define EAV_VBI_FIELD1 0x30
|
|
+
|
|
+#define SAV_VBI_FIELD2 0x60
|
|
+#define EAV_VBI_FIELD2 0x70
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Audio ADC Registers */
|
|
+#define CH_PWR_CTRL1 0x0000000e
|
|
+#define CH_PWR_CTRL2 0x0000000f
|
|
+/*****************************************************************************/
|
|
+
|
|
+#define HOST_REG1 0x000
|
|
+#define FLD_FORCE_CHIP_SEL 0x80
|
|
+#define FLD_AUTO_INC_DIS 0x20
|
|
+#define FLD_PREFETCH_EN 0x10
|
|
+/* Reserved [2:3] */
|
|
+#define FLD_DIGITAL_PWR_DN 0x02
|
|
+#define FLD_SLEEP 0x01
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define HOST_REG2 0x001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define HOST_REG3 0x002
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* added for polaris */
|
|
+#define GPIO_PIN_CTL0 0x3
|
|
+#define GPIO_PIN_CTL1 0x4
|
|
+#define GPIO_PIN_CTL2 0x5
|
|
+#define GPIO_PIN_CTL3 0x6
|
|
+#define TS1_PIN_CTL0 0x7
|
|
+#define TS1_PIN_CTL1 0x8
|
|
+/*****************************************************************************/
|
|
+
|
|
+#define FLD_CLK_IN_EN 0x80
|
|
+#define FLD_XTAL_CTRL 0x70
|
|
+#define FLD_BB_CLK_MODE 0x0C
|
|
+#define FLD_REF_DIV_PLL 0x02
|
|
+#define FLD_REF_SEL_PLL1 0x01
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define CHIP_CTRL 0x100
|
|
+/* Reserved [27] */
|
|
+/* Reserved [31:21] */
|
|
+#define FLD_CHIP_ACFG_DIS 0x00100000
|
|
+/* Reserved [19] */
|
|
+#define FLD_DUAL_MODE_ADC2 0x00040000
|
|
+#define FLD_SIF_EN 0x00020000
|
|
+#define FLD_SOFT_RST 0x00010000
|
|
+#define FLD_DEVICE_ID 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AFE_CTRL 0x104
|
|
+#define AFE_CTRL_C2HH_SRC_CTRL 0x104
|
|
+#define FLD_DIF_OUT_SEL 0xc0000000
|
|
+#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000
|
|
+#define FLD_UV_ORDER_MODE 0x02000000
|
|
+#define FLD_FUNC_MODE 0x01800000
|
|
+#define FLD_ROT1_PHASE_CTL 0x007f8000
|
|
+#define FLD_AUD_IN_SEL 0x00004000
|
|
+#define FLD_LUMA_IN_SEL 0x00002000
|
|
+#define FLD_CHROMA_IN_SEL 0x00001000
|
|
+/* reserve [11:10] */
|
|
+#define FLD_INV_SPEC_DIS 0x00000200
|
|
+#define FLD_VGA_SEL_CH3 0x00000100
|
|
+#define FLD_VGA_SEL_CH2 0x00000080
|
|
+#define FLD_VGA_SEL_CH1 0x00000040
|
|
+#define FLD_DCR_BYP_CH1 0x00000020
|
|
+#define FLD_DCR_BYP_CH2 0x00000010
|
|
+#define FLD_DCR_BYP_CH3 0x00000008
|
|
+#define FLD_EN_12DB_CH3 0x00000004
|
|
+#define FLD_EN_12DB_CH2 0x00000002
|
|
+#define FLD_EN_12DB_CH1 0x00000001
|
|
+
|
|
+/* redefine in Cx231xx */
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL1 0x108
|
|
+/* reserve [31:30] */
|
|
+#define FLD_CLAMP_LVL_CH1 0x3fff8000
|
|
+#define FLD_CLAMP_LVL_CH2 0x00007fff
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL2 0x10c
|
|
+/* reserve [31:28] */
|
|
+#define FLD_CLAMP_LVL_CH3 0x00fffe00
|
|
+#define FLD_CLAMP_WIND_LENTH 0x000001e0
|
|
+#define FLD_C2HH_SAT_MIN 0x0000001e
|
|
+#define FLD_FLT_BYP_SEL 0x00000001
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL3 0x110
|
|
+/* reserve [31:16] */
|
|
+#define FLD_ERR_GAIN_CTL 0x00070000
|
|
+#define FLD_LPF_MIN 0x0000ffff
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL4 0x114
|
|
+/* reserve [31:31] */
|
|
+#define FLD_INTG_CH1 0x7fffffff
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL5 0x118
|
|
+/* reserve [31:31] */
|
|
+#define FLD_INTG_CH2 0x7fffffff
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DC_CTRL6 0x11c
|
|
+/* reserve [31:31] */
|
|
+#define FLD_INTG_CH3 0x7fffffff
|
|
+/*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PIN_CTRL 0x120
|
|
+#define FLD_OEF_AGC_RF 0x00000001
|
|
+#define FLD_OEF_AGC_IFVGA 0x00000002
|
|
+#define FLD_OEF_AGC_IF 0x00000004
|
|
+#define FLD_REG_BO_PUD 0x80000000
|
|
+#define FLD_IR_IRQ_STAT 0x40000000
|
|
+#define FLD_AUD_IRQ_STAT 0x20000000
|
|
+#define FLD_VID_IRQ_STAT 0x10000000
|
|
+/* Reserved [27:26] */
|
|
+#define FLD_IRQ_N_OUT_EN 0x02000000
|
|
+#define FLD_IRQ_N_POLAR 0x01000000
|
|
+/* Reserved [23:6] */
|
|
+#define FLD_OE_AUX_PLL_CLK 0x00000020
|
|
+#define FLD_OE_I2S_BCLK 0x00000010
|
|
+#define FLD_OE_I2S_WCLK 0x00000008
|
|
+#define FLD_OE_AGC_IF 0x00000004
|
|
+#define FLD_OE_AGC_IFVGA 0x00000002
|
|
+#define FLD_OE_AGC_RF 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AUD_IO_CTRL 0x124
|
|
+/* Reserved [31:8] */
|
|
+#define FLD_I2S_PORT_DIR 0x00000080
|
|
+#define FLD_I2S_OUT_SRC 0x00000040
|
|
+#define FLD_AUD_CHAN3_SRC 0x00000030
|
|
+#define FLD_AUD_CHAN2_SRC 0x0000000c
|
|
+#define FLD_AUD_CHAN1_SRC 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AUD_LOCK1 0x128
|
|
+#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000
|
|
+#define FLD_AUD_LOCK_KD_SHIFT 0x30000000
|
|
+/* Reserved [27:25] */
|
|
+#define FLD_EN_AV_LOCK 0x01000000
|
|
+#define FLD_VID_COUNT 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AUD_LOCK2 0x12c
|
|
+#define FLD_AUD_LOCK_KI_MULT 0xf0000000
|
|
+#define FLD_AUD_LOCK_KD_MULT 0x0F000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000
|
|
+#define FLD_AUD_COUNT 0x000fffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AFE_DIAG_CTRL1 0x134
|
|
+/* Reserved [31:16] */
|
|
+#define FLD_CUV_DLY_LENGTH 0x0000ff00
|
|
+#define FLD_YC_DLY_LENGTH 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Poalris redefine */
|
|
+#define AFE_DIAG_CTRL3 0x138
|
|
+/* Reserved [31:26] */
|
|
+#define FLD_AUD_DUAL_FLAG_POL 0x02000000
|
|
+#define FLD_VID_DUAL_FLAG_POL 0x01000000
|
|
+/* Reserved [23:23] */
|
|
+#define FLD_COL_CLAMP_DIS_CH1 0x00400000
|
|
+#define FLD_COL_CLAMP_DIS_CH2 0x00200000
|
|
+#define FLD_COL_CLAMP_DIS_CH3 0x00100000
|
|
+
|
|
+#define TEST_CTRL1 0x144
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_LBIST_EN 0x10000000
|
|
+/* Reserved [27:10] */
|
|
+#define FLD_FI_BIST_INTR_R 0x0000200
|
|
+#define FLD_FI_BIST_INTR_L 0x0000100
|
|
+#define FLD_BIST_FAIL_AUD_PLL 0x0000080
|
|
+#define FLD_BIST_INTR_AUD_PLL 0x0000040
|
|
+#define FLD_BIST_FAIL_VID_PLL 0x0000020
|
|
+#define FLD_BIST_INTR_VID_PLL 0x0000010
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_CIR_TEST_DIS 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define TEST_CTRL2 0x148
|
|
+#define FLD_TSXCLK_POL_CTL 0x80000000
|
|
+#define FLD_ISO_CTL_SEL 0x40000000
|
|
+#define FLD_ISO_CTL_EN 0x20000000
|
|
+#define FLD_BIST_DEBUGZ 0x10000000
|
|
+#define FLD_AUD_BIST_TEST_H 0x0f000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_FLTRN_BIST_TEST_H 0x00020000
|
|
+#define FLD_VID_BIST_TEST_H 0x00010000
|
|
+/* Reserved [19:17] */
|
|
+#define FLD_BIST_TEST_H 0x00010000
|
|
+/* Reserved [15:13] */
|
|
+#define FLD_TAB_EN 0x00001000
|
|
+/* Reserved [11:0] */
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define BIST_STAT 0x14c
|
|
+#define FLD_AUD_BIST_FAIL_H 0xfff00000
|
|
+#define FLD_FLTRN_BIST_FAIL_H 0x00180000
|
|
+#define FLD_VID_BIST_FAIL_H 0x00070000
|
|
+#define FLD_AUD_BIST_TST_DONE 0x0000fff0
|
|
+#define FLD_FLTRN_BIST_TST_DONE 0x00000008
|
|
+#define FLD_VID_BIST_TST_DONE 0x00000007
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* DirectIF registers definition have been moved to DIF_reg.h */
|
|
+/*****************************************************************************/
|
|
+#define MODE_CTRL 0x400
|
|
+#define FLD_AFD_PAL60_DIS 0x20000000
|
|
+#define FLD_AFD_FORCE_SECAM 0x10000000
|
|
+#define FLD_AFD_FORCE_PALNC 0x08000000
|
|
+#define FLD_AFD_FORCE_PAL 0x04000000
|
|
+#define FLD_AFD_PALM_SEL 0x03000000
|
|
+#define FLD_CKILL_MODE 0x00300000
|
|
+#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */
|
|
+#define FLD_CLR_LOCK_STAT 0x00020000
|
|
+#define FLD_FAST_LOCK_MD 0x00010000
|
|
+#define FLD_WCEN 0x00008000
|
|
+#define FLD_CAGCEN 0x00004000
|
|
+#define FLD_CKILLEN 0x00002000
|
|
+#define FLD_AUTO_SC_LOCK 0x00001000
|
|
+#define FLD_MAN_SC_FAST_LOCK 0x00000800
|
|
+#define FLD_INPUT_MODE 0x00000600
|
|
+#define FLD_AFD_ACQUIRE 0x00000100
|
|
+#define FLD_AFD_NTSC_SEL 0x00000080
|
|
+#define FLD_AFD_PAL_SEL 0x00000040
|
|
+#define FLD_ACFG_DIS 0x00000020
|
|
+#define FLD_SQ_PIXEL 0x00000010
|
|
+#define FLD_VID_FMT_SEL 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define OUT_CTRL1 0x404
|
|
+#define FLD_POLAR 0x7f000000
|
|
+/* Reserved [23] */
|
|
+#define FLD_RND_MODE 0x00600000
|
|
+#define FLD_VIPCLAMP_EN 0x00100000
|
|
+#define FLD_VIPBLANK_EN 0x00080000
|
|
+#define FLD_VIP_OPT_AL 0x00040000
|
|
+#define FLD_IDID0_SOURCE 0x00020000
|
|
+#define FLD_DCMODE 0x00010000
|
|
+#define FLD_CLK_GATING 0x0000c000
|
|
+#define FLD_CLK_INVERT 0x00002000
|
|
+#define FLD_HSFMT 0x00001000
|
|
+#define FLD_VALIDFMT 0x00000800
|
|
+#define FLD_ACTFMT 0x00000400
|
|
+#define FLD_SWAPRAW 0x00000200
|
|
+#define FLD_CLAMPRAW_EN 0x00000100
|
|
+#define FLD_BLUE_FIELD_EN 0x00000080
|
|
+#define FLD_BLUE_FIELD_ACT 0x00000040
|
|
+#define FLD_TASKBIT_VAL 0x00000020
|
|
+#define FLD_ANC_DATA_EN 0x00000010
|
|
+#define FLD_VBIHACTRAW_EN 0x00000008
|
|
+#define FLD_MODE10B 0x00000004
|
|
+#define FLD_OUT_MODE 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define OUT_CTRL2 0x408
|
|
+#define FLD_AUD_GRP 0xc0000000
|
|
+#define FLD_SAMPLE_RATE 0x30000000
|
|
+#define FLD_AUD_ANC_EN 0x08000000
|
|
+#define FLD_EN_C 0x04000000
|
|
+#define FLD_EN_B 0x02000000
|
|
+#define FLD_EN_A 0x01000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_IDID1_LSB 0x000c0000
|
|
+#define FLD_IDID0_LSB 0x00030000
|
|
+#define FLD_IDID1_MSB 0x0000ff00
|
|
+#define FLD_IDID0_MSB 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define GEN_STAT 0x40c
|
|
+#define FLD_VCR_DETECT 0x00800000
|
|
+#define FLD_SPECIAL_PLAY_N 0x00400000
|
|
+#define FLD_VPRES 0x00200000
|
|
+#define FLD_AGC_LOCK 0x00100000
|
|
+#define FLD_CSC_LOCK 0x00080000
|
|
+#define FLD_VLOCK 0x00040000
|
|
+#define FLD_SRC_LOCK 0x00020000
|
|
+#define FLD_HLOCK 0x00010000
|
|
+#define FLD_VSYNC_N 0x00008000
|
|
+#define FLD_SRC_FIFO_UFLOW 0x00004000
|
|
+#define FLD_SRC_FIFO_OFLOW 0x00002000
|
|
+#define FLD_FIELD 0x00001000
|
|
+#define FLD_AFD_FMT_STAT 0x00000f00
|
|
+#define FLD_MV_TYPE2_PAIR 0x00000080
|
|
+#define FLD_MV_T3CS 0x00000040
|
|
+#define FLD_MV_CS 0x00000020
|
|
+#define FLD_MV_PSP 0x00000010
|
|
+/* Reserved [3] */
|
|
+#define FLD_MV_CDAT 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define INT_STAT_MASK 0x410
|
|
+#define FLD_COMB_3D_FIFO_MSK 0x80000000
|
|
+#define FLD_WSS_DAT_AVAIL_MSK 0x40000000
|
|
+#define FLD_GS2_DAT_AVAIL_MSK 0x20000000
|
|
+#define FLD_GS1_DAT_AVAIL_MSK 0x10000000
|
|
+#define FLD_CC_DAT_AVAIL_MSK 0x08000000
|
|
+#define FLD_VPRES_CHANGE_MSK 0x04000000
|
|
+#define FLD_MV_CHANGE_MSK 0x02000000
|
|
+#define FLD_END_VBI_EVEN_MSK 0x01000000
|
|
+#define FLD_END_VBI_ODD_MSK 0x00800000
|
|
+#define FLD_FMT_CHANGE_MSK 0x00400000
|
|
+#define FLD_VSYNC_TRAIL_MSK 0x00200000
|
|
+#define FLD_HLOCK_CHANGE_MSK 0x00100000
|
|
+#define FLD_VLOCK_CHANGE_MSK 0x00080000
|
|
+#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000
|
|
+#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000
|
|
+#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000
|
|
+#define FLD_COMB_3D_FIFO_STAT 0x00008000
|
|
+#define FLD_WSS_DAT_AVAIL_STAT 0x00004000
|
|
+#define FLD_GS2_DAT_AVAIL_STAT 0x00002000
|
|
+#define FLD_GS1_DAT_AVAIL_STAT 0x00001000
|
|
+#define FLD_CC_DAT_AVAIL_STAT 0x00000800
|
|
+#define FLD_VPRES_CHANGE_STAT 0x00000400
|
|
+#define FLD_MV_CHANGE_STAT 0x00000200
|
|
+#define FLD_END_VBI_EVEN_STAT 0x00000100
|
|
+#define FLD_END_VBI_ODD_STAT 0x00000080
|
|
+#define FLD_FMT_CHANGE_STAT 0x00000040
|
|
+#define FLD_VSYNC_TRAIL_STAT 0x00000020
|
|
+#define FLD_HLOCK_CHANGE_STAT 0x00000010
|
|
+#define FLD_VLOCK_CHANGE_STAT 0x00000008
|
|
+#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004
|
|
+#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002
|
|
+#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define LUMA_CTRL 0x414
|
|
+#define BRIGHTNESS_CTRL_BYTE 0x414
|
|
+#define CONTRAST_CTRL_BYTE 0x415
|
|
+#define LUMA_CTRL_BYTE_3 0x416
|
|
+#define FLD_LUMA_CORE_SEL 0x00c00000
|
|
+#define FLD_RANGE 0x00300000
|
|
+/* Reserved [19] */
|
|
+#define FLD_PEAK_EN 0x00040000
|
|
+#define FLD_PEAK_SEL 0x00030000
|
|
+#define FLD_CNTRST 0x0000ff00
|
|
+#define FLD_BRITE 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define HSCALE_CTRL 0x418
|
|
+#define FLD_HFILT 0x03000000
|
|
+#define FLD_HSCALE 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VSCALE_CTRL 0x41c
|
|
+#define FLD_LINE_AVG_DIS 0x01000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_VS_INTRLACE 0x00080000
|
|
+#define FLD_VFILT 0x00070000
|
|
+/* Reserved [15:13] */
|
|
+#define FLD_VSCALE 0x00001fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define CHROMA_CTRL 0x420
|
|
+#define USAT_CTRL_BYTE 0x420
|
|
+#define VSAT_CTRL_BYTE 0x421
|
|
+#define HUE_CTRL_BYTE 0x422
|
|
+#define FLD_C_LPF_EN 0x20000000
|
|
+#define FLD_CHR_DELAY 0x1c000000
|
|
+#define FLD_C_CORE_SEL 0x03000000
|
|
+#define FLD_HUE 0x00ff0000
|
|
+#define FLD_VSAT 0x0000ff00
|
|
+#define FLD_USAT 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_LINE_CTRL1 0x424
|
|
+#define FLD_VBI_MD_LINE4 0xff000000
|
|
+#define FLD_VBI_MD_LINE3 0x00ff0000
|
|
+#define FLD_VBI_MD_LINE2 0x0000ff00
|
|
+#define FLD_VBI_MD_LINE1 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_LINE_CTRL2 0x428
|
|
+#define FLD_VBI_MD_LINE8 0xff000000
|
|
+#define FLD_VBI_MD_LINE7 0x00ff0000
|
|
+#define FLD_VBI_MD_LINE6 0x0000ff00
|
|
+#define FLD_VBI_MD_LINE5 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_LINE_CTRL3 0x42c
|
|
+#define FLD_VBI_MD_LINE12 0xff000000
|
|
+#define FLD_VBI_MD_LINE11 0x00ff0000
|
|
+#define FLD_VBI_MD_LINE10 0x0000ff00
|
|
+#define FLD_VBI_MD_LINE9 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_LINE_CTRL4 0x430
|
|
+#define FLD_VBI_MD_LINE16 0xff000000
|
|
+#define FLD_VBI_MD_LINE15 0x00ff0000
|
|
+#define FLD_VBI_MD_LINE14 0x0000ff00
|
|
+#define FLD_VBI_MD_LINE13 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_LINE_CTRL5 0x434
|
|
+#define FLD_VBI_MD_LINE17 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_FC_CFG 0x438
|
|
+#define FLD_FC_ALT2 0xff000000
|
|
+#define FLD_FC_ALT1 0x00ff0000
|
|
+#define FLD_FC_ALT2_TYPE 0x0000f000
|
|
+#define FLD_FC_ALT1_TYPE 0x00000f00
|
|
+/* Reserved [7:1] */
|
|
+#define FLD_FC_SEARCH_MODE 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_MISC_CFG1 0x43c
|
|
+#define FLD_TTX_PKTADRU 0xfff00000
|
|
+#define FLD_TTX_PKTADRL 0x000fff00
|
|
+/* Reserved [7:6] */
|
|
+#define FLD_MOJI_PACK_DIS 0x00000020
|
|
+#define FLD_VPS_DEC_DIS 0x00000010
|
|
+#define FLD_CRI_MARG_SCALE 0x0000000c
|
|
+#define FLD_EDGE_RESYNC_EN 0x00000002
|
|
+#define FLD_ADAPT_SLICE_DIS 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_MISC_CFG2 0x440
|
|
+#define FLD_HAMMING_TYPE 0x0f000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_WSS_FIFO_RST 0x00080000
|
|
+#define FLD_GS2_FIFO_RST 0x00040000
|
|
+#define FLD_GS1_FIFO_RST 0x00020000
|
|
+#define FLD_CC_FIFO_RST 0x00010000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_VBI3_SDID 0x00000f00
|
|
+#define FLD_VBI2_SDID 0x000000f0
|
|
+#define FLD_VBI1_SDID 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_PAY1 0x444
|
|
+#define FLD_GS1_FIFO_DAT 0xFF000000
|
|
+#define FLD_GS1_STAT 0x00FF0000
|
|
+#define FLD_CC_FIFO_DAT 0x0000FF00
|
|
+#define FLD_CC_STAT 0x000000FF
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_PAY2 0x448
|
|
+#define FLD_WSS_FIFO_DAT 0xff000000
|
|
+#define FLD_WSS_STAT 0x00ff0000
|
|
+#define FLD_GS2_FIFO_DAT 0x0000ff00
|
|
+#define FLD_GS2_STAT 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST1_CFG1 0x44c
|
|
+/* Reserved [31] */
|
|
+#define FLD_VBI1_CRIWIN 0x7f000000
|
|
+#define FLD_VBI1_SLICE_DIST 0x00f00000
|
|
+#define FLD_VBI1_BITINC 0x000fff00
|
|
+#define FLD_VBI1_HDELAY 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST1_CFG2 0x450
|
|
+#define FLD_VBI1_FC_LENGTH 0x1f000000
|
|
+#define FLD_VBI1_FRAME_CODE 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST1_CFG3 0x454
|
|
+#define FLD_VBI1_HAM_EN 0x80000000
|
|
+#define FLD_VBI1_FIFO_MODE 0x70000000
|
|
+#define FLD_VBI1_FORMAT_TYPE 0x0f000000
|
|
+#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000
|
|
+#define FLD_VBI1_CRI_LENGTH 0x0000f000
|
|
+#define FLD_VBI1_CRI_MARGIN 0x00000f00
|
|
+#define FLD_VBI1_CRI_TIME 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST2_CFG1 0x458
|
|
+/* Reserved [31] */
|
|
+#define FLD_VBI2_CRIWIN 0x7f000000
|
|
+#define FLD_VBI2_SLICE_DIST 0x00f00000
|
|
+#define FLD_VBI2_BITINC 0x000fff00
|
|
+#define FLD_VBI2_HDELAY 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST2_CFG2 0x45c
|
|
+#define FLD_VBI2_FC_LENGTH 0x1f000000
|
|
+#define FLD_VBI2_FRAME_CODE 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST2_CFG3 0x460
|
|
+#define FLD_VBI2_HAM_EN 0x80000000
|
|
+#define FLD_VBI2_FIFO_MODE 0x70000000
|
|
+#define FLD_VBI2_FORMAT_TYPE 0x0f000000
|
|
+#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000
|
|
+#define FLD_VBI2_CRI_LENGTH 0x0000f000
|
|
+#define FLD_VBI2_CRI_MARGIN 0x00000f00
|
|
+#define FLD_VBI2_CRI_TIME 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST3_CFG1 0x464
|
|
+/* Reserved [31] */
|
|
+#define FLD_VBI3_CRIWIN 0x7f000000
|
|
+#define FLD_VBI3_SLICE_DIST 0x00f00000
|
|
+#define FLD_VBI3_BITINC 0x000fff00
|
|
+#define FLD_VBI3_HDELAY 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST3_CFG2 0x468
|
|
+#define FLD_VBI3_FC_LENGTH 0x1f000000
|
|
+#define FLD_VBI3_FRAME_CODE 0x00ffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_CUST3_CFG3 0x46c
|
|
+#define FLD_VBI3_HAM_EN 0x80000000
|
|
+#define FLD_VBI3_FIFO_MODE 0x70000000
|
|
+#define FLD_VBI3_FORMAT_TYPE 0x0f000000
|
|
+#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000
|
|
+#define FLD_VBI3_CRI_LENGTH 0x0000f000
|
|
+#define FLD_VBI3_CRI_MARGIN 0x00000f00
|
|
+#define FLD_VBI3_CRI_TIME 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define HORIZ_TIM_CTRL 0x470
|
|
+#define FLD_BGDEL_CNT 0xff000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_HACTIVE_CNT 0x003ff000
|
|
+/* Reserved [11:10] */
|
|
+#define FLD_HBLANK_CNT 0x000003ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VERT_TIM_CTRL 0x474
|
|
+#define FLD_V656BLANK_CNT 0xff000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_VACTIVE_CNT 0x003ff000
|
|
+/* Reserved [11:10] */
|
|
+#define FLD_VBLANK_CNT 0x000003ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC_COMB_CFG 0x478
|
|
+#define FLD_CCOMB_2LN_CHECK 0x80000000
|
|
+#define FLD_CCOMB_3LN_EN 0x40000000
|
|
+#define FLD_CCOMB_2LN_EN 0x20000000
|
|
+#define FLD_CCOMB_3D_EN 0x10000000
|
|
+/* Reserved [27] */
|
|
+#define FLD_LCOMB_3LN_EN 0x04000000
|
|
+#define FLD_LCOMB_2LN_EN 0x02000000
|
|
+#define FLD_LCOMB_3D_EN 0x01000000
|
|
+#define FLD_LUMA_LPF_SEL 0x00c00000
|
|
+#define FLD_UV_LPF_SEL 0x00300000
|
|
+#define FLD_BLEND_SLOPE 0x000f0000
|
|
+#define FLD_CCOMB_REDUCE_EN 0x00008000
|
|
+/* Reserved [14:10] */
|
|
+#define FLD_SRC_DECIM_RATIO 0x000003ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define CHROMA_VBIOFF_CFG 0x47c
|
|
+#define FLD_VBI_VOFFSET 0x1f000000
|
|
+/* Reserved [23:20] */
|
|
+#define FLD_SC_STEP 0x000fffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FIELD_COUNT 0x480
|
|
+#define FLD_FIELD_COUNT_FLD 0x000003ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MISC_TIM_CTRL 0x484
|
|
+#define FLD_DEBOUNCE_COUNT 0xc0000000
|
|
+#define FLD_VT_LINE_CNT_HYST 0x30000000
|
|
+/* Reserved [27] */
|
|
+#define FLD_AFD_STAT 0x07ff0000
|
|
+#define FLD_VPRES_VERT_EN 0x00008000
|
|
+/* Reserved [14:12] */
|
|
+#define FLD_HR32 0x00000800
|
|
+#define FLD_TDALGN 0x00000400
|
|
+#define FLD_TDFIELD 0x00000200
|
|
+/* Reserved [8:6] */
|
|
+#define FLD_TEMPDEC 0x0000003f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFE_CTRL1 0x488
|
|
+#define FLD_CLAMP_AUTO_EN 0x80000000
|
|
+#define FLD_AGC_AUTO_EN 0x40000000
|
|
+#define FLD_VGA_CRUSH_EN 0x20000000
|
|
+#define FLD_VGA_AUTO_EN 0x10000000
|
|
+#define FLD_VBI_GATE_EN 0x08000000
|
|
+#define FLD_CLAMP_LEVEL 0x07000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_CLAMP_SKIP_CNT 0x00300000
|
|
+#define FLD_AGC_GAIN 0x000fff00
|
|
+/* Reserved [7:6] */
|
|
+#define FLD_VGA_GAIN 0x0000003f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFE_CTRL2 0x48c
|
|
+#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000
|
|
+#define FLD_VGA_TRACK_RANGE 0x0000ff00
|
|
+#define FLD_VGA_SYNC 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFE_CTRL3 0x490
|
|
+#define FLD_BP_PERCENT 0xff000000
|
|
+#define FLD_DFT_THRESHOLD 0x00ff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_SYNC_WIDTH_SEL 0x00000600
|
|
+#define FLD_BP_LOOP_GAIN 0x00000300
|
|
+#define FLD_SYNC_LOOP_GAIN 0x000000c0
|
|
+/* Reserved [5:4] */
|
|
+#define FLD_AGC_LOOP_GAIN 0x0000000c
|
|
+#define FLD_DCC_LOOP_GAIN 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PLL_CTRL 0x494
|
|
+#define FLD_PLL_KD 0xff000000
|
|
+#define FLD_PLL_KI 0x00ff0000
|
|
+#define FLD_PLL_MAX_OFFSET 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define HTL_CTRL 0x498
|
|
+/* Reserved [31:24] */
|
|
+#define FLD_AUTO_LOCK_SPD 0x00080000
|
|
+#define FLD_MAN_FAST_LOCK 0x00040000
|
|
+#define FLD_HTL_15K_EN 0x00020000
|
|
+#define FLD_HTL_500K_EN 0x00010000
|
|
+#define FLD_HTL_KD 0x0000ff00
|
|
+#define FLD_HTL_KI 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define COMB_CTRL 0x49c
|
|
+#define FLD_COMB_PHASE_LIMIT 0xff000000
|
|
+#define FLD_CCOMB_ERR_LIMIT 0x00ff0000
|
|
+#define FLD_LUMA_THRESHOLD 0x0000ff00
|
|
+#define FLD_LCOMB_ERR_LIMIT 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define CRUSH_CTRL 0x4a0
|
|
+#define FLD_WTW_EN 0x00400000
|
|
+#define FLD_CRUSH_FREQ 0x00200000
|
|
+#define FLD_MAJ_SEL_EN 0x00100000
|
|
+#define FLD_MAJ_SEL 0x000c0000
|
|
+/* Reserved [17:15] */
|
|
+#define FLD_SYNC_TIP_REDUCE 0x00007e00
|
|
+/* Reserved [8:6] */
|
|
+#define FLD_SYNC_TIP_INC 0x0000003f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SOFT_RST_CTRL 0x4a4
|
|
+#define FLD_VD_SOFT_RST 0x00008000
|
|
+/* Reserved [14:12] */
|
|
+#define FLD_REG_RST_MSK 0x00000800
|
|
+#define FLD_VOF_RST_MSK 0x00000400
|
|
+#define FLD_MVDET_RST_MSK 0x00000200
|
|
+#define FLD_VBI_RST_MSK 0x00000100
|
|
+#define FLD_SCALE_RST_MSK 0x00000080
|
|
+#define FLD_CHROMA_RST_MSK 0x00000040
|
|
+#define FLD_LUMA_RST_MSK 0x00000020
|
|
+#define FLD_VTG_RST_MSK 0x00000010
|
|
+#define FLD_YCSEP_RST_MSK 0x00000008
|
|
+#define FLD_SRC_RST_MSK 0x00000004
|
|
+#define FLD_DFE_RST_MSK 0x00000002
|
|
+/* Reserved [0] */
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MV_DT_CTRL1 0x4a8
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_PSP_STOP_LINE 0x1f000000
|
|
+/* Reserved [23:21] */
|
|
+#define FLD_PSP_STRT_LINE 0x001f0000
|
|
+/* Reserved [15] */
|
|
+#define FLD_PSP_LLIMW 0x00007f00
|
|
+/* Reserved [7] */
|
|
+#define FLD_PSP_ULIMW 0x0000007f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MV_DT_CTRL2 0x4aC
|
|
+#define FLD_CS_STOPWIN 0xff000000
|
|
+#define FLD_CS_STRTWIN 0x00ff0000
|
|
+#define FLD_CS_WIDTH 0x0000ff00
|
|
+#define FLD_PSP_SPEC_VAL 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MV_DT_CTRL3 0x4B0
|
|
+#define FLD_AUTO_RATE_DIS 0x80000000
|
|
+#define FLD_HLOCK_DIS 0x40000000
|
|
+#define FLD_SEL_FIELD_CNT 0x20000000
|
|
+#define FLD_CS_TYPE2_SEL 0x10000000
|
|
+#define FLD_CS_LINE_THRSH_SEL 0x08000000
|
|
+#define FLD_CS_ATHRESH_SEL 0x04000000
|
|
+#define FLD_PSP_SPEC_SEL 0x02000000
|
|
+#define FLD_PSP_LINES_SEL 0x01000000
|
|
+#define FLD_FIELD_CNT 0x00f00000
|
|
+#define FLD_CS_TYPE2_CNT 0x000fc000
|
|
+#define FLD_CS_LINE_CNT 0x00003f00
|
|
+#define FLD_CS_ATHRESH_LEV 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define CHIP_VERSION 0x4b4
|
|
+/* Cx231xx redefine */
|
|
+#define VERSION 0x4b4
|
|
+#define FLD_REV_ID 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MISC_DIAG_CTRL 0x4b8
|
|
+/* Reserved [31:24] */
|
|
+#define FLD_SC_CONVERGE_THRESH 0x00ff0000
|
|
+#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00
|
|
+#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define VBI_PASS_CTRL 0x4bc
|
|
+#define FLD_VBI_PASS_MD 0x00200000
|
|
+#define FLD_VBI_SETUP_DIS 0x00100000
|
|
+#define FLD_PASS_LINE_CTRL 0x000fffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define VCR_DET_CTRL 0x4c0
|
|
+#define FLD_EN_FIELD_PHASE_DET 0x80000000
|
|
+#define FLD_EN_HEAD_SW_DET 0x40000000
|
|
+#define FLD_FIELD_PHASE_LENGTH 0x01ff0000
|
|
+/* Reserved [29:25] */
|
|
+#define FLD_FIELD_PHASE_DELAY 0x0000ff00
|
|
+#define FLD_FIELD_PHASE_LIMIT 0x000000f0
|
|
+#define FLD_HEAD_SW_DET_LIMIT 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DL_CTL 0x800
|
|
+#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
|
|
+#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
|
|
+#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
|
|
+#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
|
|
+/* Reserved [31:5] */
|
|
+#define FLD_START_8051 0x10000000
|
|
+#define FLD_DL_ENABLE 0x08000000
|
|
+#define FLD_DL_AUTO_INC 0x04000000
|
|
+#define FLD_DL_MAP 0x03000000
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define STD_DET_STATUS 0x804
|
|
+#define FLD_SPARE_STATUS1 0xff000000
|
|
+#define FLD_SPARE_STATUS0 0x00ff0000
|
|
+#define FLD_MOD_DET_STATUS1 0x0000ff00
|
|
+#define FLD_MOD_DET_STATUS0 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AUD_BUILD_NUM 0x806
|
|
+#define AUD_VER_NUM 0x807
|
|
+#define STD_DET_CTL 0x808
|
|
+#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
|
|
+#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
|
|
+#define FLD_SPARE_CTL0 0xff000000
|
|
+#define FLD_DIS_DBX 0x00800000
|
|
+#define FLD_DIS_BTSC 0x00400000
|
|
+#define FLD_DIS_NICAM_A2 0x00200000
|
|
+#define FLD_VIDEO_PRESENT 0x00100000
|
|
+#define FLD_DW8051_VIDEO_FORMAT 0x000f0000
|
|
+#define FLD_PREF_DEC_MODE 0x0000ff00
|
|
+#define FLD_AUD_CONFIG 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DW8051_INT 0x80c
|
|
+#define FLD_VIDEO_PRESENT_CHANGE 0x80000000
|
|
+#define FLD_VIDEO_CHANGE 0x40000000
|
|
+#define FLD_RDS_READY 0x20000000
|
|
+#define FLD_AC97_INT 0x10000000
|
|
+#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000
|
|
+#define FLD_NICAM_LOCK 0x04000000
|
|
+#define FLD_NICAM_UNLOCK 0x02000000
|
|
+#define FLD_DFT4_TH_CMP 0x01000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_LOCK_IND_INT 0x00200000
|
|
+#define FLD_DFT3_TH_CMP 0x00100000
|
|
+#define FLD_DFT2_TH_CMP 0x00080000
|
|
+#define FLD_DFT1_TH_CMP 0x00040000
|
|
+#define FLD_FM2_DFT_TH_CMP 0x00020000
|
|
+#define FLD_FM1_DFT_TH_CMP 0x00010000
|
|
+#define FLD_VIDEO_PRESENT_EN 0x00008000
|
|
+#define FLD_VIDEO_CHANGE_EN 0x00004000
|
|
+#define FLD_RDS_READY_EN 0x00002000
|
|
+#define FLD_AC97_INT_EN 0x00001000
|
|
+#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800
|
|
+#define FLD_NICAM_LOCK_EN 0x00000400
|
|
+#define FLD_NICAM_UNLOCK_EN 0x00000200
|
|
+#define FLD_DFT4_TH_CMP_EN 0x00000100
|
|
+/* Reserved [7] */
|
|
+#define FLD_DW8051_INT6_CTL1 0x00000040
|
|
+#define FLD_DW8051_INT5_CTL1 0x00000020
|
|
+#define FLD_DW8051_INT4_CTL1 0x00000010
|
|
+#define FLD_DW8051_INT3_CTL1 0x00000008
|
|
+#define FLD_DW8051_INT2_CTL1 0x00000004
|
|
+#define FLD_DW8051_INT1_CTL1 0x00000002
|
|
+#define FLD_DW8051_INT0_CTL1 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define GENERAL_CTL 0x810
|
|
+#define FLD_RDS_INT 0x80000000
|
|
+#define FLD_NBER_INT 0x40000000
|
|
+#define FLD_NLL_INT 0x20000000
|
|
+#define FLD_IFL_INT 0x10000000
|
|
+#define FLD_FDL_INT 0x08000000
|
|
+#define FLD_AFC_INT 0x04000000
|
|
+#define FLD_AMC_INT 0x02000000
|
|
+#define FLD_AC97_INT_CTL 0x01000000
|
|
+#define FLD_RDS_INT_DIS 0x00800000
|
|
+#define FLD_NBER_INT_DIS 0x00400000
|
|
+#define FLD_NLL_INT_DIS 0x00200000
|
|
+#define FLD_IFL_INT_DIS 0x00100000
|
|
+#define FLD_FDL_INT_DIS 0x00080000
|
|
+#define FLD_FC_INT_DIS 0x00040000
|
|
+#define FLD_AMC_INT_DIS 0x00020000
|
|
+#define FLD_AC97_INT_DIS 0x00010000
|
|
+#define FLD_REV_NUM 0x0000ff00
|
|
+/* Reserved [7:5] */
|
|
+#define FLD_DBX_SOFT_RESET_REG 0x00000010
|
|
+#define FLD_AD_SOFT_RESET_REG 0x00000008
|
|
+#define FLD_SRC_SOFT_RESET_REG 0x00000004
|
|
+#define FLD_CDMOD_SOFT_RESET 0x00000002
|
|
+#define FLD_8051_SOFT_RESET 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AAGC_CTL 0x814
|
|
+#define FLD_AFE_12DB_EN 0x80000000
|
|
+#define FLD_AAGC_DEFAULT_EN 0x40000000
|
|
+#define FLD_AAGC_DEFAULT 0x3f000000
|
|
+/* Reserved [23] */
|
|
+#define FLD_AAGC_GAIN 0x00600000
|
|
+#define FLD_AAGC_TH 0x001f0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_AAGC_HYST2 0x00003f00
|
|
+/* Reserved [7:6] */
|
|
+#define FLD_AAGC_HYST1 0x0000003f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define IF_SRC_CTL 0x818
|
|
+#define FLD_DBX_BYPASS 0x80000000
|
|
+/* Reserved [30:25] */
|
|
+#define FLD_IF_SRC_MODE 0x01000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_IF_SRC_PHASE_INC 0x0001ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define ANALOG_DEMOD_CTL 0x81c
|
|
+#define FLD_ROT1_PHACC_PROG 0xffff0000
|
|
+/* Reserved [15] */
|
|
+#define FLD_FM1_DELAY_FIX 0x00007000
|
|
+#define FLD_PDF4_SHIFT 0x00000c00
|
|
+#define FLD_PDF3_SHIFT 0x00000300
|
|
+#define FLD_PDF2_SHIFT 0x000000c0
|
|
+#define FLD_PDF1_SHIFT 0x00000030
|
|
+#define FLD_FMBYPASS_MODE2 0x00000008
|
|
+#define FLD_FMBYPASS_MODE1 0x00000004
|
|
+#define FLD_NICAM_MODE 0x00000002
|
|
+#define FLD_BTSC_FMRADIO_MODE 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define ROT_FREQ_CTL 0x820
|
|
+#define FLD_ROT3_PHACC_PROG 0xffff0000
|
|
+#define FLD_ROT2_PHACC_PROG 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FM_CTL 0x824
|
|
+#define FLD_FM2_DC_FB_SHIFT 0xf0000000
|
|
+#define FLD_FM2_DC_INT_SHIFT 0x0f000000
|
|
+#define FLD_FM2_AFC_RESET 0x00800000
|
|
+#define FLD_FM2_DC_PASS_IN 0x00400000
|
|
+#define FLD_FM2_DAGC_SHIFT 0x00380000
|
|
+#define FLD_FM2_CORDIC_SHIFT 0x00070000
|
|
+#define FLD_FM1_DC_FB_SHIFT 0x0000f000
|
|
+#define FLD_FM1_DC_INT_SHIFT 0x00000f00
|
|
+#define FLD_FM1_AFC_RESET 0x00000080
|
|
+#define FLD_FM1_DC_PASS_IN 0x00000040
|
|
+#define FLD_FM1_DAGC_SHIFT 0x00000038
|
|
+#define FLD_FM1_CORDIC_SHIFT 0x00000007
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define LPF_PDF_CTL 0x828
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_LPF32_SHIFT1 0x30000000
|
|
+#define FLD_LPF32_SHIFT2 0x0c000000
|
|
+#define FLD_LPF160_SHIFTA 0x03000000
|
|
+#define FLD_LPF160_SHIFTB 0x00c00000
|
|
+#define FLD_LPF160_SHIFTC 0x00300000
|
|
+#define FLD_LPF32_COEF_SEL2 0x000c0000
|
|
+#define FLD_LPF32_COEF_SEL1 0x00030000
|
|
+#define FLD_LPF160_COEF_SELC 0x0000c000
|
|
+#define FLD_LPF160_COEF_SELB 0x00003000
|
|
+#define FLD_LPF160_COEF_SELA 0x00000c00
|
|
+#define FLD_LPF160_IN_EN_REG 0x00000300
|
|
+#define FLD_PDF4_PDF_SEL 0x000000c0
|
|
+#define FLD_PDF3_PDF_SEL 0x00000030
|
|
+#define FLD_PDF2_PDF_SEL 0x0000000c
|
|
+#define FLD_PDF1_PDF_SEL 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT1_CTL1 0x82c
|
|
+#define FLD_DFT1_DWELL 0xffff0000
|
|
+#define FLD_DFT1_FREQ 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT1_CTL2 0x830
|
|
+#define FLD_DFT1_THRESHOLD 0xffffff00
|
|
+#define FLD_DFT1_CMP_CTL 0x00000080
|
|
+#define FLD_DFT1_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_DFT1_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT1_STATUS 0x834
|
|
+#define FLD_DFT1_DONE 0x80000000
|
|
+#define FLD_DFT1_TH_CMP_STAT 0x40000000
|
|
+#define FLD_DFT1_RESULT 0x3fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT2_CTL1 0x838
|
|
+#define FLD_DFT2_DWELL 0xffff0000
|
|
+#define FLD_DFT2_FREQ 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT2_CTL2 0x83C
|
|
+#define FLD_DFT2_THRESHOLD 0xffffff00
|
|
+#define FLD_DFT2_CMP_CTL 0x00000080
|
|
+#define FLD_DFT2_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_DFT2_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT2_STATUS 0x840
|
|
+#define FLD_DFT2_DONE 0x80000000
|
|
+#define FLD_DFT2_TH_CMP_STAT 0x40000000
|
|
+#define FLD_DFT2_RESULT 0x3fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT3_CTL1 0x844
|
|
+#define FLD_DFT3_DWELL 0xffff0000
|
|
+#define FLD_DFT3_FREQ 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT3_CTL2 0x848
|
|
+#define FLD_DFT3_THRESHOLD 0xffffff00
|
|
+#define FLD_DFT3_CMP_CTL 0x00000080
|
|
+#define FLD_DFT3_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_DFT3_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT3_STATUS 0x84c
|
|
+#define FLD_DFT3_DONE 0x80000000
|
|
+#define FLD_DFT3_TH_CMP_STAT 0x40000000
|
|
+#define FLD_DFT3_RESULT 0x3fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT4_CTL1 0x850
|
|
+#define FLD_DFT4_DWELL 0xffff0000
|
|
+#define FLD_DFT4_FREQ 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT4_CTL2 0x854
|
|
+#define FLD_DFT4_THRESHOLD 0xffffff00
|
|
+#define FLD_DFT4_CMP_CTL 0x00000080
|
|
+#define FLD_DFT4_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_DFT4_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DFT4_STATUS 0x858
|
|
+#define FLD_DFT4_DONE 0x80000000
|
|
+#define FLD_DFT4_TH_CMP_STAT 0x40000000
|
|
+#define FLD_DFT4_RESULT 0x3fffffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AM_MTS_DET 0x85c
|
|
+#define FLD_AM_MTS_MODE 0x80000000
|
|
+/* Reserved [30:26] */
|
|
+#define FLD_AM_SUB 0x02000000
|
|
+#define FLD_AM_GAIN_EN 0x01000000
|
|
+/* Reserved [23:16] */
|
|
+#define FLD_AMMTS_GAIN_SCALE 0x0000e000
|
|
+#define FLD_MTS_PDF_SHIFT 0x00001800
|
|
+#define FLD_AM_REG_GAIN 0x00000700
|
|
+#define FLD_AGC_REF 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define ANALOG_MUX_CTL 0x860
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_MUX21_SEL 0x10000000
|
|
+#define FLD_MUX20_SEL 0x08000000
|
|
+#define FLD_MUX19_SEL 0x04000000
|
|
+#define FLD_MUX18_SEL 0x02000000
|
|
+#define FLD_MUX17_SEL 0x01000000
|
|
+#define FLD_MUX16_SEL 0x00800000
|
|
+#define FLD_MUX15_SEL 0x00400000
|
|
+#define FLD_MUX14_SEL 0x00300000
|
|
+#define FLD_MUX13_SEL 0x000C0000
|
|
+#define FLD_MUX12_SEL 0x00020000
|
|
+#define FLD_MUX11_SEL 0x00018000
|
|
+#define FLD_MUX10_SEL 0x00004000
|
|
+#define FLD_MUX9_SEL 0x00002000
|
|
+#define FLD_MUX8_SEL 0x00001000
|
|
+#define FLD_MUX7_SEL 0x00000800
|
|
+#define FLD_MUX6_SEL 0x00000600
|
|
+#define FLD_MUX5_SEL 0x00000100
|
|
+#define FLD_MUX4_SEL 0x000000c0
|
|
+#define FLD_MUX3_SEL 0x00000030
|
|
+#define FLD_MUX2_SEL 0x0000000c
|
|
+#define FLD_MUX1_SEL 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DPLL_CTRL1 0x864
|
|
+#define DIG_PLL_CTL1 0x864
|
|
+
|
|
+#define FLD_PLL_STATUS 0x07000000
|
|
+#define FLD_BANDWIDTH_SELECT 0x00030000
|
|
+#define FLD_PLL_SHIFT_REG 0x00007000
|
|
+#define FLD_PHASE_SHIFT 0x000007ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DPLL_CTRL2 0x868
|
|
+#define DIG_PLL_CTL2 0x868
|
|
+#define FLD_PLL_UNLOCK_THR 0xff000000
|
|
+#define FLD_PLL_LOCK_THR 0x00ff0000
|
|
+/* Reserved [15:8] */
|
|
+#define FLD_AM_PDF_SEL2 0x000000c0
|
|
+#define FLD_AM_PDF_SEL1 0x00000030
|
|
+#define FLD_DPLL_FSM_CTRL 0x0000000c
|
|
+/* Reserved [1] */
|
|
+#define FLD_PLL_PILOT_DET 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DPLL_CTRL3 0x86c
|
|
+#define DIG_PLL_CTL3 0x86c
|
|
+#define FLD_DISABLE_LOOP 0x01000000
|
|
+#define FLD_A1_DS1_SEL 0x000c0000
|
|
+#define FLD_A1_DS2_SEL 0x00030000
|
|
+#define FLD_A1_KI 0x0000ff00
|
|
+#define FLD_A1_KD 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DPLL_CTRL4 0x870
|
|
+#define DIG_PLL_CTL4 0x870
|
|
+#define FLD_A2_DS1_SEL 0x000c0000
|
|
+#define FLD_A2_DS2_SEL 0x00030000
|
|
+#define FLD_A2_KI 0x0000ff00
|
|
+#define FLD_A2_KD 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DPLL_CTRL5 0x874
|
|
+#define DIG_PLL_CTL5 0x874
|
|
+#define FLD_TRK_DS1_SEL 0x000c0000
|
|
+#define FLD_TRK_DS2_SEL 0x00030000
|
|
+#define FLD_TRK_KI 0x0000ff00
|
|
+#define FLD_TRK_KD 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DEEMPH_GAIN_CTL 0x878
|
|
+#define FLD_DEEMPH2_GAIN 0xFFFF0000
|
|
+#define FLD_DEEMPH1_GAIN 0x0000FFFF
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_COEFF1 0x87c
|
|
+#define DEEMPH_COEF1 0x87c
|
|
+#define FLD_DEEMPH_B0 0xffff0000
|
|
+#define FLD_DEEMPH_A0 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_COEFF2 0x880
|
|
+#define DEEMPH_COEF2 0x880
|
|
+#define FLD_DEEMPH_B1 0xFFFF0000
|
|
+#define FLD_DEEMPH_A1 0x0000FFFF
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX1_CTL1 0x884
|
|
+#define FLD_DBX1_WBE_GAIN 0xffff0000
|
|
+#define FLD_DBX1_IN_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX1_CTL2 0x888
|
|
+#define FLD_DBX1_SE_BYPASS 0xffff0000
|
|
+#define FLD_DBX1_SE_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX1_RMS_SE 0x88C
|
|
+#define FLD_DBX1_RMS_WBE 0xffff0000
|
|
+#define FLD_DBX1_RMS_SE_FLD 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX2_CTL1 0x890
|
|
+#define FLD_DBX2_WBE_GAIN 0xffff0000
|
|
+#define FLD_DBX2_IN_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX2_CTL2 0x894
|
|
+#define FLD_DBX2_SE_BYPASS 0xffff0000
|
|
+#define FLD_DBX2_SE_GAIN 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX2_RMS_SE 0x898
|
|
+#define FLD_DBX2_RMS_WBE 0xffff0000
|
|
+#define FLD_DBX2_RMS_SE_FLD 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AM_FM_DIFF 0x89c
|
|
+/* Reserved [31] */
|
|
+#define FLD_FM_DIFF_OUT 0x7fff0000
|
|
+/* Reserved [15] */
|
|
+#define FLD_AM_DIFF_OUT 0x00007fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_FAW 0x8a0
|
|
+#define FLD_FAWDETWINEND 0xFc000000
|
|
+#define FLD_FAWDETWINSTR 0x03ff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_FAWDETTHRSHLD3 0x00000f00
|
|
+#define FLD_FAWDETTHRSHLD2 0x000000f0
|
|
+#define FLD_FAWDETTHRSHLD1 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_GAIN 0x8a4
|
|
+#define NICAM_DEEMPHGAIN 0x8a4
|
|
+/* Reserved [31:18] */
|
|
+#define FLD_DEEMPHGAIN 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_NUMER1 0x8a8
|
|
+#define NICAM_DEEMPHNUMER1 0x8a8
|
|
+/* Reserved [31:18] */
|
|
+#define FLD_DEEMPHNUMER1 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_NUMER2 0x8ac
|
|
+#define NICAM_DEEMPHNUMER2 0x8ac
|
|
+/* Reserved [31:18] */
|
|
+#define FLD_DEEMPHNUMER2 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_DENOM1 0x8b0
|
|
+#define NICAM_DEEMPHDENOM1 0x8b0
|
|
+/* Reserved [31:18] */
|
|
+#define FLD_DEEMPHDENOM1 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define DEEMPH_DENOM2 0x8b4
|
|
+#define NICAM_DEEMPHDENOM2 0x8b4
|
|
+/* Reserved [31:18] */
|
|
+#define FLD_DEEMPHDENOM2 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_ERRLOG_CTL1 0x8B8
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_ERRINTRPTTHSHLD1 0x0fff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_ERRLOGPERIOD 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_ERRLOG_CTL2 0x8bc
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_ERRINTRPTTHSHLD3 0x0fff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_ERRINTRPTTHSHLD2 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_ERRLOG_STS1 0x8c0
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_ERRLOG2 0x0fff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_ERRLOG1 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_ERRLOG_STS2 0x8c4
|
|
+/* Reserved [31:12] */
|
|
+#define FLD_ERRLOG3 0x00000fff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define NICAM_STATUS 0x8c8
|
|
+/* Reserved [31:20] */
|
|
+#define FLD_NICAM_CIB 0x000c0000
|
|
+#define FLD_NICAM_LOCK_STAT 0x00020000
|
|
+#define FLD_NICAM_MUTE 0x00010000
|
|
+#define FLD_NICAMADDIT_DATA 0x0000ffe0
|
|
+#define FLD_NICAMCNTRL 0x0000001f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DEMATRIX_CTL 0x8cc
|
|
+#define FLD_AC97_IN_SHIFT 0xf0000000
|
|
+#define FLD_I2S_IN_SHIFT 0x0f000000
|
|
+#define FLD_DEMATRIX_SEL_CTL 0x00ff0000
|
|
+/* Reserved [15:11] */
|
|
+#define FLD_DMTRX_BYPASS 0x00000400
|
|
+#define FLD_DEMATRIX_MODE 0x00000300
|
|
+/* Reserved [7:6] */
|
|
+#define FLD_PH_DBX_SEL 0x00000020
|
|
+#define FLD_PH_CH_SEL 0x00000010
|
|
+#define FLD_PHASE_FIX 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH1_CTL1 0x8d0
|
|
+/* Reserved [31:29] */
|
|
+#define FLD_PATH1_MUTE_CTL 0x1f000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_PATH1_AVC_CG 0x00300000
|
|
+#define FLD_PATH1_AVC_RT 0x000f0000
|
|
+#define FLD_PATH1_AVC_AT 0x0000f000
|
|
+#define FLD_PATH1_AVC_STEREO 0x00000800
|
|
+#define FLD_PATH1_AVC_CR 0x00000700
|
|
+#define FLD_PATH1_AVC_RMS_CON 0x000000f0
|
|
+#define FLD_PATH1_SEL_CTL 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH1_VOL_CTL 0x8d4
|
|
+#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000
|
|
+#define FLD_PATH1_BAL_LEFT 0x00008000
|
|
+#define FLD_PATH1_BAL_LEVEL 0x00007f00
|
|
+#define FLD_PATH1_VOLUME 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH1_EQ_CTL 0x8d8
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_PATH1_EQ_MID_VOL 0x003f0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_PATH1_EQ_BASS_VOL 0x00003f00
|
|
+/* Reserved [7:1] */
|
|
+#define FLD_PATH1_EQ_BAND_SEL 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH1_SC_CTL 0x8dc
|
|
+#define FLD_PATH1_SC_THRESHOLD 0x7fff0000
|
|
+#define FLD_PATH1_SC_RT 0x0000f000
|
|
+#define FLD_PATH1_SC_AT 0x00000f00
|
|
+#define FLD_PATH1_SC_STEREO 0x00000080
|
|
+#define FLD_PATH1_SC_CR 0x00000070
|
|
+#define FLD_PATH1_SC_RMS_CON 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH2_CTL1 0x8e0
|
|
+/* Reserved [31:26] */
|
|
+#define FLD_PATH2_MUTE_CTL 0x03000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_PATH2_AVC_CG 0x00300000
|
|
+#define FLD_PATH2_AVC_RT 0x000f0000
|
|
+#define FLD_PATH2_AVC_AT 0x0000f000
|
|
+#define FLD_PATH2_AVC_STEREO 0x00000800
|
|
+#define FLD_PATH2_AVC_CR 0x00000700
|
|
+#define FLD_PATH2_AVC_RMS_CON 0x000000f0
|
|
+#define FLD_PATH2_SEL_CTL 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH2_VOL_CTL 0x8e4
|
|
+#define FLD_PATH2_AVC_THRESHOLD 0xffff0000
|
|
+#define FLD_PATH2_BAL_LEFT 0x00008000
|
|
+#define FLD_PATH2_BAL_LEVEL 0x00007f00
|
|
+#define FLD_PATH2_VOLUME 0x000000ff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH2_EQ_CTL 0x8e8
|
|
+/* Reserved [31:30] */
|
|
+#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000
|
|
+/* Reserved [23:22] */
|
|
+#define FLD_PATH2_EQ_MID_VOL 0x003f0000
|
|
+/* Reserved [15:14] */
|
|
+#define FLD_PATH2_EQ_BASS_VOL 0x00003f00
|
|
+/* Reserved [7:1] */
|
|
+#define FLD_PATH2_EQ_BAND_SEL 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define PATH2_SC_CTL 0x8eC
|
|
+#define FLD_PATH2_SC_THRESHOLD 0xffff0000
|
|
+#define FLD_PATH2_SC_RT 0x0000f000
|
|
+#define FLD_PATH2_SC_AT 0x00000f00
|
|
+#define FLD_PATH2_SC_STEREO 0x00000080
|
|
+#define FLD_PATH2_SC_CR 0x00000070
|
|
+#define FLD_PATH2_SC_RMS_CON 0x0000000f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC_CTL 0x8f0
|
|
+#define FLD_SRC_STATUS 0xffffff00
|
|
+#define FLD_FIFO_LF_EN 0x000000fc
|
|
+#define FLD_BYPASS_LI 0x00000002
|
|
+#define FLD_BYPASS_PF 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC_LF_COEF 0x8f4
|
|
+#define FLD_LOOP_FILTER_COEF2 0xffff0000
|
|
+#define FLD_LOOP_FILTER_COEF1 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC1_CTL 0x8f8
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC1_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC1_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC2_CTL 0x8fc
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC2_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC2_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC3_CTL 0x900
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC3_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC3_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC4_CTL 0x904
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC4_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC4_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC5_CTL 0x908
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC5_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC5_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define SRC6_CTL 0x90c
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_SRC6_FIFO_RD_TH 0x0f000000
|
|
+/* Reserved [23:18] */
|
|
+#define FLD_SRC6_PHASE_INC 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define BAND_OUT_SEL 0x910
|
|
+#define FLD_SRC6_IN_SEL 0xc0000000
|
|
+#define FLD_SRC6_CLK_SEL 0x30000000
|
|
+#define FLD_SRC5_IN_SEL 0x0c000000
|
|
+#define FLD_SRC5_CLK_SEL 0x03000000
|
|
+#define FLD_SRC4_IN_SEL 0x00c00000
|
|
+#define FLD_SRC4_CLK_SEL 0x00300000
|
|
+#define FLD_SRC3_IN_SEL 0x000c0000
|
|
+#define FLD_SRC3_CLK_SEL 0x00030000
|
|
+#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00
|
|
+#define FLD_AC97_SRC_SEL 0x000000c0
|
|
+#define FLD_I2S_SRC_SEL 0x00000030
|
|
+#define FLD_PARALLEL2_SRC_SEL 0x0000000c
|
|
+#define FLD_PARALLEL1_SRC_SEL 0x00000003
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define I2S_IN_CTL 0x914
|
|
+/* Reserved [31:11] */
|
|
+#define FLD_I2S_UP2X_BW20K 0x00000400
|
|
+#define FLD_I2S_UP2X_BYPASS 0x00000200
|
|
+#define FLD_I2S_IN_MASTER_MODE 0x00000100
|
|
+#define FLD_I2S_IN_SONY_MODE 0x00000080
|
|
+#define FLD_I2S_IN_RIGHT_JUST 0x00000040
|
|
+#define FLD_I2S_IN_WS_SEL 0x00000020
|
|
+#define FLD_I2S_IN_BCN_DEL 0x0000001f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define I2S_OUT_CTL 0x918
|
|
+/* Reserved [31:17] */
|
|
+#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000
|
|
+/* Reserved [15:9] */
|
|
+#define FLD_I2S_OUT_MASTER_MODE 0x00000100
|
|
+#define FLD_I2S_OUT_SONY_MODE 0x00000080
|
|
+#define FLD_I2S_OUT_RIGHT_JUST 0x00000040
|
|
+#define FLD_I2S_OUT_WS_SEL 0x00000020
|
|
+#define FLD_I2S_OUT_BCN_DEL 0x0000001f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AC97_CTL 0x91c
|
|
+/* Reserved [31:26] */
|
|
+#define FLD_AC97_UP2X_BW20K 0x02000000
|
|
+#define FLD_AC97_UP2X_BYPASS 0x01000000
|
|
+/* Reserved [23:17] */
|
|
+#define FLD_AC97_RST_ACL 0x00010000
|
|
+/* Reserved [15:9] */
|
|
+#define FLD_AC97_WAKE_UP_SYNC 0x00000100
|
|
+/* Reserved [7:1] */
|
|
+#define FLD_AC97_SHUTDOWN 0x00000001
|
|
+
|
|
+/* Cx231xx redefine */
|
|
+#define QPSK_IAGC_CTL1 0x94c
|
|
+#define QPSK_IAGC_CTL2 0x950
|
|
+#define QPSK_FEPR_FREQ 0x954
|
|
+#define QPSK_BTL_CTL1 0x958
|
|
+#define QPSK_BTL_CTL2 0x95c
|
|
+#define QPSK_CTL_CTL1 0x960
|
|
+#define QPSK_CTL_CTL2 0x964
|
|
+#define QPSK_MF_FAGC_CTL 0x968
|
|
+#define QPSK_EQ_CTL 0x96c
|
|
+#define QPSK_LOCK_CTL 0x970
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FM1_DFT_CTL 0x9a8
|
|
+#define FLD_FM1_DFT_THRESHOLD 0xffff0000
|
|
+/* Reserved [15:8] */
|
|
+#define FLD_FM1_DFT_CMP_CTL 0x00000080
|
|
+#define FLD_FM1_DFT_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_FM1_DFT_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FM1_DFT_STATUS 0x9ac
|
|
+#define FLD_FM1_DFT_DONE 0x80000000
|
|
+/* Reserved [30:19] */
|
|
+#define FLD_FM_DFT_TH_CMP 0x00040000
|
|
+#define FLD_FM1_DFT 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FM2_DFT_CTL 0x9b0
|
|
+#define FLD_FM2_DFT_THRESHOLD 0xffff0000
|
|
+/* Reserved [15:8] */
|
|
+#define FLD_FM2_DFT_CMP_CTL 0x00000080
|
|
+#define FLD_FM2_DFT_AVG 0x00000070
|
|
+/* Reserved [3:1] */
|
|
+#define FLD_FM2_DFT_START 0x00000001
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define FM2_DFT_STATUS 0x9b4
|
|
+#define FLD_FM2_DFT_DONE 0x80000000
|
|
+/* Reserved [30:19] */
|
|
+#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000
|
|
+#define FLD_FM2_DFT 0x0003ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define AAGC_STATUS_REG 0x9b8
|
|
+#define AAGC_STATUS 0x9b8
|
|
+/* Reserved [31:27] */
|
|
+#define FLD_FM2_DAGC_OUT 0x07000000
|
|
+/* Reserved [23:19] */
|
|
+#define FLD_FM1_DAGC_OUT 0x00070000
|
|
+/* Reserved [15:6] */
|
|
+#define FLD_AFE_VGA_OUT 0x0000003f
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define MTS_GAIN_STATUS 0x9bc
|
|
+/* Reserved [31:14] */
|
|
+#define FLD_MTS_GAIN 0x00003fff
|
|
+
|
|
+#define RDS_OUT 0x9c0
|
|
+#define FLD_RDS_Q 0xffff0000
|
|
+#define FLD_RDS_I 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define AUTOCONFIG_REG 0x9c4
|
|
+/* Reserved [31:4] */
|
|
+#define FLD_AUTOCONFIG_MODE 0x0000000f
|
|
+
|
|
+#define FM_AFC 0x9c8
|
|
+#define FLD_FM2_AFC 0xffff0000
|
|
+#define FLD_FM1_AFC 0x0000ffff
|
|
+
|
|
+/*****************************************************************************/
|
|
+/* Cx231xx redefine */
|
|
+#define NEW_SPARE 0x9cc
|
|
+#define NEW_SPARE_REG 0x9cc
|
|
+
|
|
+/*****************************************************************************/
|
|
+#define DBX_ADJ 0x9d0
|
|
+/* Reserved [31:28] */
|
|
+#define FLD_DBX2_ADJ 0x0fff0000
|
|
+/* Reserved [15:12] */
|
|
+#define FLD_DBX1_ADJ 0x00000fff
|
|
+
|
|
+#define VID_FMT_AUTO 0
|
|
+#define VID_FMT_NTSC_M 1
|
|
+#define VID_FMT_NTSC_J 2
|
|
+#define VID_FMT_NTSC_443 3
|
|
+#define VID_FMT_PAL_BDGHI 4
|
|
+#define VID_FMT_PAL_M 5
|
|
+#define VID_FMT_PAL_N 6
|
|
+#define VID_FMT_PAL_NC 7
|
|
+#define VID_FMT_PAL_60 8
|
|
+#define VID_FMT_SECAM 12
|
|
+#define VID_FMT_SECAM_60 13
|
|
+
|
|
+#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */
|
|
+#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */
|
|
+#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */
|
|
+#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */
|
|
+
|
|
+#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
|
|
+#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
|
|
+#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
|
|
+
|
|
+#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
|
|
+#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
|
|
+#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
|
|
+
|
|
+#define TWO_TAP_FILT 0
|
|
+#define THREE_TAP_FILT 1
|
|
+#define FOUR_TAP_FILT 2
|
|
+#define FIVE_TAP_FILT 3
|
|
+
|
|
+#define AUD_CHAN_SRC_PARALLEL 0
|
|
+#define AUD_CHAN_SRC_I2S_INPUT 1
|
|
+#define AUD_CHAN_SRC_FLATIRON 2
|
|
+#define AUD_CHAN_SRC_PARALLEL3 3
|
|
+
|
|
+#define OUT_MODE_601 0
|
|
+#define OUT_MODE_656 1
|
|
+#define OUT_MODE_VIP11 2
|
|
+#define OUT_MODE_VIP20 3
|
|
+
|
|
+#define PHASE_INC_49MHZ 0x0df22
|
|
+#define PHASE_INC_56MHZ 0x0fa5b
|
|
+#define PHASE_INC_28MHZ 0x010000
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c
|
|
new file mode 100644
|
|
index 0000000..9418052
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.c
|
|
@@ -0,0 +1,701 @@
|
|
+/*
|
|
+ cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on cx88 driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/bitmap.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/mutex.h>
|
|
+
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+#include <media/msp3400.h>
|
|
+#include <media/tuner.h>
|
|
+
|
|
+#include "cx231xx.h"
|
|
+#include "cx231xx-vbi.h"
|
|
+
|
|
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
|
|
+{
|
|
+ char *errmsg = "Unknown";
|
|
+
|
|
+ switch (status) {
|
|
+ case -ENOENT:
|
|
+ errmsg = "unlinked synchronuously";
|
|
+ break;
|
|
+ case -ECONNRESET:
|
|
+ errmsg = "unlinked asynchronuously";
|
|
+ break;
|
|
+ case -ENOSR:
|
|
+ errmsg = "Buffer error (overrun)";
|
|
+ break;
|
|
+ case -EPIPE:
|
|
+ errmsg = "Stalled (device not responding)";
|
|
+ break;
|
|
+ case -EOVERFLOW:
|
|
+ errmsg = "Babble (bad cable?)";
|
|
+ break;
|
|
+ case -EPROTO:
|
|
+ errmsg = "Bit-stuff error (bad cable?)";
|
|
+ break;
|
|
+ case -EILSEQ:
|
|
+ errmsg = "CRC/Timeout (could be anything)";
|
|
+ break;
|
|
+ case -ETIME:
|
|
+ errmsg = "Device does not respond";
|
|
+ break;
|
|
+ }
|
|
+ if (packet < 0) {
|
|
+ cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status,
|
|
+ errmsg);
|
|
+ } else {
|
|
+ cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n",
|
|
+ packet, status, errmsg);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Controls the isoc copy of each urb packet
|
|
+ */
|
|
+static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf;
|
|
+ struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
+ int rc = 1;
|
|
+ unsigned char *p_buffer;
|
|
+ u32 bytes_parsed = 0, buffer_size = 0;
|
|
+ u8 sav_eav = 0;
|
|
+
|
|
+ if (!dev)
|
|
+ return 0;
|
|
+
|
|
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
|
|
+ return 0;
|
|
+
|
|
+ if (urb->status < 0) {
|
|
+ print_err_status(dev, -1, urb->status);
|
|
+ if (urb->status == -ENOENT)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ buf = dev->vbi_mode.isoc_ctl.buf;
|
|
+
|
|
+ /* get buffer pointer and length */
|
|
+ p_buffer = urb->transfer_buffer;
|
|
+ buffer_size = urb->actual_length;
|
|
+
|
|
+ if (buffer_size > 0) {
|
|
+ bytes_parsed = 0;
|
|
+
|
|
+ if (dma_q->is_partial_line) {
|
|
+ /* Handle the case where we were working on a partial
|
|
+ line */
|
|
+ sav_eav = dma_q->last_sav;
|
|
+ } else {
|
|
+ /* Check for a SAV/EAV overlapping the
|
|
+ buffer boundary */
|
|
+
|
|
+ sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
|
|
+ dma_q->partial_buf,
|
|
+ &bytes_parsed);
|
|
+ }
|
|
+
|
|
+ sav_eav &= 0xF0;
|
|
+ /* Get the first line if we have some portion of an SAV/EAV from
|
|
+ the last buffer or a partial line */
|
|
+ if (sav_eav) {
|
|
+ bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
|
|
+ sav_eav, /* SAV/EAV */
|
|
+ p_buffer + bytes_parsed, /* p_buffer */
|
|
+ buffer_size - bytes_parsed); /* buffer size */
|
|
+ }
|
|
+
|
|
+ /* Now parse data that is completely in this buffer */
|
|
+ dma_q->is_partial_line = 0;
|
|
+
|
|
+ while (bytes_parsed < buffer_size) {
|
|
+ u32 bytes_used = 0;
|
|
+
|
|
+ sav_eav = cx231xx_find_next_SAV_EAV(
|
|
+ p_buffer + bytes_parsed, /* p_buffer */
|
|
+ buffer_size - bytes_parsed, /* buffer size */
|
|
+ &bytes_used); /* bytes used to get SAV/EAV */
|
|
+
|
|
+ bytes_parsed += bytes_used;
|
|
+
|
|
+ sav_eav &= 0xF0;
|
|
+ if (sav_eav && (bytes_parsed < buffer_size)) {
|
|
+ bytes_parsed += cx231xx_get_vbi_line(dev,
|
|
+ dma_q, sav_eav, /* SAV/EAV */
|
|
+ p_buffer+bytes_parsed, /* p_buffer */
|
|
+ buffer_size-bytes_parsed);/*buf size*/
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Save the last four bytes of the buffer so we can
|
|
+ check the buffer boundary condition next time */
|
|
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
|
|
+ bytes_parsed = 0;
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------
|
|
+ Vbi buf operations
|
|
+ ------------------------------------------------------------------*/
|
|
+
|
|
+static int
|
|
+vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
|
|
+ unsigned int *size)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ u32 height = 0;
|
|
+
|
|
+ height = ((dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES);
|
|
+
|
|
+ *size = (dev->width * height * 2);
|
|
+ if (0 == *count)
|
|
+ *count = CX231XX_DEF_VBI_BUF;
|
|
+
|
|
+ if (*count < CX231XX_MIN_BUF)
|
|
+ *count = CX231XX_MIN_BUF;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* This is called *without* dev->slock held; please keep it that way */
|
|
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ unsigned long flags = 0;
|
|
+ if (in_interrupt())
|
|
+ BUG();
|
|
+
|
|
+ /* We used to wait for the buffer to finish here, but this didn't work
|
|
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
|
|
+ videobuf_queue_cancel marked it as finished for us.
|
|
+ (Also, it could wedge forever if the hardware was misconfigured.)
|
|
+
|
|
+ This should be safe; by the time we get here, the buffer isn't
|
|
+ queued anymore. If we ever start marking the buffers as
|
|
+ VIDEOBUF_ACTIVE, it won't be, though.
|
|
+ */
|
|
+ spin_lock_irqsave(&dev->vbi_mode.slock, flags);
|
|
+ if (dev->vbi_mode.isoc_ctl.buf == buf)
|
|
+ dev->vbi_mode.isoc_ctl.buf = NULL;
|
|
+ spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
|
|
+
|
|
+ videobuf_vmalloc_free(&buf->vb);
|
|
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
|
|
+}
|
|
+
|
|
+static int
|
|
+vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
|
+ enum v4l2_field field)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc = 0, urb_init = 0;
|
|
+ u32 height = 0;
|
|
+
|
|
+ height = ((dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES);
|
|
+ buf->vb.size = ((dev->width << 1) * height);
|
|
+
|
|
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
|
|
+ return -EINVAL;
|
|
+
|
|
+ buf->vb.width = dev->width;
|
|
+ buf->vb.height = height;
|
|
+ buf->vb.field = field;
|
|
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
|
|
+
|
|
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
|
|
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
|
|
+ if (rc < 0)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (!dev->vbi_mode.isoc_ctl.num_bufs)
|
|
+ urb_init = 1;
|
|
+
|
|
+ if (urb_init) {
|
|
+ rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
|
|
+ CX231XX_NUM_VBI_BUFS,
|
|
+ dev->vbi_mode.alt_max_pkt_size[0],
|
|
+ cx231xx_isoc_vbi_copy);
|
|
+ if (rc < 0)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ buf->vb.state = VIDEOBUF_PREPARED;
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ free_buffer(vq, buf);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void
|
|
+vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
|
|
+
|
|
+ buf->vb.state = VIDEOBUF_QUEUED;
|
|
+ list_add_tail(&buf->vb.queue, &vidq->active);
|
|
+
|
|
+}
|
|
+
|
|
+static void vbi_buffer_release(struct videobuf_queue *vq,
|
|
+ struct videobuf_buffer *vb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+
|
|
+
|
|
+ free_buffer(vq, buf);
|
|
+}
|
|
+
|
|
+struct videobuf_queue_ops cx231xx_vbi_qops = {
|
|
+ .buf_setup = vbi_buffer_setup,
|
|
+ .buf_prepare = vbi_buffer_prepare,
|
|
+ .buf_queue = vbi_buffer_queue,
|
|
+ .buf_release = vbi_buffer_release,
|
|
+};
|
|
+
|
|
+/* ------------------------------------------------------------------
|
|
+ URB control
|
|
+ ------------------------------------------------------------------*/
|
|
+
|
|
+/*
|
|
+ * IRQ callback, called by URB callback
|
|
+ */
|
|
+static void cx231xx_irq_vbi_callback(struct urb *urb)
|
|
+{
|
|
+ struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
+ struct cx231xx_video_mode *vmode =
|
|
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
|
|
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
|
|
+ int rc;
|
|
+
|
|
+ switch (urb->status) {
|
|
+ case 0: /* success */
|
|
+ case -ETIMEDOUT: /* NAK */
|
|
+ break;
|
|
+ case -ECONNRESET: /* kill */
|
|
+ case -ENOENT:
|
|
+ case -ESHUTDOWN:
|
|
+ return;
|
|
+ default: /* error */
|
|
+ cx231xx_err(DRIVER_NAME "urb completition error %d.\n",
|
|
+ urb->status);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Copy data from URB */
|
|
+ spin_lock(&dev->vbi_mode.slock);
|
|
+ rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb);
|
|
+ spin_unlock(&dev->vbi_mode.slock);
|
|
+
|
|
+ /* Reset status */
|
|
+ urb->status = 0;
|
|
+
|
|
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
|
|
+ if (urb->status) {
|
|
+ cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n",
|
|
+ urb->status);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Stop and Deallocate URBs
|
|
+ */
|
|
+void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
|
|
+{
|
|
+ struct urb *urb;
|
|
+ int i;
|
|
+
|
|
+ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.nfields = -1;
|
|
+ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
|
|
+ urb = dev->vbi_mode.isoc_ctl.urb[i];
|
|
+ if (urb) {
|
|
+ if (!irqs_disabled())
|
|
+ usb_kill_urb(urb);
|
|
+ else
|
|
+ usb_unlink_urb(urb);
|
|
+
|
|
+ if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
|
|
+
|
|
+ kfree(dev->vbi_mode.isoc_ctl.
|
|
+ transfer_buffer[i]);
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
|
|
+ NULL;
|
|
+ }
|
|
+ usb_free_urb(urb);
|
|
+ dev->vbi_mode.isoc_ctl.urb[i] = NULL;
|
|
+ }
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL;
|
|
+ }
|
|
+
|
|
+ kfree(dev->vbi_mode.isoc_ctl.urb);
|
|
+ kfree(dev->vbi_mode.isoc_ctl.transfer_buffer);
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.urb = NULL;
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer = NULL;
|
|
+ dev->vbi_mode.isoc_ctl.num_bufs = 0;
|
|
+
|
|
+ cx231xx_capture_start(dev, 0, Vbi);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
|
|
+
|
|
+/*
|
|
+ * Allocate URBs and start IRQ
|
|
+ */
|
|
+int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
|
|
+ int num_bufs, int max_pkt_size,
|
|
+ int (*isoc_copy) (struct cx231xx *dev,
|
|
+ struct urb *urb))
|
|
+{
|
|
+ struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
|
|
+ int i;
|
|
+ int sb_size, pipe;
|
|
+ struct urb *urb;
|
|
+ int rc;
|
|
+
|
|
+ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n");
|
|
+
|
|
+ /* De-allocates all pending stuff */
|
|
+ cx231xx_uninit_vbi_isoc(dev);
|
|
+
|
|
+ /* clear if any halt */
|
|
+ usb_clear_halt(dev->udev,
|
|
+ usb_rcvbulkpipe(dev->udev,
|
|
+ dev->vbi_mode.end_point_addr));
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy;
|
|
+ dev->vbi_mode.isoc_ctl.num_bufs = num_bufs;
|
|
+ dma_q->pos = 0;
|
|
+ dma_q->is_partial_line = 0;
|
|
+ dma_q->last_sav = 0;
|
|
+ dma_q->current_field = -1;
|
|
+ dma_q->bytes_left_in_line = dev->width << 1;
|
|
+ dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES);
|
|
+ dma_q->lines_completed = 0;
|
|
+ for (i = 0; i < 8; i++)
|
|
+ dma_q->partial_buf[i] = 0;
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
|
|
+ GFP_KERNEL);
|
|
+ if (!dev->vbi_mode.isoc_ctl.urb) {
|
|
+ cx231xx_errdev("cannot alloc memory for usb buffers\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer =
|
|
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
|
|
+ if (!dev->vbi_mode.isoc_ctl.transfer_buffer) {
|
|
+ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
|
|
+ kfree(dev->vbi_mode.isoc_ctl.urb);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size;
|
|
+ dev->vbi_mode.isoc_ctl.buf = NULL;
|
|
+
|
|
+ sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size;
|
|
+
|
|
+ /* allocate urbs and transfer buffers */
|
|
+ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
|
|
+
|
|
+ urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
+ if (!urb) {
|
|
+ cx231xx_err(DRIVER_NAME
|
|
+ ": cannot alloc isoc_ctl.urb %i\n", i);
|
|
+ cx231xx_uninit_vbi_isoc(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ dev->vbi_mode.isoc_ctl.urb[i] = urb;
|
|
+ urb->transfer_flags = 0;
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
|
|
+ kzalloc(sb_size, GFP_KERNEL);
|
|
+ if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
|
|
+ cx231xx_err(DRIVER_NAME
|
|
+ ": unable to allocate %i bytes for transfer"
|
|
+ " buffer %i%s\n", sb_size, i,
|
|
+ in_interrupt() ? " while in int" : "");
|
|
+ cx231xx_uninit_vbi_isoc(dev);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
|
|
+ usb_fill_bulk_urb(urb, dev->udev, pipe,
|
|
+ dev->vbi_mode.isoc_ctl.transfer_buffer[i],
|
|
+ sb_size, cx231xx_irq_vbi_callback, dma_q);
|
|
+ }
|
|
+
|
|
+ init_waitqueue_head(&dma_q->wq);
|
|
+
|
|
+ /* submit urbs and enables IRQ */
|
|
+ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
|
|
+ rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC);
|
|
+ if (rc) {
|
|
+ cx231xx_err(DRIVER_NAME
|
|
+ ": submit of urb %i failed (error=%i)\n", i,
|
|
+ rc);
|
|
+ cx231xx_uninit_vbi_isoc(dev);
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cx231xx_capture_start(dev, 1, Vbi);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
|
|
+
|
|
+u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size)
|
|
+{
|
|
+ u32 bytes_copied = 0;
|
|
+ int current_field = -1;
|
|
+
|
|
+ switch (sav_eav) {
|
|
+
|
|
+ case SAV_VBI_FIELD1:
|
|
+ current_field = 1;
|
|
+ break;
|
|
+
|
|
+ case SAV_VBI_FIELD2:
|
|
+ current_field = 2;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (current_field < 0)
|
|
+ return bytes_copied;
|
|
+
|
|
+ dma_q->last_sav = sav_eav;
|
|
+
|
|
+ bytes_copied =
|
|
+ cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
|
|
+ current_field);
|
|
+
|
|
+ return bytes_copied;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Announces that a buffer were filled and request the next
|
|
+ */
|
|
+static inline void vbi_buffer_filled(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q,
|
|
+ struct cx231xx_buffer *buf)
|
|
+{
|
|
+ /* Advice that buffer was filled */
|
|
+ /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */
|
|
+
|
|
+ buf->vb.state = VIDEOBUF_DONE;
|
|
+ buf->vb.field_count++;
|
|
+ do_gettimeofday(&buf->vb.ts);
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.buf = NULL;
|
|
+
|
|
+ list_del(&buf->vb.queue);
|
|
+ wake_up(&buf->vb.done);
|
|
+}
|
|
+
|
|
+u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_line, u32 length, int field_number)
|
|
+{
|
|
+ u32 bytes_to_copy;
|
|
+ struct cx231xx_buffer *buf;
|
|
+ u32 _line_size = dev->width * 2;
|
|
+
|
|
+ if (dma_q->current_field != field_number)
|
|
+ cx231xx_reset_vbi_buffer(dev, dma_q);
|
|
+
|
|
+ /* get the buffer pointer */
|
|
+ buf = dev->vbi_mode.isoc_ctl.buf;
|
|
+
|
|
+ /* Remember the field number for next time */
|
|
+ dma_q->current_field = field_number;
|
|
+
|
|
+ bytes_to_copy = dma_q->bytes_left_in_line;
|
|
+ if (bytes_to_copy > length)
|
|
+ bytes_to_copy = length;
|
|
+
|
|
+ if (dma_q->lines_completed >= dma_q->lines_per_field) {
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+ dma_q->is_partial_line =
|
|
+ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dma_q->is_partial_line = 1;
|
|
+
|
|
+ /* If we don't have a buffer, just return the number of bytes we would
|
|
+ have copied if we had a buffer. */
|
|
+ if (!buf) {
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+ dma_q->is_partial_line =
|
|
+ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
|
|
+ return bytes_to_copy;
|
|
+ }
|
|
+
|
|
+ /* copy the data to video buffer */
|
|
+ cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
|
|
+
|
|
+ dma_q->pos += bytes_to_copy;
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+
|
|
+ if (dma_q->bytes_left_in_line == 0) {
|
|
+
|
|
+ dma_q->bytes_left_in_line = _line_size;
|
|
+ dma_q->lines_completed++;
|
|
+ dma_q->is_partial_line = 0;
|
|
+
|
|
+ if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
|
|
+
|
|
+ vbi_buffer_filled(dev, dma_q, buf);
|
|
+
|
|
+ dma_q->pos = 0;
|
|
+ buf = NULL;
|
|
+ dma_q->lines_completed = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return bytes_to_copy;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * video-buf generic routine to get the next available buffer
|
|
+ */
|
|
+static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
|
|
+ struct cx231xx_buffer **buf)
|
|
+{
|
|
+ struct cx231xx_video_mode *vmode =
|
|
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
|
|
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
|
|
+ char *outp;
|
|
+
|
|
+ if (list_empty(&dma_q->active)) {
|
|
+ cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
|
|
+ dev->vbi_mode.isoc_ctl.buf = NULL;
|
|
+ *buf = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Get the next buffer */
|
|
+ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
|
|
+
|
|
+ /* Cleans up buffer - Usefull for testing for frame/URB loss */
|
|
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
|
|
+ memset(outp, 0, (*buf)->vb.size);
|
|
+
|
|
+ dev->vbi_mode.isoc_ctl.buf = *buf;
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q)
|
|
+{
|
|
+ struct cx231xx_buffer *buf;
|
|
+
|
|
+ buf = dev->vbi_mode.isoc_ctl.buf;
|
|
+
|
|
+ if (buf == NULL) {
|
|
+ /* first try to get the buffer */
|
|
+ get_next_vbi_buf(dma_q, &buf);
|
|
+
|
|
+ dma_q->pos = 0;
|
|
+ dma_q->current_field = -1;
|
|
+ }
|
|
+
|
|
+ dma_q->bytes_left_in_line = dev->width << 1;
|
|
+ dma_q->lines_completed = 0;
|
|
+}
|
|
+
|
|
+int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_buffer, u32 bytes_to_copy)
|
|
+{
|
|
+ u8 *p_out_buffer = NULL;
|
|
+ u32 current_line_bytes_copied = 0;
|
|
+ struct cx231xx_buffer *buf;
|
|
+ u32 _line_size = dev->width << 1;
|
|
+ void *startwrite;
|
|
+ int offset, lencopy;
|
|
+
|
|
+ buf = dev->vbi_mode.isoc_ctl.buf;
|
|
+
|
|
+ if (buf == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
|
|
+
|
|
+ if (dma_q->bytes_left_in_line != _line_size) {
|
|
+ current_line_bytes_copied =
|
|
+ _line_size - dma_q->bytes_left_in_line;
|
|
+ }
|
|
+
|
|
+ offset = (dma_q->lines_completed * _line_size) +
|
|
+ current_line_bytes_copied;
|
|
+
|
|
+ /* prepare destination address */
|
|
+ startwrite = p_out_buffer + offset;
|
|
+
|
|
+ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
|
|
+ bytes_to_copy : dma_q->bytes_left_in_line;
|
|
+
|
|
+ memcpy(startwrite, p_buffer, lencopy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q)
|
|
+{
|
|
+ u32 height = 0;
|
|
+
|
|
+ height = ((dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES);
|
|
+ return (dma_q->lines_completed == height) ? 1 : 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/video/cx231xx/cx231xx-vbi.h
|
|
new file mode 100644
|
|
index 0000000..89c7fe8
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.h
|
|
@@ -0,0 +1,65 @@
|
|
+/*
|
|
+ cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on cx88 driver
|
|
+
|
|
+ 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.
|
|
+ */
|
|
+
|
|
+#ifndef _CX231XX_VBI_H
|
|
+#define _CX231XX_VBI_H
|
|
+
|
|
+extern struct videobuf_queue_ops cx231xx_vbi_qops;
|
|
+
|
|
+#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
|
|
+#define NTSC_VBI_END_LINE 21
|
|
+#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
|
|
+
|
|
+#define PAL_VBI_START_LINE 6
|
|
+#define PAL_VBI_END_LINE 23
|
|
+#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
|
|
+
|
|
+#define VBI_STRIDE 1440
|
|
+#define VBI_SAMPLES_PER_LINE 1440
|
|
+
|
|
+#define CX231XX_NUM_VBI_PACKETS 4
|
|
+#define CX231XX_NUM_VBI_BUFS 5
|
|
+
|
|
+/* stream functions */
|
|
+int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
|
|
+ int num_bufs, int max_pkt_size,
|
|
+ int (*isoc_copy) (struct cx231xx *dev,
|
|
+ struct urb *urb));
|
|
+
|
|
+void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
|
|
+
|
|
+/* vbi data copy functions */
|
|
+u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
|
|
+
|
|
+u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_line, u32 length, int field_number);
|
|
+
|
|
+void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q);
|
|
+
|
|
+int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_buffer, u32 bytes_to_copy);
|
|
+
|
|
+u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q);
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
|
|
new file mode 100644
|
|
index 0000000..d660c08
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
|
|
@@ -0,0 +1,2439 @@
|
|
+/*
|
|
+ cx231xx-video.c - driver for Conexant Cx23100/101/102
|
|
+ USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+ Based on cx23885 driver
|
|
+ Based on cx88 driver
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ This program is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ GNU General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with this program; if not, write to the Free Software
|
|
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/bitmap.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/mutex.h>
|
|
+
|
|
+#include <media/v4l2-common.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-chip-ident.h>
|
|
+#include <media/msp3400.h>
|
|
+#include <media/tuner.h>
|
|
+
|
|
+#include "dvb_frontend.h"
|
|
+
|
|
+#include "cx231xx.h"
|
|
+#include "cx231xx-vbi.h"
|
|
+
|
|
+#define CX231XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
|
|
+
|
|
+#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
|
|
+#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
|
|
+
|
|
+#define cx231xx_videodbg(fmt, arg...) do {\
|
|
+ if (video_debug) \
|
|
+ printk(KERN_INFO "%s %s :"fmt, \
|
|
+ dev->name, __func__ , ##arg); } while (0)
|
|
+
|
|
+static unsigned int isoc_debug;
|
|
+module_param(isoc_debug, int, 0644);
|
|
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
|
|
+
|
|
+#define cx231xx_isocdbg(fmt, arg...) \
|
|
+do {\
|
|
+ if (isoc_debug) { \
|
|
+ printk(KERN_INFO "%s %s :"fmt, \
|
|
+ dev->name, __func__ , ##arg); \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
+MODULE_DESCRIPTION(DRIVER_DESC);
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
+static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
+static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
+static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
|
|
+
|
|
+module_param_array(card, int, NULL, 0444);
|
|
+module_param_array(video_nr, int, NULL, 0444);
|
|
+module_param_array(vbi_nr, int, NULL, 0444);
|
|
+module_param_array(radio_nr, int, NULL, 0444);
|
|
+
|
|
+MODULE_PARM_DESC(card, "card type");
|
|
+MODULE_PARM_DESC(video_nr, "video device numbers");
|
|
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
|
|
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
|
|
+
|
|
+static unsigned int video_debug;
|
|
+module_param(video_debug, int, 0644);
|
|
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
|
|
+
|
|
+/* supported video standards */
|
|
+static struct cx231xx_fmt format[] = {
|
|
+ {
|
|
+ .name = "16bpp YUY2, 4:2:2, packed",
|
|
+ .fourcc = V4L2_PIX_FMT_YUYV,
|
|
+ .depth = 16,
|
|
+ .reg = 0,
|
|
+ },
|
|
+};
|
|
+
|
|
+/* supported controls */
|
|
+/* Common to all boards */
|
|
+
|
|
+/* ------------------------------------------------------------------- */
|
|
+
|
|
+static const struct v4l2_queryctrl no_ctl = {
|
|
+ .name = "42",
|
|
+ .flags = V4L2_CTRL_FLAG_DISABLED,
|
|
+};
|
|
+
|
|
+static struct cx231xx_ctrl cx231xx_ctls[] = {
|
|
+ /* --- video --- */
|
|
+ {
|
|
+ .v = {
|
|
+ .id = V4L2_CID_BRIGHTNESS,
|
|
+ .name = "Brightness",
|
|
+ .minimum = 0x00,
|
|
+ .maximum = 0xff,
|
|
+ .step = 1,
|
|
+ .default_value = 0x7f,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ },
|
|
+ .off = 128,
|
|
+ .reg = LUMA_CTRL,
|
|
+ .mask = 0x00ff,
|
|
+ .shift = 0,
|
|
+ }, {
|
|
+ .v = {
|
|
+ .id = V4L2_CID_CONTRAST,
|
|
+ .name = "Contrast",
|
|
+ .minimum = 0,
|
|
+ .maximum = 0xff,
|
|
+ .step = 1,
|
|
+ .default_value = 0x3f,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ },
|
|
+ .off = 0,
|
|
+ .reg = LUMA_CTRL,
|
|
+ .mask = 0xff00,
|
|
+ .shift = 8,
|
|
+ }, {
|
|
+ .v = {
|
|
+ .id = V4L2_CID_HUE,
|
|
+ .name = "Hue",
|
|
+ .minimum = 0,
|
|
+ .maximum = 0xff,
|
|
+ .step = 1,
|
|
+ .default_value = 0x7f,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ },
|
|
+ .off = 128,
|
|
+ .reg = CHROMA_CTRL,
|
|
+ .mask = 0xff0000,
|
|
+ .shift = 16,
|
|
+ }, {
|
|
+ /* strictly, this only describes only U saturation.
|
|
+ * V saturation is handled specially through code.
|
|
+ */
|
|
+ .v = {
|
|
+ .id = V4L2_CID_SATURATION,
|
|
+ .name = "Saturation",
|
|
+ .minimum = 0,
|
|
+ .maximum = 0xff,
|
|
+ .step = 1,
|
|
+ .default_value = 0x7f,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ },
|
|
+ .off = 0,
|
|
+ .reg = CHROMA_CTRL,
|
|
+ .mask = 0x00ff,
|
|
+ .shift = 0,
|
|
+ }, {
|
|
+ /* --- audio --- */
|
|
+ .v = {
|
|
+ .id = V4L2_CID_AUDIO_MUTE,
|
|
+ .name = "Mute",
|
|
+ .minimum = 0,
|
|
+ .maximum = 1,
|
|
+ .default_value = 1,
|
|
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
+ },
|
|
+ .reg = PATH1_CTL1,
|
|
+ .mask = (0x1f << 24),
|
|
+ .shift = 24,
|
|
+ }, {
|
|
+ .v = {
|
|
+ .id = V4L2_CID_AUDIO_VOLUME,
|
|
+ .name = "Volume",
|
|
+ .minimum = 0,
|
|
+ .maximum = 0x3f,
|
|
+ .step = 1,
|
|
+ .default_value = 0x3f,
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
+ },
|
|
+ .reg = PATH1_VOL_CTL,
|
|
+ .mask = 0xff,
|
|
+ .shift = 0,
|
|
+ }
|
|
+};
|
|
+static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls);
|
|
+
|
|
+static const u32 cx231xx_user_ctrls[] = {
|
|
+ V4L2_CID_USER_CLASS,
|
|
+ V4L2_CID_BRIGHTNESS,
|
|
+ V4L2_CID_CONTRAST,
|
|
+ V4L2_CID_SATURATION,
|
|
+ V4L2_CID_HUE,
|
|
+ V4L2_CID_AUDIO_VOLUME,
|
|
+#if 0
|
|
+ V4L2_CID_AUDIO_BALANCE,
|
|
+#endif
|
|
+ V4L2_CID_AUDIO_MUTE,
|
|
+ 0
|
|
+};
|
|
+
|
|
+static const u32 *ctrl_classes[] = {
|
|
+ cx231xx_user_ctrls,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+/* ------------------------------------------------------------------
|
|
+ Video buffer and parser functions
|
|
+ ------------------------------------------------------------------*/
|
|
+
|
|
+/*
|
|
+ * Announces that a buffer were filled and request the next
|
|
+ */
|
|
+static inline void buffer_filled(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q,
|
|
+ struct cx231xx_buffer *buf)
|
|
+{
|
|
+ /* Advice that buffer was filled */
|
|
+ cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
|
|
+ buf->vb.state = VIDEOBUF_DONE;
|
|
+ buf->vb.field_count++;
|
|
+ do_gettimeofday(&buf->vb.ts);
|
|
+
|
|
+ dev->video_mode.isoc_ctl.buf = NULL;
|
|
+
|
|
+ list_del(&buf->vb.queue);
|
|
+ wake_up(&buf->vb.done);
|
|
+}
|
|
+
|
|
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
|
|
+{
|
|
+ char *errmsg = "Unknown";
|
|
+
|
|
+ switch (status) {
|
|
+ case -ENOENT:
|
|
+ errmsg = "unlinked synchronuously";
|
|
+ break;
|
|
+ case -ECONNRESET:
|
|
+ errmsg = "unlinked asynchronuously";
|
|
+ break;
|
|
+ case -ENOSR:
|
|
+ errmsg = "Buffer error (overrun)";
|
|
+ break;
|
|
+ case -EPIPE:
|
|
+ errmsg = "Stalled (device not responding)";
|
|
+ break;
|
|
+ case -EOVERFLOW:
|
|
+ errmsg = "Babble (bad cable?)";
|
|
+ break;
|
|
+ case -EPROTO:
|
|
+ errmsg = "Bit-stuff error (bad cable?)";
|
|
+ break;
|
|
+ case -EILSEQ:
|
|
+ errmsg = "CRC/Timeout (could be anything)";
|
|
+ break;
|
|
+ case -ETIME:
|
|
+ errmsg = "Device does not respond";
|
|
+ break;
|
|
+ }
|
|
+ if (packet < 0) {
|
|
+ cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
|
|
+ } else {
|
|
+ cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
|
|
+ packet, status, errmsg);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * video-buf generic routine to get the next available buffer
|
|
+ */
|
|
+static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
|
|
+ struct cx231xx_buffer **buf)
|
|
+{
|
|
+ struct cx231xx_video_mode *vmode =
|
|
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
|
|
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
|
|
+
|
|
+ char *outp;
|
|
+
|
|
+ if (list_empty(&dma_q->active)) {
|
|
+ cx231xx_isocdbg("No active queue to serve\n");
|
|
+ dev->video_mode.isoc_ctl.buf = NULL;
|
|
+ *buf = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Get the next buffer */
|
|
+ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
|
|
+
|
|
+ /* Cleans up buffer - Usefull for testing for frame/URB loss */
|
|
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
|
|
+ memset(outp, 0, (*buf)->vb.size);
|
|
+
|
|
+ dev->video_mode.isoc_ctl.buf = *buf;
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Controls the isoc copy of each urb packet
|
|
+ */
|
|
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf;
|
|
+ struct cx231xx_dmaqueue *dma_q = urb->context;
|
|
+ unsigned char *outp = NULL;
|
|
+ int i, rc = 1;
|
|
+ unsigned char *p_buffer;
|
|
+ u32 bytes_parsed = 0, buffer_size = 0;
|
|
+ u8 sav_eav = 0;
|
|
+
|
|
+ if (!dev)
|
|
+ return 0;
|
|
+
|
|
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
|
|
+ return 0;
|
|
+
|
|
+ if (urb->status < 0) {
|
|
+ print_err_status(dev, -1, urb->status);
|
|
+ if (urb->status == -ENOENT)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ buf = dev->video_mode.isoc_ctl.buf;
|
|
+ if (buf != NULL)
|
|
+ outp = videobuf_to_vmalloc(&buf->vb);
|
|
+
|
|
+ for (i = 0; i < urb->number_of_packets; i++) {
|
|
+ int status = urb->iso_frame_desc[i].status;
|
|
+
|
|
+ if (status < 0) {
|
|
+ print_err_status(dev, i, status);
|
|
+ if (urb->iso_frame_desc[i].status != -EPROTO)
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
|
|
+ /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
|
|
+ continue;
|
|
+ }
|
|
+ if (urb->iso_frame_desc[i].actual_length >
|
|
+ dev->video_mode.max_pkt_size) {
|
|
+ cx231xx_isocdbg("packet bigger than packet size");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* get buffer pointer and length */
|
|
+ p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
|
|
+ buffer_size = urb->iso_frame_desc[i].actual_length;
|
|
+ bytes_parsed = 0;
|
|
+
|
|
+ if (dma_q->is_partial_line) {
|
|
+ /* Handle the case of a partial line */
|
|
+ sav_eav = dma_q->last_sav;
|
|
+ } else {
|
|
+ /* Check for a SAV/EAV overlapping
|
|
+ the buffer boundary */
|
|
+ sav_eav =
|
|
+ cx231xx_find_boundary_SAV_EAV(p_buffer,
|
|
+ dma_q->partial_buf,
|
|
+ &bytes_parsed);
|
|
+ }
|
|
+
|
|
+ sav_eav &= 0xF0;
|
|
+ /* Get the first line if we have some portion of an SAV/EAV from
|
|
+ the last buffer or a partial line */
|
|
+ if (sav_eav) {
|
|
+ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
|
|
+ sav_eav, /* SAV/EAV */
|
|
+ p_buffer + bytes_parsed, /* p_buffer */
|
|
+ buffer_size - bytes_parsed);/* buf size */
|
|
+ }
|
|
+
|
|
+ /* Now parse data that is completely in this buffer */
|
|
+ /* dma_q->is_partial_line = 0; */
|
|
+
|
|
+ while (bytes_parsed < buffer_size) {
|
|
+ u32 bytes_used = 0;
|
|
+
|
|
+ sav_eav = cx231xx_find_next_SAV_EAV(
|
|
+ p_buffer + bytes_parsed, /* p_buffer */
|
|
+ buffer_size - bytes_parsed, /* buf size */
|
|
+ &bytes_used);/* bytes used to get SAV/EAV */
|
|
+
|
|
+ bytes_parsed += bytes_used;
|
|
+
|
|
+ sav_eav &= 0xF0;
|
|
+ if (sav_eav && (bytes_parsed < buffer_size)) {
|
|
+ bytes_parsed += cx231xx_get_video_line(dev,
|
|
+ dma_q, sav_eav, /* SAV/EAV */
|
|
+ p_buffer + bytes_parsed,/* p_buffer */
|
|
+ buffer_size - bytes_parsed);/*buf size*/
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Save the last four bytes of the buffer so we can check the
|
|
+ buffer boundary condition next time */
|
|
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
|
|
+ bytes_parsed = 0;
|
|
+
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
|
|
+ u32 *p_bytes_used)
|
|
+{
|
|
+ u32 bytes_used;
|
|
+ u8 boundary_bytes[8];
|
|
+ u8 sav_eav = 0;
|
|
+
|
|
+ *p_bytes_used = 0;
|
|
+
|
|
+ /* Create an array of the last 4 bytes of the last buffer and the first
|
|
+ 4 bytes of the current buffer. */
|
|
+
|
|
+ memcpy(boundary_bytes, partial_buf, 4);
|
|
+ memcpy(boundary_bytes + 4, p_buffer, 4);
|
|
+
|
|
+ /* Check for the SAV/EAV in the boundary buffer */
|
|
+ sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
|
|
+ &bytes_used);
|
|
+
|
|
+ if (sav_eav) {
|
|
+ /* found a boundary SAV/EAV. Updates the bytes used to reflect
|
|
+ only those used in the new buffer */
|
|
+ *p_bytes_used = bytes_used - 4;
|
|
+ }
|
|
+
|
|
+ return sav_eav;
|
|
+}
|
|
+
|
|
+u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
|
|
+{
|
|
+ u32 i;
|
|
+ u8 sav_eav = 0;
|
|
+
|
|
+ /*
|
|
+ * Don't search if the buffer size is less than 4. It causes a page
|
|
+ * fault since buffer_size - 4 evaluates to a large number in that
|
|
+ * case.
|
|
+ */
|
|
+ if (buffer_size < 4) {
|
|
+ *p_bytes_used = buffer_size;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < (buffer_size - 3); i++) {
|
|
+
|
|
+ if ((p_buffer[i] == 0xFF) &&
|
|
+ (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
|
|
+
|
|
+ *p_bytes_used = i + 4;
|
|
+ sav_eav = p_buffer[i + 3];
|
|
+ return sav_eav;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *p_bytes_used = buffer_size;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+u32 cx231xx_get_video_line(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
|
|
+ u8 *p_buffer, u32 buffer_size)
|
|
+{
|
|
+ u32 bytes_copied = 0;
|
|
+ int current_field = -1;
|
|
+
|
|
+ switch (sav_eav) {
|
|
+ case SAV_ACTIVE_VIDEO_FIELD1:
|
|
+ /* looking for skipped line which occurred in PAL 720x480 mode.
|
|
+ In this case, there will be no active data contained
|
|
+ between the SAV and EAV */
|
|
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
|
|
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
|
|
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
|
|
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
|
|
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
|
|
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
|
|
+ return bytes_copied;
|
|
+ current_field = 1;
|
|
+ break;
|
|
+
|
|
+ case SAV_ACTIVE_VIDEO_FIELD2:
|
|
+ /* looking for skipped line which occurred in PAL 720x480 mode.
|
|
+ In this case, there will be no active data contained between
|
|
+ the SAV and EAV */
|
|
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
|
|
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
|
|
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
|
|
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
|
|
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
|
|
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
|
|
+ return bytes_copied;
|
|
+ current_field = 2;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ dma_q->last_sav = sav_eav;
|
|
+
|
|
+ bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
|
|
+ buffer_size, current_field);
|
|
+
|
|
+ return bytes_copied;
|
|
+}
|
|
+
|
|
+u32 cx231xx_copy_video_line(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q, u8 *p_line,
|
|
+ u32 length, int field_number)
|
|
+{
|
|
+ u32 bytes_to_copy;
|
|
+ struct cx231xx_buffer *buf;
|
|
+ u32 _line_size = dev->width * 2;
|
|
+
|
|
+ if (dma_q->current_field != field_number)
|
|
+ cx231xx_reset_video_buffer(dev, dma_q);
|
|
+
|
|
+ /* get the buffer pointer */
|
|
+ buf = dev->video_mode.isoc_ctl.buf;
|
|
+
|
|
+ /* Remember the field number for next time */
|
|
+ dma_q->current_field = field_number;
|
|
+
|
|
+ bytes_to_copy = dma_q->bytes_left_in_line;
|
|
+ if (bytes_to_copy > length)
|
|
+ bytes_to_copy = length;
|
|
+
|
|
+ if (dma_q->lines_completed >= dma_q->lines_per_field) {
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
|
|
+ 0 : 1;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dma_q->is_partial_line = 1;
|
|
+
|
|
+ /* If we don't have a buffer, just return the number of bytes we would
|
|
+ have copied if we had a buffer. */
|
|
+ if (!buf) {
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
|
|
+ ? 0 : 1;
|
|
+ return bytes_to_copy;
|
|
+ }
|
|
+
|
|
+ /* copy the data to video buffer */
|
|
+ cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
|
|
+
|
|
+ dma_q->pos += bytes_to_copy;
|
|
+ dma_q->bytes_left_in_line -= bytes_to_copy;
|
|
+
|
|
+ if (dma_q->bytes_left_in_line == 0) {
|
|
+ dma_q->bytes_left_in_line = _line_size;
|
|
+ dma_q->lines_completed++;
|
|
+ dma_q->is_partial_line = 0;
|
|
+
|
|
+ if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
|
|
+ buffer_filled(dev, dma_q, buf);
|
|
+
|
|
+ dma_q->pos = 0;
|
|
+ buf = NULL;
|
|
+ dma_q->lines_completed = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return bytes_to_copy;
|
|
+}
|
|
+
|
|
+void cx231xx_reset_video_buffer(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q)
|
|
+{
|
|
+ struct cx231xx_buffer *buf;
|
|
+
|
|
+ /* handle the switch from field 1 to field 2 */
|
|
+ if (dma_q->current_field == 1) {
|
|
+ if (dma_q->lines_completed >= dma_q->lines_per_field)
|
|
+ dma_q->field1_done = 1;
|
|
+ else
|
|
+ dma_q->field1_done = 0;
|
|
+ }
|
|
+
|
|
+ buf = dev->video_mode.isoc_ctl.buf;
|
|
+
|
|
+ if (buf == NULL) {
|
|
+ u8 *outp = NULL;
|
|
+ /* first try to get the buffer */
|
|
+ get_next_buf(dma_q, &buf);
|
|
+
|
|
+ if (buf)
|
|
+ outp = videobuf_to_vmalloc(&buf->vb);
|
|
+
|
|
+ dma_q->pos = 0;
|
|
+ dma_q->field1_done = 0;
|
|
+ dma_q->current_field = -1;
|
|
+ }
|
|
+
|
|
+ /* reset the counters */
|
|
+ dma_q->bytes_left_in_line = dev->width << 1;
|
|
+ dma_q->lines_completed = 0;
|
|
+}
|
|
+
|
|
+int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_buffer, u32 bytes_to_copy)
|
|
+{
|
|
+ u8 *p_out_buffer = NULL;
|
|
+ u32 current_line_bytes_copied = 0;
|
|
+ struct cx231xx_buffer *buf;
|
|
+ u32 _line_size = dev->width << 1;
|
|
+ void *startwrite;
|
|
+ int offset, lencopy;
|
|
+
|
|
+ buf = dev->video_mode.isoc_ctl.buf;
|
|
+
|
|
+ if (buf == NULL)
|
|
+ return -1;
|
|
+
|
|
+ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
|
|
+
|
|
+ current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
|
|
+
|
|
+ /* Offset field 2 one line from the top of the buffer */
|
|
+ offset = (dma_q->current_field == 1) ? 0 : _line_size;
|
|
+
|
|
+ /* Offset for field 2 */
|
|
+ startwrite = p_out_buffer + offset;
|
|
+
|
|
+ /* lines already completed in the current field */
|
|
+ startwrite += (dma_q->lines_completed * _line_size * 2);
|
|
+
|
|
+ /* bytes already completed in the current line */
|
|
+ startwrite += current_line_bytes_copied;
|
|
+
|
|
+ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
|
|
+ bytes_to_copy : dma_q->bytes_left_in_line;
|
|
+
|
|
+ if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
|
|
+ return 0;
|
|
+
|
|
+ /* The below copies the UYVY data straight into video buffer */
|
|
+ cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void cx231xx_swab(u16 *from, u16 *to, u16 len)
|
|
+{
|
|
+ u16 i;
|
|
+
|
|
+ if (len <= 0)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < len / 2; i++)
|
|
+ to[i] = (from[i] << 8) | (from[i] >> 8);
|
|
+}
|
|
+
|
|
+u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
|
|
+{
|
|
+ u8 buffer_complete = 0;
|
|
+
|
|
+ /* Dual field stream */
|
|
+ buffer_complete = ((dma_q->current_field == 2) &&
|
|
+ (dma_q->lines_completed >= dma_q->lines_per_field) &&
|
|
+ dma_q->field1_done);
|
|
+
|
|
+ return buffer_complete;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------
|
|
+ Videobuf operations
|
|
+ ------------------------------------------------------------------*/
|
|
+
|
|
+static int
|
|
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ struct v4l2_frequency f;
|
|
+
|
|
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
|
|
+ if (0 == *count)
|
|
+ *count = CX231XX_DEF_BUF;
|
|
+
|
|
+ if (*count < CX231XX_MIN_BUF)
|
|
+ *count = CX231XX_MIN_BUF;
|
|
+
|
|
+ /* Ask tuner to go to analog mode */
|
|
+ memset(&f, 0, sizeof(f));
|
|
+ f.frequency = dev->ctl_freq;
|
|
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
|
|
+
|
|
+ call_all(dev, tuner, s_frequency, &f);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* This is called *without* dev->slock held; please keep it that way */
|
|
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ unsigned long flags = 0;
|
|
+
|
|
+ if (in_interrupt())
|
|
+ BUG();
|
|
+
|
|
+ /* We used to wait for the buffer to finish here, but this didn't work
|
|
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
|
|
+ videobuf_queue_cancel marked it as finished for us.
|
|
+ (Also, it could wedge forever if the hardware was misconfigured.)
|
|
+
|
|
+ This should be safe; by the time we get here, the buffer isn't
|
|
+ queued anymore. If we ever start marking the buffers as
|
|
+ VIDEOBUF_ACTIVE, it won't be, though.
|
|
+ */
|
|
+ spin_lock_irqsave(&dev->video_mode.slock, flags);
|
|
+ if (dev->video_mode.isoc_ctl.buf == buf)
|
|
+ dev->video_mode.isoc_ctl.buf = NULL;
|
|
+ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
|
|
+
|
|
+ videobuf_vmalloc_free(&buf->vb);
|
|
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
|
|
+}
|
|
+
|
|
+static int
|
|
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
|
+ enum v4l2_field field)
|
|
+{
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc = 0, urb_init = 0;
|
|
+
|
|
+ /* The only currently supported format is 16 bits/pixel */
|
|
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
|
|
+ + 7) >> 3;
|
|
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
|
|
+ return -EINVAL;
|
|
+
|
|
+ buf->vb.width = dev->width;
|
|
+ buf->vb.height = dev->height;
|
|
+ buf->vb.field = field;
|
|
+
|
|
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
|
|
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
|
|
+ if (rc < 0)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (!dev->video_mode.isoc_ctl.num_bufs)
|
|
+ urb_init = 1;
|
|
+
|
|
+ if (urb_init) {
|
|
+ rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
|
|
+ CX231XX_NUM_BUFS,
|
|
+ dev->video_mode.max_pkt_size,
|
|
+ cx231xx_isoc_copy);
|
|
+ if (rc < 0)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ buf->vb.state = VIDEOBUF_PREPARED;
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ free_buffer(vq, buf);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
|
|
+
|
|
+ buf->vb.state = VIDEOBUF_QUEUED;
|
|
+ list_add_tail(&buf->vb.queue, &vidq->active);
|
|
+
|
|
+}
|
|
+
|
|
+static void buffer_release(struct videobuf_queue *vq,
|
|
+ struct videobuf_buffer *vb)
|
|
+{
|
|
+ struct cx231xx_buffer *buf =
|
|
+ container_of(vb, struct cx231xx_buffer, vb);
|
|
+ struct cx231xx_fh *fh = vq->priv_data;
|
|
+ struct cx231xx *dev = (struct cx231xx *)fh->dev;
|
|
+
|
|
+ cx231xx_isocdbg("cx231xx: called buffer_release\n");
|
|
+
|
|
+ free_buffer(vq, buf);
|
|
+}
|
|
+
|
|
+static struct videobuf_queue_ops cx231xx_video_qops = {
|
|
+ .buf_setup = buffer_setup,
|
|
+ .buf_prepare = buffer_prepare,
|
|
+ .buf_queue = buffer_queue,
|
|
+ .buf_release = buffer_release,
|
|
+};
|
|
+
|
|
+/********************* v4l2 interface **************************************/
|
|
+
|
|
+void video_mux(struct cx231xx *dev, int index)
|
|
+{
|
|
+
|
|
+ struct v4l2_routing route;
|
|
+
|
|
+ route.input = INPUT(index)->vmux;
|
|
+ route.output = 0;
|
|
+ dev->video_input = index;
|
|
+ dev->ctl_ainput = INPUT(index)->amux;
|
|
+
|
|
+ cx231xx_set_video_input_mux(dev, index);
|
|
+
|
|
+ cx25840_call(dev, video, s_routing, &route);
|
|
+
|
|
+ cx231xx_set_audio_input(dev, dev->ctl_ainput);
|
|
+
|
|
+ cx231xx_info("video_mux : %d\n", index);
|
|
+
|
|
+ /* do mode control overrides if required */
|
|
+ cx231xx_do_mode_ctrl_overrides(dev);
|
|
+}
|
|
+
|
|
+/* Usage lock check functions */
|
|
+static int res_get(struct cx231xx_fh *fh)
|
|
+{
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc = 0;
|
|
+
|
|
+ /* This instance already has stream_on */
|
|
+ if (fh->stream_on)
|
|
+ return rc;
|
|
+
|
|
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
+ if (dev->stream_on)
|
|
+ return -EBUSY;
|
|
+ dev->stream_on = 1;
|
|
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
|
+ if (dev->vbi_stream_on)
|
|
+ return -EBUSY;
|
|
+ dev->vbi_stream_on = 1;
|
|
+ } else
|
|
+ return -EINVAL;
|
|
+
|
|
+ fh->stream_on = 1;
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int res_check(struct cx231xx_fh *fh)
|
|
+{
|
|
+ return fh->stream_on;
|
|
+}
|
|
+
|
|
+static void res_free(struct cx231xx_fh *fh)
|
|
+{
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ fh->stream_on = 0;
|
|
+
|
|
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ dev->stream_on = 0;
|
|
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
|
|
+ dev->vbi_stream_on = 0;
|
|
+}
|
|
+
|
|
+static int check_dev(struct cx231xx *dev)
|
|
+{
|
|
+ if (dev->state & DEV_DISCONNECTED) {
|
|
+ cx231xx_errdev("v4l2 ioctl: device not present\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ if (dev->state & DEV_MISCONFIGURED) {
|
|
+ cx231xx_errdev("v4l2 ioctl: device is misconfigured; "
|
|
+ "close and open it again\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void get_scale(struct cx231xx *dev,
|
|
+ unsigned int width, unsigned int height,
|
|
+ unsigned int *hscale, unsigned int *vscale)
|
|
+{
|
|
+ unsigned int maxw = norm_maxw(dev);
|
|
+ unsigned int maxh = norm_maxh(dev);
|
|
+
|
|
+ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
|
|
+ if (*hscale >= 0x4000)
|
|
+ *hscale = 0x3fff;
|
|
+
|
|
+ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
|
|
+ if (*vscale >= 0x4000)
|
|
+ *vscale = 0x3fff;
|
|
+
|
|
+ dev->hscale = *hscale;
|
|
+ dev->vscale = *vscale;
|
|
+
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------
|
|
+ IOCTL vidioc handling
|
|
+ ------------------------------------------------------------------*/
|
|
+
|
|
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ f->fmt.pix.width = dev->width;
|
|
+ f->fmt.pix.height = dev->height;
|
|
+ f->fmt.pix.pixelformat = dev->format->fourcc;;
|
|
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;;
|
|
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
|
|
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
+
|
|
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(format); i++)
|
|
+ if (format[i].fourcc == fourcc)
|
|
+ return &format[i];
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int width = f->fmt.pix.width;
|
|
+ int height = f->fmt.pix.height;
|
|
+ unsigned int maxw = norm_maxw(dev);
|
|
+ unsigned int maxh = norm_maxh(dev);
|
|
+ unsigned int hscale, vscale;
|
|
+ struct cx231xx_fmt *fmt;
|
|
+
|
|
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
|
+ if (!fmt) {
|
|
+ cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
|
|
+ f->fmt.pix.pixelformat);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* width must even because of the YUYV format
|
|
+ height must be even because of interlacing */
|
|
+ height &= 0xfffe;
|
|
+ width &= 0xfffe;
|
|
+
|
|
+ if (unlikely(height < 32))
|
|
+ height = 32;
|
|
+ if (unlikely(height > maxh))
|
|
+ height = maxh;
|
|
+ if (unlikely(width < 48))
|
|
+ width = 48;
|
|
+ if (unlikely(width > maxw))
|
|
+ width = maxw;
|
|
+
|
|
+ get_scale(dev, width, height, &hscale, &vscale);
|
|
+
|
|
+ width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
|
|
+ height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
|
|
+
|
|
+ f->fmt.pix.width = width;
|
|
+ f->fmt.pix.height = height;
|
|
+ f->fmt.pix.pixelformat = fmt->fourcc;
|
|
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
|
|
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
|
|
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+ struct cx231xx_fmt *fmt;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ vidioc_try_fmt_vid_cap(file, priv, f);
|
|
+
|
|
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
|
+ if (!fmt) {
|
|
+ rc = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
|
|
+ cx231xx_errdev("%s queue busy\n", __func__);
|
|
+ rc = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (dev->stream_on && !fh->stream_on) {
|
|
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
|
|
+ rc = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* set new image size */
|
|
+ dev->width = f->fmt.pix.width;
|
|
+ dev->height = f->fmt.pix.height;
|
|
+ dev->format = fmt;
|
|
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
|
|
+
|
|
+ call_all(dev, video, s_fmt, f);
|
|
+
|
|
+ /* Set the correct alternate setting for this resolution */
|
|
+ cx231xx_resolution_set(dev);
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id * id)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ *id = dev->norm;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ struct v4l2_format f;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ dev->norm = *norm;
|
|
+
|
|
+ /* Adjusts width/height, if needed */
|
|
+ f.fmt.pix.width = dev->width;
|
|
+ f.fmt.pix.height = dev->height;
|
|
+ vidioc_try_fmt_vid_cap(file, priv, &f);
|
|
+
|
|
+ /* set new image size */
|
|
+ dev->width = f.fmt.pix.width;
|
|
+ dev->height = f.fmt.pix.height;
|
|
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
|
|
+
|
|
+ call_all(dev, tuner, s_std, dev->norm);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ cx231xx_resolution_set(dev);
|
|
+
|
|
+ /* do mode control overrides */
|
|
+ cx231xx_do_mode_ctrl_overrides(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const char *iname[] = {
|
|
+ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
|
|
+ [CX231XX_VMUX_SVIDEO] = "S-Video",
|
|
+ [CX231XX_VMUX_TELEVISION] = "Television",
|
|
+ [CX231XX_VMUX_CABLE] = "Cable TV",
|
|
+ [CX231XX_VMUX_DVB] = "DVB",
|
|
+ [CX231XX_VMUX_DEBUG] = "for debug only",
|
|
+};
|
|
+
|
|
+static int vidioc_enum_input(struct file *file, void *priv,
|
|
+ struct v4l2_input *i)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ unsigned int n;
|
|
+
|
|
+ n = i->index;
|
|
+ if (n >= MAX_CX231XX_INPUT)
|
|
+ return -EINVAL;
|
|
+ if (0 == INPUT(n)->type)
|
|
+ return -EINVAL;
|
|
+
|
|
+ i->index = n;
|
|
+ i->type = V4L2_INPUT_TYPE_CAMERA;
|
|
+
|
|
+ strcpy(i->name, iname[INPUT(n)->type]);
|
|
+
|
|
+ if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
|
|
+ (CX231XX_VMUX_CABLE == INPUT(n)->type))
|
|
+ i->type = V4L2_INPUT_TYPE_TUNER;
|
|
+
|
|
+ i->std = dev->vdev->tvnorms;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ *i = dev->video_input;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if (i >= MAX_CX231XX_INPUT)
|
|
+ return -EINVAL;
|
|
+ if (0 == INPUT(i)->type)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ video_mux(dev, i);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ switch (a->index) {
|
|
+ case CX231XX_AMUX_VIDEO:
|
|
+ strcpy(a->name, "Television");
|
|
+ break;
|
|
+ case CX231XX_AMUX_LINE_IN:
|
|
+ strcpy(a->name, "Line In");
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ a->index = dev->ctl_ainput;
|
|
+ a->capability = V4L2_AUDCAP_STEREO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int status = 0;
|
|
+
|
|
+ /* Doesn't allow manual routing */
|
|
+ if (a->index != dev->ctl_ainput)
|
|
+ return -EINVAL;
|
|
+
|
|
+ dev->ctl_ainput = INPUT(a->index)->amux;
|
|
+ status = cx231xx_set_audio_input(dev, dev->ctl_ainput);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static int vidioc_queryctrl(struct file *file, void *priv,
|
|
+ struct v4l2_queryctrl *qc)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int id = qc->id;
|
|
+ int i;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
|
|
+ if (unlikely(qc->id == 0))
|
|
+ return -EINVAL;
|
|
+
|
|
+ memset(qc, 0, sizeof(*qc));
|
|
+
|
|
+ qc->id = id;
|
|
+
|
|
+ if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < CX231XX_CTLS; i++)
|
|
+ if (cx231xx_ctls[i].v.id == qc->id)
|
|
+ break;
|
|
+
|
|
+ if (i == CX231XX_CTLS) {
|
|
+ *qc = no_ctl;
|
|
+ return 0;
|
|
+ }
|
|
+ *qc = cx231xx_ctls[i].v;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, core, queryctrl, qc);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (qc->type)
|
|
+ return 0;
|
|
+ else
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
+ struct v4l2_control *ctrl)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, core, g_ctrl, ctrl);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
+ struct v4l2_control *ctrl)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, core, s_ctrl, ctrl);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if (0 != t->index)
|
|
+ return -EINVAL;
|
|
+
|
|
+ strcpy(t->name, "Tuner");
|
|
+
|
|
+ t->type = V4L2_TUNER_ANALOG_TV;
|
|
+ t->capability = V4L2_TUNER_CAP_NORM;
|
|
+ t->rangehigh = 0xffffffffUL;
|
|
+ t->signal = 0xffff; /* LOCKED */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if (0 != t->index)
|
|
+ return -EINVAL;
|
|
+#if 0
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, tuner, s_tuner, t);
|
|
+ mutex_unlock(&dev->lock);
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_g_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
|
|
+ f->frequency = dev->ctl_freq;
|
|
+
|
|
+ call_all(dev, tuner, g_frequency, f);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_s_frequency(struct file *file, void *priv,
|
|
+ struct v4l2_frequency *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if (0 != f->tuner)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
|
|
+ return -EINVAL;
|
|
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set pre channel change settings in DIF first */
|
|
+ rc = cx231xx_tuner_pre_channel_change(dev);
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ dev->ctl_freq = f->frequency;
|
|
+
|
|
+ if (dev->tuner_type == TUNER_XC5000) {
|
|
+ if (dev->cx231xx_set_analog_freq != NULL)
|
|
+ dev->cx231xx_set_analog_freq(dev, f->frequency);
|
|
+ } else
|
|
+ call_all(dev, tuner, s_frequency, f);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ /* set post channel change settings in DIF first */
|
|
+ rc = cx231xx_tuner_post_channel_change(dev);
|
|
+
|
|
+ cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+
|
|
+/*
|
|
+ -R, --list-registers=type=<host/i2cdrv/i2caddr>,
|
|
+ chip=<chip>[,min=<addr>,max=<addr>]
|
|
+ dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
|
|
+ -r, --set-register=type=<host/i2cdrv/i2caddr>,
|
|
+ chip=<chip>,reg=<addr>,val=<val>
|
|
+ set the register [VIDIOC_DBG_S_REGISTER]
|
|
+
|
|
+ if type == host, then <chip> is the hosts chip ID (default 0)
|
|
+ if type == i2cdrv (default), then <chip> is the I2C driver name or ID
|
|
+ if type == i2caddr, then <chip> is the 7-bit I2C address
|
|
+*/
|
|
+
|
|
+static int vidioc_g_register(struct file *file, void *priv,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int ret = 0;
|
|
+ u8 value[4] = { 0, 0, 0, 0 };
|
|
+ u32 data = 0;
|
|
+
|
|
+ switch (reg->match.type) {
|
|
+ case V4L2_CHIP_MATCH_HOST:
|
|
+ switch (reg->match.addr) {
|
|
+ case 0: /* Cx231xx - internal registers */
|
|
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
|
|
+ (u16)reg->reg, value, 4);
|
|
+ reg->val = value[0] | value[1] << 8 |
|
|
+ value[2] << 16 | value[3] << 24;
|
|
+ break;
|
|
+ case 1: /* AFE - read byte */
|
|
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 2, &data, 1);
|
|
+ reg->val = le32_to_cpu(data & 0xff);
|
|
+ break;
|
|
+ case 14: /* AFE - read dword */
|
|
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 2, &data, 4);
|
|
+ reg->val = le32_to_cpu(data);
|
|
+ break;
|
|
+ case 2: /* Video Block - read byte */
|
|
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ (u16)reg->reg, 2, &data, 1);
|
|
+ reg->val = le32_to_cpu(data & 0xff);
|
|
+ break;
|
|
+ case 24: /* Video Block - read dword */
|
|
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
|
|
+ (u16)reg->reg, 2, &data, 4);
|
|
+ reg->val = le32_to_cpu(data);
|
|
+ break;
|
|
+ case 3: /* I2S block - read byte */
|
|
+ ret = cx231xx_read_i2c_data(dev,
|
|
+ I2S_BLK_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 1,
|
|
+ &data, 1);
|
|
+ reg->val = le32_to_cpu(data & 0xff);
|
|
+ break;
|
|
+ case 34: /* I2S Block - read dword */
|
|
+ ret =
|
|
+ cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 1, &data, 4);
|
|
+ reg->val = le32_to_cpu(data);
|
|
+ break;
|
|
+ }
|
|
+ return ret < 0 ? ret : 0;
|
|
+
|
|
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
|
|
+ call_all(dev, core, g_register, reg);
|
|
+ return 0;
|
|
+ case V4L2_CHIP_MATCH_I2C_ADDR:
|
|
+ /* Not supported yet */
|
|
+ return -EINVAL;
|
|
+ default:
|
|
+ if (!v4l2_chip_match_host(®->match))
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, core, g_register, reg);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vidioc_s_register(struct file *file, void *priv,
|
|
+ struct v4l2_dbg_register *reg)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int ret = 0;
|
|
+ __le64 buf;
|
|
+ u32 value;
|
|
+ u8 data[4] = { 0, 0, 0, 0 };
|
|
+
|
|
+ buf = cpu_to_le64(reg->val);
|
|
+
|
|
+ switch (reg->match.type) {
|
|
+ case V4L2_CHIP_MATCH_HOST:
|
|
+ {
|
|
+ value = (u32) buf & 0xffffffff;
|
|
+
|
|
+ switch (reg->match.addr) {
|
|
+ case 0: /* cx231xx internal registers */
|
|
+ data[0] = (u8) value;
|
|
+ data[1] = (u8) (value >> 8);
|
|
+ data[2] = (u8) (value >> 16);
|
|
+ data[3] = (u8) (value >> 24);
|
|
+ ret = cx231xx_write_ctrl_reg(dev,
|
|
+ VRT_SET_REGISTER,
|
|
+ (u16)reg->reg, data,
|
|
+ 4);
|
|
+ break;
|
|
+ case 1: /* AFE - read byte */
|
|
+ ret = cx231xx_write_i2c_data(dev,
|
|
+ AFE_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 2,
|
|
+ value, 1);
|
|
+ break;
|
|
+ case 14: /* AFE - read dword */
|
|
+ ret = cx231xx_write_i2c_data(dev,
|
|
+ AFE_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 2,
|
|
+ value, 4);
|
|
+ break;
|
|
+ case 2: /* Video Block - read byte */
|
|
+ ret =
|
|
+ cx231xx_write_i2c_data(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ (u16)reg->reg, 2,
|
|
+ value, 1);
|
|
+ break;
|
|
+ case 24: /* Video Block - read dword */
|
|
+ ret =
|
|
+ cx231xx_write_i2c_data(dev,
|
|
+ VID_BLK_I2C_ADDRESS,
|
|
+ (u16)reg->reg, 2,
|
|
+ value, 4);
|
|
+ break;
|
|
+ case 3: /* I2S block - read byte */
|
|
+ ret =
|
|
+ cx231xx_write_i2c_data(dev,
|
|
+ I2S_BLK_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 1,
|
|
+ value, 1);
|
|
+ break;
|
|
+ case 34: /* I2S block - read dword */
|
|
+ ret =
|
|
+ cx231xx_write_i2c_data(dev,
|
|
+ I2S_BLK_DEVICE_ADDRESS,
|
|
+ (u16)reg->reg, 1,
|
|
+ value, 4);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return ret < 0 ? ret : 0;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, core, s_register, reg);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int vidioc_cropcap(struct file *file, void *priv,
|
|
+ struct v4l2_cropcap *cc)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ cc->bounds.left = 0;
|
|
+ cc->bounds.top = 0;
|
|
+ cc->bounds.width = dev->width;
|
|
+ cc->bounds.height = dev->height;
|
|
+ cc->defrect = cc->bounds;
|
|
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
|
|
+ cc->pixelaspect.denominator = 59;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_streamon(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type type)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ rc = res_get(fh);
|
|
+
|
|
+ if (likely(rc >= 0))
|
|
+ rc = videobuf_streamon(&fh->vb_vidq);
|
|
+
|
|
+ call_all(dev, video, s_stream, 1);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vidioc_streamoff(struct file *file, void *priv,
|
|
+ enum v4l2_buf_type type)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
|
|
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
|
|
+ return -EINVAL;
|
|
+ if (type != fh->type)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ cx25840_call(dev, video, s_stream, 0);
|
|
+
|
|
+ videobuf_streamoff(&fh->vb_vidq);
|
|
+ res_free(fh);
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *cap)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
|
|
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
|
|
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
|
+
|
|
+ cap->version = CX231XX_VERSION_CODE;
|
|
+
|
|
+ cap->capabilities = V4L2_CAP_VBI_CAPTURE |
|
|
+#if 0
|
|
+ V4L2_CAP_SLICED_VBI_CAPTURE |
|
|
+#endif
|
|
+ V4L2_CAP_VIDEO_CAPTURE |
|
|
+ V4L2_CAP_AUDIO |
|
|
+ V4L2_CAP_READWRITE |
|
|
+ V4L2_CAP_STREAMING;
|
|
+
|
|
+ if (dev->tuner_type != TUNER_ABSENT)
|
|
+ cap->capabilities |= V4L2_CAP_TUNER;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_fmtdesc *f)
|
|
+{
|
|
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
|
|
+ return -EINVAL;
|
|
+
|
|
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
|
|
+ f->pixelformat = format[f->index].fourcc;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Sliced VBI ioctls */
|
|
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ f->fmt.sliced.service_set = 0;
|
|
+
|
|
+ call_all(dev, video, g_fmt, f);
|
|
+
|
|
+ if (f->fmt.sliced.service_set == 0)
|
|
+ rc = -EINVAL;
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, video, g_fmt, f);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (f->fmt.sliced.service_set == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* RAW VBI ioctls */
|
|
+
|
|
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
|
|
+ 35468950 : 28636363;
|
|
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
|
|
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
|
|
+ f->fmt.vbi.offset = 64 * 4;
|
|
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
|
|
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES;
|
|
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
|
|
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ if (dev->vbi_stream_on && !fh->stream_on) {
|
|
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
|
|
+ f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
|
|
+ 35468950 : 28636363;
|
|
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
|
|
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
|
|
+ f->fmt.vbi.offset = 244;
|
|
+ f->fmt.vbi.flags = 0;
|
|
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
|
|
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_LINES : NTSC_VBI_LINES;
|
|
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
|
|
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
|
|
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
|
|
+
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+static int vidioc_reqbufs(struct file *file, void *priv,
|
|
+ struct v4l2_requestbuffers *rb)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
|
|
+}
|
|
+
|
|
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ return videobuf_querybuf(&fh->vb_vidq, b);
|
|
+}
|
|
+
|
|
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ return videobuf_qbuf(&fh->vb_vidq, b);
|
|
+}
|
|
+
|
|
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
|
|
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
|
|
+{
|
|
+ struct cx231xx_fh *fh = priv;
|
|
+
|
|
+ return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* ----------------------------------------------------------- */
|
|
+/* RADIO ESPECIFIC IOCTLS */
|
|
+/* ----------------------------------------------------------- */
|
|
+
|
|
+static int radio_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *cap)
|
|
+{
|
|
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
|
|
+
|
|
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
|
|
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
|
|
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
|
|
+
|
|
+ cap->version = CX231XX_VERSION_CODE;
|
|
+ cap->capabilities = V4L2_CAP_TUNER;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
|
+{
|
|
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
|
|
+
|
|
+ if (unlikely(t->index > 0))
|
|
+ return -EINVAL;
|
|
+
|
|
+ strcpy(t->name, "Radio");
|
|
+ t->type = V4L2_TUNER_RADIO;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, tuner, s_tuner, t);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i)
|
|
+{
|
|
+ if (i->index != 0)
|
|
+ return -EINVAL;
|
|
+ strcpy(i->name, "Radio");
|
|
+ i->type = V4L2_INPUT_TYPE_TUNER;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|
+{
|
|
+ if (unlikely(a->index))
|
|
+ return -EINVAL;
|
|
+
|
|
+ strcpy(a->name, "Radio");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
|
|
+{
|
|
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
|
|
+
|
|
+ if (0 != t->index)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ call_all(dev, tuner, s_tuner, t);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int radio_queryctrl(struct file *file, void *priv,
|
|
+ struct v4l2_queryctrl *c)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
|
|
+ return -EINVAL;
|
|
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
|
|
+ for (i = 0; i < CX231XX_CTLS; i++)
|
|
+ if (cx231xx_ctls[i].v.id == c->id)
|
|
+ break;
|
|
+ *c = cx231xx_ctls[i].v;
|
|
+ } else
|
|
+ *c = no_ctl;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_v4l2_open()
|
|
+ * inits the device and starts isoc transfer
|
|
+ */
|
|
+static int cx231xx_v4l2_open(struct file *filp)
|
|
+{
|
|
+ int minor = video_devdata(filp)->minor;
|
|
+ int errCode = 0, radio = 0;
|
|
+ struct cx231xx *dev = NULL;
|
|
+ struct cx231xx_fh *fh;
|
|
+ enum v4l2_buf_type fh_type = 0;
|
|
+
|
|
+ dev = cx231xx_get_device(minor, &fh_type, &radio);
|
|
+ if (NULL == dev)
|
|
+ return -ENODEV;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ cx231xx_videodbg("open minor=%d type=%s users=%d\n",
|
|
+ minor, v4l2_type_names[fh_type], dev->users);
|
|
+
|
|
+#if 0
|
|
+ errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
|
|
+ if (errCode < 0) {
|
|
+ cx231xx_errdev
|
|
+ ("Device locked on digital mode. Can't open analog\n");
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
|
|
+ if (!fh) {
|
|
+ cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ fh->dev = dev;
|
|
+ fh->radio = radio;
|
|
+ fh->type = fh_type;
|
|
+ filp->private_data = fh;
|
|
+
|
|
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
|
|
+ dev->width = norm_maxw(dev);
|
|
+ dev->height = norm_maxh(dev);
|
|
+ dev->hscale = 0;
|
|
+ dev->vscale = 0;
|
|
+
|
|
+ /* Power up in Analog TV mode */
|
|
+ cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
|
|
+
|
|
+#if 0
|
|
+ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
|
|
+#endif
|
|
+ cx231xx_resolution_set(dev);
|
|
+
|
|
+ /* set video alternate setting */
|
|
+ cx231xx_set_video_alternate(dev);
|
|
+
|
|
+ /* Needed, since GPIO might have disabled power of
|
|
+ some i2c device */
|
|
+ cx231xx_config_i2c(dev);
|
|
+
|
|
+ /* device needs to be initialized before isoc transfer */
|
|
+ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
|
|
+ video_mux(dev, dev->video_input);
|
|
+
|
|
+ }
|
|
+ if (fh->radio) {
|
|
+ cx231xx_videodbg("video_open: setting radio device\n");
|
|
+
|
|
+ /* cx231xx_start_radio(dev); */
|
|
+
|
|
+ call_all(dev, tuner, s_radio);
|
|
+ }
|
|
+
|
|
+ dev->users++;
|
|
+
|
|
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
|
|
+ NULL, &dev->video_mode.slock,
|
|
+ fh->type, V4L2_FIELD_INTERLACED,
|
|
+ sizeof(struct cx231xx_buffer), fh);
|
|
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
|
+ /* Set the required alternate setting VBI interface works in
|
|
+ Bulk mode only */
|
|
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
|
+
|
|
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
|
|
+ NULL, &dev->vbi_mode.slock,
|
|
+ fh->type, V4L2_FIELD_SEQ_TB,
|
|
+ sizeof(struct cx231xx_buffer), fh);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ return errCode;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_realease_resources()
|
|
+ * unregisters the v4l2,i2c and usb devices
|
|
+ * called when the device gets disconected or at module unload
|
|
+*/
|
|
+void cx231xx_release_analog_resources(struct cx231xx *dev)
|
|
+{
|
|
+
|
|
+ /*FIXME: I2C IR should be disconnected */
|
|
+
|
|
+ if (dev->radio_dev) {
|
|
+ if (-1 != dev->radio_dev->minor)
|
|
+ video_unregister_device(dev->radio_dev);
|
|
+ else
|
|
+ video_device_release(dev->radio_dev);
|
|
+ dev->radio_dev = NULL;
|
|
+ }
|
|
+ if (dev->vbi_dev) {
|
|
+ cx231xx_info("V4L2 device /dev/vbi%d deregistered\n",
|
|
+ dev->vbi_dev->num);
|
|
+ if (-1 != dev->vbi_dev->minor)
|
|
+ video_unregister_device(dev->vbi_dev);
|
|
+ else
|
|
+ video_device_release(dev->vbi_dev);
|
|
+ dev->vbi_dev = NULL;
|
|
+ }
|
|
+ if (dev->vdev) {
|
|
+ cx231xx_info("V4L2 device /dev/video%d deregistered\n",
|
|
+ dev->vdev->num);
|
|
+ if (-1 != dev->vdev->minor)
|
|
+ video_unregister_device(dev->vdev);
|
|
+ else
|
|
+ video_device_release(dev->vdev);
|
|
+ dev->vdev = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_v4l2_close()
|
|
+ * stops streaming and deallocates all resources allocated by the v4l2
|
|
+ * calls and ioctls
|
|
+ */
|
|
+static int cx231xx_v4l2_close(struct file *filp)
|
|
+{
|
|
+ struct cx231xx_fh *fh = filp->private_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+
|
|
+ cx231xx_videodbg("users=%d\n", dev->users);
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+
|
|
+ if (res_check(fh))
|
|
+ res_free(fh);
|
|
+
|
|
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
|
+ videobuf_stop(&fh->vb_vidq);
|
|
+ videobuf_mmap_free(&fh->vb_vidq);
|
|
+
|
|
+ /* the device is already disconnect,
|
|
+ free the remaining resources */
|
|
+ if (dev->state & DEV_DISCONNECTED) {
|
|
+ cx231xx_release_resources(dev);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ kfree(dev);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* do this before setting alternate! */
|
|
+ cx231xx_uninit_vbi_isoc(dev);
|
|
+
|
|
+ /* set alternate 0 */
|
|
+ if (!dev->vbi_or_sliced_cc_mode)
|
|
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
|
|
+ else
|
|
+ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
|
|
+
|
|
+ kfree(fh);
|
|
+ dev->users--;
|
|
+ wake_up_interruptible_nr(&dev->open, 1);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (dev->users == 1) {
|
|
+ videobuf_stop(&fh->vb_vidq);
|
|
+ videobuf_mmap_free(&fh->vb_vidq);
|
|
+
|
|
+ /* the device is already disconnect,
|
|
+ free the remaining resources */
|
|
+ if (dev->state & DEV_DISCONNECTED) {
|
|
+ cx231xx_release_resources(dev);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ kfree(dev);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Save some power by putting tuner to sleep */
|
|
+ call_all(dev, core, s_standby, 0);
|
|
+
|
|
+ /* do this before setting alternate! */
|
|
+ cx231xx_uninit_isoc(dev);
|
|
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
|
|
+
|
|
+ /* set alternate 0 */
|
|
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
|
|
+ }
|
|
+ kfree(fh);
|
|
+ dev->users--;
|
|
+ wake_up_interruptible_nr(&dev->open, 1);
|
|
+ mutex_unlock(&dev->lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_v4l2_read()
|
|
+ * will allocate buffers when called for the first time
|
|
+ */
|
|
+static ssize_t
|
|
+cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
|
|
+ loff_t *pos)
|
|
+{
|
|
+ struct cx231xx_fh *fh = filp->private_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
|
|
+ (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
|
|
+ mutex_lock(&dev->lock);
|
|
+ rc = res_get(fh);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (unlikely(rc < 0))
|
|
+ return rc;
|
|
+
|
|
+ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
|
|
+ filp->f_flags & O_NONBLOCK);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_v4l2_poll()
|
|
+ * will allocate buffers when called for the first time
|
|
+ */
|
|
+static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table * wait)
|
|
+{
|
|
+ struct cx231xx_fh *fh = filp->private_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ rc = res_get(fh);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (unlikely(rc < 0))
|
|
+ return POLLERR;
|
|
+
|
|
+ if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
|
|
+ (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type))
|
|
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
|
|
+ else
|
|
+ return POLLERR;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * cx231xx_v4l2_mmap()
|
|
+ */
|
|
+static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct cx231xx_fh *fh = filp->private_data;
|
|
+ struct cx231xx *dev = fh->dev;
|
|
+ int rc;
|
|
+
|
|
+ rc = check_dev(dev);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ mutex_lock(&dev->lock);
|
|
+ rc = res_get(fh);
|
|
+ mutex_unlock(&dev->lock);
|
|
+
|
|
+ if (unlikely(rc < 0))
|
|
+ return rc;
|
|
+
|
|
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
|
|
+
|
|
+ cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
|
|
+ (unsigned long)vma->vm_start,
|
|
+ (unsigned long)vma->vm_end -
|
|
+ (unsigned long)vma->vm_start, rc);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static const struct v4l2_file_operations cx231xx_v4l_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = cx231xx_v4l2_open,
|
|
+ .release = cx231xx_v4l2_close,
|
|
+ .read = cx231xx_v4l2_read,
|
|
+ .poll = cx231xx_v4l2_poll,
|
|
+ .mmap = cx231xx_v4l2_mmap,
|
|
+ .ioctl = video_ioctl2,
|
|
+};
|
|
+
|
|
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
|
|
+ .vidioc_querycap = vidioc_querycap,
|
|
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
|
|
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
|
|
+ .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
|
|
+ .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
|
|
+ .vidioc_g_audio = vidioc_g_audio,
|
|
+ .vidioc_s_audio = vidioc_s_audio,
|
|
+ .vidioc_cropcap = vidioc_cropcap,
|
|
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
|
|
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
|
|
+ .vidioc_reqbufs = vidioc_reqbufs,
|
|
+ .vidioc_querybuf = vidioc_querybuf,
|
|
+ .vidioc_qbuf = vidioc_qbuf,
|
|
+ .vidioc_dqbuf = vidioc_dqbuf,
|
|
+ .vidioc_s_std = vidioc_s_std,
|
|
+ .vidioc_g_std = vidioc_g_std,
|
|
+ .vidioc_enum_input = vidioc_enum_input,
|
|
+ .vidioc_g_input = vidioc_g_input,
|
|
+ .vidioc_s_input = vidioc_s_input,
|
|
+ .vidioc_queryctrl = vidioc_queryctrl,
|
|
+ .vidioc_g_ctrl = vidioc_g_ctrl,
|
|
+ .vidioc_s_ctrl = vidioc_s_ctrl,
|
|
+ .vidioc_streamon = vidioc_streamon,
|
|
+ .vidioc_streamoff = vidioc_streamoff,
|
|
+ .vidioc_g_tuner = vidioc_g_tuner,
|
|
+ .vidioc_s_tuner = vidioc_s_tuner,
|
|
+ .vidioc_g_frequency = vidioc_g_frequency,
|
|
+ .vidioc_s_frequency = vidioc_s_frequency,
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .vidioc_g_register = vidioc_g_register,
|
|
+ .vidioc_s_register = vidioc_s_register,
|
|
+#endif
|
|
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
|
|
+ .vidiocgmbuf = vidiocgmbuf,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static struct video_device cx231xx_vbi_template;
|
|
+
|
|
+static const struct video_device cx231xx_video_template = {
|
|
+ .fops = &cx231xx_v4l_fops,
|
|
+ .release = video_device_release,
|
|
+ .ioctl_ops = &video_ioctl_ops,
|
|
+ .minor = -1,
|
|
+ .tvnorms = V4L2_STD_ALL,
|
|
+ .current_norm = V4L2_STD_PAL,
|
|
+};
|
|
+
|
|
+static const struct v4l2_file_operations radio_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = cx231xx_v4l2_open,
|
|
+ .release = cx231xx_v4l2_close,
|
|
+ .ioctl = video_ioctl2,
|
|
+};
|
|
+
|
|
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
|
|
+ .vidioc_querycap = radio_querycap,
|
|
+ .vidioc_g_tuner = radio_g_tuner,
|
|
+ .vidioc_enum_input = radio_enum_input,
|
|
+ .vidioc_g_audio = radio_g_audio,
|
|
+ .vidioc_s_tuner = radio_s_tuner,
|
|
+ .vidioc_s_audio = radio_s_audio,
|
|
+ .vidioc_s_input = radio_s_input,
|
|
+ .vidioc_queryctrl = radio_queryctrl,
|
|
+ .vidioc_g_ctrl = vidioc_g_ctrl,
|
|
+ .vidioc_s_ctrl = vidioc_s_ctrl,
|
|
+ .vidioc_g_frequency = vidioc_g_frequency,
|
|
+ .vidioc_s_frequency = vidioc_s_frequency,
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
+ .vidioc_g_register = vidioc_g_register,
|
|
+ .vidioc_s_register = vidioc_s_register,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static struct video_device cx231xx_radio_template = {
|
|
+ .name = "cx231xx-radio",
|
|
+ .fops = &radio_fops,
|
|
+ .ioctl_ops = &radio_ioctl_ops,
|
|
+ .minor = -1,
|
|
+};
|
|
+
|
|
+/******************************** usb interface ******************************/
|
|
+
|
|
+static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
|
|
+ const struct video_device
|
|
+ *template, const char *type_name)
|
|
+{
|
|
+ struct video_device *vfd;
|
|
+
|
|
+ vfd = video_device_alloc();
|
|
+ if (NULL == vfd)
|
|
+ return NULL;
|
|
+
|
|
+ *vfd = *template;
|
|
+ vfd->minor = -1;
|
|
+ vfd->v4l2_dev = &dev->v4l2_dev;
|
|
+ vfd->release = video_device_release;
|
|
+ vfd->debug = video_debug;
|
|
+
|
|
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
|
|
+
|
|
+ return vfd;
|
|
+}
|
|
+
|
|
+int cx231xx_register_analog_devices(struct cx231xx *dev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ cx231xx_info("%s: v4l2 driver version %d.%d.%d\n",
|
|
+ dev->name,
|
|
+ (CX231XX_VERSION_CODE >> 16) & 0xff,
|
|
+ (CX231XX_VERSION_CODE >> 8) & 0xff,
|
|
+ CX231XX_VERSION_CODE & 0xff);
|
|
+
|
|
+ /* set default norm */
|
|
+ /*dev->norm = cx231xx_video_template.current_norm; */
|
|
+ dev->width = norm_maxw(dev);
|
|
+ dev->height = norm_maxh(dev);
|
|
+ dev->interlaced = 0;
|
|
+ dev->hscale = 0;
|
|
+ dev->vscale = 0;
|
|
+
|
|
+ /* Analog specific initialization */
|
|
+ dev->format = &format[0];
|
|
+ /* video_mux(dev, dev->video_input); */
|
|
+
|
|
+ /* Audio defaults */
|
|
+ dev->mute = 1;
|
|
+ dev->volume = 0x1f;
|
|
+
|
|
+ /* enable vbi capturing */
|
|
+ /* write code here... */
|
|
+
|
|
+ /* allocate and fill video video_device struct */
|
|
+ dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
|
|
+ if (!dev->vdev) {
|
|
+ cx231xx_errdev("cannot allocate video_device.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ /* register v4l2 video video_device */
|
|
+ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
|
|
+ video_nr[dev->devno]);
|
|
+ if (ret) {
|
|
+ cx231xx_errdev("unable to register video device (error=%i).\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ cx231xx_info("%s/0: registered device video%d [v4l2]\n",
|
|
+ dev->name, dev->vdev->num);
|
|
+
|
|
+ /* Initialize VBI template */
|
|
+ memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
|
|
+ sizeof(cx231xx_vbi_template));
|
|
+ strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
|
|
+
|
|
+ /* Allocate and fill vbi video_device struct */
|
|
+ dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
|
|
+
|
|
+ /* register v4l2 vbi video_device */
|
|
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
|
|
+ vbi_nr[dev->devno]);
|
|
+ if (ret < 0) {
|
|
+ cx231xx_errdev("unable to register vbi device\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ cx231xx_info("%s/0: registered device vbi%d\n",
|
|
+ dev->name, dev->vbi_dev->num);
|
|
+
|
|
+ if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
|
|
+ dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
|
|
+ "radio");
|
|
+ if (!dev->radio_dev) {
|
|
+ cx231xx_errdev("cannot allocate video_device.\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
|
|
+ radio_nr[dev->devno]);
|
|
+ if (ret < 0) {
|
|
+ cx231xx_errdev("can't register radio device\n");
|
|
+ return ret;
|
|
+ }
|
|
+ cx231xx_info("Registered radio device as /dev/radio%d\n",
|
|
+ dev->radio_dev->num);
|
|
+ }
|
|
+
|
|
+ cx231xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
|
|
+ dev->vdev->num, dev->vbi_dev->num);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
|
|
new file mode 100644
|
|
index 0000000..aa4a23e
|
|
--- /dev/null
|
|
+++ b/drivers/media/video/cx231xx/cx231xx.h
|
|
@@ -0,0 +1,779 @@
|
|
+/*
|
|
+ cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices
|
|
+
|
|
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
|
|
+ Based on em28xx driver
|
|
+
|
|
+ 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.
|
|
+ */
|
|
+
|
|
+#ifndef _CX231XX_H
|
|
+#define _CX231XX_H
|
|
+
|
|
+#include <linux/videodev2.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/ioctl.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/i2c-algo-bit.h>
|
|
+#include <linux/mutex.h>
|
|
+
|
|
+
|
|
+#include <media/videobuf-vmalloc.h>
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/ir-kbd-i2c.h>
|
|
+#if defined(CONFIG_VIDEO_CX231XX_DVB) || \
|
|
+ defined(CONFIG_VIDEO_CX231XX_DVB_MODULE)
|
|
+#include <media/videobuf-dvb.h>
|
|
+#endif
|
|
+
|
|
+#include "cx231xx-reg.h"
|
|
+#include "cx231xx-pcb-cfg.h"
|
|
+#include "cx231xx-conf-reg.h"
|
|
+
|
|
+#define DRIVER_NAME "cx231xx"
|
|
+#define PWR_SLEEP_INTERVAL 5
|
|
+
|
|
+/* I2C addresses for control block in Cx231xx */
|
|
+#define AFE_DEVICE_ADDRESS 0x60
|
|
+#define I2S_BLK_DEVICE_ADDRESS 0x98
|
|
+#define VID_BLK_I2C_ADDRESS 0x88
|
|
+#define DIF_USE_BASEBAND 0xFFFFFFFF
|
|
+
|
|
+/* Boards supported by driver */
|
|
+#define CX231XX_BOARD_UNKNOWN 0
|
|
+#define CX231XX_BOARD_CNXT_RDE_250 1
|
|
+#define CX231XX_BOARD_CNXT_RDU_250 2
|
|
+
|
|
+/* Limits minimum and default number of buffers */
|
|
+#define CX231XX_MIN_BUF 4
|
|
+#define CX231XX_DEF_BUF 12
|
|
+#define CX231XX_DEF_VBI_BUF 6
|
|
+
|
|
+#define VBI_LINE_COUNT 17
|
|
+#define VBI_LINE_LENGTH 1440
|
|
+
|
|
+/*Limits the max URB message size */
|
|
+#define URB_MAX_CTRL_SIZE 80
|
|
+
|
|
+/* Params for validated field */
|
|
+#define CX231XX_BOARD_NOT_VALIDATED 1
|
|
+#define CX231XX_BOARD_VALIDATED 0
|
|
+
|
|
+/* maximum number of cx231xx boards */
|
|
+#define CX231XX_MAXBOARDS 8
|
|
+
|
|
+/* maximum number of frames that can be queued */
|
|
+#define CX231XX_NUM_FRAMES 5
|
|
+
|
|
+/* number of buffers for isoc transfers */
|
|
+#define CX231XX_NUM_BUFS 8
|
|
+
|
|
+/* number of packets for each buffer
|
|
+ windows requests only 40 packets .. so we better do the same
|
|
+ this is what I found out for all alternate numbers there!
|
|
+ */
|
|
+#define CX231XX_NUM_PACKETS 40
|
|
+
|
|
+/* default alternate; 0 means choose the best */
|
|
+#define CX231XX_PINOUT 0
|
|
+
|
|
+#define CX231XX_INTERLACED_DEFAULT 1
|
|
+
|
|
+/* time to wait when stopping the isoc transfer */
|
|
+#define CX231XX_URB_TIMEOUT \
|
|
+ msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
|
|
+
|
|
+enum cx231xx_mode {
|
|
+ CX231XX_SUSPEND,
|
|
+ CX231XX_ANALOG_MODE,
|
|
+ CX231XX_DIGITAL_MODE,
|
|
+};
|
|
+
|
|
+enum cx231xx_std_mode {
|
|
+ CX231XX_TV_AIR = 0,
|
|
+ CX231XX_TV_CABLE
|
|
+};
|
|
+
|
|
+enum cx231xx_stream_state {
|
|
+ STREAM_OFF,
|
|
+ STREAM_INTERRUPT,
|
|
+ STREAM_ON,
|
|
+};
|
|
+
|
|
+struct cx231xx;
|
|
+
|
|
+struct cx231xx_usb_isoc_ctl {
|
|
+ /* max packet size of isoc transaction */
|
|
+ int max_pkt_size;
|
|
+
|
|
+ /* number of allocated urbs */
|
|
+ int num_bufs;
|
|
+
|
|
+ /* urb for isoc transfers */
|
|
+ struct urb **urb;
|
|
+
|
|
+ /* transfer buffers for isoc transfer */
|
|
+ char **transfer_buffer;
|
|
+
|
|
+ /* Last buffer command and region */
|
|
+ u8 cmd;
|
|
+ int pos, size, pktsize;
|
|
+
|
|
+ /* Last field: ODD or EVEN? */
|
|
+ int field;
|
|
+
|
|
+ /* Stores incomplete commands */
|
|
+ u32 tmp_buf;
|
|
+ int tmp_buf_len;
|
|
+
|
|
+ /* Stores already requested buffers */
|
|
+ struct cx231xx_buffer *buf;
|
|
+
|
|
+ /* Stores the number of received fields */
|
|
+ int nfields;
|
|
+
|
|
+ /* isoc urb callback */
|
|
+ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
|
|
+};
|
|
+
|
|
+struct cx231xx_fmt {
|
|
+ char *name;
|
|
+ u32 fourcc; /* v4l2 format id */
|
|
+ int depth;
|
|
+ int reg;
|
|
+};
|
|
+
|
|
+/* buffer for one video frame */
|
|
+struct cx231xx_buffer {
|
|
+ /* common v4l buffer stuff -- must be first */
|
|
+ struct videobuf_buffer vb;
|
|
+
|
|
+ struct list_head frame;
|
|
+ int top_field;
|
|
+ int receiving;
|
|
+};
|
|
+
|
|
+struct cx231xx_dmaqueue {
|
|
+ struct list_head active;
|
|
+ struct list_head queued;
|
|
+
|
|
+ wait_queue_head_t wq;
|
|
+
|
|
+ /* Counters to control buffer fill */
|
|
+ int pos;
|
|
+ u8 is_partial_line;
|
|
+ u8 partial_buf[8];
|
|
+ u8 last_sav;
|
|
+ int current_field;
|
|
+ u32 bytes_left_in_line;
|
|
+ u32 lines_completed;
|
|
+ u8 field1_done;
|
|
+ u32 lines_per_field;
|
|
+};
|
|
+
|
|
+/* inputs */
|
|
+
|
|
+#define MAX_CX231XX_INPUT 4
|
|
+
|
|
+enum cx231xx_itype {
|
|
+ CX231XX_VMUX_COMPOSITE1 = 1,
|
|
+ CX231XX_VMUX_SVIDEO,
|
|
+ CX231XX_VMUX_TELEVISION,
|
|
+ CX231XX_VMUX_CABLE,
|
|
+ CX231XX_RADIO,
|
|
+ CX231XX_VMUX_DVB,
|
|
+ CX231XX_VMUX_DEBUG
|
|
+};
|
|
+
|
|
+enum cx231xx_v_input {
|
|
+ CX231XX_VIN_1_1 = 0x1,
|
|
+ CX231XX_VIN_2_1,
|
|
+ CX231XX_VIN_3_1,
|
|
+ CX231XX_VIN_4_1,
|
|
+ CX231XX_VIN_1_2 = 0x01,
|
|
+ CX231XX_VIN_2_2,
|
|
+ CX231XX_VIN_3_2,
|
|
+ CX231XX_VIN_1_3 = 0x1,
|
|
+ CX231XX_VIN_2_3,
|
|
+ CX231XX_VIN_3_3,
|
|
+};
|
|
+
|
|
+/* cx231xx has two audio inputs: tuner and line in */
|
|
+enum cx231xx_amux {
|
|
+ /* This is the only entry for cx231xx tuner input */
|
|
+ CX231XX_AMUX_VIDEO, /* cx231xx tuner */
|
|
+ CX231XX_AMUX_LINE_IN, /* Line In */
|
|
+};
|
|
+
|
|
+struct cx231xx_reg_seq {
|
|
+ unsigned char bit;
|
|
+ unsigned char val;
|
|
+ int sleep;
|
|
+};
|
|
+
|
|
+struct cx231xx_input {
|
|
+ enum cx231xx_itype type;
|
|
+ unsigned int vmux;
|
|
+ enum cx231xx_amux amux;
|
|
+ struct cx231xx_reg_seq *gpio;
|
|
+};
|
|
+
|
|
+#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr])
|
|
+
|
|
+enum cx231xx_decoder {
|
|
+ CX231XX_NODECODER,
|
|
+ CX231XX_AVDECODER
|
|
+};
|
|
+
|
|
+enum CX231XX_I2C_MASTER_PORT {
|
|
+ I2C_0 = 0,
|
|
+ I2C_1 = 1,
|
|
+ I2C_2 = 2,
|
|
+ I2C_3 = 3
|
|
+};
|
|
+
|
|
+struct cx231xx_board {
|
|
+ char *name;
|
|
+ int vchannels;
|
|
+ int tuner_type;
|
|
+ int tuner_addr;
|
|
+ v4l2_std_id norm; /* tv norm */
|
|
+
|
|
+ /* demod related */
|
|
+ int demod_addr;
|
|
+ u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
|
|
+
|
|
+ /* GPIO Pins */
|
|
+ struct cx231xx_reg_seq *dvb_gpio;
|
|
+ struct cx231xx_reg_seq *suspend_gpio;
|
|
+ struct cx231xx_reg_seq *tuner_gpio;
|
|
+ u8 tuner_sif_gpio;
|
|
+ u8 tuner_scl_gpio;
|
|
+ u8 tuner_sda_gpio;
|
|
+
|
|
+ /* PIN ctrl */
|
|
+ u32 ctl_pin_status_mask;
|
|
+ u8 agc_analog_digital_select_gpio;
|
|
+ u32 gpio_pin_status_mask;
|
|
+
|
|
+ /* i2c masters */
|
|
+ u8 tuner_i2c_master;
|
|
+ u8 demod_i2c_master;
|
|
+
|
|
+ unsigned int max_range_640_480:1;
|
|
+ unsigned int has_dvb:1;
|
|
+ unsigned int valid:1;
|
|
+
|
|
+ unsigned char xclk, i2c_speed;
|
|
+
|
|
+ enum cx231xx_decoder decoder;
|
|
+
|
|
+ struct cx231xx_input input[MAX_CX231XX_INPUT];
|
|
+ struct cx231xx_input radio;
|
|
+ IR_KEYTAB_TYPE *ir_codes;
|
|
+};
|
|
+
|
|
+/* device states */
|
|
+enum cx231xx_dev_state {
|
|
+ DEV_INITIALIZED = 0x01,
|
|
+ DEV_DISCONNECTED = 0x02,
|
|
+ DEV_MISCONFIGURED = 0x04,
|
|
+};
|
|
+
|
|
+enum AFE_MODE {
|
|
+ AFE_MODE_LOW_IF,
|
|
+ AFE_MODE_BASEBAND,
|
|
+ AFE_MODE_EU_HI_IF,
|
|
+ AFE_MODE_US_HI_IF,
|
|
+ AFE_MODE_JAPAN_HI_IF
|
|
+};
|
|
+
|
|
+enum AUDIO_INPUT {
|
|
+ AUDIO_INPUT_MUTE,
|
|
+ AUDIO_INPUT_LINE,
|
|
+ AUDIO_INPUT_TUNER_TV,
|
|
+ AUDIO_INPUT_SPDIF,
|
|
+ AUDIO_INPUT_TUNER_FM
|
|
+};
|
|
+
|
|
+#define CX231XX_AUDIO_BUFS 5
|
|
+#define CX231XX_NUM_AUDIO_PACKETS 64
|
|
+#define CX231XX_CAPTURE_STREAM_EN 1
|
|
+#define CX231XX_STOP_AUDIO 0
|
|
+#define CX231XX_START_AUDIO 1
|
|
+
|
|
+/* cx231xx extensions */
|
|
+#define CX231XX_AUDIO 0x10
|
|
+#define CX231XX_DVB 0x20
|
|
+
|
|
+struct cx231xx_audio {
|
|
+ char name[50];
|
|
+ char *transfer_buffer[CX231XX_AUDIO_BUFS];
|
|
+ struct urb *urb[CX231XX_AUDIO_BUFS];
|
|
+ struct usb_device *udev;
|
|
+ unsigned int capture_transfer_done;
|
|
+ struct snd_pcm_substream *capture_pcm_substream;
|
|
+
|
|
+ unsigned int hwptr_done_capture;
|
|
+ struct snd_card *sndcard;
|
|
+
|
|
+ int users, shutdown;
|
|
+ enum cx231xx_stream_state capture_stream;
|
|
+ spinlock_t slock;
|
|
+
|
|
+ int alt; /* alternate */
|
|
+ int max_pkt_size; /* max packet size of isoc transaction */
|
|
+ int num_alt; /* Number of alternative settings */
|
|
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
|
|
+ u16 end_point_addr;
|
|
+};
|
|
+
|
|
+struct cx231xx;
|
|
+
|
|
+struct cx231xx_fh {
|
|
+ struct cx231xx *dev;
|
|
+ unsigned int stream_on:1; /* Locks streams */
|
|
+ int radio;
|
|
+
|
|
+ struct videobuf_queue vb_vidq;
|
|
+
|
|
+ enum v4l2_buf_type type;
|
|
+};
|
|
+
|
|
+/*****************************************************************/
|
|
+/* set/get i2c */
|
|
+/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */
|
|
+#define I2C_SPEED_1M 0x0
|
|
+#define I2C_SPEED_400K 0x1
|
|
+#define I2C_SPEED_100K 0x2
|
|
+#define I2C_SPEED_5M 0x3
|
|
+
|
|
+/* 0-- STOP transaction */
|
|
+#define I2C_STOP 0x0
|
|
+/* 1-- do not transmit STOP at end of transaction */
|
|
+#define I2C_NOSTOP 0x1
|
|
+/* 1--alllow slave to insert clock wait states */
|
|
+#define I2C_SYNC 0x1
|
|
+
|
|
+struct cx231xx_i2c {
|
|
+ struct cx231xx *dev;
|
|
+
|
|
+ int nr;
|
|
+
|
|
+ /* i2c i/o */
|
|
+ struct i2c_adapter i2c_adap;
|
|
+ struct i2c_algo_bit_data i2c_algo;
|
|
+ struct i2c_client i2c_client;
|
|
+ u32 i2c_rc;
|
|
+
|
|
+ /* different settings for each bus */
|
|
+ u8 i2c_period;
|
|
+ u8 i2c_nostop;
|
|
+ u8 i2c_reserve;
|
|
+};
|
|
+
|
|
+struct cx231xx_i2c_xfer_data {
|
|
+ u8 dev_addr;
|
|
+ u8 direction; /* 1 - IN, 0 - OUT */
|
|
+ u8 saddr_len; /* sub address len */
|
|
+ u16 saddr_dat; /* sub addr data */
|
|
+ u8 buf_size; /* buffer size */
|
|
+ u8 *p_buffer; /* pointer to the buffer */
|
|
+};
|
|
+
|
|
+struct VENDOR_REQUEST_IN {
|
|
+ u8 bRequest;
|
|
+ u16 wValue;
|
|
+ u16 wIndex;
|
|
+ u16 wLength;
|
|
+ u8 direction;
|
|
+ u8 bData;
|
|
+ u8 *pBuff;
|
|
+};
|
|
+
|
|
+struct cx231xx_ctrl {
|
|
+ struct v4l2_queryctrl v;
|
|
+ u32 off;
|
|
+ u32 reg;
|
|
+ u32 mask;
|
|
+ u32 shift;
|
|
+};
|
|
+
|
|
+enum TRANSFER_TYPE {
|
|
+ Raw_Video = 0,
|
|
+ Audio,
|
|
+ Vbi, /* VANC */
|
|
+ Sliced_cc, /* HANC */
|
|
+ TS1_serial_mode,
|
|
+ TS2,
|
|
+ TS1_parallel_mode
|
|
+} ;
|
|
+
|
|
+struct cx231xx_video_mode {
|
|
+ /* Isoc control struct */
|
|
+ struct cx231xx_dmaqueue vidq;
|
|
+ struct cx231xx_usb_isoc_ctl isoc_ctl;
|
|
+ spinlock_t slock;
|
|
+
|
|
+ /* usb transfer */
|
|
+ int alt; /* alternate */
|
|
+ int max_pkt_size; /* max packet size of isoc transaction */
|
|
+ int num_alt; /* Number of alternative settings */
|
|
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
|
|
+ u16 end_point_addr;
|
|
+};
|
|
+
|
|
+/* main device struct */
|
|
+struct cx231xx {
|
|
+ /* generic device properties */
|
|
+ char name[30]; /* name (including minor) of the device */
|
|
+ int model; /* index in the device_data struct */
|
|
+ int devno; /* marks the number of this device */
|
|
+
|
|
+ struct cx231xx_board board;
|
|
+
|
|
+ unsigned int stream_on:1; /* Locks streams */
|
|
+ unsigned int vbi_stream_on:1; /* Locks streams for VBI */
|
|
+ unsigned int has_audio_class:1;
|
|
+ unsigned int has_alsa_audio:1;
|
|
+
|
|
+ struct cx231xx_fmt *format;
|
|
+
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ struct v4l2_subdev *sd_cx25840;
|
|
+ struct v4l2_subdev *sd_tuner;
|
|
+
|
|
+ struct cx231xx_IR *ir;
|
|
+
|
|
+ struct list_head devlist;
|
|
+
|
|
+ int tuner_type; /* type of the tuner */
|
|
+ int tuner_addr; /* tuner address */
|
|
+
|
|
+ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
|
|
+ struct cx231xx_i2c i2c_bus[3];
|
|
+ unsigned int xc_fw_load_done:1;
|
|
+ struct mutex gpio_i2c_lock;
|
|
+
|
|
+ /* video for linux */
|
|
+ int users; /* user count for exclusive use */
|
|
+ struct video_device *vdev; /* video for linux device struct */
|
|
+ v4l2_std_id norm; /* selected tv norm */
|
|
+ int ctl_freq; /* selected frequency */
|
|
+ unsigned int ctl_ainput; /* selected audio input */
|
|
+ int mute;
|
|
+ int volume;
|
|
+
|
|
+ /* frame properties */
|
|
+ int width; /* current frame width */
|
|
+ int height; /* current frame height */
|
|
+ unsigned hscale; /* horizontal scale factor (see datasheet) */
|
|
+ unsigned vscale; /* vertical scale factor (see datasheet) */
|
|
+ int interlaced; /* 1=interlace fileds, 0=just top fileds */
|
|
+
|
|
+ struct cx231xx_audio adev;
|
|
+
|
|
+ /* states */
|
|
+ enum cx231xx_dev_state state;
|
|
+
|
|
+ struct work_struct request_module_wk;
|
|
+
|
|
+ /* locks */
|
|
+ struct mutex lock;
|
|
+ struct mutex ctrl_urb_lock; /* protects urb_buf */
|
|
+ struct list_head inqueue, outqueue;
|
|
+ wait_queue_head_t open, wait_frame, wait_stream;
|
|
+ struct video_device *vbi_dev;
|
|
+ struct video_device *radio_dev;
|
|
+
|
|
+ unsigned char eedata[256];
|
|
+
|
|
+ struct cx231xx_video_mode video_mode;
|
|
+ struct cx231xx_video_mode vbi_mode;
|
|
+ struct cx231xx_video_mode sliced_cc_mode;
|
|
+ struct cx231xx_video_mode ts1_mode;
|
|
+
|
|
+ struct usb_device *udev; /* the usb device */
|
|
+ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
|
|
+
|
|
+ /* helper funcs that call usb_control_msg */
|
|
+ int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
|
|
+ char *buf, int len);
|
|
+ int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
|
|
+ char *buf, int len);
|
|
+ int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus,
|
|
+ struct cx231xx_i2c_xfer_data *req_data);
|
|
+ int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr,
|
|
+ u8 *buf, u8 len);
|
|
+ int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr,
|
|
+ u8 *buf, u8 len);
|
|
+
|
|
+ int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq);
|
|
+ int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev);
|
|
+
|
|
+ enum cx231xx_mode mode;
|
|
+
|
|
+ struct cx231xx_dvb *dvb;
|
|
+
|
|
+ /* Cx231xx supported PCB config's */
|
|
+ struct pcb_config current_pcb_config;
|
|
+ u8 current_scenario_idx;
|
|
+ u8 interface_count;
|
|
+ u8 max_iad_interface_count;
|
|
+
|
|
+ /* GPIO related register direction and values */
|
|
+ u32 gpio_dir;
|
|
+ u32 gpio_val;
|
|
+
|
|
+ /* Power Modes */
|
|
+ int power_mode;
|
|
+
|
|
+ /* afe parameters */
|
|
+ enum AFE_MODE afe_mode;
|
|
+ u32 afe_ref_count;
|
|
+
|
|
+ /* video related parameters */
|
|
+ u32 video_input;
|
|
+ u32 active_mode;
|
|
+ u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */
|
|
+ enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */
|
|
+
|
|
+};
|
|
+
|
|
+#define cx25840_call(cx231xx, o, f, args...) \
|
|
+ v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
|
|
+#define tuner_call(cx231xx, o, f, args...) \
|
|
+ v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args)
|
|
+#define call_all(dev, o, f, args...) \
|
|
+ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
|
|
+
|
|
+struct cx231xx_ops {
|
|
+ struct list_head next;
|
|
+ char *name;
|
|
+ int id;
|
|
+ int (*init) (struct cx231xx *);
|
|
+ int (*fini) (struct cx231xx *);
|
|
+};
|
|
+
|
|
+/* call back functions in dvb module */
|
|
+int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq);
|
|
+int cx231xx_reset_analog_tuner(struct cx231xx *dev);
|
|
+
|
|
+/* Provided by cx231xx-i2c.c */
|
|
+void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
|
|
+int cx231xx_i2c_register(struct cx231xx_i2c *bus);
|
|
+int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
|
|
+
|
|
+/* Internal block control functions */
|
|
+int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
|
|
+ u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
|
|
+int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
|
|
+ u16 saddr, u8 saddr_len, u32 data, u8 data_len);
|
|
+int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
|
|
+ u16 register_address, u8 bit_start, u8 bit_end,
|
|
+ u32 value);
|
|
+int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
|
|
+ u16 saddr, u32 mask, u32 value);
|
|
+u32 cx231xx_set_field(u32 field_mask, u32 data);
|
|
+
|
|
+/* afe related functions */
|
|
+int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
|
|
+int cx231xx_afe_init_channels(struct cx231xx *dev);
|
|
+int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev);
|
|
+int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux);
|
|
+int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode);
|
|
+int cx231xx_afe_update_power_control(struct cx231xx *dev,
|
|
+ enum AV_MODE avmode);
|
|
+int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input);
|
|
+
|
|
+/* i2s block related functions */
|
|
+int cx231xx_i2s_blk_initialize(struct cx231xx *dev);
|
|
+int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
|
|
+ enum AV_MODE avmode);
|
|
+int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input);
|
|
+
|
|
+/* DIF related functions */
|
|
+int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
|
|
+ u32 function_mode, u32 standard);
|
|
+int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
|
|
+int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
|
|
+int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
|
|
+
|
|
+/* video parser functions */
|
|
+u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size,
|
|
+ u32 *p_bytes_used);
|
|
+u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
|
|
+ u32 *p_bytes_used);
|
|
+int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_buffer, u32 bytes_to_copy);
|
|
+void cx231xx_reset_video_buffer(struct cx231xx *dev,
|
|
+ struct cx231xx_dmaqueue *dma_q);
|
|
+u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q);
|
|
+u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 *p_line, u32 length, int field_number);
|
|
+u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
|
|
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
|
|
+void cx231xx_swab(u16 *from, u16 *to, u16 len);
|
|
+
|
|
+/* Provided by cx231xx-core.c */
|
|
+
|
|
+u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count);
|
|
+void cx231xx_queue_unusedframes(struct cx231xx *dev);
|
|
+void cx231xx_release_buffers(struct cx231xx *dev);
|
|
+
|
|
+/* read from control pipe */
|
|
+int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
|
|
+ char *buf, int len);
|
|
+
|
|
+/* write to control pipe */
|
|
+int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
|
|
+ char *buf, int len);
|
|
+int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode);
|
|
+
|
|
+int cx231xx_send_vendor_cmd(struct cx231xx *dev,
|
|
+ struct VENDOR_REQUEST_IN *ven_req);
|
|
+int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
|
|
+ struct cx231xx_i2c_xfer_data *req_data);
|
|
+
|
|
+/* Gpio related functions */
|
|
+int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
|
|
+ u8 len, u8 request, u8 direction);
|
|
+int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
|
|
+int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
|
|
+int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value);
|
|
+int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number,
|
|
+ int pin_value);
|
|
+
|
|
+int cx231xx_gpio_i2c_start(struct cx231xx *dev);
|
|
+int cx231xx_gpio_i2c_end(struct cx231xx *dev);
|
|
+int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data);
|
|
+int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf);
|
|
+int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev);
|
|
+int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev);
|
|
+int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev);
|
|
+
|
|
+int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
|
|
+int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
|
|
+
|
|
+/* audio related functions */
|
|
+int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
|
|
+ enum AUDIO_INPUT audio_input);
|
|
+
|
|
+int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
|
|
+int cx231xx_resolution_set(struct cx231xx *dev);
|
|
+int cx231xx_set_video_alternate(struct cx231xx *dev);
|
|
+int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
|
|
+int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
|
|
+ int num_bufs, int max_pkt_size,
|
|
+ int (*isoc_copy) (struct cx231xx *dev,
|
|
+ struct urb *urb));
|
|
+void cx231xx_uninit_isoc(struct cx231xx *dev);
|
|
+int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
|
|
+int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
|
|
+
|
|
+/* Device list functions */
|
|
+void cx231xx_release_resources(struct cx231xx *dev);
|
|
+void cx231xx_release_analog_resources(struct cx231xx *dev);
|
|
+int cx231xx_register_analog_devices(struct cx231xx *dev);
|
|
+void cx231xx_remove_from_devlist(struct cx231xx *dev);
|
|
+void cx231xx_add_into_devlist(struct cx231xx *dev);
|
|
+struct cx231xx *cx231xx_get_device(int minor,
|
|
+ enum v4l2_buf_type *fh_type, int *has_radio);
|
|
+void cx231xx_init_extension(struct cx231xx *dev);
|
|
+void cx231xx_close_extension(struct cx231xx *dev);
|
|
+
|
|
+/* hardware init functions */
|
|
+int cx231xx_dev_init(struct cx231xx *dev);
|
|
+void cx231xx_dev_uninit(struct cx231xx *dev);
|
|
+void cx231xx_config_i2c(struct cx231xx *dev);
|
|
+int cx231xx_config(struct cx231xx *dev);
|
|
+
|
|
+/* Stream control functions */
|
|
+int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask);
|
|
+int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask);
|
|
+
|
|
+int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type);
|
|
+
|
|
+/* Power control functions */
|
|
+int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode);
|
|
+int cx231xx_power_suspend(struct cx231xx *dev);
|
|
+
|
|
+/* chip specific control functions */
|
|
+int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
|
|
+int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
|
|
+ u8 analog_or_digital);
|
|
+int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex);
|
|
+
|
|
+/* video audio decoder related functions */
|
|
+void video_mux(struct cx231xx *dev, int index);
|
|
+int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input);
|
|
+int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input);
|
|
+int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev);
|
|
+int cx231xx_set_audio_input(struct cx231xx *dev, u8 input);
|
|
+void get_scale(struct cx231xx *dev,
|
|
+ unsigned int width, unsigned int height,
|
|
+ unsigned int *hscale, unsigned int *vscale);
|
|
+
|
|
+/* Provided by cx231xx-video.c */
|
|
+int cx231xx_register_extension(struct cx231xx_ops *dev);
|
|
+void cx231xx_unregister_extension(struct cx231xx_ops *dev);
|
|
+void cx231xx_init_extension(struct cx231xx *dev);
|
|
+void cx231xx_close_extension(struct cx231xx *dev);
|
|
+
|
|
+/* Provided by cx231xx-cards.c */
|
|
+extern void cx231xx_pre_card_setup(struct cx231xx *dev);
|
|
+extern void cx231xx_card_setup(struct cx231xx *dev);
|
|
+extern struct cx231xx_board cx231xx_boards[];
|
|
+extern struct usb_device_id cx231xx_id_table[];
|
|
+extern const unsigned int cx231xx_bcount;
|
|
+void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir);
|
|
+int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
|
|
+
|
|
+/* Provided by cx231xx-input.c */
|
|
+int cx231xx_ir_init(struct cx231xx *dev);
|
|
+int cx231xx_ir_fini(struct cx231xx *dev);
|
|
+
|
|
+/* printk macros */
|
|
+
|
|
+#define cx231xx_err(fmt, arg...) do {\
|
|
+ printk(KERN_ERR fmt , ##arg); } while (0)
|
|
+
|
|
+#define cx231xx_errdev(fmt, arg...) do {\
|
|
+ printk(KERN_ERR "%s: "fmt,\
|
|
+ dev->name , ##arg); } while (0)
|
|
+
|
|
+#define cx231xx_info(fmt, arg...) do {\
|
|
+ printk(KERN_INFO "%s: "fmt,\
|
|
+ dev->name , ##arg); } while (0)
|
|
+#define cx231xx_warn(fmt, arg...) do {\
|
|
+ printk(KERN_WARNING "%s: "fmt,\
|
|
+ dev->name , ##arg); } while (0)
|
|
+
|
|
+static inline unsigned int norm_maxw(struct cx231xx *dev)
|
|
+{
|
|
+ if (dev->board.max_range_640_480)
|
|
+ return 640;
|
|
+ else
|
|
+ return 720;
|
|
+}
|
|
+
|
|
+static inline unsigned int norm_maxh(struct cx231xx *dev)
|
|
+{
|
|
+ if (dev->board.max_range_640_480)
|
|
+ return 480;
|
|
+ else
|
|
+ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
|
|
+}
|
|
+#endif
|
|
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
|
|
index 93d74be..2f846f5 100644
|
|
--- a/drivers/media/video/cx25840/cx25840-audio.c
|
|
+++ b/drivers/media/video/cx25840/cx25840-audio.c
|
|
@@ -32,7 +32,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
|
|
/* common for all inputs and rates */
|
|
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
|
|
- if (!state->is_cx23885)
|
|
+ if (!state->is_cx23885 && !state->is_cx231xx)
|
|
cx25840_write(client, 0x127, 0x50);
|
|
|
|
if (state->aud_input != CX25840_AUDIO_SERIAL) {
|
|
@@ -43,11 +43,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
* so avoid destroying registers. */
|
|
break;
|
|
}
|
|
- /* VID_PLL and AUX_PLL */
|
|
- cx25840_write4(client, 0x108, 0x1006040f);
|
|
|
|
- /* AUX_PLL_FRAC */
|
|
- cx25840_write4(client, 0x110, 0x01bb39ee);
|
|
+ if (!state->is_cx231xx) {
|
|
+ /* VID_PLL and AUX_PLL */
|
|
+ cx25840_write4(client, 0x108, 0x1006040f);
|
|
+
|
|
+ /* AUX_PLL_FRAC */
|
|
+ cx25840_write4(client, 0x110, 0x01bb39ee);
|
|
+ }
|
|
|
|
if (state->is_cx25836)
|
|
break;
|
|
@@ -64,11 +67,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
* so avoid destroying registers. */
|
|
break;
|
|
}
|
|
- /* VID_PLL and AUX_PLL */
|
|
- cx25840_write4(client, 0x108, 0x1009040f);
|
|
|
|
- /* AUX_PLL_FRAC */
|
|
- cx25840_write4(client, 0x110, 0x00ec6bd6);
|
|
+ if (!state->is_cx231xx) {
|
|
+ /* VID_PLL and AUX_PLL */
|
|
+ cx25840_write4(client, 0x108, 0x1009040f);
|
|
+
|
|
+ /* AUX_PLL_FRAC */
|
|
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
|
|
+ }
|
|
|
|
if (state->is_cx25836)
|
|
break;
|
|
@@ -85,11 +91,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
* so avoid destroying registers. */
|
|
break;
|
|
}
|
|
- /* VID_PLL and AUX_PLL */
|
|
- cx25840_write4(client, 0x108, 0x100a040f);
|
|
|
|
- /* AUX_PLL_FRAC */
|
|
- cx25840_write4(client, 0x110, 0x0098d6e5);
|
|
+ if (!state->is_cx231xx) {
|
|
+ /* VID_PLL and AUX_PLL */
|
|
+ cx25840_write4(client, 0x108, 0x100a040f);
|
|
+
|
|
+ /* AUX_PLL_FRAC */
|
|
+ cx25840_write4(client, 0x110, 0x0098d6e5);
|
|
+ }
|
|
|
|
if (state->is_cx25836)
|
|
break;
|
|
@@ -108,11 +117,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
* so avoid destroying registers. */
|
|
break;
|
|
}
|
|
- /* VID_PLL and AUX_PLL */
|
|
- cx25840_write4(client, 0x108, 0x1e08040f);
|
|
|
|
- /* AUX_PLL_FRAC */
|
|
- cx25840_write4(client, 0x110, 0x012a0869);
|
|
+ if (!state->is_cx231xx) {
|
|
+ /* VID_PLL and AUX_PLL */
|
|
+ cx25840_write4(client, 0x108, 0x1e08040f);
|
|
+
|
|
+ /* AUX_PLL_FRAC */
|
|
+ cx25840_write4(client, 0x110, 0x012a0869);
|
|
+ }
|
|
|
|
if (state->is_cx25836)
|
|
break;
|
|
@@ -136,11 +148,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
break;
|
|
}
|
|
|
|
- /* VID_PLL and AUX_PLL */
|
|
- cx25840_write4(client, 0x108, 0x1809040f);
|
|
|
|
- /* AUX_PLL_FRAC */
|
|
- cx25840_write4(client, 0x110, 0x00ec6bd6);
|
|
+ if (!state->is_cx231xx) {
|
|
+ /* VID_PLL and AUX_PLL */
|
|
+ cx25840_write4(client, 0x108, 0x1809040f);
|
|
+
|
|
+ /* AUX_PLL_FRAC */
|
|
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
|
|
+ }
|
|
|
|
if (state->is_cx25836)
|
|
break;
|
|
@@ -155,7 +170,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
break;
|
|
|
|
case 48000:
|
|
- if (!state->is_cx23885) {
|
|
+ if (!state->is_cx23885 && !state->is_cx231xx) {
|
|
/* VID_PLL and AUX_PLL */
|
|
cx25840_write4(client, 0x108, 0x180a040f);
|
|
|
|
@@ -166,7 +181,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
|
|
if (state->is_cx25836)
|
|
break;
|
|
|
|
- if (!state->is_cx23885) {
|
|
+ if (!state->is_cx23885 && !state->is_cx231xx) {
|
|
/* src1_ctl */
|
|
cx25840_write4(client, 0x8f8, 0x08018000);
|
|
|
|
@@ -227,10 +242,9 @@ void cx25840_audio_set_path(struct i2c_client *client)
|
|
/* deassert soft reset */
|
|
cx25840_and_or(client, 0x810, ~0x1, 0x00);
|
|
|
|
- if (state->is_cx23885) {
|
|
- /* Ensure the controller is running when we exit */
|
|
+ /* Ensure the controller is running when we exit */
|
|
+ if (state->is_cx23885 || state->is_cx231xx)
|
|
cx25840_and_or(client, 0x803, ~0x10, 0x10);
|
|
- }
|
|
}
|
|
|
|
static int get_volume(struct i2c_client *client)
|
|
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
|
|
index 737ee4e..f8ed3c0 100644
|
|
--- a/drivers/media/video/cx25840/cx25840-core.c
|
|
+++ b/drivers/media/video/cx25840/cx25840-core.c
|
|
@@ -345,6 +345,77 @@ static void cx23885_initialize(struct i2c_client *client)
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
+static void cx231xx_initialize(struct i2c_client *client)
|
|
+{
|
|
+ DEFINE_WAIT(wait);
|
|
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
|
+ struct workqueue_struct *q;
|
|
+
|
|
+ /* Internal Reset */
|
|
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
|
|
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
|
|
+
|
|
+ /* Stop microcontroller */
|
|
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
|
|
+
|
|
+ /* DIF in reset? */
|
|
+ cx25840_write(client, 0x398, 0);
|
|
+
|
|
+ /* Trust the default xtal, no division */
|
|
+ /* This changes for the cx23888 products */
|
|
+ cx25840_write(client, 0x2, 0x76);
|
|
+
|
|
+ /* Bring down the regulator for AUX clk */
|
|
+ cx25840_write(client, 0x1, 0x40);
|
|
+
|
|
+ /* Disable DIF bypass */
|
|
+ cx25840_write4(client, 0x33c, 0x00000001);
|
|
+
|
|
+ /* DIF Src phase inc */
|
|
+ cx25840_write4(client, 0x340, 0x0df7df83);
|
|
+
|
|
+ /* Luma */
|
|
+ cx25840_write4(client, 0x414, 0x00107d12);
|
|
+
|
|
+ /* Chroma */
|
|
+ cx25840_write4(client, 0x420, 0x3d008282);
|
|
+
|
|
+ /* ADC2 input select */
|
|
+ cx25840_write(client, 0x102, 0x10);
|
|
+
|
|
+ /* VIN1 & VIN5 */
|
|
+ cx25840_write(client, 0x103, 0x11);
|
|
+
|
|
+ /* Enable format auto detect */
|
|
+ cx25840_write(client, 0x400, 0);
|
|
+ /* Fast subchroma lock */
|
|
+ /* White crush, Chroma AGC & Chroma Killer enabled */
|
|
+ cx25840_write(client, 0x401, 0xe8);
|
|
+
|
|
+ /* Do the firmware load in a work handler to prevent.
|
|
+ Otherwise the kernel is blocked waiting for the
|
|
+ bit-banging i2c interface to finish uploading the
|
|
+ firmware. */
|
|
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
|
|
+ init_waitqueue_head(&state->fw_wait);
|
|
+ q = create_singlethread_workqueue("cx25840_fw");
|
|
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
|
|
+ queue_work(q, &state->fw_work);
|
|
+ schedule();
|
|
+ finish_wait(&state->fw_wait, &wait);
|
|
+ destroy_workqueue(q);
|
|
+
|
|
+ cx25840_std_setup(client);
|
|
+
|
|
+ /* (re)set input */
|
|
+ set_input(client, state->vid_input, state->aud_input);
|
|
+
|
|
+ /* start microcontroller */
|
|
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------- */
|
|
+
|
|
void cx25840_std_setup(struct i2c_client *client)
|
|
{
|
|
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
|
|
@@ -414,39 +485,41 @@ void cx25840_std_setup(struct i2c_client *client)
|
|
}
|
|
|
|
/* DEBUG: Displays configured PLL frequency */
|
|
- pll_int = cx25840_read(client, 0x108);
|
|
- pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
|
|
- pll_post = cx25840_read(client, 0x109);
|
|
- v4l_dbg(1, cx25840_debug, client,
|
|
- "PLL regs = int: %u, frac: %u, post: %u\n",
|
|
- pll_int, pll_frac, pll_post);
|
|
-
|
|
- if (pll_post) {
|
|
- int fin, fsc;
|
|
- int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
|
|
-
|
|
- pll /= pll_post;
|
|
- v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
|
|
- pll / 1000000, pll % 1000000);
|
|
- v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
|
|
- pll / 8000000, (pll / 8) % 1000000);
|
|
-
|
|
- fin = ((u64)src_decimation * pll) >> 12;
|
|
+ if (!state->is_cx231xx) {
|
|
+ pll_int = cx25840_read(client, 0x108);
|
|
+ pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
|
|
+ pll_post = cx25840_read(client, 0x109);
|
|
v4l_dbg(1, cx25840_debug, client,
|
|
- "ADC Sampling freq = %d.%06d MHz\n",
|
|
- fin / 1000000, fin % 1000000);
|
|
-
|
|
- fsc = (((u64)sc) * pll) >> 24L;
|
|
- v4l_dbg(1, cx25840_debug, client,
|
|
- "Chroma sub-carrier freq = %d.%06d MHz\n",
|
|
- fsc / 1000000, fsc % 1000000);
|
|
-
|
|
- v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
|
|
- "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
|
|
- "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
|
|
- "sc 0x%06x\n",
|
|
- hblank, hactive, vblank, vactive, vblank656,
|
|
- src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
|
|
+ "PLL regs = int: %u, frac: %u, post: %u\n",
|
|
+ pll_int, pll_frac, pll_post);
|
|
+
|
|
+ if (pll_post) {
|
|
+ int fin, fsc;
|
|
+ int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
|
|
+
|
|
+ pll /= pll_post;
|
|
+ v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
|
|
+ pll / 1000000, pll % 1000000);
|
|
+ v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
|
|
+ pll / 8000000, (pll / 8) % 1000000);
|
|
+
|
|
+ fin = ((u64)src_decimation * pll) >> 12;
|
|
+ v4l_dbg(1, cx25840_debug, client,
|
|
+ "ADC Sampling freq = %d.%06d MHz\n",
|
|
+ fin / 1000000, fin % 1000000);
|
|
+
|
|
+ fsc = (((u64)sc) * pll) >> 24L;
|
|
+ v4l_dbg(1, cx25840_debug, client,
|
|
+ "Chroma sub-carrier freq = %d.%06d MHz\n",
|
|
+ fsc / 1000000, fsc % 1000000);
|
|
+
|
|
+ v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
|
|
+ "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
|
|
+ "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
|
|
+ "sc 0x%06x\n",
|
|
+ hblank, hactive, vblank, vactive, vblank656,
|
|
+ src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
|
|
+ }
|
|
}
|
|
|
|
/* Sets horizontal blanking delay and active lines */
|
|
@@ -596,7 +669,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
|
|
* configuration in reg (for the cx23885) so we have no
|
|
* need to attempt to flip bits for earlier av decoders.
|
|
*/
|
|
- if (!state->is_cx23885) {
|
|
+ if (!state->is_cx23885 && !state->is_cx231xx) {
|
|
switch (aud_input) {
|
|
case CX25840_AUDIO_SERIAL:
|
|
/* do nothing, use serial audio input */
|
|
@@ -619,7 +692,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
|
|
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
|
|
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
|
|
|
|
- if (!state->is_cx23885) {
|
|
+ if (!state->is_cx23885 && !state->is_cx231xx) {
|
|
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
|
|
cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
|
|
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
|
|
@@ -659,6 +732,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
|
|
*/
|
|
cx25840_write(client, 0x918, 0xa0);
|
|
cx25840_write(client, 0x919, 0x01);
|
|
+ } else if (state->is_cx231xx) {
|
|
+ /* Audio channel 1 src : Parallel 1 */
|
|
+ cx25840_write(client, 0x124, 0x03);
|
|
+
|
|
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
|
|
+ cx25840_write(client, 0x914, 0xa0);
|
|
+
|
|
+ /* I2S_OUT_CTL:
|
|
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
|
|
+ * I2S_OUT_MASTER_MODE = Master
|
|
+ */
|
|
+ cx25840_write(client, 0x918, 0xa0);
|
|
+ cx25840_write(client, 0x919, 0x01);
|
|
}
|
|
|
|
return 0;
|
|
@@ -1118,6 +1204,8 @@ static int cx25840_init(struct v4l2_subdev *sd, u32 val)
|
|
cx25836_initialize(client);
|
|
else if (state->is_cx23885)
|
|
cx23885_initialize(client);
|
|
+ else if (state->is_cx231xx)
|
|
+ cx231xx_initialize(client);
|
|
else
|
|
cx25840_initialize(client);
|
|
}
|
|
@@ -1159,7 +1247,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
|
|
v4l_dbg(1, cx25840_debug, client, "%s output\n",
|
|
enable ? "enable" : "disable");
|
|
if (enable) {
|
|
- if (state->is_cx23885) {
|
|
+ if (state->is_cx23885 || state->is_cx231xx) {
|
|
u8 v = (cx25840_read(client, 0x421) | 0x0b);
|
|
cx25840_write(client, 0x421, v);
|
|
} else {
|
|
@@ -1169,7 +1257,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
|
|
state->is_cx25836 ? 0x04 : 0x07);
|
|
}
|
|
} else {
|
|
- if (state->is_cx23885) {
|
|
+ if (state->is_cx23885 || state->is_cx231xx) {
|
|
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
|
|
cx25840_write(client, 0x421, v);
|
|
} else {
|
|
@@ -1350,6 +1438,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
|
|
cx25836_initialize(client);
|
|
else if (state->is_cx23885)
|
|
cx23885_initialize(client);
|
|
+ else if (state->is_cx231xx)
|
|
+ cx231xx_initialize(client);
|
|
else
|
|
cx25840_initialize(client);
|
|
return 0;
|
|
@@ -1449,6 +1539,8 @@ static int cx25840_probe(struct i2c_client *client,
|
|
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
|
|
} else if (device_id == 0x1313) {
|
|
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
|
|
+ } else if ((device_id & 0xfff0) == 0x5A30) {
|
|
+ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
|
|
}
|
|
else {
|
|
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
|
|
@@ -1471,6 +1563,7 @@ static int cx25840_probe(struct i2c_client *client,
|
|
state->c = client;
|
|
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
|
|
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
|
|
+ state->is_cx231xx = (device_id == 0x5a3e);
|
|
state->vid_input = CX25840_COMPOSITE7;
|
|
state->aud_input = CX25840_AUDIO8;
|
|
state->audclk_freq = 48000;
|
|
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
|
|
index 9ad0eb8..814b565 100644
|
|
--- a/drivers/media/video/cx25840/cx25840-core.h
|
|
+++ b/drivers/media/video/cx25840/cx25840-core.h
|
|
@@ -50,6 +50,7 @@ struct cx25840_state {
|
|
u32 rev;
|
|
int is_cx25836;
|
|
int is_cx23885;
|
|
+ int is_cx231xx;
|
|
int is_initialized;
|
|
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
|
|
struct work_struct fw_work; /* work entry for fw load */
|
|
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
|
|
index 0b2dceb..0df53b0 100644
|
|
--- a/drivers/media/video/cx25840/cx25840-firmware.c
|
|
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
|
|
@@ -25,6 +25,7 @@
|
|
|
|
#define FWFILE "v4l-cx25840.fw"
|
|
#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
|
|
+#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw"
|
|
|
|
/*
|
|
* Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
|
|
@@ -96,9 +97,17 @@ int cx25840_loadfw(struct i2c_client *client)
|
|
u8 buffer[FWSEND];
|
|
const u8 *ptr;
|
|
int size, retval;
|
|
+ int MAX_BUF_SIZE = FWSEND;
|
|
|
|
if (state->is_cx23885)
|
|
firmware = FWFILE_CX23885;
|
|
+ else if (state->is_cx231xx)
|
|
+ firmware = FWFILE_CX231XX;
|
|
+
|
|
+ if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) {
|
|
+ v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
|
|
+ MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
|
|
+ }
|
|
|
|
if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
|
|
v4l_err(client, "unable to open firmware %s\n", firmware);
|
|
@@ -113,7 +122,7 @@ int cx25840_loadfw(struct i2c_client *client)
|
|
size = fw->size;
|
|
ptr = fw->data;
|
|
while (size > 0) {
|
|
- int len = min(FWSEND - 2, size);
|
|
+ int len = min(MAX_BUF_SIZE - 2, size);
|
|
|
|
memcpy(buffer + 2, ptr, len);
|
|
|
|
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
|
|
index f27604a..f9d48c9 100644
|
|
--- a/include/linux/i2c-id.h
|
|
+++ b/include/linux/i2c-id.h
|
|
@@ -88,6 +88,7 @@
|
|
#define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */
|
|
#define I2C_HW_B_CX23885 0x010022 /* conexant 23885 based tv cards (bus1) */
|
|
#define I2C_HW_B_AU0828 0x010023 /* auvitek au0828 usb bridge */
|
|
+#define I2C_HW_B_CX231XX 0x010024 /* Conexant CX231XX USB based cards */
|
|
#define I2C_HW_B_HDPVR 0x010025 /* Hauppauge HD PVR */
|
|
|
|
/* --- SGI adapters */
|
|
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
|
|
index ea1bf5b..3d7533d 100644
|
|
--- a/include/linux/mmc/sdio_ids.h
|
|
+++ b/include/linux/mmc/sdio_ids.h
|
|
@@ -24,6 +24,14 @@
|
|
*/
|
|
|
|
#define SDIO_VENDOR_ID_MARVELL 0x02df
|
|
+#define SDIO_VENDOR_ID_SIANO 0x039a
|
|
+
|
|
#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
|
|
+#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
|
|
+#define SDIO_DEVICE_ID_SIANO_NOVA_A0 0x1100
|
|
+#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
|
+#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
|
|
+#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
|
|
+#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
|
|
|
|
#endif
|