diff options
Diffstat (limited to 'drivers/media/pci')
93 files changed, 5858 insertions, 2420 deletions
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 5c16c9c2203e..f8cec8e8cf82 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -20,6 +20,7 @@ source "drivers/media/pci/ivtv/Kconfig" source "drivers/media/pci/zoran/Kconfig" source "drivers/media/pci/saa7146/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" +source "drivers/media/pci/tw68/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT @@ -41,6 +42,7 @@ source "drivers/media/pci/b2c2/Kconfig" source "drivers/media/pci/pluto2/Kconfig" source "drivers/media/pci/dm1105/Kconfig" source "drivers/media/pci/pt1/Kconfig" +source "drivers/media/pci/pt3/Kconfig" source "drivers/media/pci/mantis/Kconfig" source "drivers/media/pci/ngene/Kconfig" source "drivers/media/pci/ddbridge/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index e5b53fb569ef..a12926e4b51f 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -7,10 +7,10 @@ obj-y += ttpci/ \ pluto2/ \ dm1105/ \ pt1/ \ + pt3/ \ mantis/ \ ngene/ \ ddbridge/ \ - b2c2/ \ saa7146/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ @@ -22,6 +22,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ +obj-$(CONFIG_VIDEO_TW68) += tw68/ obj-$(CONFIG_VIDEO_MEYE) += meye/ obj-$(CONFIG_STA2X11_VIP) += sta2x11/ obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 970e542d3a51..4a8176c09fc9 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1531,7 +1531,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, { struct bttv_buffer *old; unsigned long flags; - int retval = 0; dprintk("switch_overlay: enter [new=%p]\n", new); if (new) @@ -1551,7 +1550,7 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, if (NULL == new) free_btres_lock(btv,fh,RESOURCE_OVERLAY); dprintk("switch_overlay: done\n"); - return retval; + return 0; } /* ----------------------------------------------------------------------- */ @@ -3856,7 +3855,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT), BT848_INT_MASK); - }; + } bttv_print_irqbits(stat,astat); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index 0e788fca992c..c22c4ae06844 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -674,11 +674,9 @@ static int dst_ca_release(struct inode *inode, struct file *file) static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) { - ssize_t bytes_read = 0; - dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); - return bytes_read; + return 0; } static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c index 180077c49123..ffb6acdc575f 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.c +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c @@ -80,7 +80,7 @@ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, int period_elapsed = 0; int length; - dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, + dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc, pcm_data, num_bytes); substream = cxsc->capture_pcm_substream; diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c index a1c1cec05f98..c6c83445f8bf 100644 --- a/drivers/media/pci/cx18/cx18-firmware.c +++ b/drivers/media/pci/cx18/cx18-firmware.c @@ -130,7 +130,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) } } if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) - CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size); size = fw->size; release_firmware(fw); cx18_setup_page(cx, SCB_OFFSET); @@ -164,7 +164,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; while (offset + sizeof(seghdr) < fw->size) { - const u32 *shptr = src + offset / 4; + const __le32 *shptr = (__force __le32 *)src + offset / 4; seghdr.sync1 = le32_to_cpu(shptr[0]); seghdr.sync2 = le32_to_cpu(shptr[1]); @@ -202,7 +202,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, offset += seghdr.size; } if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) - CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", + CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n", fn, apu_version, fw->size); size = fw->size; release_firmware(fw); diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c index 8884537bd62f..2a247d264b87 100644 --- a/drivers/media/pci/cx18/cx18-queue.c +++ b/drivers/media/pci/cx18/cx18-queue.c @@ -364,7 +364,7 @@ int cx18_stream_alloc(struct cx18_stream *s) ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); - CX18_ERR("Max buffers = %zd\n", + CX18_ERR("Max buffers = %zu\n", bufsz / sizeof(struct cx18_mdl_ent)); return -ENOMEM; } diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index e12c006e6e2d..f613314b360b 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -3,12 +3,11 @@ config VIDEO_CX23885 depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND select SND_PCM select I2C_ALGOBIT - select VIDEO_BTCX select VIDEO_TUNER select VIDEO_TVEEPROM depends on RC_CORE - select VIDEOBUF_DVB - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DVB + select VIDEOBUF2_DMA_SG select VIDEO_CX25840 select VIDEO_CX2341X select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT @@ -32,12 +31,16 @@ config VIDEO_CX23885 select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index 2a2cafb8cf5b..a2cbdcf15a8c 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ccflags-y += -Idrivers/media/i2c -ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c index 2926f7fadccd..2bbbf545b042 100644 --- a/drivers/media/pci/cx23885/altera-ci.c +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -16,10 +16,6 @@ * 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. */ /* @@ -52,8 +48,8 @@ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| * +-------+-------+-------+-------+-------+-------+-------+-------+ */ -#include <media/videobuf-dma-sg.h> -#include <media/videobuf-dvb.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> #include "altera-ci.h" #include "dvb_ca_en50221.h" diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h index 4998c96caebe..5028f0cf83f4 100644 --- a/drivers/media/pci/cx23885/altera-ci.h +++ b/drivers/media/pci/cx23885/altera-ci.h @@ -16,10 +16,6 @@ * 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 __ALTERA_CI_H #define __ALTERA_CI_H diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c index 16fa7ea4d4aa..631e4f24aea6 100644 --- a/drivers/media/pci/cx23885/cimax2.c +++ b/drivers/media/pci/cx23885/cimax2.c @@ -17,10 +17,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h index 518744a4c8a5..565e958f6f8d 100644 --- a/drivers/media/pci/cx23885/cimax2.h +++ b/drivers/media/pci/cx23885/cimax2.h @@ -17,10 +17,6 @@ * 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 CIMAX2_H diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index bf89fc88692e..3948db386fb5 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -18,10 +18,6 @@ * 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> @@ -865,6 +861,11 @@ static int cx23885_api_cmd(struct cx23885_dev *dev, return err; } +static int cx23885_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + return cx23885_mbox_func(priv, cmd, in, out, data); +} + static int cx23885_find_mailbox(struct cx23885_dev *dev) { u32 signature[4] = { @@ -941,7 +942,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) if (firmware->size != CX23885_FIRM_IMAGE_SIZE) { printk(KERN_ERR "ERROR: Firmware size mismatch " - "(have %zd, expected %d)\n", + "(have %zu, expected %d)\n", firmware->size, CX23885_FIRM_IMAGE_SIZE); release_firmware(firmware); return -1; @@ -1033,12 +1034,12 @@ static void cx23885_codec_settings(struct cx23885_dev *dev) cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, dev->ts1.height, dev->ts1.width); - dev->mpeg_params.width = dev->ts1.width; - dev->mpeg_params.height = dev->ts1.height; - dev->mpeg_params.is_50hz = + dev->cxhdl.width = dev->ts1.width; + dev->cxhdl.height = dev->ts1.height; + dev->cxhdl.is_50hz = (dev->encodernorm.id & V4L2_STD_625_50) != 0; - cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params); + cx2341x_handler_setup(&dev->cxhdl); cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); @@ -1137,85 +1138,107 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder) /* ------------------------------------------------------------------ */ -static int bb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_fh *fh = q->priv_data; - - fh->dev->ts1.ts_packet_size = mpeglinesize; - fh->dev->ts1.ts_packet_count = mpeglines; - - *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; - *count = mpegbufs; + struct cx23885_dev *dev = q->drv_priv; + dev->ts1.ts_packet_size = mpeglinesize; + dev->ts1.ts_packet_count = mpeglines; + *num_planes = 1; + sizes[0] = mpeglinesize * mpeglines; + *num_buffers = mpegbufs; return 0; } -static int bb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - return cx23885_buf_prepare(q, &fh->dev->ts1, - (struct cx23885_buffer *)vb, - field); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = + container_of(vb, struct cx23885_buffer, vb); + + return cx23885_buf_prepare(buf, &dev->ts1); } -static void bb_buf_queue(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(dev, buf); + + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void bb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_queue(struct vb2_buffer *vb) { - cx23885_free_buffer(q, (struct cx23885_buffer *)vb); -} + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); -static struct videobuf_queue_ops cx23885_qops = { - .buf_setup = bb_buf_setup, - .buf_prepare = bb_buf_prepare, - .buf_queue = bb_buf_queue, - .buf_release = bb_buf_release, -}; + cx23885_buf_queue(&dev->ts1, buf); +} -/* ------------------------------------------------------------------ */ +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->ts1.mpegq; + unsigned long flags; + int ret; + + ret = cx23885_initialize_codec(dev, 1); + if (ret == 0) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + cx23885_start_dma(&dev->ts1, dmaq, buf); + return 0; + } + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); -static const u32 *ctrl_classes[] = { - cx2341x_mpeg_ctrls, - NULL -}; + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&dev->slock, flags); + return ret; +} -static int cx23885_queryctrl(struct cx23885_dev *dev, - struct v4l2_queryctrl *qctrl) +static void cx23885_stop_streaming(struct vb2_queue *q) { - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; + struct cx23885_dev *dev = q->drv_priv; - /* MPEG V4L2 controls */ - if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) - qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + /* stop mpeg capture */ + cx23885_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); - return 0; + msleep(500); + cx23885_417_check_encoder(dev); + cx23885_cancel_buffers(&dev->ts1); } -static int cx23885_querymenu(struct cx23885_dev *dev, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; +static struct vb2_ops cx23885_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; - qctrl.id = qmenu->id; - cx23885_queryctrl(dev, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); -} +/* ------------------------------------------------------------------ */ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); *id = dev->tvnorm; return 0; @@ -1223,29 +1246,26 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) if (id & cx23885_tvnorms[i].id) break; if (i == ARRAY_SIZE(cx23885_tvnorms)) return -EINVAL; - dev->encodernorm = cx23885_tvnorms[i]; - /* Have the drier core notify the subdevices */ - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, id); - mutex_unlock(&dev->lock); - - return 0; + ret = cx23885_set_tvnorm(dev, id); + if (!ret) + dev->encodernorm = cx23885_tvnorms[i]; + return ret; } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); return cx23885_enum_input(dev, i); } @@ -1263,8 +1283,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1281,8 +1300,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1296,8 +1314,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1315,27 +1332,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, return cx23885_set_frequency(file, priv, f); } -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_get_control(dev, ctl); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_set_control(dev, ctl); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); struct cx23885_tsport *tsport = &dev->ts1; strlcpy(cap->driver, dev->name, sizeof(cap->driver)); @@ -1368,8 +1368,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; @@ -1378,285 +1377,63 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = 0; f->fmt.pix.width = dev->ts1.width; f->fmt.pix.height = dev->ts1.height; - f->fmt.pix.field = fh->mpegq.field; - dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; f->fmt.pix.colorspace = 0; - dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n", + dev->ts1.width, dev->ts1.height); return 0; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; f->fmt.pix.colorspace = 0; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_reqbufs(&fh->mpegq, p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_querybuf(&fh->mpegq, p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_qbuf(&fh->mpegq, p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct cx23885_fh *fh = priv; - - return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK); -} - - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_streamon(&fh->mpegq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_streamoff(&fh->mpegq); -} - -static int vidioc_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); -} - -static int vidioc_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); - - if (err == 0) { - err = cx2341x_update(dev, cx23885_mbox_func, - &dev->mpeg_params, &p); - dev->mpeg_params = p; - } - return err; -} - -static int vidioc_try_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *f) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); - return err; -} - static int vidioc_log_status(struct file *file, void *priv) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); char name[32 + 2]; snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO - "%s/2: ============ START LOG STATUS ============\n", - dev->name); call_all(dev, core, log_status); - cx2341x_log_status(&dev->mpeg_params, name); - printk(KERN_INFO - "%s/2: ============= END LOG STATUS =============\n", - dev->name); + v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name); return 0; } -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *a) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - return cx23885_querymenu(dev, a); -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - - return cx23885_queryctrl(dev, c); -} - -static int mpeg_open(struct file *file) -{ - struct cx23885_dev *dev = video_drvdata(file); - struct cx23885_fh *fh; - - dprintk(2, "%s()\n", __func__); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - - file->private_data = fh; - fh->dev = dev; - - videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops, - &dev->pci->dev, &dev->ts1.slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx23885_buffer), - fh, NULL); - return 0; -} - -static int mpeg_release(struct file *file) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - /* FIXME: Review this crap */ - /* Shut device down on last close */ - if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { - if (atomic_dec_return(&dev->v4l_reader_count) == 0) { - /* stop mpeg capture */ - cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX23885_END_NOW, CX23885_MPEG_CAPTURE, - CX23885_RAW_BITS_NONE); - - msleep(500); - cx23885_417_check_encoder(dev); - - cx23885_cancel_buffers(&fh->dev->ts1); - } - } - - if (fh->mpegq.streaming) - videobuf_streamoff(&fh->mpegq); - if (fh->mpegq.reading) - videobuf_read_stop(&fh->mpegq); - - videobuf_mmap_free(&fh->mpegq); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static ssize_t mpeg_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ - /* Start mpeg encoder on first read. */ - if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { - if (atomic_inc_return(&dev->v4l_reader_count) == 1) { - if (cx23885_initialize_codec(dev, 1) < 0) - return -EINVAL; - } - } - - return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int mpeg_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s\n", __func__); - - return videobuf_poll_stream(file, &fh->mpegq, wait); -} - -static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - dprintk(2, "%s()\n", __func__); - - return videobuf_mmap_mapper(&fh->mpegq, vma); -} - static struct v4l2_file_operations mpeg_fops = { .owner = THIS_MODULE, - .open = mpeg_open, - .release = mpeg_release, - .read = mpeg_read, - .poll = mpeg_poll, - .mmap = mpeg_mmap, - .ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { @@ -1669,25 +1446,19 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .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_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, - .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = vidioc_log_status, - .vidioc_querymenu = vidioc_querymenu, - .vidioc_queryctrl = vidioc_queryctrl, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_chip_info = cx23885_g_chip_info, .vidioc_g_register = cx23885_g_register, @@ -1711,6 +1482,7 @@ void cx23885_417_unregister(struct cx23885_dev *dev) video_unregister_device(dev->v4l_device); else video_device_release(dev->v4l_device); + v4l2_ctrl_handler_free(&dev->cxhdl.hdl); dev->v4l_device = NULL; } } @@ -1742,6 +1514,7 @@ int cx23885_417_register(struct cx23885_dev *dev) /* FIXME: Port1 hardcoded here */ int err = -ENODEV; struct cx23885_tsport *tsport = &dev->ts1; + struct vb2_queue *q; dprintk(1, "%s()\n", __func__); @@ -1757,14 +1530,36 @@ int cx23885_417_register(struct cx23885_dev *dev) tsport->height = 576; tsport->width = 720; - cx2341x_fill_defaults(&dev->mpeg_params); - - dev->mpeg_params.port = CX2341X_PORT_SERIAL; + dev->cxhdl.port = CX2341X_PORT_SERIAL; + err = cx2341x_handler_init(&dev->cxhdl, 50); + if (err) + return err; + dev->cxhdl.priv = dev; + dev->cxhdl.func = cx23885_api_func; + cx2341x_handler_set_50hz(&dev->cxhdl, tsport->height == 576); + v4l2_ctrl_add_handler(&dev->ctrl_handler, &dev->cxhdl.hdl, NULL); /* Allocate and initialize V4L video device */ dev->v4l_device = cx23885_video_dev_alloc(tsport, dev->pci, &cx23885_mpeg_template, "mpeg"); + q = &dev->vb2_mpegq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + return err; video_set_drvdata(dev->v4l_device, dev); + dev->v4l_device->lock = &dev->lock; + dev->v4l_device->queue = q; err = video_register_device(dev->v4l_device, VFL_TYPE_GRABBER, -1); if (err < 0) { diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index 554798dcedd0..ae7c2e89ad1c 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -15,10 +15,6 @@ * 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> @@ -84,6 +80,82 @@ MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); #define AUD_INT_MCHG_IRQ (1 << 21) #define GP_COUNT_CONTROL_RESET 0x3 +static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages) +{ + struct cx23885_audio_buffer *buf = chip->buf; + struct page *pg; + int i; + + buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == buf->vaddr) { + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + return -ENOMEM; + } + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)buf->vaddr, + nr_pages << PAGE_SHIFT); + + memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); + buf->nr_pages = nr_pages; + + buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist)); + if (NULL == buf->sglist) + goto vzalloc_err; + + sg_init_table(buf->sglist, buf->nr_pages); + for (i = 0; i < buf->nr_pages; i++) { + pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE); + if (NULL == pg) + goto vmalloc_to_page_err; + sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0); + } + return 0; + +vmalloc_to_page_err: + vfree(buf->sglist); + buf->sglist = NULL; +vzalloc_err: + vfree(buf->vaddr); + buf->vaddr = NULL; + return -ENOMEM; +} + +static int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev) +{ + struct cx23885_audio_buffer *buf = dev->buf; + + buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, + buf->nr_pages, PCI_DMA_FROMDEVICE); + + if (0 == buf->sglen) { + pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +static int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev) +{ + struct cx23885_audio_buffer *buf = dev->buf; + + if (!buf->sglen) + return 0; + + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + buf->sglen = 0; + return 0; +} + +static int cx23885_alsa_dma_free(struct cx23885_audio_buffer *buf) +{ + vfree(buf->sglist); + buf->sglist = NULL; + vfree(buf->vaddr); + buf->vaddr = NULL; + return 0; +} + /* * BOARD Specific: Sets audio DMA */ @@ -198,15 +270,18 @@ int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask) static int dsp_buffer_free(struct cx23885_audio_dev *chip) { + struct cx23885_riscmem *risc; + BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); - videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); - videobuf_dma_free(chip->dma_risc); - btcx_riscmem_free(chip->pci, &chip->buf->risc); + cx23885_alsa_dma_unmap(chip); + cx23885_alsa_dma_free(chip->buf); + risc = &chip->buf->risc; + pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); kfree(chip->buf); - chip->dma_risc = NULL; + chip->buf = NULL; chip->dma_size = 0; return 0; @@ -289,6 +364,7 @@ static int snd_cx23885_close(struct snd_pcm_substream *substream) return 0; } + /* * hw_params callback */ @@ -296,8 +372,6 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); - struct videobuf_dmabuf *dma; - struct cx23885_audio_buffer *buf; int ret; @@ -318,19 +392,18 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, return -ENOMEM; buf->bpl = chip->period_size; + chip->buf = buf; - dma = &buf->dma; - videobuf_dma_init(dma); - ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + ret = cx23885_alsa_dma_init(chip, (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_dma_map(&chip->pci->dev, dma); + ret = cx23885_alsa_dma_map(chip); if (ret < 0) goto error; - ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist, + ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); if (ret < 0) goto error; @@ -340,10 +413,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - chip->buf = buf; - chip->dma_risc = dma; - - substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_area = chip->buf->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; @@ -351,6 +421,7 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, error: kfree(buf); + chip->buf = NULL; return ret; } diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c index c443b7ac5adf..877dad89107e 100644 --- a/drivers/media/pci/cx23885/cx23885-av.c +++ b/drivers/media/pci/cx23885/cx23885-av.c @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include "cx23885.h" diff --git a/drivers/media/pci/cx23885/cx23885-av.h b/drivers/media/pci/cx23885/cx23885-av.h index d2915c3e53a2..97f232f8efb9 100644 --- a/drivers/media/pci/cx23885/cx23885-av.h +++ b/drivers/media/pci/cx23885/cx23885-av.h @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _CX23885_AV_H_ diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index c2b608007190..88c257d1161b 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -13,10 +13,6 @@ * 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> @@ -679,6 +675,11 @@ struct cx23885_board cx23885_boards[] = { .amux = CX25840_AUDIO7, } }, }, + [CX23885_BOARD_DVBSKY_T9580] = { + .name = "DVBSky T9580", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -934,6 +935,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x18ac, .subdevice = 0xdb98, .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2, + }, { + .subvendor = 0x4254, + .subdevice = 0x9580, + .card = CX23885_BOARD_DVBSKY_T9580, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1528,6 +1533,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); mdelay(60); break; + case CX23885_BOARD_DVBSKY_T9580: + /* enable GPIO3-18 pins */ + cx_write(MC417_CTL, 0x00000037); + cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1); + cx23885_gpio_clear(dev, GPIO_2 | GPIO_11); + mdelay(100); + cx23885_gpio_set(dev, GPIO_2 | GPIO_11); + break; } } @@ -1851,6 +1864,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_DVBSKY_T9580: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0x8; /* Serial bus */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -1913,6 +1934,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_AVERMEDIA_HC81R: case CX23885_BOARD_TBS_6980: case CX23885_BOARD_TBS_6981: + case CX23885_BOARD_DVBSKY_T9580: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); @@ -1970,5 +1992,3 @@ void cx23885_card_setup(struct cx23885_dev *dev) } } } - -/* ------------------------------------------------------------------ */ diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index edcd79db1e4e..331eddac7222 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -13,10 +13,6 @@ * 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> @@ -420,39 +416,23 @@ static int cx23885_risc_decode(u32 risc) return incr[risc >> 28] ? incr[risc >> 28] : 1; } -void cx23885_wakeup(struct cx23885_tsport *port, +static void cx23885_wakeup(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_dev *dev = port->dev; struct cx23885_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) - break; - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - - /* count comes from the hw and is is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - v4l2_get_timestamp(&buf->vb.ts); - dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, - count, buf->count); - buf->vb.state = VIDEOBUF_DONE; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - if (bc != 1) - printk(KERN_WARNING "%s: %d buffers handled (should be 1)\n", - __func__, bc); + return; + buf = list_entry(q->active.next, + struct cx23885_buffer, queue); + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.sequence = q->count++; + dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, + count, q->count); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } int cx23885_sram_channel_setup(struct cx23885_dev *dev, @@ -482,8 +462,8 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, lines = 6; BUG_ON(lines < 2); - cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - cx_write(8 + 4, 8); + cx_write(8 + 0, RISC_JUMP | RISC_CNT_RESET); + cx_write(8 + 4, 12); cx_write(8 + 8, 0); /* write CDT */ @@ -590,7 +570,7 @@ void cx23885_sram_channel_dump(struct cx23885_dev *dev, } static void cx23885_risc_disasm(struct cx23885_tsport *port, - struct btcx_riscmem *risc) + struct cx23885_riscmem *risc) { struct cx23885_dev *dev = port->dev; unsigned int i, j, n; @@ -699,10 +679,6 @@ static int get_resources(struct cx23885_dev *dev) return -EBUSY; } -static void cx23885_timeout(unsigned long data); -int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value); - static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno) { @@ -719,11 +695,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, port->nr = portno; INIT_LIST_HEAD(&port->mpegq.active); - INIT_LIST_HEAD(&port->mpegq.queued); - port->mpegq.timeout.function = cx23885_timeout; - port->mpegq.timeout.data = (unsigned long)port; - init_timer(&port->mpegq.timeout); - mutex_init(&port->frontends.lock); INIT_LIST_HEAD(&port->frontends.felist); port->frontends.active_fe_id = 0; @@ -776,9 +747,6 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, BUG(); } - cx23885_risc_stopper(dev->pci, &port->mpegq.stopper, - port->reg_dma_ctl, port->dma_ctl_val, 0x00); - return 0; } @@ -1089,11 +1057,18 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, - unsigned int lines, unsigned int lpi) + unsigned int lines, unsigned int lpi, bool jump) { struct scatterlist *sg; unsigned int line, todo, sol; + + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(0); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + } + /* sync instruction */ if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); @@ -1146,14 +1121,13 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, return rp; } -int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions, fields; __le32 *rp; - int rc; fields = 0; if (UNSET != top_offset) @@ -1168,19 +1142,20 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + instructions += 5; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; if (UNSET != top_offset) rp = cx23885_risc_field(rp, sglist, top_offset, 0, - bpl, padding, lines, 0); + bpl, padding, lines, 0, true); if (UNSET != bottom_offset) rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, - bpl, padding, lines, 0); + bpl, padding, lines, 0, UNSET == top_offset); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1189,14 +1164,13 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, } int cx23885_risc_databuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi) { u32 instructions; __le32 *rp; - int rc; /* estimate risc mem: worst case is one write per page border + one write per scan line + syncs + jump (all 2 dwords). Here @@ -1204,16 +1178,17 @@ int cx23885_risc_databuffer(struct pci_dev *pci, than PAGE_SIZE */ /* Jump and write need an extra dword */ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; - instructions += 1; + instructions += 4; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, - bpl, 0, lines, lpi); + bpl, 0, lines, lpi, lpi == 0); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -1221,14 +1196,13 @@ int cx23885_risc_databuffer(struct pci_dev *pci, return 0; } -int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx23885_risc_vbibuffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) { u32 instructions, fields; __le32 *rp; - int rc; fields = 0; if (UNSET != top_offset) @@ -1243,22 +1217,23 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions*12); - if (rc < 0) - return rc; + instructions += 5; + risc->size = instructions * 12; + risc->cpu = pci_alloc_consistent(pci, risc->size, &risc->dma); + if (risc->cpu == NULL) + return -ENOMEM; /* write risc instructions */ rp = risc->cpu; /* Sync to line 6, so US CC line 21 will appear in line '12' * in the userland vbi payload */ if (UNSET != top_offset) - rp = cx23885_risc_field(rp, sglist, top_offset, 6, - bpl, padding, lines, 0); + rp = cx23885_risc_field(rp, sglist, top_offset, 0, + bpl, padding, lines, 0, true); if (UNSET != bottom_offset) - rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207, - bpl, padding, lines, 0); + rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, + bpl, padding, lines, 0, UNSET == top_offset); @@ -1269,38 +1244,12 @@ int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, } -int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value) +void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf) { - __le32 *rp; - int rc; - - rc = btcx_riscmem_alloc(pci, risc, 4*16); - if (rc < 0) - return rc; - - /* write risc instructions */ - rp = risc->cpu; - *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2); - *(rp++) = cpu_to_le32(reg); - *(rp++) = cpu_to_le32(value); - *(rp++) = cpu_to_le32(mask); - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc->dma); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - return 0; -} - -void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + struct cx23885_riscmem *risc = &buf->risc; BUG_ON(in_interrupt()); - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma); } static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) @@ -1355,7 +1304,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk)); } -static int cx23885_start_dma(struct cx23885_tsport *port, +int cx23885_start_dma(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, struct cx23885_buffer *buf) { @@ -1363,7 +1312,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, u32 reg; dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, - buf->vb.width, buf->vb.height, buf->vb.field); + dev->width, dev->height, dev->field); /* Stop the fifo and risc engine for this port */ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); @@ -1379,7 +1328,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, } /* write TS length to chip */ - cx_write(port->reg_lngth, buf->vb.width); + cx_write(port->reg_lngth, port->ts_packet_size); if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) && (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) { @@ -1408,7 +1357,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, /* NOTE: this is 2 (reserved) for portb, does it matter? */ /* reset counter to zero */ cx_write(port->reg_gpcnt_ctl, 3); - q->count = 1; + q->count = 0; /* Set VIDB pins to input */ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { @@ -1497,134 +1446,83 @@ static int cx23885_stop_dma(struct cx23885_tsport *port) return 0; } -int cx23885_restart_queue(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q) -{ - struct cx23885_dev *dev = port->dev; - struct cx23885_buffer *buf; - - dprintk(5, "%s()\n", __func__); - if (list_empty(&q->active)) { - struct cx23885_buffer *prev; - prev = NULL; - - dprintk(5, "%s() queue is empty\n", __func__); - - for (;;) { - if (list_empty(&q->queued)) - return 0; - buf = list_entry(q->queued.next, struct cx23885_buffer, - vb.queue); - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx23885_start_dma(port, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(5, "[%p/%d] restart_queue - f/active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(5, "[%p/%d] restart_queue - m/active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } - return 0; - } - - buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_dma(port, q, buf); - list_for_each_entry(buf, &q->active, vb.queue) - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - return 0; -} - /* ------------------------------------------------------------------ */ -int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, - struct cx23885_buffer *buf, enum v4l2_field field) +int cx23885_buf_prepare(struct cx23885_buffer *buf, struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; int size = port->ts_packet_size * port->ts_packet_count; + struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0); int rc; dprintk(1, "%s: %p\n", __func__, buf); - if (0 != buf->vb.baddr && buf->vb.bsize < size) + if (vb2_plane_size(&buf->vb, 0) < size) return -EINVAL; + vb2_set_plane_payload(&buf->vb, 0, size); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - buf->vb.width = port->ts_packet_size; - buf->vb.height = port->ts_packet_count; - buf->vb.size = size; - buf->vb.field = field /*V4L2_FIELD_TOP*/; - - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - cx23885_risc_databuffer(dev->pci, &buf->risc, - videobuf_to_dma(&buf->vb)->sglist, - buf->vb.width, buf->vb.height, 0); - } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!rc) + return -EIO; - fail: - cx23885_free_buffer(q, buf); - return rc; + cx23885_risc_databuffer(dev->pci, &buf->risc, + sgt->sgl, + port->ts_packet_size, port->ts_packet_count, 0); + return 0; } +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) { struct cx23885_buffer *prev; struct cx23885_dev *dev = port->dev; struct cx23885_dmaqueue *cx88q = &port->mpegq; + unsigned long flags; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + spin_lock_irqsave(&dev->slock, flags); if (list_empty(&cx88q->active)) { - dprintk(1, "queue is empty - first active\n"); - list_add_tail(&buf->vb.queue, &cx88q->active); - cx23885_start_dma(port, cx88q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = cx88q->count++; - mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT); + list_add_tail(&buf->queue, &cx88q->active); dprintk(1, "[%p/%d] %s - first active\n", - buf, buf->vb.i, __func__); + buf, buf->vb.v4l2_buf.index, __func__); } else { - dprintk(1, "queue is not empty - append to active\n"); + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(cx88q->active.prev, struct cx23885_buffer, - vb.queue); - list_add_tail(&buf->vb.queue, &cx88q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = cx88q->count++; + queue); + list_add_tail(&buf->queue, &cx88q->active); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */ dprintk(1, "[%p/%d] %s - append to active\n", - buf, buf->vb.i, __func__); + buf, buf->vb.v4l2_buf.index, __func__); } + spin_unlock_irqrestore(&dev->slock, flags); } /* ----------------------------------------------------------- */ -static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, - int restart) +static void do_cancel_buffers(struct cx23885_tsport *port, char *reason) { struct cx23885_dev *dev = port->dev; struct cx23885_dmaqueue *q = &port->mpegq; @@ -1634,16 +1532,11 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, spin_lock_irqsave(&port->slock, flags); while (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); + queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); dprintk(1, "[%p/%d] %s - dma=0x%08lx\n", - buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); - } - if (restart) { - dprintk(1, "restarting queue\n"); - cx23885_restart_queue(port, q); + buf, buf->vb.v4l2_buf.index, reason, (unsigned long)buf->risc.dma); } spin_unlock_irqrestore(&port->slock, flags); } @@ -1651,27 +1544,10 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, void cx23885_cancel_buffers(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; - struct cx23885_dmaqueue *q = &port->mpegq; - - dprintk(1, "%s()\n", __func__); - del_timer_sync(&q->timeout); - cx23885_stop_dma(port); - do_cancel_buffers(port, "cancel", 0); -} - -static void cx23885_timeout(unsigned long data) -{ - struct cx23885_tsport *port = (struct cx23885_tsport *)data; - struct cx23885_dev *dev = port->dev; dprintk(1, "%s()\n", __func__); - - if (debug > 5) - cx23885_sram_channel_dump(dev, - &dev->sram_channels[port->sram_chno]); - cx23885_stop_dma(port); - do_cancel_buffers(port, "timeout", 1); + do_cancel_buffers(port, "cancel"); } int cx23885_irq_417(struct cx23885_dev *dev, u32 status) @@ -1721,11 +1597,6 @@ int cx23885_irq_417(struct cx23885_dev *dev, u32 status) spin_lock(&port->slock); cx23885_wakeup(port, &port->mpegq, count); spin_unlock(&port->slock); - } else if (status & VID_B_MSK_RISCI2) { - dprintk(7, " VID_B_MSK_RISCI2\n"); - spin_lock(&port->slock); - cx23885_restart_queue(port, &port->mpegq); - spin_unlock(&port->slock); } if (status) { cx_write(port->reg_ts_int_stat, status); @@ -1777,14 +1648,6 @@ static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) cx23885_wakeup(port, &port->mpegq, count); spin_unlock(&port->slock); - } else if (status & VID_BC_MSK_RISCI2) { - - dprintk(7, " (RISCI2 0x%08x)\n", VID_BC_MSK_RISCI2); - - spin_lock(&port->slock); - cx23885_restart_queue(port, &port->mpegq); - spin_unlock(&port->slock); - } if (status) { cx_write(port->reg_ts_int_stat, status); @@ -2087,6 +1950,7 @@ static int cx23885_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { struct cx23885_dev *dev; + struct v4l2_ctrl_handler *hdl; int err; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -2097,6 +1961,14 @@ static int cx23885_initdev(struct pci_dev *pci_dev, if (err < 0) goto fail_free; + hdl = &dev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 6); + if (hdl->error) { + err = hdl->error; + goto fail_ctrl; + } + dev->v4l2_dev.ctrl_handler = hdl; + /* Prepare to handle notifications from subdevices */ cx23885_v4l2_dev_notify_init(dev); @@ -2104,12 +1976,12 @@ static int cx23885_initdev(struct pci_dev *pci_dev, dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; - goto fail_unreg; + goto fail_ctrl; } if (cx23885_dev_setup(dev) < 0) { err = -EINVAL; - goto fail_unreg; + goto fail_ctrl; } /* print pci info */ @@ -2157,7 +2029,8 @@ static int cx23885_initdev(struct pci_dev *pci_dev, fail_irq: cx23885_dev_unregister(dev); -fail_unreg: +fail_ctrl: + v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(&dev->v4l2_dev); fail_free: kfree(dev); @@ -2180,6 +2053,7 @@ static void cx23885_finidev(struct pci_dev *pci_dev) free_irq(pci_dev->irq, dev); cx23885_dev_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 968fecc32f9c..13734b8c7917 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -13,10 +13,6 @@ * 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> @@ -73,6 +69,10 @@ #include "a8293.h" #include "mb86a20s.h" #include "si2165.h" +#include "si2168.h" +#include "si2157.h" +#include "m88ds3103.h" +#include "m88ts2022.h" static unsigned int debug; @@ -91,59 +91,95 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* ------------------------------------------------------------------ */ -static int dvb_buf_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_tsport *port = q->priv_data; + struct cx23885_tsport *port = q->drv_priv; port->ts_packet_size = 188 * 4; port->ts_packet_count = 32; - - *size = port->ts_packet_size * port->ts_packet_count; - *count = 32; + *num_planes = 1; + sizes[0] = port->ts_packet_size * port->ts_packet_count; + *num_buffers = 32; return 0; } -static int dvb_buf_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, enum v4l2_field field) + +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_tsport *port = q->priv_data; - return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = + container_of(vb, struct cx23885_buffer, vb); + + return cx23885_buf_prepare(buf, port); } -static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_tsport *port = q->priv_data; - cx23885_buf_queue(port, (struct cx23885_buffer *)vb); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_dev *dev = port->dev; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(dev, buf); + + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void dvb_buf_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void buffer_queue(struct vb2_buffer *vb) { - cx23885_free_buffer(q, (struct cx23885_buffer *)vb); + struct cx23885_tsport *port = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + + cx23885_buf_queue(port, buf); } static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) { - struct videobuf_dvb_frontends *f; - struct videobuf_dvb_frontend *fe; + struct vb2_dvb_frontends *f; + struct vb2_dvb_frontend *fe; f = &port->frontends; if (f->gate <= 1) /* undefined or fe0 */ - fe = videobuf_dvb_get_frontend(f, 1); + fe = vb2_dvb_get_frontend(f, 1); else - fe = videobuf_dvb_get_frontend(f, f->gate); + fe = vb2_dvb_get_frontend(f, f->gate); if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); } -static struct videobuf_queue_ops dvb_qops = { - .buf_setup = dvb_buf_setup, - .buf_prepare = dvb_buf_prepare, - .buf_queue = dvb_buf_queue, - .buf_release = dvb_buf_release, +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct cx23885_tsport *port = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &port->mpegq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + cx23885_start_dma(port, dmaq, buf); + return 0; +} + +static void cx23885_stop_streaming(struct vb2_queue *q) +{ + struct cx23885_tsport *port = q->drv_priv; + + cx23885_cancel_buffers(port); +} + +static struct vb2_ops dvb_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, }; static struct s5h1409_config hauppauge_generic_config = { @@ -551,6 +587,35 @@ static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static int dvbsky_t9580_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); + + switch (voltage) { + case SEC_VOLTAGE_13: + cx23885_gpio_set(dev, GPIO_1); + cx23885_gpio_clear(dev, GPIO_0); + break; + case SEC_VOLTAGE_18: + cx23885_gpio_set(dev, GPIO_1); + cx23885_gpio_set(dev, GPIO_0); + break; + case SEC_VOLTAGE_OFF: + cx23885_gpio_clear(dev, GPIO_1); + cx23885_gpio_clear(dev, GPIO_0); + break; + } + + /* call the frontend set_voltage function */ + port->fe_set_voltage(fe, voltage); + + return 0; +} + static int cx23885_dvb_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -715,6 +780,19 @@ static const struct si2165_config hauppauge_hvr4400_si2165_config = { .ref_freq_Hz = 16000000, }; +static const struct m88ds3103_config dvbsky_t9580_m88ds3103_config = { + .i2c_addr = 0x68, + .clock = 27000000, + .i2c_wr_max = 33, + .clock_out = 0, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .lnb_en_pol = 1, + .lnb_hv_pol = 0, + .agc = 0x99, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -863,16 +941,23 @@ static int dvb_register(struct cx23885_tsport *port) struct dib7000p_ops dib7000p_ops; struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; - struct videobuf_dvb_frontend *fe0, *fe1 = NULL; + struct vb2_dvb_frontend *fe0, *fe1 = NULL; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + struct m88ts2022_config m88ts2022_config; + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client_demod; + struct i2c_client *client_tuner; int mfe_shared = 0; /* bus not shared by default */ int ret; /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); + fe0 = vb2_dvb_get_frontend(&port->frontends, 1); if (!fe0) return -EINVAL; - /* init struct videobuf_dvb */ + /* init struct vb2_dvb */ fe0->dvb.name = dev->name; /* multi-frontend gate control is undefined or defaults to fe0 */ @@ -1392,7 +1477,7 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend); } /* MFE frontend 2 */ - fe1 = videobuf_dvb_get_frontend(&port->frontends, 2); + fe1 = vb2_dvb_get_frontend(&port->frontends, 2); if (fe1 == NULL) goto frontend_detach; /* DVB-C init */ @@ -1491,7 +1576,7 @@ static int dvb_register(struct cx23885_tsport *port) &hauppauge_hvr4400_si2165_config, &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) { - fe0->dvb.frontend->ops.i2c_gate_ctrl = 0; + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; if (!dvb_attach(tda18271_attach, fe0->dvb.frontend, 0x60, &i2c_bus2->i2c_adap, @@ -1501,6 +1586,97 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_DVBSKY_T9580: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + switch (port->nr) { + /* port b - satellite */ + case 1: + /* attach frontend */ + fe0->dvb.frontend = dvb_attach(m88ds3103_attach, + &dvbsky_t9580_m88ds3103_config, + &i2c_bus2->i2c_adap, &adapter); + if (fe0->dvb.frontend == NULL) + break; + + /* attach tuner */ + m88ts2022_config.fe = fe0->dvb.frontend; + m88ts2022_config.clock = 27000000; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &m88ts2022_config; + request_module(info.type); + client_tuner = i2c_new_device(adapter, &info); + if (client_tuner == NULL || + client_tuner->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + goto frontend_detach; + } + + /* delegate signal strength measurement to tuner */ + fe0->dvb.frontend->ops.read_signal_strength = + fe0->dvb.frontend->ops.tuner_ops.get_rf_strength; + + /* + * for setting the voltage we need to set GPIOs on + * the card. + */ + port->fe_set_voltage = + fe0->dvb.frontend->ops.set_voltage; + fe0->dvb.frontend->ops.set_voltage = + dvbsky_t9580_set_voltage; + + port->i2c_client_tuner = client_tuner; + + break; + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + if (client_demod == NULL || + client_demod->dev.driver == NULL) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + si2157_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client_tuner = i2c_new_device(adapter, &info); + if (client_tuner == NULL || + client_tuner->dev.driver == NULL) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -1532,7 +1708,7 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ - ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, + ret = vb2_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, mfe_shared); if (ret) goto frontend_detach; @@ -1575,20 +1751,36 @@ static int dvb_register(struct cx23885_tsport *port) memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); break; } + case CX23885_BOARD_DVBSKY_T9580: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr > 2) + break; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + printk(KERN_INFO "DVBSky T9580 port %d MAC address: %pM\n", + port->nr, eeprom + 0xc0 + (port->nr-1) * 8); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + + (port->nr-1) * 8, 6); + break; + } } return ret; frontend_detach: port->gate_ctrl = NULL; - videobuf_dvb_dealloc_frontends(&port->frontends); + vb2_dvb_dealloc_frontends(&port->frontends); return -EINVAL; } int cx23885_dvb_register(struct cx23885_tsport *port) { - struct videobuf_dvb_frontend *fe0; + struct vb2_dvb_frontend *fe0; struct cx23885_dev *dev = port->dev; int err, i; @@ -1605,13 +1797,15 @@ int cx23885_dvb_register(struct cx23885_tsport *port) port->num_frontends); for (i = 1; i <= port->num_frontends; i++) { - if (videobuf_dvb_alloc_frontend( + struct vb2_queue *q; + + if (vb2_dvb_alloc_frontend( &port->frontends, i) == NULL) { printk(KERN_ERR "%s() failed to alloc\n", __func__); return -ENOMEM; } - fe0 = videobuf_dvb_get_frontend(&port->frontends, i); + fe0 = vb2_dvb_get_frontend(&port->frontends, i); if (!fe0) err = -EINVAL; @@ -1627,10 +1821,21 @@ int cx23885_dvb_register(struct cx23885_tsport *port) /* dvb stuff */ /* We have to init the queue for each frontend on a port. */ printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name); - videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops, - &dev->pci->dev, &port->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, - sizeof(struct cx23885_buffer), port, NULL); + q = &fe0->dvb.dvbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = port; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &dvb_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + return err; } err = dvb_register(port); if (err != 0) @@ -1642,18 +1847,27 @@ int cx23885_dvb_register(struct cx23885_tsport *port) int cx23885_dvb_unregister(struct cx23885_tsport *port) { - struct videobuf_dvb_frontend *fe0; - - /* FIXME: in an error condition where the we have - * an expected number of frontends (attach problem) - * then this might not clean up correctly, if 1 - * is invalid. - * This comment only applies to future boards IF they - * implement MFE support. - */ - fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); + struct vb2_dvb_frontend *fe0; + struct i2c_client *client; + + /* remove I2C client for tuner */ + client = port->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C client for demodulator */ + client = port->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + fe0 = vb2_dvb_get_frontend(&port->frontends, 1); + if (fe0 && fe0->dvb.frontend) - videobuf_dvb_unregister_bus(&port->frontends); + vb2_dvb_unregister_bus(&port->frontends); switch (port->dev->board) { case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: @@ -1668,4 +1882,3 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) return 0; } - diff --git a/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c index 5444cc526008..6f817d8732da 100644 --- a/drivers/media/pci/cx23885/cx23885-f300.c +++ b/drivers/media/pci/cx23885/cx23885-f300.c @@ -22,10 +22,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c index 4887314339cb..fd71306af6e2 100644 --- a/drivers/media/pci/cx23885/cx23885-i2c.c +++ b/drivers/media/pci/cx23885/cx23885-i2c.c @@ -13,10 +13,6 @@ * 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> @@ -386,11 +382,3 @@ void cx23885_av_clk(struct cx23885_dev *dev, int enable) i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); } - -/* ----------------------------------------------------------------------- */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 1940c18e186c..9d37fe661691 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -28,11 +28,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include <linux/slab.h> diff --git a/drivers/media/pci/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h index 87dc44e69977..6199c7e86e83 100644 --- a/drivers/media/pci/cx23885/cx23885-input.h +++ b/drivers/media/pci/cx23885/cx23885-input.h @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _CX23885_INPUT_H_ diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c index 271d69d1ca8c..d2cdd40f79f5 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.c +++ b/drivers/media/pci/cx23885/cx23885-ioctl.c @@ -15,10 +15,6 @@ * 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 "cx23885.h" @@ -28,7 +24,7 @@ int cx23885_g_chip_info(struct file *file, void *fh, struct v4l2_dbg_chip_info *chip) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (chip->match.addr > 1) return -EINVAL; @@ -64,7 +60,7 @@ static int cx23417_g_register(struct cx23885_dev *dev, int cx23885_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (reg->match.addr > 1) return -EINVAL; @@ -96,7 +92,7 @@ static int cx23417_s_register(struct cx23885_dev *dev, int cx23885_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) { - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (reg->match.addr > 1) return -EINVAL; diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h index 92d9f0774366..cc5dbb6c1afc 100644 --- a/drivers/media/pci/cx23885/cx23885-ioctl.h +++ b/drivers/media/pci/cx23885/cx23885-ioctl.h @@ -15,10 +15,6 @@ * 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 _CX23885_IOCTL_H_ diff --git a/drivers/media/pci/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c index bfef19359291..89dc4cc3e1ce 100644 --- a/drivers/media/pci/cx23885/cx23885-ir.c +++ b/drivers/media/pci/cx23885/cx23885-ir.c @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include <media/v4l2-device.h> diff --git a/drivers/media/pci/cx23885/cx23885-ir.h b/drivers/media/pci/cx23885/cx23885-ir.h index 0c9d8bda9e28..8e93d1f10ae0 100644 --- a/drivers/media/pci/cx23885/cx23885-ir.h +++ b/drivers/media/pci/cx23885/cx23885-ir.h @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _CX23885_IR_H_ diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h index a99936e0cbc2..2d3cbafe2402 100644 --- a/drivers/media/pci/cx23885/cx23885-reg.h +++ b/drivers/media/pci/cx23885/cx23885-reg.h @@ -13,10 +13,6 @@ * 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 _CX23885_REG_H_ diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c index a1154f035bc1..a7c6ef8f3ea3 100644 --- a/drivers/media/pci/cx23885/cx23885-vbi.c +++ b/drivers/media/pci/cx23885/cx23885-vbi.c @@ -13,10 +13,6 @@ * 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> @@ -42,33 +38,32 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ #define VBI_LINE_LENGTH 1440 -#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 VBI_NTSC_LINE_COUNT 12 +#define VBI_PAL_LINE_COUNT 18 int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); + f->fmt.vbi.sampling_rate = 27000000; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 0; + f->fmt.vbi.flags = 0; if (dev->tvnorm & V4L2_STD_525_60) { /* ntsc */ - f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; - f->fmt.vbi.sampling_rate = 27000000; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 0; - f->fmt.vbi.flags = 0; - f->fmt.vbi.start[0] = 10; - f->fmt.vbi.count[0] = 17; - f->fmt.vbi.start[1] = 263 + 10 + 1; - f->fmt.vbi.count[1] = 17; + f->fmt.vbi.start[0] = V4L2_VBI_ITU_525_F1_START + 9; + f->fmt.vbi.start[1] = V4L2_VBI_ITU_525_F2_START + 9; + f->fmt.vbi.count[0] = VBI_NTSC_LINE_COUNT; + f->fmt.vbi.count[1] = VBI_NTSC_LINE_COUNT; } else if (dev->tvnorm & V4L2_STD_625_50) { /* pal */ - f->fmt.vbi.sampling_rate = 35468950; - f->fmt.vbi.start[0] = 7 - 1; - f->fmt.vbi.start[1] = 319 - 1; + f->fmt.vbi.start[0] = V4L2_VBI_ITU_625_F1_START + 5; + f->fmt.vbi.start[1] = V4L2_VBI_ITU_625_F2_START + 5; + f->fmt.vbi.count[0] = VBI_PAL_LINE_COUNT; + f->fmt.vbi.count[1] = VBI_PAL_LINE_COUNT; } return 0; @@ -94,15 +89,6 @@ int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status) handled++; } - if (status & VID_BC_MSK_VBI_RISCI2) { - dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__); - dprintk(2, "stopper vbi\n"); - spin_lock(&dev->slock); - cx23885_restart_vbi_queue(dev, &dev->vbiq); - spin_unlock(&dev->slock); - handled++; - } - return handled; } @@ -114,13 +100,13 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], - buf->vb.width, buf->risc.dma); + VBI_LINE_LENGTH, buf->risc.dma); /* reset counter */ cx_write(VID_A_GPCNT_CTL, 3); cx_write(VID_A_VBI_CTRL, 3); cx_write(VBI_A_GPCNT_CTL, 3); - q->count = 1; + q->count = 0; /* enable irq */ cx23885_irq_add_enable(dev, 0x01); @@ -133,163 +119,153 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, return 0; } +/* ------------------------------------------------------------------ */ -int cx23885_restart_vbi_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_buffer *buf; - struct list_head *item; - - if (list_empty(&q->active)) - return 0; - - buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_vbi_dma(dev, q, buf); - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx23885_buffer, vb.queue); - buf->count = q->count++; - } - mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); + struct cx23885_dev *dev = q->drv_priv; + unsigned lines = VBI_PAL_LINE_COUNT; + + if (dev->tvnorm & V4L2_STD_525_60) + lines = VBI_NTSC_LINE_COUNT; + *num_planes = 1; + sizes[0] = lines * VBI_LINE_LENGTH * 2; return 0; } -void cx23885_vbi_timeout(unsigned long data) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_dev *dev = (struct cx23885_dev *)data; - struct cx23885_dmaqueue *q = &dev->vbiq; - struct cx23885_buffer *buf; - unsigned long flags; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + unsigned lines = VBI_PAL_LINE_COUNT; + int ret; - /* Stop the VBI engine */ - cx_clear(VID_A_DMA_CTL, 0x22); + if (dev->tvnorm & V4L2_STD_525_60) + lines = VBI_NTSC_LINE_COUNT; - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name, - buf, buf->vb.i, (unsigned long)buf->risc.dma); - } - cx23885_restart_vbi_queue(dev, q); - spin_unlock_irqrestore(&dev->slock, flags); -} + if (vb2_plane_size(vb, 0) < lines * VBI_LINE_LENGTH * 2) + return -EINVAL; + vb2_set_plane_payload(vb, 0, lines * VBI_LINE_LENGTH * 2); -/* ------------------------------------------------------------------ */ -#define VBI_LINE_LENGTH 1440 -#define VBI_LINE_COUNT 17 + ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; + cx23885_risc_vbibuffer(dev->pci, &buf->risc, + sgt->sgl, + 0, VBI_LINE_LENGTH * lines, + VBI_LINE_LENGTH, 0, + lines); return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static void buffer_finish(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - unsigned int size; - int rc; - - size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < size) - return -EINVAL; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - buf->vb.width = VBI_LINE_LENGTH; - buf->vb.height = VBI_LINE_COUNT; - buf->vb.size = size; - buf->vb.field = V4L2_FIELD_SEQ_TB; - - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - cx23885_risc_vbibuffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->vb.width * buf->vb.height, - buf->vb.width, 0, - buf->vb.height); - } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); - fail: - cx23885_free_buffer(q, buf); - return rc; + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void buffer_queue(struct vb2_buffer *vb) { - struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); - struct cx23885_buffer *prev; - struct cx23885_fh *fh = vq->priv_data; - struct cx23885_dev *dev = fh->dev; - struct cx23885_dmaqueue *q = &dev->vbiq; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); + struct cx23885_buffer *prev; + struct cx23885_dmaqueue *q = &dev->vbiq; + unsigned long flags; + + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx23885_start_vbi_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->queue, &q->active); + spin_unlock_irqrestore(&dev->slock, flags); dprintk(2, "[%p/%d] vbi_queue - first active\n", - buf, buf->vb.i); + buf, buf->vb.v4l2_buf.index); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx23885_buffer, - vb.queue); - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; + queue); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->queue, &q->active); + spin_unlock_irqrestore(&dev->slock, flags); prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */ dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); + buf, buf->vb.v4l2_buf.index); } } -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) { - struct cx23885_buffer *buf = - container_of(vb, struct cx23885_buffer, vb); + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vbiq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); - cx23885_free_buffer(q, buf); + cx23885_start_vbi_dma(dev, dmaq, buf); + return 0; } -struct videobuf_queue_ops cx23885_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, -}; +static void cx23885_stop_streaming(struct vb2_queue *q) +{ + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vbiq; + unsigned long flags; -/* ------------------------------------------------------------------ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ + cx_clear(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */ + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dev->slock, flags); +} + + +struct vb2_ops cx23885_vbi_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 91e4cb457296..682a4f95df6b 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -13,10 +13,6 @@ * 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> @@ -35,6 +31,7 @@ #include "cx23885-video.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> #include "cx23885-ioctl.h" #include "tuner-xc2028.h" @@ -48,15 +45,12 @@ MODULE_LICENSE("GPL"); static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; 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(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); @@ -79,77 +73,14 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* static data */ #define FORMAT_FLAGS_PACKED 0x01 -#if 0 -static struct cx23885_fmt formats[] = { - { - .name = "8 bpp, gray", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "15 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_RGB555, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "15 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB555X, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "16 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_RGB565, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "16 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB565X, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "24 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_BGR24, - .depth = 24, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "32 bpp RGB, le", - .fourcc = V4L2_PIX_FMT_BGR32, - .depth = 32, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "32 bpp RGB, be", - .fourcc = V4L2_PIX_FMT_RGB32, - .depth = 32, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, -}; -#else static struct cx23885_fmt formats[] = { { -#if 0 - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16, - .flags = FORMAT_FLAGS_PACKED, - }, { -#endif .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .flags = FORMAT_FLAGS_PACKED, } }; -#endif static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) { @@ -158,163 +89,27 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) for (i = 0; i < ARRAY_SIZE(formats); i++) if (formats[i].fourcc == fourcc) return formats+i; - - printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__, - (fourcc & 0xff), - ((fourcc >> 8) & 0xff), - ((fourcc >> 16) & 0xff), - ((fourcc >> 24) & 0xff) - ); return NULL; } /* ------------------------------------------------------------------- */ -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static struct cx23885_ctrl cx23885_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 = 0x7f, - .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 = -127, - .maximum = 128, - .step = 1, - .default_value = 0x0, - .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 = 0x7f, - .step = 1, - .default_value = 0x3f, - .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 = 65535, - .step = 65535 / 100, - .default_value = 65535, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - .reg = PATH1_VOL_CTL, - .mask = 0xff, - .shift = 0, - } -}; -static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); - -/* Must be sorted from low to high control ID! */ -static const u32 cx23885_user_ctrls[] = { - V4L2_CID_USER_CLASS, - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_AUDIO_VOLUME, - V4L2_CID_AUDIO_MUTE, - 0 -}; - -static const u32 *ctrl_classes[] = { - cx23885_user_ctrls, - NULL -}; - void cx23885_video_wakeup(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) - break; - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - - /* count comes from the hw and is is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - - v4l2_get_timestamp(&buf->vb.ts); - dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, - count, buf->count); - buf->vb.state = VIDEOBUF_DONE; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } + if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - if (bc != 1) - printk(KERN_ERR "%s: %d buffers handled (should be 1)\n", - __func__, bc); + return; + buf = list_entry(q->active.next, + struct cx23885_buffer, queue); + + buf->vb.v4l2_buf.sequence = q->count++; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.v4l2_buf.index, + count, q->count); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) @@ -324,6 +119,12 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) (unsigned int)norm, v4l2_norm_to_name(norm)); + if (dev->tvnorm != norm) { + if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) || + vb2_is_busy(&dev->vb2_mpegq)) + return -EBUSY; + } + dev->tvnorm = norm; call_all(dev, video, s_std, norm); @@ -345,79 +146,13 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; + vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s (%s)", cx23885_boards[dev->board].name, type); video_set_drvdata(vfd, dev); return vfd; } -static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) -{ - int i; - - if (qctrl->id < V4L2_CID_BASE || - qctrl->id >= V4L2_CID_LASTP1) - return -EINVAL; - for (i = 0; i < CX23885_CTLS; i++) - if (cx23885_ctls[i].v.id == qctrl->id) - break; - if (i == CX23885_CTLS) { - *qctrl = no_ctl; - return 0; - } - *qctrl = cx23885_ctls[i].v; - return 0; -} - -/* ------------------------------------------------------------------- */ -/* resource management */ - -static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh, - unsigned int bit) -{ - dprintk(1, "%s()\n", __func__); - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - mutex_lock(&dev->lock); - if (dev->resources & bit) { - /* no, someone else uses it */ - mutex_unlock(&dev->lock); - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - dprintk(1, "res: get %d\n", bit); - mutex_unlock(&dev->lock); - return 1; -} - -static int res_check(struct cx23885_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct cx23885_dev *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, - unsigned int bits) -{ - BUG_ON((fh->resources & bits) != bits); - dprintk(1, "%s()\n", __func__); - - mutex_lock(&dev->lock); - fh->resources &= ~bits; - dev->resources &= ~bits; - dprintk(1, "res: put %d\n", bits); - mutex_unlock(&dev->lock); -} - int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) { /* 8 bit registers, 8 bit values */ @@ -567,7 +302,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, /* reset counter */ cx_write(VID_A_GPCNT_CTL, 3); - q->count = 1; + q->count = 0; /* enable irq */ cx23885_irq_add_enable(dev, 0x01); @@ -580,479 +315,206 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, return 0; } - -static int cx23885_restart_video_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q) -{ - struct cx23885_buffer *buf, *prev; - struct list_head *item; - dprintk(1, "%s()\n", __func__); - - if (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx23885_buffer, - vb.queue); - dprintk(2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - cx23885_start_video_dma(dev, q, buf); - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx23885_buffer, - vb.queue); - buf->count = q->count++; - } - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&q->queued)) - return 0; - buf = list_entry(q->queued.next, struct cx23885_buffer, - vb.queue); - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx23885_start_video_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] restart_queue - first active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ - dprintk(2, "[%p/%d] restart_queue - move to active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } -} - -static int buffer_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx23885_fh *fh = q->priv_data; + struct cx23885_dev *dev = q->drv_priv; - *size = fh->fmt->depth*fh->width*fh->height >> 3; - if (0 == *count) - *count = 32; - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; + *num_planes = 1; + sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3; return 0; } -static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb) { - struct cx23885_fh *fh = q->priv_data; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); - int rc, init_buffer = 0; u32 line0_offset, line1_offset; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); int field_tff; + int ret; - BUG_ON(NULL == fh->fmt); - if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) || - fh->height < 32 || fh->height > norm_maxh(dev->tvnorm)) - return -EINVAL; - buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + buf->bpl = (dev->width * dev->fmt->depth) >> 3; + + if (vb2_plane_size(vb, 0) < dev->height * buf->bpl) return -EINVAL; + vb2_set_plane_payload(vb, 0, dev->height * buf->bpl); - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - init_buffer = 1; - } + ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - init_buffer = 1; - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) - goto fail; - } + switch (dev->field) { + case V4L2_FIELD_TOP: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, UNSET, + buf->bpl, 0, dev->height); + break; + case V4L2_FIELD_BOTTOM: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, UNSET, 0, + buf->bpl, 0, dev->height); + break; + case V4L2_FIELD_INTERLACED: + if (dev->tvnorm & V4L2_STD_525_60) + /* NTSC or */ + field_tff = 1; + else + field_tff = 0; + + if (cx23885_boards[dev->board].force_bff) + /* PAL / SECAM OR 888 in NTSC MODE */ + field_tff = 0; - if (init_buffer) { - buf->bpl = buf->vb.width * buf->fmt->depth >> 3; - switch (buf->vb.field) { - case V4L2_FIELD_TOP: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, 0, UNSET, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_BOTTOM: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, UNSET, 0, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_INTERLACED: - if (dev->tvnorm & V4L2_STD_NTSC) - /* NTSC or */ - field_tff = 1; - else - field_tff = 0; - - if (cx23885_boards[dev->board].force_bff) - /* PAL / SECAM OR 888 in NTSC MODE */ - field_tff = 0; - - if (field_tff) { - /* cx25840 transmits NTSC bottom field first */ - dprintk(1, "%s() Creating TFF/NTSC risc\n", + if (field_tff) { + /* cx25840 transmits NTSC bottom field first */ + dprintk(1, "%s() Creating TFF/NTSC risc\n", __func__); - line0_offset = buf->bpl; - line1_offset = 0; - } else { - /* All other formats are top field first */ - dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n", + line0_offset = buf->bpl; + line1_offset = 0; + } else { + /* All other formats are top field first */ + dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n", __func__); - line0_offset = 0; - line1_offset = buf->bpl; - } - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, line0_offset, - line1_offset, - buf->bpl, buf->bpl, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_TB: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->bpl * (buf->vb.height >> 1), - buf->bpl, 0, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_BT: - cx23885_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - buf->bpl * (buf->vb.height >> 1), 0, - buf->bpl, 0, - buf->vb.height >> 1); - break; - default: - BUG(); + line0_offset = 0; + line1_offset = buf->bpl; } + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, line0_offset, + line1_offset, + buf->bpl, buf->bpl, + dev->height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + 0, buf->bpl * (dev->height >> 1), + buf->bpl, 0, + dev->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + cx23885_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + buf->bpl * (dev->height >> 1), 0, + buf->bpl, 0, + dev->height >> 1); + break; + default: + BUG(); } - dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.i, - fh->width, fh->height, fh->fmt->depth, fh->fmt->name, + dprintk(2, "[%p/%d] buffer_init - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", + buf, buf->vb.v4l2_buf.index, + dev->width, dev->height, dev->fmt->depth, dev->fmt->name, (unsigned long)buf->risc.dma); - - buf->vb.state = VIDEOBUF_PREPARED; return 0; +} + +static void buffer_finish(struct vb2_buffer *vb) +{ + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; + struct cx23885_buffer *buf = container_of(vb, + struct cx23885_buffer, vb); + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + + cx23885_free_buffer(vb->vb2_queue->drv_priv, buf); - fail: - cx23885_free_buffer(q, buf); - return rc; + dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE); } -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* + * The risc program for each buffer works as follows: it starts with a simple + * 'JUMP to addr + 12', which is effectively a NOP. Then the code to DMA the + * buffer follows and at the end we have a JUMP back to the start + 12 (skipping + * the initial JUMP). + * + * This is the risc program of the first buffer to be queued if the active list + * is empty and it just keeps DMAing this buffer without generating any + * interrupts. + * + * If a new buffer is added then the initial JUMP in the code for that buffer + * will generate an interrupt which signals that the previous buffer has been + * DMAed successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void buffer_queue(struct vb2_buffer *vb) { + struct cx23885_dev *dev = vb->vb2_queue->drv_priv; struct cx23885_buffer *buf = container_of(vb, struct cx23885_buffer, vb); struct cx23885_buffer *prev; - struct cx23885_fh *fh = vq->priv_data; - struct cx23885_dev *dev = fh->dev; struct cx23885_dmaqueue *q = &dev->vidq; + unsigned long flags; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + /* add jump to start */ + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", - buf, buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx23885_start_video_dma(dev, q, buf); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&q->active)) { + list_add_tail(&buf->queue, &q->active); dprintk(2, "[%p/%d] buffer_queue - first active\n", - buf, buf->vb.i); - + buf, buf->vb.v4l2_buf.index); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx23885_buffer, - vb.queue); - if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", - buf, buf->vb.i); - } - } -} - -static void buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) -{ - struct cx23885_buffer *buf = container_of(vb, - struct cx23885_buffer, vb); - - cx23885_free_buffer(q, buf); -} - -static struct videobuf_queue_ops cx23885_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -static struct videobuf_queue *get_queue(struct cx23885_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return &fh->vidq; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return &fh->vbiq; - default: - BUG(); - return NULL; - } -} - -static int get_resource(struct cx23885_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return RESOURCE_VBI; - default: - BUG(); - return 0; + queue); + list_add_tail(&buf->queue, &q->active); + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.v4l2_buf.index); } + spin_unlock_irqrestore(&dev->slock, flags); } -static int video_open(struct file *file) +static int cx23885_start_streaming(struct vb2_queue *q, unsigned int count) { - struct video_device *vdev = video_devdata(file); - struct cx23885_dev *dev = video_drvdata(file); - struct cx23885_fh *fh; - enum v4l2_buf_type type = 0; - int radio = 0; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - case VFL_TYPE_RADIO: - radio = 1; - break; - } - - dprintk(1, "open dev=%s radio=%d type=%s\n", - video_device_node_name(vdev), radio, v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - file->private_data = fh; - fh->dev = dev; - fh->radio = radio; - fh->type = type; - fh->width = 320; - fh->height = 240; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); - - videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx23885_buffer), - fh, NULL); - - videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct cx23885_buffer), - fh, NULL); - - - dprintk(1, "post videobuf_queue_init()\n"); + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vidq; + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); + cx23885_start_video_dma(dev, dmaq, buf); return 0; } -static ssize_t video_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +static void cx23885_stop_streaming(struct vb2_queue *q) { - struct cx23885_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (res_locked(fh->dev, RESOURCE_VIDEO)) - return -EBUSY; - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (!res_get(fh->dev, fh, RESOURCE_VBI)) - return -EBUSY; - return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, - file->f_flags & O_NONBLOCK); - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_buffer *buf; - unsigned int rc = POLLERR; - - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { - if (!res_get(fh->dev, fh, RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(file, &fh->vbiq, wait); - } - - mutex_lock(&fh->vidq.vb_lock); - if (res_check(fh, RESOURCE_VIDEO)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - goto done; - buf = list_entry(fh->vidq.stream.next, - struct cx23885_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx23885_buffer *)fh->vidq.read_buf; - if (NULL == buf) - goto done; - } - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - rc = POLLIN|POLLRDNORM; - else - rc = 0; -done: - mutex_unlock(&fh->vidq.vb_lock); - return rc; -} - -static int video_release(struct file *file) -{ - struct cx23885_fh *fh = file->private_data; - struct cx23885_dev *dev = fh->dev; - - /* turn off overlay */ - if (res_check(fh, RESOURCE_OVERLAY)) { - /* FIXME */ - res_free(dev, fh, RESOURCE_OVERLAY); - } + struct cx23885_dev *dev = q->drv_priv; + struct cx23885_dmaqueue *dmaq = &dev->vidq; + unsigned long flags; - /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO)) { - videobuf_queue_cancel(&fh->vidq); - res_free(dev, fh, RESOURCE_VIDEO); - } - if (fh->vidq.read_buf) { - buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } + cx_clear(VID_A_DMA_CTL, 0x11); + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx23885_buffer *buf = list_entry(dmaq->active.next, + struct cx23885_buffer, queue); - /* stop vbi capture */ - if (res_check(fh, RESOURCE_VBI)) { - if (fh->vbiq.streaming) - videobuf_streamoff(&fh->vbiq); - if (fh->vbiq.reading) - videobuf_read_stop(&fh->vbiq); - res_free(dev, fh, RESOURCE_VBI); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - - videobuf_mmap_free(&fh->vidq); - videobuf_mmap_free(&fh->vbiq); - - file->private_data = NULL; - kfree(fh); - - /* We are not putting the tuner to sleep here on exit, because - * we want to use the mpeg encoder in another session to capture - * tuner video. Closing this will result in no video to the encoder. - */ - - return 0; -} - -static int video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx23885_fh *fh = file->private_data; - - return videobuf_mmap_mapper(get_queue(fh), vma); -} - -/* ------------------------------------------------------------------ */ -/* VIDEO CTRL IOCTLS */ - -int cx23885_get_control(struct cx23885_dev *dev, - struct v4l2_control *ctl) -{ - dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); - call_all(dev, core, g_ctrl, ctl); - return 0; -} - -int cx23885_set_control(struct cx23885_dev *dev, - struct v4l2_control *ctl) -{ - dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__); - call_all(dev, core, s_ctrl, ctl); - - return 0; + spin_unlock_irqrestore(&dev->slock, flags); } -static void init_controls(struct cx23885_dev *dev) -{ - struct v4l2_control ctrl; - int i; - - for (i = 0; i < CX23885_CTLS; i++) { - ctrl.id = cx23885_ctls[i].v.id; - ctrl.value = cx23885_ctls[i].v.default_value; - - cx23885_set_control(dev, &ctrl); - } -} +static struct vb2_ops cx23885_video_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx23885_start_streaming, + .stop_streaming = cx23885_stop_streaming, +}; /* ------------------------------------------------------------------ */ /* VIDEO IOCTLS */ @@ -1060,16 +522,17 @@ static void init_controls(struct cx23885_dev *dev) static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = video_drvdata(file); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; + (f->fmt.pix.width * dev->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1077,7 +540,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); struct cx23885_fmt *fmt; enum v4l2_field field; unsigned int maxw, maxh; @@ -1102,9 +565,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: break; default: - return -EINVAL; + field = V4L2_FIELD_INTERLACED; + break; } f->fmt.pix.field = field; @@ -1114,6 +580,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1121,8 +588,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; int err; @@ -1131,34 +597,44 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (0 != err) return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; + + if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) || + vb2_is_busy(&dev->vb2_mpegq)) + return -EBUSY; + + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, - fh->width, fh->height, fh->vidq.field); + dev->width, dev->height, dev->field); v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); call_all(dev, video, s_mbus_fmt, &mbus_fmt); v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + /* s_mbus_fmt overwrites f->fmt.pix.field, restore it */ + f->fmt.pix.field = dev->field; return 0; } static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); strcpy(cap->driver, "cx23885"); strlcpy(cap->card, cx23885_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_VBI_CAPTURE; + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_VBI) + cap->device_caps |= V4L2_CAP_VBI_CAPTURE; + else + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = cap->device_caps | V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1175,85 +651,9 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_reqbufs(get_queue(fh), p); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_querybuf(get_queue(fh), p); -} - -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_qbuf(get_queue(fh), p); -} - -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx23885_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, - file->f_flags & O_NONBLOCK); -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - dprintk(1, "%s()\n", __func__); - - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; - if (unlikely(i != fh->type)) - return -EINVAL; - - if (unlikely(!res_get(dev, fh, get_resource(fh)))) - return -EBUSY; - - /* Don't start VBI streaming unless vida streaming - * has already started. - */ - if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) && - ((cx_read(VID_A_DMA_CTL) & 0x11) == 0)) - return -EINVAL; - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; - int err, res; - dprintk(1, "%s()\n", __func__); - - if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = get_resource(fh); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - res_free(dev, fh, res); - return 0; -} - static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); *id = dev->tvnorm; @@ -1262,14 +662,10 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, tvnorms); - mutex_unlock(&dev->lock); - - return 0; + return cx23885_set_tvnorm(dev, tvnorms); } int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) @@ -1299,16 +695,16 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name, iname[INPUT(n)->type]); + i->std = CX23885_NORMS; if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) || (CX23885_VMUX_CABLE == INPUT(n)->type)) { i->type = V4L2_INPUT_TYPE_TUNER; - i->std = CX23885_NORMS; + i->audioset = 4; + } else { + /* Two selectable audio inputs for non-tv inputs */ + i->audioset = 3; } - /* Two selectable audio inputs for non-tv inputs */ - if (INPUT(n)->type != CX23885_VMUX_TELEVISION) - i->audioset = 0x3; - if (dev->input == n) { /* enum'd input matches our configured input. * Ask the video decoder to process the call @@ -1324,14 +720,14 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s()\n", __func__); return cx23885_enum_input(dev, i); } int cx23885_get_input(struct file *file, void *priv, unsigned int *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); *i = dev->input; dprintk(1, "%s() returns %d\n", __func__, *i); @@ -1345,7 +741,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) int cx23885_set_input(struct file *file, void *priv, unsigned int i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); dprintk(1, "%s(%d)\n", __func__, i); @@ -1357,13 +753,11 @@ int cx23885_set_input(struct file *file, void *priv, unsigned int i) if (INPUT(i)->type == 0) return -EINVAL; - mutex_lock(&dev->lock); cx23885_video_mux(dev, i); /* By default establish the default audio input for the card also */ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */ cx23885_audio_mux(dev, i); - mutex_unlock(&dev->lock); return 0; } @@ -1374,39 +768,32 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_log_status(struct file *file, void *priv) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); - printk(KERN_INFO - "%s/0: ============ START LOG STATUS ============\n", - dev->name); call_all(dev, core, log_status); - printk(KERN_INFO - "%s/0: ============= END LOG STATUS =============\n", - dev->name); return 0; } static int cx23885_query_audinput(struct file *file, void *priv, struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); static const char *iname[] = { [0] = "Baseband L/R 1", [1] = "Baseband L/R 2", + [2] = "TV", }; unsigned int n; dprintk(1, "%s()\n", __func__); n = i->index; - if (n >= 2) + if (n >= 3) return -EINVAL; memset(i, 0, sizeof(*i)); i->index = n; strcpy(i->name, iname[n]); - i->capability = V4L2_AUDCAP_STEREO; - i->mode = V4L2_AUDMODE_AVL; + i->capability = V4L2_AUDCAP_STEREO; return 0; } @@ -1420,9 +807,13 @@ static int vidioc_enum_audinput(struct file *file, void *priv, static int vidioc_g_audinput(struct file *file, void *priv, struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); - i->index = dev->audinput; + if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) || + (CX23885_VMUX_CABLE == INPUT(dev->input)->type)) + i->index = 2; + else + i->index = dev->audinput; dprintk(1, "%s(input=%d)\n", __func__, i->index); return cx23885_query_audinput(file, priv, i); @@ -1431,8 +822,13 @@ static int vidioc_g_audinput(struct file *file, void *priv, static int vidioc_s_audinput(struct file *file, void *priv, const struct v4l2_audio *i) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - if (i->index >= 2) + struct cx23885_dev *dev = video_drvdata(file); + + if ((CX23885_VMUX_TELEVISION == INPUT(dev->input)->type) || + (CX23885_VMUX_CABLE == INPUT(dev->input)->type)) { + return i->index != 2 ? -EINVAL : 0; + } + if (i->index > 1) return -EINVAL; dprintk(1, "%s(%d)\n", __func__, i->index); @@ -1445,35 +841,10 @@ static int vidioc_s_audinput(struct file *file, void *priv, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (unlikely(qctrl->id == 0)) - return -EINVAL; - return cx23885_ctrl_query(qctrl); -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_get_control(dev, ctl); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - - return cx23885_set_control(dev, ctl); -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1489,7 +860,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; @@ -1504,14 +875,12 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; - /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; call_all(dev, tuner, g_frequency, f); @@ -1521,20 +890,23 @@ static int vidioc_g_frequency(struct file *file, void *priv, static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency *f) { - struct v4l2_control ctrl; + struct v4l2_ctrl *mute; + int old_mute_val = 1; if (dev->tuner_type == TUNER_ABSENT) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL; - mutex_lock(&dev->lock); dev->freq = f->frequency; /* I need to mute audio here */ - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = 1; - cx23885_set_control(dev, &ctrl); + mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (mute) { + old_mute_val = v4l2_ctrl_g_ctrl(mute); + if (!old_mute_val) + v4l2_ctrl_s_ctrl(mute, 1); + } call_all(dev, tuner, s_frequency, f); @@ -1542,10 +914,8 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency msleep(100); /* I need to unmute audio here */ - ctrl.value = 0; - cx23885_set_control(dev, &ctrl); - - mutex_unlock(&dev->lock); + if (old_mute_val == 0) + v4l2_ctrl_s_ctrl(mute, old_mute_val); return 0; } @@ -1553,8 +923,9 @@ static int cx23885_set_freq(struct cx23885_dev *dev, const struct v4l2_frequency static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, const struct v4l2_frequency *f) { - struct v4l2_control ctrl; - struct videobuf_dvb_frontend *vfe; + struct v4l2_ctrl *mute; + int old_mute_val = 1; + struct vb2_dvb_frontend *vfe; struct dvb_frontend *fe; struct analog_parameters params = { @@ -1564,21 +935,22 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, .frequency = f->frequency }; - mutex_lock(&dev->lock); dev->freq = f->frequency; /* I need to mute audio here */ - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = 1; - cx23885_set_control(dev, &ctrl); + mute = v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (mute) { + old_mute_val = v4l2_ctrl_g_ctrl(mute); + if (!old_mute_val) + v4l2_ctrl_s_ctrl(mute, 1); + } /* If HVR1850 */ dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__, params.frequency, f->tuner, params.std); - vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1); + vfe = vb2_dvb_get_frontend(&dev->ts2.frontends, 1); if (!vfe) { - mutex_unlock(&dev->lock); return -EINVAL; } @@ -1600,10 +972,8 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, msleep(100); /* I need to unmute audio here */ - ctrl.value = 0; - cx23885_set_control(dev, &ctrl); - - mutex_unlock(&dev->lock); + if (old_mute_val == 0) + v4l2_ctrl_s_ctrl(mute, old_mute_val); return 0; } @@ -1611,8 +981,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, int cx23885_set_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct cx23885_fh *fh = priv; - struct cx23885_dev *dev = fh->dev; + struct cx23885_dev *dev = video_drvdata(file); int ret; switch (dev->board) { @@ -1636,28 +1005,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, /* ----------------------------------------------------------- */ -static void cx23885_vid_timeout(unsigned long data) -{ - struct cx23885_dev *dev = (struct cx23885_dev *)data; - struct cx23885_dmaqueue *q = &dev->vidq; - struct cx23885_buffer *buf; - unsigned long flags; - - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, - struct cx23885_buffer, vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n", - dev->name, buf, buf->vb.i, - (unsigned long)buf->risc.dma); - } - cx23885_restart_video_queue(dev, q); - spin_unlock_irqrestore(&dev->slock, flags); -} - int cx23885_video_irq(struct cx23885_dev *dev, u32 status) { u32 mask, count; @@ -1702,13 +1049,6 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) spin_unlock(&dev->slock); handled++; } - if (status & VID_BC_MSK_RISCI2) { - dprintk(2, "stopper video\n"); - spin_lock(&dev->slock); - cx23885_restart_video_queue(dev, &dev->vidq); - spin_unlock(&dev->slock); - handled++; - } /* Allow the VBI framework to process it's payload */ handled += cx23885_vbi_irq(dev, status); @@ -1721,12 +1061,12 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = video_mmap, - .ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -1738,21 +1078,19 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt, .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt, .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .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_log_status = vidioc_log_status, - .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, @@ -1765,6 +1103,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enumaudio = vidioc_enum_audinput, .vidioc_g_audio = vidioc_g_audinput, .vidioc_s_audio = vidioc_s_audinput, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device cx23885_vbi_template; @@ -1775,14 +1115,6 @@ static struct video_device cx23885_video_template = { .tvnorms = CX23885_NORMS, }; -static const struct v4l2_file_operations radio_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .ioctl = video_ioctl2, -}; - - void cx23885_video_unregister(struct cx23885_dev *dev) { dprintk(1, "%s()\n", __func__); @@ -1794,7 +1126,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev) else video_device_release(dev->vbi_dev); dev->vbi_dev = NULL; - btcx_riscmem_free(dev->pci, &dev->vbiq.stopper); } if (dev->video_dev) { if (video_is_registered(dev->video_dev)) @@ -1802,8 +1133,6 @@ void cx23885_video_unregister(struct cx23885_dev *dev) else video_device_release(dev->video_dev); dev->video_dev = NULL; - - btcx_riscmem_free(dev->pci, &dev->vidq.stopper); } if (dev->audio_dev) @@ -1812,6 +1141,7 @@ void cx23885_video_unregister(struct cx23885_dev *dev) int cx23885_video_register(struct cx23885_dev *dev) { + struct vb2_queue *q; int err; dprintk(1, "%s()\n", __func__); @@ -1822,24 +1152,16 @@ int cx23885_video_register(struct cx23885_dev *dev) strcpy(cx23885_vbi_template.name, "cx23885-vbi"); dev->tvnorm = V4L2_STD_NTSC_M; + dev->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); + dev->field = V4L2_FIELD_INTERLACED; + dev->width = 720; + dev->height = norm_maxh(dev->tvnorm); /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); - dev->vidq.timeout.function = cx23885_vid_timeout; - dev->vidq.timeout.data = (unsigned long)dev; - init_timer(&dev->vidq.timeout); - cx23885_risc_stopper(dev->pci, &dev->vidq.stopper, - VID_A_DMA_CTL, 0x11, 0x00); /* init vbi dma queues */ INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); - dev->vbiq.timeout.function = cx23885_vbi_timeout; - dev->vbiq.timeout.data = (unsigned long)dev; - init_timer(&dev->vbiq.timeout); - cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper, - VID_A_DMA_CTL, 0x22, 0x00); cx23885_irq_add_enable(dev, 0x01); @@ -1893,9 +1215,49 @@ int cx23885_video_register(struct cx23885_dev *dev) } } + /* initial device configuration */ + mutex_lock(&dev->lock); + cx23885_set_tvnorm(dev, dev->tvnorm); + cx23885_video_mux(dev, 0); + cx23885_audio_mux(dev, 0); + mutex_unlock(&dev->lock); + + q = &dev->vb2_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_video_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + + q = &dev->vb2_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct cx23885_buffer); + q->ops = &cx23885_vbi_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + /* register Video device */ dev->video_dev = cx23885_vdev_init(dev, dev->pci, &cx23885_video_template, "video"); + dev->video_dev->queue = &dev->vb2_vidq; err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, video_nr[dev->nr]); if (err < 0) { @@ -1909,6 +1271,7 @@ int cx23885_video_register(struct cx23885_dev *dev) /* register VBI device */ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci, &cx23885_vbi_template, "vbi"); + dev->vbi_dev->queue = &dev->vb2_vbiq; err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->nr]); if (err < 0) { @@ -1922,18 +1285,9 @@ int cx23885_video_register(struct cx23885_dev *dev) /* Register ALSA audio device */ dev->audio_dev = cx23885_audio_register(dev); - /* initial device configuration */ - mutex_lock(&dev->lock); - cx23885_set_tvnorm(dev, dev->tvnorm); - init_controls(dev); - cx23885_video_mux(dev, 0); - cx23885_audio_mux(dev, 0); - mutex_unlock(&dev->lock); - return 0; fail_unreg: cx23885_video_unregister(dev); return err; } - diff --git a/drivers/media/pci/cx23885/cx23885-video.h b/drivers/media/pci/cx23885/cx23885-video.h index c961a2b0de0f..291e8f3189f0 100644 --- a/drivers/media/pci/cx23885/cx23885-video.h +++ b/drivers/media/pci/cx23885/cx23885-video.h @@ -12,11 +12,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _CX23885_VIDEO_H_ diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 0e086c03da67..6c35e6115969 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -13,10 +13,6 @@ * 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/pci.h> @@ -25,19 +21,20 @@ #include <linux/slab.h> #include <media/v4l2-device.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ctrls.h> #include <media/tuner.h> #include <media/tveeprom.h> -#include <media/videobuf-dma-sg.h> -#include <media/videobuf-dvb.h> +#include <media/videobuf2-dma-sg.h> +#include <media/videobuf2-dvb.h> #include <media/rc-core.h> -#include "btcx-risc.h" #include "cx23885-reg.h" #include "media/cx2341x.h" #include <linux/mutex.h> -#define CX23885_VERSION "0.0.3" +#define CX23885_VERSION "0.0.4" #define UNSET (-1U) @@ -46,9 +43,6 @@ /* Max number of inputs by card */ #define MAX_CX23885_INPUT 8 #define INPUT(nr) (&cx23885_boards[dev->board].input[nr]) -#define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 -#define RESOURCE_VBI 4 #define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ @@ -98,6 +92,7 @@ #define CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200 42 #define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE 43 #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2 44 +#define CX23885_BOARD_DVBSKY_T9580 45 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -131,14 +126,6 @@ struct cx23885_fmt { u32 cxformat; }; -struct cx23885_ctrl { - struct v4l2_queryctrl v; - u32 off; - u32 reg; - u32 mask; - u32 shift; -}; - struct cx23885_tvnorm { char *name; v4l2_std_id id; @@ -146,30 +133,6 @@ struct cx23885_tvnorm { u32 cxoformat; }; -struct cx23885_fh { - struct cx23885_dev *dev; - enum v4l2_buf_type type; - int radio; - u32 resources; - - /* video overlay */ - struct v4l2_window win; - struct v4l2_clip *clips; - unsigned int nclips; - - /* video capture */ - struct cx23885_fmt *fmt; - unsigned int width, height; - - /* vbi capture */ - struct videobuf_queue vidq; - struct videobuf_queue vbiq; - - /* MPEG Encoder specifics ONLY */ - struct videobuf_queue mpegq; - atomic_t v4l_reading; -}; - enum cx23885_itype { CX23885_VMUX_COMPOSITE1 = 1, CX23885_VMUX_COMPOSITE2, @@ -189,14 +152,22 @@ enum cx23885_src_sel_type { CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO }; +struct cx23885_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + /* buffer for one video frame */ struct cx23885_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head queue; /* cx23885 specific */ unsigned int bpl; - struct btcx_riscmem risc; + struct cx23885_riscmem risc; struct cx23885_fmt *fmt; u32 count; }; @@ -268,9 +239,6 @@ struct cx23885_i2c { struct cx23885_dmaqueue { struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; u32 count; }; @@ -280,7 +248,7 @@ struct cx23885_tsport { int nr; int sram_chno; - struct videobuf_dvb_frontends frontends; + struct vb2_dvb_frontends frontends; /* dma queues */ struct cx23885_dmaqueue mpegq; @@ -326,7 +294,12 @@ struct cx23885_tsport { /* Workaround for a temp dvb_frontend that the tuner can attached to */ struct dvb_frontend analog_fe; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + int (*set_frontend)(struct dvb_frontend *fe); + int (*fe_set_voltage)(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); }; struct cx23885_kernel_ir { @@ -339,8 +312,11 @@ struct cx23885_kernel_ir { struct cx23885_audio_buffer { unsigned int bpl; - struct btcx_riscmem risc; - struct videobuf_dmabuf dma; + struct cx23885_riscmem risc; + void *vaddr; + struct scatterlist *sglist; + int sglen; + int nr_pages; }; struct cx23885_audio_dev { @@ -358,8 +334,6 @@ struct cx23885_audio_dev { unsigned int period_size; unsigned int num_periods; - struct videobuf_dmabuf *dma_risc; - struct cx23885_audio_buffer *buf; struct snd_pcm_substream *substream; @@ -368,6 +342,7 @@ struct cx23885_audio_dev { struct cx23885_dev { atomic_t refcount; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; /* pci stuff */ struct pci_dev *pci; @@ -407,7 +382,6 @@ struct cx23885_dev { } bridge; /* Analog video */ - u32 resources; unsigned int input; unsigned int audinput; /* Selectable audio input */ u32 tvaudio; @@ -417,7 +391,6 @@ struct cx23885_dev { unsigned int tuner_bus; unsigned int radio_type; unsigned char radio_addr; - unsigned int has_radio; struct v4l2_subdev *sd_cx25840; struct work_struct cx25840_work; @@ -435,17 +408,24 @@ struct cx23885_dev { u32 freq; struct video_device *video_dev; struct video_device *vbi_dev; - struct video_device *radio_dev; + + /* video capture */ + struct cx23885_fmt *fmt; + unsigned int width, height; + unsigned field; struct cx23885_dmaqueue vidq; + struct vb2_queue vb2_vidq; struct cx23885_dmaqueue vbiq; + struct vb2_queue vb2_vbiq; + spinlock_t slock; /* MPEG Encoder ONLY settings */ u32 cx23417_mailbox; - struct cx2341x_mpeg_params mpeg_params; + struct cx2341x_handler cxhdl; struct video_device *v4l_device; - atomic_t v4l_reader_count; + struct vb2_queue vb2_mpegq; struct cx23885_tvnorm encodernorm; /* Analog raw audio */ @@ -521,26 +501,21 @@ extern int cx23885_sram_channel_setup(struct cx23885_dev *dev, extern void cx23885_sram_channel_dump(struct cx23885_dev *dev, struct sram_channel *ch); -extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value); - -extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +extern int cx23885_risc_buffer(struct pci_dev *pci, struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); extern int cx23885_risc_vbibuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, struct scatterlist *sglist, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); +int cx23885_start_dma(struct cx23885_tsport *port, + struct cx23885_dmaqueue *q, + struct cx23885_buffer *buf); void cx23885_cancel_buffers(struct cx23885_tsport *port); -extern int cx23885_restart_queue(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q); - -extern void cx23885_wakeup(struct cx23885_tsport *port, - struct cx23885_dmaqueue *q, u32 count); extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); @@ -574,13 +549,11 @@ extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); extern int cx23885_dvb_register(struct cx23885_tsport *port); extern int cx23885_dvb_unregister(struct cx23885_tsport *port); -extern int cx23885_buf_prepare(struct videobuf_queue *q, - struct cx23885_tsport *port, - struct cx23885_buffer *buf, - enum v4l2_field field); +extern int cx23885_buf_prepare(struct cx23885_buffer *buf, + struct cx23885_tsport *port); extern void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf); -extern void cx23885_free_buffer(struct videobuf_queue *q, +extern void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf); /* ----------------------------------------------------------- */ @@ -595,8 +568,6 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i); int cx23885_set_input(struct file *file, void *priv, unsigned int i); int cx23885_get_input(struct file *file, void *priv, unsigned int *i); int cx23885_set_frequency(struct file *file, void *priv, const struct v4l2_frequency *f); -int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl); -int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl); int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm); /* ----------------------------------------------------------- */ @@ -604,9 +575,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm); extern int cx23885_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f); extern void cx23885_vbi_timeout(unsigned long data); -extern struct videobuf_queue_ops cx23885_vbi_qops; -extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev, - struct cx23885_dmaqueue *q); +extern struct vb2_ops cx23885_vbi_qops; extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status); /* cx23885-i2c.c */ @@ -638,7 +607,7 @@ extern struct cx23885_audio_dev *cx23885_audio_register( extern void cx23885_audio_unregister(struct cx23885_dev *dev); extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask); extern int cx23885_risc_databuffer(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx23885_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, @@ -649,15 +618,10 @@ extern int cx23885_risc_databuffer(struct pci_dev *pci, static inline unsigned int norm_maxw(v4l2_std_id norm) { - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; + return (norm & V4L2_STD_525_60) ? 720 : 768; } static inline unsigned int norm_maxh(v4l2_std_id norm) { - return (norm & V4L2_STD_625_50) ? 576 : 480; -} - -static inline unsigned int norm_swidth(v4l2_std_id norm) -{ - return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; + return (norm & V4L2_STD_525_60) ? 480 : 576; } diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index c2ff5fc01157..c1aa888af705 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include <linux/kfifo.h> diff --git a/drivers/media/pci/cx23885/cx23888-ir.h b/drivers/media/pci/cx23885/cx23888-ir.h index d2de41caaf1d..ff74a93575d6 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.h +++ b/drivers/media/pci/cx23885/cx23888-ir.h @@ -14,11 +14,6 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _CX23888_IR_H_ diff --git a/drivers/media/pci/cx23885/netup-eeprom.c b/drivers/media/pci/cx23885/netup-eeprom.c index 98a48f500684..b6542ee4385b 100644 --- a/drivers/media/pci/cx23885/netup-eeprom.c +++ b/drivers/media/pci/cx23885/netup-eeprom.c @@ -17,10 +17,6 @@ * 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. */ # diff --git a/drivers/media/pci/cx23885/netup-eeprom.h b/drivers/media/pci/cx23885/netup-eeprom.h index 13926e18feba..90cac5b655d5 100644 --- a/drivers/media/pci/cx23885/netup-eeprom.h +++ b/drivers/media/pci/cx23885/netup-eeprom.h @@ -16,10 +16,6 @@ * 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 NETUP_EEPROM_H diff --git a/drivers/media/pci/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c index 0044fef7ca24..76d9487aafc8 100644 --- a/drivers/media/pci/cx23885/netup-init.c +++ b/drivers/media/pci/cx23885/netup-init.c @@ -17,10 +17,6 @@ * 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 "cx23885.h" diff --git a/drivers/media/pci/cx23885/netup-init.h b/drivers/media/pci/cx23885/netup-init.h index d26ae4b1590e..daaa212adfba 100644 --- a/drivers/media/pci/cx23885/netup-init.h +++ b/drivers/media/pci/cx23885/netup-init.h @@ -17,9 +17,5 @@ * 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. */ extern void netup_initialize(struct cx23885_dev *dev); diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c index 1f43be0b04c8..a664997e1958 100644 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.c +++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c @@ -330,8 +330,9 @@ int cx25821_write_frame(struct cx25821_channel *chan, if (frame_size - curpos < count) count = frame_size - curpos; - memcpy((char *)out->_data_buf_virt_addr + frame_offset + curpos, - data, count); + if (copy_from_user((__force char *)out->_data_buf_virt_addr + frame_offset + curpos, + data, count)) + return -EFAULT; curpos += count; if (curpos == frame_size) { out->_frame_count++; diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index e18a7ace08b1..851754bf1291 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -78,19 +78,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE2, .vmux = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE3, .vmux = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE4, .vmux = 3, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE] = { .name = "Hauppauge WinTV 34xxx models", @@ -99,23 +99,23 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xff00, // internal decoder - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0xff01, // mono from tuner chip - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xff02, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xff02, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xff01, @@ -127,13 +127,13 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, }, [CX88_BOARD_PIXELVIEW] = { .name = "PixelView", @@ -141,17 +141,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xff00, // internal decoder - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xff10, @@ -164,19 +164,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x03ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x03fe, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x03fe, - }}, + } }, }, [CX88_BOARD_WINFAST2000XP_EXPERT] = { .name = "Leadtek Winfast 2000XP Expert", @@ -185,28 +185,28 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00F5e700, .gpio1 = 0x00003004, .gpio2 = 0x00F5e700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00F5c700, .gpio1 = 0x00003004, .gpio2 = 0x00F5c700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00F5c700, .gpio1 = 0x00003004, .gpio2 = 0x00F5c700, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00F5d700, @@ -222,19 +222,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio1 = 0xe09f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio1 = 0xe05f, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio1 = 0xe05f, - }}, + } }, .radio = { .gpio1 = 0xe0df, .type = CX88_RADIO, @@ -249,25 +249,25 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000040bf, .gpio1 = 0x000080c0, .gpio2 = 0x0000ff40, - }}, + } }, .radio = { .type = CX88_RADIO, .vmux = 3, @@ -283,14 +283,14 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0035e700, .gpio1 = 0x00003004, .gpio2 = 0x0035e700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -298,14 +298,14 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x00003004, .gpio2 = 0x0035c700, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0035c700, .gpio1 = 0x0035c700, .gpio2 = 0x02000000, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0035d700, @@ -322,22 +322,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000bde2, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000bde6, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000bde6, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000bd62, @@ -351,16 +351,16 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE2, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, }, [CX88_BOARD_PROLINK_PLAYTVPVR] = { .name = "Prolink PlayTV PVR", @@ -369,19 +369,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xbff0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xbff3, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xbff3, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xbff0, @@ -394,16 +394,16 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000fde6, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000fde2, @@ -417,22 +417,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc08, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc68, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000fbf, .gpio2 = 0x0000fc68, - }}, + } }, }, [CX88_BOARD_KWORLD_DVB_T] = { .name = "KWorld/VStream XPert DVB-T", @@ -440,17 +440,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { @@ -459,15 +459,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000027df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000027df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_LTV883] = { @@ -476,23 +476,23 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x07f8, - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0x07f9, // mono from tuner chip - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000007fa, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000007fa, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x000007f8, @@ -521,23 +521,23 @@ static const struct cx88_board cx88_boards[] = { 0 - normal RF 1 - high RF */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0f0d, - },{ + }, { .type = CX88_VMUX_CABLE, .vmux = 0, .gpio0 = 0x0f05, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0f00, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0f00, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_DVB_T1] = { @@ -546,10 +546,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_CONEXANT_DVB_T1] = { @@ -558,10 +558,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PROVIDEO_PV259] = { @@ -570,11 +570,11 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .audioroute = 1, - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { @@ -583,15 +583,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000027df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000027df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DNTV_LIVE_DVB_T] = { @@ -600,17 +600,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000700, .gpio2 = 0x00000101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000700, .gpio2 = 0x00000101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PCHDTV_HD3000] = { @@ -632,19 +632,19 @@ static const struct cx88_board cx88_boards[] = { * * GPIO[16] = Remote control input */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00008484, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00008400, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00008400, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00008404, @@ -659,25 +659,25 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xed1a, .gpio2 = 0x00ff, - },{ + }, { .type = CX88_VMUX_DEBUG, .vmux = 0, .gpio0 = 0xff01, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xff02, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xed92, .gpio2 = 0x00ff, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xed96, @@ -692,22 +692,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00009d80, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00009d76, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00009d76, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00009d00, @@ -722,19 +722,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 1, .gpio1 = 0x0000e03f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 2, .gpio1 = 0x0000e07f, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 3, .gpio1 = 0x0000e07f, - }} + } } }, [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = { .name = "PixelView PlayTV Ultra Pro (Stereo)", @@ -745,19 +745,19 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, /* Some variants use a tda9874 and so need the tvaudio module. */ .audio_chip = CX88_AUDIO_TVAUDIO, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xbf61, /* internal decoder */ - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xbf63, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xbf63, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xbf60, @@ -770,19 +770,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x97ed, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x97e9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x97e9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_ADSTECH_DVB_T_PCI] = { @@ -791,32 +791,32 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = { .name = "TerraTec Cinergy 1400 DVB-T", .tuner_type = TUNER_ABSENT, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = { @@ -826,19 +826,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x87fd, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x87f9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x87f9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = { @@ -848,22 +848,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, .gpio0 = 0x0000cd73, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 1, .gpio0 = 0x0000cd73, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_TELEVISION, .vmux = 3, .gpio0 = 0x0000cdb3, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .vmux = 2, @@ -876,21 +876,21 @@ static const struct cx88_board cx88_boards[] = { /* Alexander Wold <awold@bigfoot.com> */ .name = "Kworld V-Stream Xpert DVD", .tuner_type = UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x03000000, .gpio1 = 0x01000000, .gpio2 = 0x02000000, .gpio3 = 0x00100000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x03000000, .gpio1 = 0x01000000, .gpio2 = 0x02000000, .gpio3 = 0x00100000, - }}, + } }, }, [CX88_BOARD_ATI_HDTVWONDER] = { .name = "ATI HDTV Wonder", @@ -898,28 +898,28 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00000ff7, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00000ffe, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00000ffe, .gpio1 = 0x000000ff, .gpio2 = 0x00000001, .gpio3 = 0x00000000, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_WINFAST_DTV1000] = { @@ -928,16 +928,16 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_AVERTV_303] = { @@ -947,28 +947,28 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00ff, .gpio1 = 0xe09f, .gpio2 = 0x0010, .gpio3 = 0x0000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00ff, .gpio1 = 0xe05f, .gpio2 = 0x0010, .gpio3 = 0x0000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00ff, .gpio1 = 0xe05f, .gpio2 = 0x0010, .gpio3 = 0x0000, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = { .name = "Hauppauge Nova-S-Plus DVB-S", @@ -978,22 +978,22 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .audio_chip = CX88_AUDIO_WM8775, .i2sinputcntl = 2, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = { @@ -1002,10 +1002,10 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_DVBS_100] = { @@ -1015,22 +1015,22 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_chip = CX88_AUDIO_WM8775, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_HVR1100] = { @@ -1040,16 +1040,16 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, - }}, + } }, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB, }, @@ -1060,13 +1060,13 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - }}, + } }, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB, }, @@ -1078,19 +1078,19 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xf80808, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xf80808, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xf80808, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0xf80808, @@ -1106,17 +1106,17 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0700, .gpio2 = 0x0101, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0700, .gpio2 = 0x0101, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = { @@ -1125,15 +1125,15 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x000067df, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x000067df, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { @@ -1142,22 +1142,22 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x3de2, .gpio2 = 0x00ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x3de6, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x3de6, .audioroute = 1, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x3de6, @@ -1171,19 +1171,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000a75f, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000a75b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000a75b, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PCHDTV_HD5500] = { @@ -1193,19 +1193,19 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x87fd, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x87f9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x87f9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_MCE200_DELUXE] = { @@ -1217,11 +1217,11 @@ static const struct cx88_board cx88_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000BDE6 - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = { @@ -1233,11 +1233,11 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x5da6, - }}, + } }, .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = { @@ -1246,19 +1246,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0788, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x078b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x078b, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x074a, @@ -1271,7 +1271,7 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00017304, @@ -1299,7 +1299,7 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x0000b207, .gpio2 = 0x0001d701, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00015702, @@ -1316,35 +1316,35 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00017300, .gpio1 = 0x00008207, .gpio2 = 0x00000000, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00018300, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00018301, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00018301, .gpio1 = 0x0000f207, .gpio2 = 0x00017304, .gpio3 = 0x02000000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x00015702, @@ -1360,13 +1360,13 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_HAUPPAUGE_HVR3000] = { @@ -1377,25 +1377,25 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, .audio_chip = CX88_AUDIO_WM8775, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x84bf, /* 1: TV Audio / FM Mono */ .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x84bf, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x84bf, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x84bf, @@ -1411,19 +1411,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0709, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x070b, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x070b, - }}, + } }, }, [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", @@ -1431,28 +1431,28 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x003fffff, .gpio1 = 0x00e00000, .gpio2 = 0x003fffff, .gpio3 = 0x02000000, - }}, + } }, }, [CX88_BOARD_HAUPPAUGE_HVR1300] = { .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", @@ -1465,25 +1465,25 @@ static const struct cx88_board cx88_boards[] = { /* * gpio0 as reported by Mike Crash <mike AT mikecrash.com> */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xef88, /* 1: TV Audio / FM Mono */ .audioroute = 1, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xef88, /* 2: Line-In */ .audioroute = 2, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xef88, /* 2: Line-In */ .audioroute = 2, - }}, + } }, .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, .radio = { .type = CX88_RADIO, @@ -1510,19 +1510,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DEBUG, .vmux = 3, .gpio0 = 0x04ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x07fa, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x07fa, - }}, + } }, }, [CX88_BOARD_PINNACLE_PCTV_HD_800i] = { .name = "Pinnacle PCTV HD 800i", @@ -1530,24 +1530,24 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x04fb, .gpio1 = 0x10ff, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x04fb, .gpio1 = 0x10ef, .audioroute = 1, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x04fb, .gpio1 = 0x10ef, .audioroute = 1, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = { @@ -1557,7 +1557,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x000027df, /* Unconfirmed */ @@ -1815,19 +1815,19 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x10df, - },{ + }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x16d9, - },{ + }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x16d9, - }}, + } }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PROLINK_PV_8000GT] = { @@ -1967,7 +1967,7 @@ static const struct cx88_board cx88_boards[] = { * 3: Line-In Expansion * 4: FM Stereo */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xc4bf, @@ -2001,7 +2001,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2013,7 +2013,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2025,7 +2025,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2037,7 +2037,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2049,7 +2049,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2061,7 +2061,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2073,7 +2073,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, .gpio0 = 0x8080, @@ -2086,7 +2086,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2098,7 +2098,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2110,7 +2110,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2170,7 +2170,7 @@ static const struct cx88_board cx88_boards[] = { * 13: audio source (0=tuner audio,1=line in) * 14: FM (0=on,1=off ???) */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2211,7 +2211,7 @@ static const struct cx88_board cx88_boards[] = { * 13: audio source (0=tuner audio,1=line in) * 14: FM (0=on,1=off ???) */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2229,7 +2229,7 @@ static const struct cx88_board cx88_boards[] = { .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ .gpio2 = 0x0000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0400, /* pin 2 = 0 */ @@ -2252,7 +2252,7 @@ static const struct cx88_board cx88_boards[] = { * 14: 0: FM radio * 16: 0: RF input is cable */ - .input = {{ + .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0403, @@ -2280,7 +2280,7 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0xF0F7, .gpio2 = 0x0101, .gpio3 = 0x0000, - }}, + } }, .radio = { .type = CX88_RADIO, .gpio0 = 0x0403, @@ -2308,7 +2308,7 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .input = {{ + .input = { { .type = CX88_VMUX_DVB, .vmux = 0, } }, @@ -2324,19 +2324,19 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x3400, .card = CX88_BOARD_HAUPPAUGE, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x3401, .card = CX88_BOARD_HAUPPAUGE, - },{ + }, { .subvendor = 0x14c7, .subdevice = 0x0106, .card = CX88_BOARD_GDI, - },{ + }, { .subvendor = 0x14c7, .subdevice = 0x0107, /* with mpeg encoder */ .card = CX88_BOARD_GDI, - },{ + }, { .subvendor = PCI_VENDOR_ID_ATI, .subdevice = 0x00f8, .card = CX88_BOARD_ATI_WONDER_PRO, @@ -2348,176 +2348,176 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6611, .card = CX88_BOARD_WINFAST2000XP_EXPERT, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6613, /* NTSC */ .card = CX88_BOARD_WINFAST2000XP_EXPERT, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6620, .card = CX88_BOARD_WINFAST_DV2000, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x663b, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x663c, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x000b, .card = CX88_BOARD_AVERTV_STUDIO_303, - },{ + }, { .subvendor = 0x1462, .subdevice = 0x8606, .card = CX88_BOARD_MSI_TVANYWHERE_MASTER, - },{ + }, { .subvendor = 0x10fc, .subdevice = 0xd003, .card = CX88_BOARD_IODATA_GVVCP3PCI, - },{ + }, { .subvendor = 0x1043, .subdevice = 0x4823, /* with mpeg encoder */ .card = CX88_BOARD_ASUS_PVR_416, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08a6, .card = CX88_BOARD_KWORLD_DVB_T, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd810, .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd820, .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb00, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9002, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0187, .card = CX88_BOARD_CONEXANT_DVB_T1, - },{ + }, { .subvendor = 0x1540, .subdevice = 0x2580, .card = CX88_BOARD_PROVIDEO_PV259, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb10, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, - },{ + }, { .subvendor = 0x1554, .subdevice = 0x4811, .card = CX88_BOARD_PIXELVIEW, - },{ + }, { .subvendor = 0x7063, .subdevice = 0x3000, /* HD-3000 card */ .card = CX88_BOARD_PCHDTV_HD3000, - },{ + }, { .subvendor = 0x17de, .subdevice = 0xa8a6, .card = CX88_BOARD_DNTV_LIVE_DVB_T, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x2801, .card = CX88_BOARD_HAUPPAUGE_ROSLYN, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0342, .card = CX88_BOARD_DIGITALLOGIC_MEC, - },{ + }, { .subvendor = 0x10fc, .subdevice = 0xd035, .card = CX88_BOARD_IODATA_GVBCTV7E, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0334, .card = CX88_BOARD_ADSTECH_DVB_T_PCI, - },{ + }, { .subvendor = 0x153b, .subdevice = 0x1166, .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd500, .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x8011, .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550, - },{ + }, { .subvendor = PCI_VENDOR_ID_ATI, .subdevice = 0xa101, .card = CX88_BOARD_ATI_HDTVWONDER, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x665f, .card = CX88_BOARD_WINFAST_DTV1000, - },{ + }, { .subvendor = 0x1461, .subdevice = 0x000a, .card = CX88_BOARD_AVERTV_303, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9200, .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9201, .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9202, .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08b2, .card = CX88_BOARD_KWORLD_DVBS_100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9400, .card = CX88_BOARD_HAUPPAUGE_HVR1100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9402, .card = CX88_BOARD_HAUPPAUGE_HVR1100, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9800, .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9802, .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9001, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x1822, .subdevice = 0x0025, .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x08a1, .card = CX88_BOARD_KWORLD_DVB_T_CX22702, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb50, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb54, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, /* Re-branded DViCO: DigitalNow DVB-T Dual */ - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb11, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, @@ -2530,55 +2530,55 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x17de, .subdevice = 0x0840, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb40, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xdb44, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, - },{ + }, { .subvendor = 0x7063, .subdevice = 0x5500, .card = CX88_BOARD_PCHDTV_HD5500, - },{ + }, { .subvendor = 0x17de, .subdevice = 0x0841, .card = CX88_BOARD_KWORLD_MCE200_DELUXE, - },{ + }, { .subvendor = 0x1822, .subdevice = 0x0019, .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, - },{ + }, { .subvendor = 0x1554, .subdevice = 0x4813, .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0842, .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x665e, .card = CX88_BOARD_WINFAST_DTV2000H, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6f2b, .card = CX88_BOARD_WINFAST_DTV2000H_J, - },{ + }, { .subvendor = 0x18ac, .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, - },{ + }, { .subvendor = 0x14f1, .subdevice = 0x0084, .card = CX88_BOARD_GENIATECH_DVBS, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1404, .card = CX88_BOARD_HAUPPAUGE_HVR3000, @@ -2590,60 +2590,60 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x18ac, .subdevice = 0xdccd, .card = CX88_BOARD_SAMSUNG_SMT_7020, - },{ + }, { .subvendor = 0x1461, .subdevice = 0xc111, /* AverMedia M150-D */ /* This board is known to work with the ASUS PVR416 config */ .card = CX88_BOARD_ASUS_PVR_416, - },{ + }, { .subvendor = 0xc180, .subdevice = 0xc980, .card = CX88_BOARD_TE_DTV_250_OEM_SWANN, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9600, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9601, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9602, .card = CX88_BOARD_HAUPPAUGE_HVR1300, - },{ + }, { .subvendor = 0x107d, .subdevice = 0x6632, .card = CX88_BOARD_LEADTEK_PVR2000, - },{ + }, { .subvendor = 0x12ab, .subdevice = 0x2300, /* Club3D Zap TV2100 */ .card = CX88_BOARD_KWORLD_DVB_T_CX22702, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x9000, .card = CX88_BOARD_HAUPPAUGE_DVB_T1, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1400, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1401, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x0070, .subdevice = 0x1402, .card = CX88_BOARD_HAUPPAUGE_HVR3000, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ .card = CX88_BOARD_KWORLD_DVBS_100, - },{ + }, { .subvendor = 0x1421, .subdevice = 0x0390, .card = CX88_BOARD_ADSTECH_PTV_390, - },{ + }, { .subvendor = 0x11bd, .subdevice = 0x0051, .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index ed8cb9037b6f..ce27e6d4f16e 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -696,7 +696,6 @@ static struct videobuf_queue *get_queue(struct file *file) return &fh->vbiq; default: BUG(); - return NULL; } } @@ -711,7 +710,6 @@ static int get_resource(struct file *file) return RESOURCE_VBI; default: BUG(); - return 0; } } @@ -812,7 +810,6 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) file->f_flags & O_NONBLOCK); default: BUG(); - return 0; } } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index da8f848be3b8..c82e855a0814 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -149,7 +149,7 @@ static u32 ddb_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -struct i2c_algorithm ddb_i2c_algo = { +static struct i2c_algorithm ddb_i2c_algo = { .master_xfer = ddb_i2c_master_xfer, .functionality = ddb_i2c_functionality, }; @@ -266,7 +266,7 @@ static void io_free(struct pci_dev *pdev, u8 **vbuf, for (i = 0; i < num; i++) { if (vbuf[i]) { pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); - vbuf[i] = 0; + vbuf[i] = NULL; } } } @@ -440,7 +440,7 @@ static u32 ddb_output_free(struct ddb_output *output) } static ssize_t ddb_output_write(struct ddb_output *output, - const u8 *buf, size_t count) + const __user u8 *buf, size_t count) { struct ddb *dev = output->port->dev; u32 idx, off, stat = output->stat; @@ -506,7 +506,7 @@ static u32 ddb_input_avail(struct ddb_input *input) return 0; } -static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) +static ssize_t ddb_input_read(struct ddb_input *input, __user u8 *buf, size_t count) { struct ddb *dev = input->port->dev; u32 left = count; @@ -849,7 +849,7 @@ static int dvb_input_attach(struct ddb_input *input) return ret; input->attached = 4; - input->fe = 0; + input->fe = NULL; switch (port->type) { case DDB_TUNER_DVBS_ST: if (demod_attach_stv0900(input, 0) < 0) @@ -895,7 +895,7 @@ static int dvb_input_attach(struct ddb_input *input) /****************************************************************************/ /****************************************************************************/ -static ssize_t ts_write(struct file *file, const char *buf, +static ssize_t ts_write(struct file *file, const __user char *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -920,7 +920,7 @@ static ssize_t ts_write(struct file *file, const char *buf, return (left == count) ? -EAGAIN : (count - left); } -static ssize_t ts_read(struct file *file, char *buf, +static ssize_t ts_read(struct file *file, __user char *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -975,11 +975,9 @@ static const struct file_operations ci_fops = { .open = dvb_generic_open, .release = dvb_generic_release, .poll = ts_poll, - .mmap = 0, }; static struct dvb_device dvbdev_ci = { - .priv = 0, .readers = -1, .writers = -1, .users = -1, @@ -1038,7 +1036,7 @@ static void output_tasklet(unsigned long data) } -struct cxd2099_cfg cxd_cfg = { +static struct cxd2099_cfg cxd_cfg = { .bitrate = 62000, .adr = 0x40, .polarity = 1, @@ -1127,7 +1125,7 @@ static void ddb_ports_detach(struct ddb *dev) ddb_output_stop(port->output); dvb_ca_en50221_release(port->en); kfree(port->en); - port->en = 0; + port->en = NULL; dvb_unregister_adapter(&port->output->adap); } break; @@ -1413,9 +1411,9 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) #define DDB_MAGIC 'd' struct ddb_flashio { - __u8 *write_buf; + __user __u8 *write_buf; __u32 write_len; - __u8 *read_buf; + __user __u8 *read_buf; __u32 read_len; }; @@ -1439,7 +1437,7 @@ static int ddb_open(struct inode *inode, struct file *file) static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ddb *dev = file->private_data; - void *parg = (void *)arg; + __user void *parg = (__user void *)arg; int res; switch (cmd) { @@ -1558,7 +1556,7 @@ static void ddb_remove(struct pci_dev *pdev) ddb_device_destroy(dev); ddb_unmap(dev); - pci_set_drvdata(pdev, 0); + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } @@ -1637,7 +1635,7 @@ fail1: fail: printk(KERN_ERR "fail\n"); ddb_unmap(dev); - pci_set_drvdata(pdev, 0); + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return -1; } diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 8b1b41d2a52d..be87fbd90456 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -156,7 +156,7 @@ struct ddb_port { struct ddb { struct pci_dev *pdev; - unsigned char *regs; + unsigned char __iomem *regs; struct ddb_port port[DDB_MAX_PORT]; struct ddb_i2c i2c[DDB_MAX_I2C]; struct ddb_input input[DDB_MAX_INPUT]; @@ -173,12 +173,10 @@ struct ddb { /****************************************************************************/ #define ddbwritel(_val, _adr) writel((_val), \ - (char *) (dev->regs+(_adr))) -#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) -#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ - (dev->regs+(_adr)), (_src), (_count)) -#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ - (dev->regs+(_adr)), (_count)) + dev->regs+(_adr)) +#define ddbreadl(_adr) readl(dev->regs+(_adr)) +#define ddbcpyto(_adr, _src, _count) memcpy_toio(dev->regs+(_adr), (_src), (_count)) +#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), dev->regs+(_adr), (_count)) /****************************************************************************/ diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index e8826c535ccd..ed11716731e9 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -614,7 +614,7 @@ static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) static void dm1105_set_dma_addr(struct dm1105_dev *dev) { - dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); + dm_writel(DM1105_STADR, (__force u32)cpu_to_le32(dev->dma_addr)); } static int dm1105_dma_map(struct dm1105_dev *dev) diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c index 7a9b98bc208b..7bf9cbca4fa6 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c @@ -81,7 +81,7 @@ static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, int period_elapsed = 0; int length; - dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc, + dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc, pcm_data, num_bytes); substream = itvsc->capture_pcm_substream; diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c index ed73edd2bcd3..4b0e758a7bce 100644 --- a/drivers/media/pci/ivtv/ivtv-firmware.c +++ b/drivers/media/pci/ivtv/ivtv-firmware.c @@ -65,7 +65,7 @@ retry: the wrong file was sometimes loaded. So we check filesizes to see if at least the right-sized file was loaded. If not, then we retry. */ - IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); + IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zu)\n", fn, size, fw->size); release_firmware(fw); retries--; goto retry; @@ -76,7 +76,7 @@ retry: dst++; src++; } - IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size); + IVTV_INFO("Loaded %s firmware (%zu bytes)\n", fn, fw->size); release_firmware(fw); return size; } diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c index 19a7c9b990a3..ab6d5d25aa6f 100644 --- a/drivers/media/pci/ivtv/ivtv-irq.c +++ b/drivers/media/pci/ivtv/ivtv-irq.c @@ -192,11 +192,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || s->type == IVTV_DEC_STREAM_TYPE_VBI)) { s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET); - write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); + write_dec_sync(DMA_MAGIC_COOKIE, offset - IVTV_DECODER_OFFSET); } else { s->pending_backup = read_enc(offset); - write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); + write_enc_sync(DMA_MAGIC_COOKIE, offset); } s->pending_offset = offset; } @@ -275,13 +275,11 @@ static void dma_post(struct ivtv_stream *s) if (x == 0 && ivtv_use_dma(s)) { offset = s->dma_last_offset; - if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) + if (le32_to_cpu(u32buf[offset / 4]) != DMA_MAGIC_COOKIE) { - for (offset = 0; offset < 64; offset++) { - if (u32buf[offset] == DMA_MAGIC_COOKIE) { + for (offset = 0; offset < 64; offset++) + if (le32_to_cpu(u32buf[offset]) == DMA_MAGIC_COOKIE) break; - } - } offset *= 4; if (offset == 256) { IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c index 68a29f8bdf73..1032db6bb789 100644 --- a/drivers/media/pci/mantis/hopper_vp3028.c +++ b/drivers/media/pci/mantis/hopper_vp3028.c @@ -34,7 +34,7 @@ #include "mantis_dvb.h" #include "hopper_vp3028.h" -struct zl10353_config hopper_vp3028_config = { +static struct zl10353_config hopper_vp3028_config = { .demod_address = 0x0f, }; diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h index f2410cf0a6bf..8ff448bb792d 100644 --- a/drivers/media/pci/mantis/mantis_common.h +++ b/drivers/media/pci/mantis/mantis_common.h @@ -127,7 +127,7 @@ struct mantis_pci { u32 last_block; u8 *buf_cpu; dma_addr_t buf_dma; - u32 *risc_cpu; + __le32 *risc_cpu; dma_addr_t risc_dma; struct tasklet_struct tasklet; diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c index 115003e8d19d..12a6adb2bd7e 100644 --- a/drivers/media/pci/mantis/mantis_vp1033.c +++ b/drivers/media/pci/mantis/mantis_vp1033.c @@ -35,7 +35,7 @@ #include "mantis_vp1033.h" #include "mantis_reg.h" -u8 lgtdqcs001f_inittab[] = { +static u8 lgtdqcs001f_inittab[] = { 0x01, 0x15, 0x02, 0x30, 0x03, 0x00, @@ -150,7 +150,7 @@ static int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe, return 0; } -struct stv0299_config lgtdqcs001f_config = { +static struct stv0299_config lgtdqcs001f_config = { .demod_address = 0x68, .inittab = lgtdqcs001f_inittab, .mclk = 88000000UL, diff --git a/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c index 430ae84ce528..7c1bd167225c 100644 --- a/drivers/media/pci/mantis/mantis_vp1034.c +++ b/drivers/media/pci/mantis/mantis_vp1034.c @@ -36,7 +36,7 @@ #include "mantis_vp1034.h" #include "mantis_reg.h" -struct mb86a16_config vp1034_mb86a16_config = { +static struct mb86a16_config vp1034_mb86a16_config = { .demod_address = 0x08, .set_voltage = vp1034_set_voltage, }; diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c index 07a20748b707..7082fcbc94a1 100644 --- a/drivers/media/pci/mantis/mantis_vp1041.c +++ b/drivers/media/pci/mantis/mantis_vp1041.c @@ -263,7 +263,7 @@ static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { { 0xffff , 0xff }, }; -struct stb0899_config vp1041_stb0899_config = { +static struct stb0899_config vp1041_stb0899_config = { .init_dev = vp1041_stb0899_s1_init_1, .init_s2_demod = stb0899_s2_init_2, .init_s1_demod = vp1041_stb0899_s1_init_3, @@ -300,7 +300,7 @@ struct stb0899_config vp1041_stb0899_config = { .tuner_set_rfsiggain = NULL, }; -struct stb6100_config vp1041_stb6100_config = { +static struct stb6100_config vp1041_stb6100_config = { .tuner_address = 0x60, .refclock = 27000000, }; diff --git a/drivers/media/pci/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c index 1ca6837fbe46..8d48b5abe04a 100644 --- a/drivers/media/pci/mantis/mantis_vp2033.c +++ b/drivers/media/pci/mantis/mantis_vp2033.c @@ -37,12 +37,12 @@ #define MANTIS_MODEL_NAME "VP-2033" #define MANTIS_DEV_TYPE "DVB-C" -struct tda1002x_config vp2033_tda1002x_cu1216_config = { +static struct tda1002x_config vp2033_tda1002x_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; -struct tda10023_config vp2033_tda10023_cu1216_config = { +static struct tda10023_config vp2033_tda10023_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; diff --git a/drivers/media/pci/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c index d480741afd78..8dd17d7c0881 100644 --- a/drivers/media/pci/mantis/mantis_vp2040.c +++ b/drivers/media/pci/mantis/mantis_vp2040.c @@ -37,12 +37,12 @@ #define MANTIS_MODEL_NAME "VP-2040" #define MANTIS_DEV_TYPE "DVB-C" -struct tda1002x_config vp2040_tda1002x_cu1216_config = { +static struct tda1002x_config vp2040_tda1002x_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; -struct tda10023_config vp2040_tda10023_cu1216_config = { +static struct tda10023_config vp2040_tda10023_cu1216_config = { .demod_address = 0x18 >> 1, .invert = 1, }; diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c index c09308cd3ac6..5c1dd925bdd5 100644 --- a/drivers/media/pci/mantis/mantis_vp3030.c +++ b/drivers/media/pci/mantis/mantis_vp3030.c @@ -35,11 +35,11 @@ #include "mantis_dvb.h" #include "mantis_vp3030.h" -struct zl10353_config mantis_vp3030_config = { +static struct zl10353_config mantis_vp3030_config = { .demod_address = 0x0f, }; -struct tda665x_config env57h12d5_config = { +static struct tda665x_config env57h12d5_config = { .name = "ENV57H12D5 (ET-50DT)", .addr = 0x60, .frequency_min = 47000000, diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 9e82d2105d53..039bed3cc919 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -696,7 +696,7 @@ static struct ngene_info ngene_info_m780 = { .demod_attach = { NULL, demod_attach_lg330x }, /* Ensure these are NULL else the frame will call them (as funcs) */ - .tuner_attach = { 0, 0, 0, 0 }, + .tuner_attach = { NULL, NULL, NULL, NULL }, .fe_config = { NULL, &aver_m780 }, .avf = { 0 }, diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 4930b55fd5f4..e29bc3af4baf 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -57,15 +57,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define dprintk if (debug) printk -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) -#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) -#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) +#define ngwritel(dat, adr) writel((dat), dev->iomem + (adr)) +#define ngwriteb(dat, adr) writeb((dat), dev->iomem + (adr)) #define ngreadl(adr) readl(dev->iomem + (adr)) #define ngreadb(adr) readb(dev->iomem + (adr)) -#define ngcpyto(adr, src, count) memcpy_toio((char *) \ - (dev->iomem + (adr)), (src), (count)) -#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ - (dev->iomem + (adr)), (count)) +#define ngcpyto(adr, src, count) memcpy_toio(dev->iomem + (adr), (src), (count)) +#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), dev->iomem + (adr), (count)) /****************************************************************************/ /* nGene interrupt handler **************************************************/ @@ -1592,7 +1590,7 @@ static void cxd_detach(struct ngene *dev) dvb_ca_en50221_release(ci->en); kfree(ci->en); - ci->en = 0; + ci->en = NULL; } /***********************************/ diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c index fcb16a615aab..59bb2858c8d0 100644 --- a/drivers/media/pci/ngene/ngene-dvb.c +++ b/drivers/media/pci/ngene/ngene-dvb.c @@ -47,7 +47,7 @@ /* COMMAND API interface ****************************************************/ /****************************************************************************/ -static ssize_t ts_write(struct file *file, const char *buf, +static ssize_t ts_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -59,12 +59,12 @@ static ssize_t ts_write(struct file *file, const char *buf, (&dev->tsout_rbuf) >= count) < 0) return 0; - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); + dvb_ringbuffer_write_user(&dev->tsout_rbuf, buf, count); return count; } -static ssize_t ts_read(struct file *file, char *buf, +static ssize_t ts_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct dvb_device *dvbdev = file->private_data; @@ -97,7 +97,6 @@ static const struct file_operations ci_fops = { }; struct dvb_device ngene_dvbdev_ci = { - .priv = 0, .readers = -1, .writers = -1, .users = -1, diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index 22c39ff6bfa0..51e2fbd18b1b 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -737,7 +737,7 @@ typedef void (tx_cb_t)(struct ngene *, u32); struct ngene { int nr; struct pci_dev *pci_dev; - unsigned char *iomem; + unsigned char __iomem *iomem; /*struct i2c_adapter i2c_adapter;*/ diff --git a/drivers/media/pci/pt3/Kconfig b/drivers/media/pci/pt3/Kconfig new file mode 100644 index 000000000000..16c208ae0079 --- /dev/null +++ b/drivers/media/pci/pt3/Kconfig @@ -0,0 +1,10 @@ +config DVB_PT3 + tristate "Earthsoft PT3 cards" + depends on DVB_CORE && PCI && I2C + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT + help + Support for Earthsoft PT3 PCIe cards. + + Say Y or M if you own such a device and want to use it. diff --git a/drivers/media/pci/pt3/Makefile b/drivers/media/pci/pt3/Makefile new file mode 100644 index 000000000000..396f146b1c18 --- /dev/null +++ b/drivers/media/pci/pt3/Makefile @@ -0,0 +1,8 @@ + +earth-pt3-objs += pt3.o pt3_i2c.o pt3_dma.o + +obj-$(CONFIG_DVB_PT3) += earth-pt3.o + +ccflags-y += -Idrivers/media/dvb-core +ccflags-y += -Idrivers/media/dvb-frontends +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c new file mode 100644 index 000000000000..1fdeac11501a --- /dev/null +++ b/drivers/media/pci/pt3/pt3.c @@ -0,0 +1,876 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.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 version 2. + * + * + * 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. + */ + +#include <linux/freezer.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/string.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include "pt3.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static bool one_adapter; +module_param(one_adapter, bool, 0444); +MODULE_PARM_DESC(one_adapter, "Place FE's together under one adapter."); + +static int num_bufs = 4; +module_param(num_bufs, int, 0444); +MODULE_PARM_DESC(num_bufs, "Number of DMA buffer (188KiB) per FE."); + + +static const struct i2c_algorithm pt3_i2c_algo = { + .master_xfer = &pt3_i2c_master_xfer, + .functionality = &pt3_i2c_functionality, +}; + +static const struct pt3_adap_config adap_conf[PT3_NUM_FE] = { + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x11), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1c0042", 0x63), + }, + .tuner_cfg.qm1d1c0042 = { + .lpf = 1, + }, + .init_freq = 1049480 - 300, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x10), + }, + .tuner_info = { + I2C_BOARD_INFO("mxl301rf", 0x62), + }, + .init_freq = 515142857, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x13), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1c0042", 0x60), + }, + .tuner_cfg.qm1d1c0042 = { + .lpf = 1, + }, + .init_freq = 1049480 + 300, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x12), + }, + .tuner_info = { + I2C_BOARD_INFO("mxl301rf", 0x61), + }, + .init_freq = 521142857, + }, +}; + + +struct reg_val { + u8 reg; + u8 val; +}; + +static int +pt3_demod_write(struct pt3_adapter *adap, const struct reg_val *data, int num) +{ + struct i2c_msg msg; + int i, ret; + + ret = 0; + msg.addr = adap->i2c_demod->addr; + msg.flags = 0; + msg.len = 2; + for (i = 0; i < num; i++) { + msg.buf = (u8 *)&data[i]; + ret = i2c_transfer(adap->i2c_demod->adapter, &msg, 1); + if (ret == 0) + ret = -EREMOTE; + if (ret < 0) + return ret; + } + return 0; +} + +static inline void pt3_lnb_ctrl(struct pt3_board *pt3, bool on) +{ + iowrite32((on ? 0x0f : 0x0c), pt3->regs[0] + REG_SYSTEM_W); +} + +static inline struct pt3_adapter *pt3_find_adapter(struct dvb_frontend *fe) +{ + struct pt3_board *pt3; + int i; + + if (one_adapter) { + pt3 = fe->dvb->priv; + for (i = 0; i < PT3_NUM_FE; i++) + if (pt3->adaps[i]->fe == fe) + return pt3->adaps[i]; + } + return container_of(fe->dvb, struct pt3_adapter, dvb_adap); +} + +/* + * all 4 tuners in PT3 are packaged in a can module (Sharp VA4M6JC2103). + * it seems that they share the power lines and Amp power line and + * adaps[3] controls those powers. + */ +static int +pt3_set_tuner_power(struct pt3_board *pt3, bool tuner_on, bool amp_on) +{ + struct reg_val rv = { 0x1e, 0x99 }; + + if (tuner_on) + rv.val |= 0x40; + if (amp_on) + rv.val |= 0x04; + return pt3_demod_write(pt3->adaps[PT3_NUM_FE - 1], &rv, 1); +} + +static int pt3_set_lna(struct dvb_frontend *fe) +{ + struct pt3_adapter *adap; + struct pt3_board *pt3; + u32 val; + int ret; + + /* LNA is shared btw. 2 TERR-tuners */ + + adap = pt3_find_adapter(fe); + val = fe->dtv_property_cache.lna; + if (val == LNA_AUTO || val == adap->cur_lna) + return 0; + + pt3 = adap->dvb_adap.priv; + if (mutex_lock_interruptible(&pt3->lock)) + return -ERESTARTSYS; + if (val) + pt3->lna_on_cnt++; + else + pt3->lna_on_cnt--; + + if (val && pt3->lna_on_cnt <= 1) { + pt3->lna_on_cnt = 1; + ret = pt3_set_tuner_power(pt3, true, true); + } else if (!val && pt3->lna_on_cnt <= 0) { + pt3->lna_on_cnt = 0; + ret = pt3_set_tuner_power(pt3, true, false); + } else + ret = 0; + mutex_unlock(&pt3->lock); + adap->cur_lna = (val != 0); + return ret; +} + +static int pt3_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +{ + struct pt3_adapter *adap; + struct pt3_board *pt3; + bool on; + + /* LNB power is shared btw. 2 SAT-tuners */ + + adap = pt3_find_adapter(fe); + on = (volt != SEC_VOLTAGE_OFF); + if (on == adap->cur_lnb) + return 0; + adap->cur_lnb = on; + pt3 = adap->dvb_adap.priv; + if (mutex_lock_interruptible(&pt3->lock)) + return -ERESTARTSYS; + if (on) + pt3->lnb_on_cnt++; + else + pt3->lnb_on_cnt--; + + if (on && pt3->lnb_on_cnt <= 1) { + pt3->lnb_on_cnt = 1; + pt3_lnb_ctrl(pt3, true); + } else if (!on && pt3->lnb_on_cnt <= 0) { + pt3->lnb_on_cnt = 0; + pt3_lnb_ctrl(pt3, false); + } + mutex_unlock(&pt3->lock); + return 0; +} + +/* register values used in pt3_fe_init() */ + +static const struct reg_val init0_sat[] = { + { 0x03, 0x01 }, + { 0x1e, 0x10 }, +}; +static const struct reg_val init0_ter[] = { + { 0x01, 0x40 }, + { 0x1c, 0x10 }, +}; +static const struct reg_val cfg_sat[] = { + { 0x1c, 0x15 }, + { 0x1f, 0x04 }, +}; +static const struct reg_val cfg_ter[] = { + { 0x1d, 0x01 }, +}; + +/* + * pt3_fe_init: initialize demod sub modules and ISDB-T tuners all at once. + * + * As for demod IC (TC90522) and ISDB-T tuners (MxL301RF), + * the i2c sequences for init'ing them are not public and hidden in a ROM, + * and include the board specific configurations as well. + * They are stored in a lump and cannot be taken out / accessed separately, + * thus cannot be moved to the FE/tuner driver. + */ +static int pt3_fe_init(struct pt3_board *pt3) +{ + int i, ret; + struct dvb_frontend *fe; + + pt3_i2c_reset(pt3); + ret = pt3_init_all_demods(pt3); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to init demod chips."); + return ret; + } + + /* additional config? */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = pt3_demod_write(pt3->adaps[i], + init0_sat, ARRAY_SIZE(init0_sat)); + else + ret = pt3_demod_write(pt3->adaps[i], + init0_ter, ARRAY_SIZE(init0_ter)); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "demod[%d] faild in init sequence0.", i); + return ret; + } + ret = fe->ops.init(fe); + if (ret < 0) + return ret; + } + + usleep_range(2000, 4000); + ret = pt3_set_tuner_power(pt3, true, false); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to control tuner module."); + return ret; + } + + /* output pin configuration */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.delsys[0] == SYS_ISDBS) + ret = pt3_demod_write(pt3->adaps[i], + cfg_sat, ARRAY_SIZE(cfg_sat)); + else + ret = pt3_demod_write(pt3->adaps[i], + cfg_ter, ARRAY_SIZE(cfg_ter)); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "demod[%d] faild in init sequence1.", i); + return ret; + } + } + usleep_range(4000, 6000); + + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.delsys[0] != SYS_ISDBS) + continue; + /* init and wake-up ISDB-S tuners */ + ret = fe->ops.tuner_ops.init(fe); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "Failed to init SAT-tuner[%d].", i); + return ret; + } + } + ret = pt3_init_all_mxl301rf(pt3); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners."); + return ret; + } + + ret = pt3_set_tuner_power(pt3, true, true); + if (ret < 0) { + dev_warn(&pt3->pdev->dev, "Failed to control tuner module."); + return ret; + } + + /* Wake up all tuners and make an initial tuning, + * in order to avoid interference among the tuners in the module, + * according to the doc from the manufacturer. + */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + ret = 0; + if (fe->ops.delsys[0] == SYS_ISDBT) + ret = fe->ops.tuner_ops.init(fe); + /* set only when called from pt3_probe(), not resume() */ + if (ret == 0 && fe->dtv_property_cache.frequency == 0) { + fe->dtv_property_cache.frequency = + adap_conf[i].init_freq; + ret = fe->ops.tuner_ops.set_params(fe); + } + if (ret < 0) { + dev_warn(&pt3->pdev->dev, + "Failed in initial tuning of tuner[%d].", i); + return ret; + } + } + + /* and sleep again, waiting to be opened by users. */ + for (i = 0; i < PT3_NUM_FE; i++) { + fe = pt3->adaps[i]->fe; + if (fe->ops.tuner_ops.sleep) + ret = fe->ops.tuner_ops.sleep(fe); + if (ret < 0) + break; + if (fe->ops.sleep) + ret = fe->ops.sleep(fe); + if (ret < 0) + break; + if (fe->ops.delsys[0] == SYS_ISDBS) + fe->ops.set_voltage = &pt3_set_voltage; + else + fe->ops.set_lna = &pt3_set_lna; + } + if (i < PT3_NUM_FE) { + dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby.", i); + return ret; + } + return 0; +} + + +static int pt3_attach_fe(struct pt3_board *pt3, int i) +{ + struct i2c_board_info info; + struct tc90522_config cfg; + struct i2c_client *cl; + struct dvb_adapter *dvb_adap; + int ret; + + info = adap_conf[i].demod_info; + cfg = adap_conf[i].demod_cfg; + cfg.tuner_i2c = NULL; + info.platform_data = &cfg; + + ret = -ENODEV; + request_module("tc90522"); + cl = i2c_new_device(&pt3->i2c_adap, &info); + if (!cl || !cl->dev.driver) + return -ENODEV; + pt3->adaps[i]->i2c_demod = cl; + if (!try_module_get(cl->dev.driver->owner)) + goto err_demod_i2c_unregister_device; + + if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) { + struct qm1d1c0042_config tcfg; + + tcfg = adap_conf[i].tuner_cfg.qm1d1c0042; + tcfg.fe = cfg.fe; + info = adap_conf[i].tuner_info; + info.platform_data = &tcfg; + request_module("qm1d1c0042"); + cl = i2c_new_device(cfg.tuner_i2c, &info); + } else { + struct mxl301rf_config tcfg; + + tcfg = adap_conf[i].tuner_cfg.mxl301rf; + tcfg.fe = cfg.fe; + info = adap_conf[i].tuner_info; + info.platform_data = &tcfg; + request_module("mxl301rf"); + cl = i2c_new_device(cfg.tuner_i2c, &info); + } + if (!cl || !cl->dev.driver) + goto err_demod_module_put; + pt3->adaps[i]->i2c_tuner = cl; + if (!try_module_get(cl->dev.driver->owner)) + goto err_tuner_i2c_unregister_device; + + dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap; + ret = dvb_register_frontend(dvb_adap, cfg.fe); + if (ret < 0) + goto err_tuner_module_put; + pt3->adaps[i]->fe = cfg.fe; + return 0; + +err_tuner_module_put: + module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner); +err_tuner_i2c_unregister_device: + i2c_unregister_device(pt3->adaps[i]->i2c_tuner); +err_demod_module_put: + module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner); +err_demod_i2c_unregister_device: + i2c_unregister_device(pt3->adaps[i]->i2c_demod); + + return ret; +} + + +static int pt3_fetch_thread(void *data) +{ + struct pt3_adapter *adap = data; + ktime_t delay; + bool was_frozen; + +#define PT3_INITIAL_BUF_DROPS 4 +#define PT3_FETCH_DELAY 10 +#define PT3_FETCH_DELAY_DELTA 2 + + pt3_init_dmabuf(adap); + adap->num_discard = PT3_INITIAL_BUF_DROPS; + + dev_dbg(adap->dvb_adap.device, + "PT3: [%s] started.\n", adap->thread->comm); + set_freezable(); + while (!kthread_freezable_should_stop(&was_frozen)) { + if (was_frozen) + adap->num_discard = PT3_INITIAL_BUF_DROPS; + + pt3_proc_dma(adap); + + delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC); + set_current_state(TASK_UNINTERRUPTIBLE); + freezable_schedule_hrtimeout_range(&delay, + PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC, + HRTIMER_MODE_REL); + } + dev_dbg(adap->dvb_adap.device, + "PT3: [%s] exited.\n", adap->thread->comm); + adap->thread = NULL; + return 0; +} + +static int pt3_start_streaming(struct pt3_adapter *adap) +{ + struct task_struct *thread; + + /* start fetching thread */ + thread = kthread_run(pt3_fetch_thread, adap, "pt3-ad%i-dmx%i", + adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + if (IS_ERR(thread)) { + int ret = PTR_ERR(thread); + + dev_warn(adap->dvb_adap.device, + "PT3 (adap:%d, dmx:%d): failed to start kthread.\n", + adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + return ret; + } + adap->thread = thread; + + return pt3_start_dma(adap); +} + +static int pt3_stop_streaming(struct pt3_adapter *adap) +{ + int ret; + + ret = pt3_stop_dma(adap); + if (ret) + dev_warn(adap->dvb_adap.device, + "PT3: failed to stop streaming of adap:%d/FE:%d\n", + adap->dvb_adap.num, adap->fe->id); + + /* kill the fetching thread */ + ret = kthread_stop(adap->thread); + return ret; +} + +static int pt3_start_feed(struct dvb_demux_feed *feed) +{ + struct pt3_adapter *adap; + + if (signal_pending(current)) + return -EINTR; + + adap = container_of(feed->demux, struct pt3_adapter, demux); + adap->num_feeds++; + if (adap->thread) + return 0; + if (adap->num_feeds != 1) { + dev_warn(adap->dvb_adap.device, + "%s: unmatched start/stop_feed in adap:%i/dmx:%i.\n", + __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id); + adap->num_feeds = 1; + } + + return pt3_start_streaming(adap); + +} + +static int pt3_stop_feed(struct dvb_demux_feed *feed) +{ + struct pt3_adapter *adap; + + adap = container_of(feed->demux, struct pt3_adapter, demux); + + adap->num_feeds--; + if (adap->num_feeds > 0 || !adap->thread) + return 0; + adap->num_feeds = 0; + + return pt3_stop_streaming(adap); +} + + +static int pt3_alloc_adapter(struct pt3_board *pt3, int index) +{ + int ret; + struct pt3_adapter *adap; + struct dvb_adapter *da; + + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) { + dev_err(&pt3->pdev->dev, "failed to alloc mem for adapter.\n"); + return -ENOMEM; + } + pt3->adaps[index] = adap; + adap->adap_idx = index; + + if (index == 0 || !one_adapter) { + ret = dvb_register_adapter(&adap->dvb_adap, "PT3 DVB", + THIS_MODULE, &pt3->pdev->dev, adapter_nr); + if (ret < 0) { + dev_err(&pt3->pdev->dev, + "failed to register adapter dev.\n"); + goto err_mem; + } + da = &adap->dvb_adap; + } else + da = &pt3->adaps[0]->dvb_adap; + + adap->dvb_adap.priv = pt3; + adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + adap->demux.priv = adap; + adap->demux.feednum = 256; + adap->demux.filternum = 256; + adap->demux.start_feed = pt3_start_feed; + adap->demux.stop_feed = pt3_stop_feed; + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + dev_err(&pt3->pdev->dev, "failed to init dmx dev.\n"); + goto err_adap; + } + + adap->dmxdev.filternum = 256; + adap->dmxdev.demux = &adap->demux.dmx; + ret = dvb_dmxdev_init(&adap->dmxdev, da); + if (ret < 0) { + dev_err(&pt3->pdev->dev, "failed to init dmxdev.\n"); + goto err_demux; + } + + ret = pt3_alloc_dmabuf(adap); + if (ret) { + dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers.\n"); + goto err_dmabuf; + } + + return 0; + +err_dmabuf: + pt3_free_dmabuf(adap); + dvb_dmxdev_release(&adap->dmxdev); +err_demux: + dvb_dmx_release(&adap->demux); +err_adap: + if (index == 0 || !one_adapter) + dvb_unregister_adapter(da); +err_mem: + kfree(adap); + pt3->adaps[index] = NULL; + return ret; +} + +static void pt3_cleanup_adapter(struct pt3_board *pt3, int index) +{ + struct pt3_adapter *adap; + struct dmx_demux *dmx; + + adap = pt3->adaps[index]; + if (adap == NULL) + return; + + /* stop demux kthread */ + if (adap->thread) + pt3_stop_streaming(adap); + + dmx = &adap->demux.dmx; + dmx->close(dmx); + if (adap->fe) { + adap->fe->callback = NULL; + if (adap->fe->frontend_priv) + dvb_unregister_frontend(adap->fe); + if (adap->i2c_tuner) { + module_put(adap->i2c_tuner->dev.driver->owner); + i2c_unregister_device(adap->i2c_tuner); + } + if (adap->i2c_demod) { + module_put(adap->i2c_demod->dev.driver->owner); + i2c_unregister_device(adap->i2c_demod); + } + } + pt3_free_dmabuf(adap); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + if (index == 0 || !one_adapter) + dvb_unregister_adapter(&adap->dvb_adap); + kfree(adap); + pt3->adaps[index] = NULL; +} + +#ifdef CONFIG_PM_SLEEP + +static int pt3_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt3_board *pt3 = pci_get_drvdata(pdev); + int i; + struct pt3_adapter *adap; + + for (i = 0; i < PT3_NUM_FE; i++) { + adap = pt3->adaps[i]; + if (adap->num_feeds > 0) + pt3_stop_dma(adap); + dvb_frontend_suspend(adap->fe); + pt3_free_dmabuf(adap); + } + + pt3_lnb_ctrl(pt3, false); + pt3_set_tuner_power(pt3, false, false); + return 0; +} + +static int pt3_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt3_board *pt3 = pci_get_drvdata(pdev); + int i, ret; + struct pt3_adapter *adap; + + ret = pt3_fe_init(pt3); + if (ret) + return ret; + + if (pt3->lna_on_cnt > 0) + pt3_set_tuner_power(pt3, true, true); + if (pt3->lnb_on_cnt > 0) + pt3_lnb_ctrl(pt3, true); + + for (i = 0; i < PT3_NUM_FE; i++) { + adap = pt3->adaps[i]; + dvb_frontend_resume(adap->fe); + ret = pt3_alloc_dmabuf(adap); + if (ret) { + dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs.\n"); + continue; + } + if (adap->num_feeds > 0) + pt3_start_dma(adap); + } + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + + +static void pt3_remove(struct pci_dev *pdev) +{ + struct pt3_board *pt3; + int i; + + pt3 = pci_get_drvdata(pdev); + for (i = PT3_NUM_FE - 1; i >= 0; i--) + pt3_cleanup_adapter(pt3, i); + i2c_del_adapter(&pt3->i2c_adap); + kfree(pt3->i2c_buf); + pci_iounmap(pt3->pdev, pt3->regs[0]); + pci_iounmap(pt3->pdev, pt3->regs[1]); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(pt3); +} + +static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + u8 rev; + u32 ver; + int i, ret; + struct pt3_board *pt3; + struct i2c_adapter *i2c; + + if (pci_read_config_byte(pdev, PCI_REVISION_ID, &rev) || rev != 1) + return -ENODEV; + + ret = pci_enable_device(pdev); + if (ret < 0) + return -ENODEV; + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret < 0) + goto err_disable_device; + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret == 0) + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + else { + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret == 0) + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + else { + dev_err(&pdev->dev, "Failed to set DMA mask.\n"); + goto err_release_regions; + } + dev_info(&pdev->dev, "Use 32bit DMA.\n"); + } + + pt3 = kzalloc(sizeof(*pt3), GFP_KERNEL); + if (!pt3) { + dev_err(&pdev->dev, "Failed to alloc mem for this dev.\n"); + ret = -ENOMEM; + goto err_release_regions; + } + pci_set_drvdata(pdev, pt3); + pt3->pdev = pdev; + mutex_init(&pt3->lock); + pt3->regs[0] = pci_ioremap_bar(pdev, 0); + pt3->regs[1] = pci_ioremap_bar(pdev, 2); + if (pt3->regs[0] == NULL || pt3->regs[1] == NULL) { + dev_err(&pdev->dev, "Failed to ioremap.\n"); + ret = -ENOMEM; + goto err_kfree; + } + + ver = ioread32(pt3->regs[0] + REG_VERSION); + if ((ver >> 16) != 0x0301) { + dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported", + ver >> 24, (ver & 0x00ff0000) >> 16); + ret = -ENODEV; + goto err_iounmap; + } + + pt3->num_bufs = clamp_val(num_bufs, MIN_DATA_BUFS, MAX_DATA_BUFS); + + pt3->i2c_buf = kmalloc(sizeof(*pt3->i2c_buf), GFP_KERNEL); + if (pt3->i2c_buf == NULL) { + dev_err(&pdev->dev, "Failed to alloc mem for i2c.\n"); + ret = -ENOMEM; + goto err_iounmap; + } + i2c = &pt3->i2c_adap; + i2c->owner = THIS_MODULE; + i2c->algo = &pt3_i2c_algo; + i2c->algo_data = NULL; + i2c->dev.parent = &pdev->dev; + strlcpy(i2c->name, DRV_NAME, sizeof(i2c->name)); + i2c_set_adapdata(i2c, pt3); + ret = i2c_add_adapter(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add i2c adapter.\n"); + goto err_i2cbuf; + } + + for (i = 0; i < PT3_NUM_FE; i++) { + ret = pt3_alloc_adapter(pt3, i); + if (ret < 0) + break; + + ret = pt3_attach_fe(pt3, i); + if (ret < 0) + break; + } + if (i < PT3_NUM_FE) { + dev_err(&pdev->dev, "Failed to create FE%d.\n", i); + goto err_cleanup_adapters; + } + + ret = pt3_fe_init(pt3); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init frontends.\n"); + i = PT3_NUM_FE - 1; + goto err_cleanup_adapters; + } + + dev_info(&pdev->dev, + "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x).\n", + ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff); + return 0; + +err_cleanup_adapters: + while (i >= 0) + pt3_cleanup_adapter(pt3, i--); + i2c_del_adapter(i2c); +err_i2cbuf: + kfree(pt3->i2c_buf); +err_iounmap: + if (pt3->regs[0]) + pci_iounmap(pdev, pt3->regs[0]); + if (pt3->regs[1]) + pci_iounmap(pdev, pt3->regs[1]); +err_kfree: + kfree(pt3); +err_release_regions: + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); + return ret; + +} + +static const struct pci_device_id pt3_id_table[] = { + { PCI_DEVICE_SUB(0x1172, 0x4c15, 0xee8d, 0x0368) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, pt3_id_table); + +static SIMPLE_DEV_PM_OPS(pt3_pm_ops, pt3_suspend, pt3_resume); + +static struct pci_driver pt3_driver = { + .name = DRV_NAME, + .probe = pt3_probe, + .remove = pt3_remove, + .id_table = pt3_id_table, + + .driver.pm = &pt3_pm_ops, +}; + +module_pci_driver(pt3_driver); + +MODULE_DESCRIPTION("Earthsoft PT3 Driver"); +MODULE_AUTHOR("Akihiro TSUKADA"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h new file mode 100644 index 000000000000..1b3f2ad25db3 --- /dev/null +++ b/drivers/media/pci/pt3/pt3.h @@ -0,0 +1,186 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.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 version 2. + * + * + * 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. + */ + +#ifndef PT3_H +#define PT3_H + +#include <linux/atomic.h> +#include <linux/types.h> + +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +#include "tc90522.h" +#include "mxl301rf.h" +#include "qm1d1c0042.h" + +#define DRV_NAME KBUILD_MODNAME + +#define PT3_NUM_FE 4 + +/* + * register index of the FPGA chip + */ +#define REG_VERSION 0x00 +#define REG_BUS 0x04 +#define REG_SYSTEM_W 0x08 +#define REG_SYSTEM_R 0x0c +#define REG_I2C_W 0x10 +#define REG_I2C_R 0x14 +#define REG_RAM_W 0x18 +#define REG_RAM_R 0x1c +#define REG_DMA_BASE 0x40 /* regs for FE[i] = REG_DMA_BASE + 0x18 * i */ +#define OFST_DMA_DESC_L 0x00 +#define OFST_DMA_DESC_H 0x04 +#define OFST_DMA_CTL 0x08 +#define OFST_TS_CTL 0x0c +#define OFST_STATUS 0x10 +#define OFST_TS_ERR 0x14 + +/* + * internal buffer for I2C + */ +#define PT3_I2C_MAX 4091 +struct pt3_i2cbuf { + u8 data[PT3_I2C_MAX]; + u8 tmp; + u32 num_cmds; +}; + +/* + * DMA things + */ +#define TS_PACKET_SZ 188 +/* DMA transfers must not cross 4GiB, so use one page / transfer */ +#define DATA_XFER_SZ 4096 +#define DATA_BUF_XFERS 47 +/* (num_bufs * DATA_BUF_SZ) % TS_PACKET_SZ must be 0 */ +#define DATA_BUF_SZ (DATA_BUF_XFERS * DATA_XFER_SZ) +#define MAX_DATA_BUFS 16 +#define MIN_DATA_BUFS 2 + +#define DESCS_IN_PAGE (PAGE_SIZE / sizeof(struct xfer_desc)) +#define MAX_NUM_XFERS (MAX_DATA_BUFS * DATA_BUF_XFERS) +#define MAX_DESC_BUFS DIV_ROUND_UP(MAX_NUM_XFERS, DESCS_IN_PAGE) + +/* DMA transfer description. + * device is passed a pointer to this struct, dma-reads it, + * and gets the DMA buffer ring for storing TS data. + */ +struct xfer_desc { + u32 addr_l; /* bus address of target data buffer */ + u32 addr_h; + u32 size; + u32 next_l; /* bus adddress of the next xfer_desc */ + u32 next_h; +}; + +/* A DMA mapping of a page containing xfer_desc's */ +struct xfer_desc_buffer { + dma_addr_t b_addr; + struct xfer_desc *descs; /* PAGE_SIZE (xfer_desc[DESCS_IN_PAGE]) */ +}; + +/* A DMA mapping of a data buffer */ +struct dma_data_buffer { + dma_addr_t b_addr; + u8 *data; /* size: u8[PAGE_SIZE] */ +}; + +/* + * device things + */ +struct pt3_adap_config { + struct i2c_board_info demod_info; + struct tc90522_config demod_cfg; + + struct i2c_board_info tuner_info; + union tuner_config { + struct qm1d1c0042_config qm1d1c0042; + struct mxl301rf_config mxl301rf; + } tuner_cfg; + u32 init_freq; +}; + +struct pt3_adapter { + struct dvb_adapter dvb_adap; /* dvb_adap.priv => struct pt3_board */ + int adap_idx; + + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dvb_frontend *fe; + struct i2c_client *i2c_demod; + struct i2c_client *i2c_tuner; + + /* data fetch thread */ + struct task_struct *thread; + int num_feeds; + + bool cur_lna; + bool cur_lnb; /* current LNB power status (on/off) */ + + /* items below are for DMA */ + struct dma_data_buffer buffer[MAX_DATA_BUFS]; + int buf_idx; + int buf_ofs; + int num_bufs; /* == pt3_board->num_bufs */ + int num_discard; /* how many access units to discard initially */ + + struct xfer_desc_buffer desc_buf[MAX_DESC_BUFS]; + int num_desc_bufs; /* == num_bufs * DATA_BUF_XFERS / DESCS_IN_PAGE */ +}; + + +struct pt3_board { + struct pci_dev *pdev; + void __iomem *regs[2]; + /* regs[0]: registers, regs[1]: internal memory, used for I2C */ + + struct mutex lock; + + /* LNB power shared among sat-FEs */ + int lnb_on_cnt; /* LNB power on count */ + + /* LNA shared among terr-FEs */ + int lna_on_cnt; /* booster enabled count */ + + int num_bufs; /* number of DMA buffers allocated/mapped per FE */ + + struct i2c_adapter i2c_adap; + struct pt3_i2cbuf *i2c_buf; + + struct pt3_adapter *adaps[PT3_NUM_FE]; +}; + + +/* + * prototypes + */ +extern int pt3_alloc_dmabuf(struct pt3_adapter *adap); +extern void pt3_init_dmabuf(struct pt3_adapter *adap); +extern void pt3_free_dmabuf(struct pt3_adapter *adap); +extern int pt3_start_dma(struct pt3_adapter *adap); +extern int pt3_stop_dma(struct pt3_adapter *adap); +extern int pt3_proc_dma(struct pt3_adapter *adap); + +extern int pt3_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num); +extern u32 pt3_i2c_functionality(struct i2c_adapter *adap); +extern void pt3_i2c_reset(struct pt3_board *pt3); +extern int pt3_init_all_demods(struct pt3_board *pt3); +extern int pt3_init_all_mxl301rf(struct pt3_board *pt3); +#endif /* PT3_H */ diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c new file mode 100644 index 000000000000..f0ce90437fac --- /dev/null +++ b/drivers/media/pci/pt3/pt3_dma.c @@ -0,0 +1,225 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.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 version 2. + * + * + * 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. + */ +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/pci.h> + +#include "pt3.h" + +#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128) +#define PT3_BUF_CANARY (0x74) + +static u32 get_dma_base(int idx) +{ + int i; + + i = (idx == 1 || idx == 2) ? 3 - idx : idx; + return REG_DMA_BASE + 0x18 * i; +} + +int pt3_stop_dma(struct pt3_adapter *adap) +{ + struct pt3_board *pt3 = adap->dvb_adap.priv; + u32 base; + u32 stat; + int retry; + + base = get_dma_base(adap->adap_idx); + stat = ioread32(pt3->regs[0] + base + OFST_STATUS); + if (!(stat & 0x01)) + return 0; + + iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); + for (retry = 0; retry < 5; retry++) { + stat = ioread32(pt3->regs[0] + base + OFST_STATUS); + if (!(stat & 0x01)) + return 0; + msleep(50); + } + return -EIO; +} + +int pt3_start_dma(struct pt3_adapter *adap) +{ + struct pt3_board *pt3 = adap->dvb_adap.priv; + u32 base = get_dma_base(adap->adap_idx); + + iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); + iowrite32(lower_32_bits(adap->desc_buf[0].b_addr), + pt3->regs[0] + base + OFST_DMA_DESC_L); + iowrite32(upper_32_bits(adap->desc_buf[0].b_addr), + pt3->regs[0] + base + OFST_DMA_DESC_H); + iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL); + return 0; +} + + +static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs) +{ + *ofs += PT3_ACCESS_UNIT; + if (*ofs >= DATA_BUF_SZ) { + *ofs -= DATA_BUF_SZ; + (*idx)++; + if (*idx == adap->num_bufs) + *idx = 0; + } + return &adap->buffer[*idx].data[*ofs]; +} + +int pt3_proc_dma(struct pt3_adapter *adap) +{ + int idx, ofs; + + idx = adap->buf_idx; + ofs = adap->buf_ofs; + + if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY) + return 0; + + while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) { + u8 *p; + + p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs]; + if (adap->num_discard > 0) + adap->num_discard--; + else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) { + dvb_dmx_swfilter_packets(&adap->demux, p, + (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ); + dvb_dmx_swfilter_packets(&adap->demux, + adap->buffer[idx].data, ofs / TS_PACKET_SZ); + } else + dvb_dmx_swfilter_packets(&adap->demux, p, + PT3_ACCESS_UNIT / TS_PACKET_SZ); + + *p = PT3_BUF_CANARY; + adap->buf_idx = idx; + adap->buf_ofs = ofs; + } + return 0; +} + +void pt3_init_dmabuf(struct pt3_adapter *adap) +{ + int idx, ofs; + u8 *p; + + idx = 0; + ofs = 0; + p = adap->buffer[0].data; + /* mark the whole buffers as "not written yet" */ + while (idx < adap->num_bufs) { + p[ofs] = PT3_BUF_CANARY; + ofs += PT3_ACCESS_UNIT; + if (ofs >= DATA_BUF_SZ) { + ofs -= DATA_BUF_SZ; + idx++; + p = adap->buffer[idx].data; + } + } + adap->buf_idx = 0; + adap->buf_ofs = 0; +} + +void pt3_free_dmabuf(struct pt3_adapter *adap) +{ + struct pt3_board *pt3; + int i; + + pt3 = adap->dvb_adap.priv; + for (i = 0; i < adap->num_bufs; i++) + dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ, + adap->buffer[i].data, adap->buffer[i].b_addr); + adap->num_bufs = 0; + + for (i = 0; i < adap->num_desc_bufs; i++) + dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE, + adap->desc_buf[i].descs, adap->desc_buf[i].b_addr); + adap->num_desc_bufs = 0; +} + + +int pt3_alloc_dmabuf(struct pt3_adapter *adap) +{ + struct pt3_board *pt3; + void *p; + int i, j; + int idx, ofs; + int num_desc_bufs; + dma_addr_t data_addr, desc_addr; + struct xfer_desc *d; + + pt3 = adap->dvb_adap.priv; + adap->num_bufs = 0; + adap->num_desc_bufs = 0; + for (i = 0; i < pt3->num_bufs; i++) { + p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ, + &adap->buffer[i].b_addr, GFP_KERNEL); + if (p == NULL) + goto failed; + adap->buffer[i].data = p; + adap->num_bufs++; + } + pt3_init_dmabuf(adap); + + /* build circular-linked pointers (xfer_desc) to the data buffers*/ + idx = 0; + ofs = 0; + num_desc_bufs = + DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE); + for (i = 0; i < num_desc_bufs; i++) { + p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE, + &desc_addr, GFP_KERNEL); + if (p == NULL) + goto failed; + adap->num_desc_bufs++; + adap->desc_buf[i].descs = p; + adap->desc_buf[i].b_addr = desc_addr; + + if (i > 0) { + d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1]; + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + } + for (j = 0; j < DESCS_IN_PAGE; j++) { + data_addr = adap->buffer[idx].b_addr + ofs; + d = &adap->desc_buf[i].descs[j]; + d->addr_l = lower_32_bits(data_addr); + d->addr_h = upper_32_bits(data_addr); + d->size = DATA_XFER_SZ; + + desc_addr += sizeof(struct xfer_desc); + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + + ofs += DATA_XFER_SZ; + if (ofs >= DATA_BUF_SZ) { + ofs -= DATA_BUF_SZ; + idx++; + if (idx >= adap->num_bufs) { + desc_addr = adap->desc_buf[0].b_addr; + d->next_l = lower_32_bits(desc_addr); + d->next_h = upper_32_bits(desc_addr); + return 0; + } + } + } + } + return 0; + +failed: + pt3_free_dmabuf(adap); + return -ENOMEM; +} diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c new file mode 100644 index 000000000000..ec6a8a2e4744 --- /dev/null +++ b/drivers/media/pci/pt3/pt3_i2c.c @@ -0,0 +1,240 @@ +/* + * Earthsoft PT3 driver + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.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 version 2. + * + * + * 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. + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/pci.h> + +#include "pt3.h" + +#define PT3_I2C_BASE 2048 +#define PT3_CMD_ADDR_NORMAL 0 +#define PT3_CMD_ADDR_INIT_DEMOD 4096 +#define PT3_CMD_ADDR_INIT_TUNER (4096 + 2042) + +/* masks for I2C status register */ +#define STAT_SEQ_RUNNING 0x1 +#define STAT_SEQ_ERROR 0x6 +#define STAT_NO_SEQ 0x8 + +#define PT3_I2C_RUN (1 << 16) +#define PT3_I2C_RESET (1 << 17) + +enum ctl_cmd { + I_END, + I_ADDRESS, + I_CLOCK_L, + I_CLOCK_H, + I_DATA_L, + I_DATA_H, + I_RESET, + I_SLEEP, + I_DATA_L_NOP = 0x08, + I_DATA_H_NOP = 0x0c, + I_DATA_H_READ = 0x0d, + I_DATA_H_ACK0 = 0x0e, + I_DATA_H_ACK1 = 0x0f, +}; + + +static void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd) +{ + int buf_idx; + + if ((cbuf->num_cmds % 2) == 0) + cbuf->tmp = cmd; + else { + cbuf->tmp |= cmd << 4; + buf_idx = cbuf->num_cmds / 2; + if (buf_idx < ARRAY_SIZE(cbuf->data)) + cbuf->data[buf_idx] = cbuf->tmp; + } + cbuf->num_cmds++; +} + +static void put_end(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_END); + if (cbuf->num_cmds % 2) + cmdbuf_add(cbuf, I_END); +} + +static void put_start(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_DATA_H); + cmdbuf_add(cbuf, I_CLOCK_H); + cmdbuf_add(cbuf, I_DATA_L); + cmdbuf_add(cbuf, I_CLOCK_L); +} + +static void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val) +{ + u8 mask; + + mask = 0x80; + for (mask = 0x80; mask > 0; mask >>= 1) + cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP); + cmdbuf_add(cbuf, I_DATA_H_ACK0); +} + +static void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size) +{ + int i, j; + + for (i = 0; i < size; i++) { + for (j = 0; j < 8; j++) + cmdbuf_add(cbuf, I_DATA_H_READ); + cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP); + } +} + +static void put_stop(struct pt3_i2cbuf *cbuf) +{ + cmdbuf_add(cbuf, I_DATA_L); + cmdbuf_add(cbuf, I_CLOCK_H); + cmdbuf_add(cbuf, I_DATA_H); +} + + +/* translates msgs to internal commands for bit-banging */ +static void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num) +{ + int i, j; + bool rd; + + cbuf->num_cmds = 0; + for (i = 0; i < num; i++) { + rd = !!(msgs[i].flags & I2C_M_RD); + put_start(cbuf); + put_byte_write(cbuf, msgs[i].addr << 1 | rd); + if (rd) + put_byte_read(cbuf, msgs[i].len); + else + for (j = 0; j < msgs[i].len; j++) + put_byte_write(cbuf, msgs[i].buf[j]); + } + if (num > 0) { + put_stop(cbuf); + put_end(cbuf); + } +} + +static int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait) +{ + int i; + u32 v; + + for (i = 0; i < max_wait; i++) { + v = ioread32(pt3->regs[0] + REG_I2C_R); + if (!(v & STAT_SEQ_RUNNING)) + break; + usleep_range(500, 750); + } + if (i >= max_wait) + return -EIO; + if (result) + *result = v; + return 0; +} + +/* send [pre-]translated i2c msgs stored at addr */ +static int send_i2c_cmd(struct pt3_board *pt3, u32 addr) +{ + u32 ret; + + /* make sure that previous transactions had finished */ + if (wait_i2c_result(pt3, NULL, 50)) { + dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n", + __func__); + return -EIO; + } + + iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W); + usleep_range(200, 300); + /* wait for the current transaction to finish */ + if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) { + dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__); + return -EIO; + } + return 0; +} + + +/* init commands for each demod are combined into one transaction + * and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD. + */ +int pt3_init_all_demods(struct pt3_board *pt3) +{ + ioread32(pt3->regs[0] + REG_I2C_R); + return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD); +} + +/* init commands for two ISDB-T tuners are hidden in ROM. */ +int pt3_init_all_mxl301rf(struct pt3_board *pt3) +{ + usleep_range(1000, 2000); + return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER); +} + +void pt3_i2c_reset(struct pt3_board *pt3) +{ + iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W); +} + +/* + * I2C algorithm + */ +int +pt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct pt3_board *pt3; + struct pt3_i2cbuf *cbuf; + int i; + void __iomem *p; + + pt3 = i2c_get_adapdata(adap); + cbuf = pt3->i2c_buf; + + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RECV_LEN) { + dev_warn(&pt3->pdev->dev, + "(%s) I2C_M_RECV_LEN not supported.\n", + __func__); + return -EINVAL; + } + + translate(cbuf, msgs, num); + memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2, + cbuf->data, cbuf->num_cmds); + + if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0) + return -EIO; + + p = pt3->regs[1] + PT3_I2C_BASE; + for (i = 0; i < num; i++) + if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) { + memcpy_fromio(msgs[i].buf, p, msgs[i].len); + p += msgs[i].len; + } + + return num; +} + +u32 pt3_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 18ae75546302..b44e0d70907e 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -63,3 +63,11 @@ config VIDEO_SAA7134_DVB To compile this driver as a module, choose M here: the module will be called saa7134-dvb. + +config VIDEO_SAA7134_GO7007 + tristate "go7007 support for saa7134 based TV cards" + depends on VIDEO_SAA7134 + depends on VIDEO_GO7007 + ---help--- + Enables saa7134 driver support for boards with go7007 + MPEG encoder (WIS Voyager or compatible). diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile index 58de9b085689..09c43da67588 100644 --- a/drivers/media/pci/saa7134/Makefile +++ b/drivers/media/pci/saa7134/Makefile @@ -5,6 +5,7 @@ saa7134-y += saa7134-video.o saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o +obj-$(CONFIG_VIDEO_SAA7134_GO7007) += saa7134-go7007.o obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o @@ -14,3 +15,4 @@ ccflags-y += -I$(srctree)/drivers/media/i2c ccflags-y += -I$(srctree)/drivers/media/tuners ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +ccflags-y += -I$(srctree)/drivers/media/usb/go7007 diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index 6e4bdb90aa92..3ca078057755 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5827,6 +5827,29 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0000800, }, }, + [SAA7134_BOARD_WIS_VOYAGER] = { + .name = "WIS Voyager or compatible", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_GO7007, + .inputs = { { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + }, }; @@ -7080,6 +7103,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2055, /* AverTV Satellite Hybrid+FM A706 */ .driver_data = SAA7134_BOARD_AVERMEDIA_A706, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1905, /* WIS */ + .subdevice = 0x7007, + .driver_data = SAA7134_BOARD_WIS_VOYAGER, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 9ff03a69ced4..236ed725f933 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -160,6 +160,8 @@ static void request_module_async(struct work_struct *work){ request_module("saa7134-empress"); if (card_is_dvb(dev)) request_module("saa7134-dvb"); + if (card_is_go7007(dev)) + request_module("saa7134-go7007"); if (alsa) { if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) request_module("saa7134-alsa"); @@ -563,8 +565,12 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id) saa7134_irq_vbi_done(dev,status); if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && - card_has_mpeg(dev)) - saa7134_irq_ts_done(dev,status); + card_has_mpeg(dev)) { + if (dev->mops->irq_ts_done != NULL) + dev->mops->irq_ts_done(dev, status); + else + saa7134_irq_ts_done(dev, status); + } if (report & SAA7134_IRQ_REPORT_GPIO16) { switch (dev->has_remote) { diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c new file mode 100644 index 000000000000..54e650b4dff1 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-go7007.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <asm/byteorder.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "saa7134.h" +#include "saa7134-reg.h" +#include "go7007-priv.h" + +/*#define GO7007_HPI_DEBUG*/ + +enum hpi_address { + HPI_ADDR_VIDEO_BUFFER = 0xe4, + HPI_ADDR_INIT_BUFFER = 0xea, + HPI_ADDR_INTR_RET_VALUE = 0xee, + HPI_ADDR_INTR_RET_DATA = 0xec, + HPI_ADDR_INTR_STATUS = 0xf4, + HPI_ADDR_INTR_WR_PARAM = 0xf6, + HPI_ADDR_INTR_WR_INDEX = 0xf8, +}; + +enum gpio_command { + GPIO_COMMAND_RESET = 0x00, /* 000b */ + GPIO_COMMAND_REQ1 = 0x04, /* 001b */ + GPIO_COMMAND_WRITE = 0x20, /* 010b */ + GPIO_COMMAND_REQ2 = 0x24, /* 011b */ + GPIO_COMMAND_READ = 0x80, /* 100b */ + GPIO_COMMAND_VIDEO = 0x84, /* 101b */ + GPIO_COMMAND_IDLE = 0xA0, /* 110b */ + GPIO_COMMAND_ADDR = 0xA4, /* 111b */ +}; + +struct saa7134_go7007 { + struct v4l2_subdev sd; + struct saa7134_dev *dev; + u8 *top; + u8 *bottom; + dma_addr_t top_dma; + dma_addr_t bottom_dma; +}; + +static inline struct saa7134_go7007 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7134_go7007, sd); +} + +static const struct go7007_board_info board_voyager = { + .flags = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_inputs = 1, + .inputs = { + { + .name = "SAA7134", + }, + }, +}; + +/********************* Driver for GPIO HPI interface *********************/ + +static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Read low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data = saa_readb(SAA7134_GPIO_GPSTATUS0); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Read high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8; + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int saa7134_go7007_interface_reset(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u16 intr_val, intr_data; + int count = 20; + + saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */ + saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4); + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET); + msleep(1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + msleep(10); + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + saa_readb(SAA7134_GPIO_GPSTATUS2); + /*pr_debug("status is %s\n", saa_readb(SAA7134_GPIO_GPSTATUS2) & 0x40 ? "OK" : "not OK"); */ + + /* enter command mode...(?) */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + + do { + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_readb(SAA7134_GPIO_GPSTATUS2); + /*pr_info("gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ + } while (--count > 0); + + /* Wait for an interrupt to indicate successful hardware reset */ + if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x55aa) { + pr_err("saa7134-go7007: unable to reset the GO7007\n"); + return -1; + } + return 0; +} + +static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + int i; + u16 status_reg; + +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); +#endif + + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0010)) + break; + msleep(10); + } + if (i == 100) { + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data); + gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr); + + return 0; +} + +static int saa7134_go7007_read_interrupt(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + /* XXX we need to wait if there is no interrupt available */ + go->interrupt_available = 1; + gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); + gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: ReadInterrupt: %04x %04x\n", + go->interrupt_value, go->interrupt_data); +#endif + return 0; +} + +static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, + unsigned long status) +{ + struct go7007 *go = video_get_drvdata(dev->empress_dev); + struct saa7134_go7007 *saa = go->hpi_context; + + if (!vb2_is_streaming(&go->vidq)) + return; + if (0 != (status & 0x000f0000)) + pr_debug("saa7134-go7007: irq: lost %ld\n", + (status >> 16) & 0x0f); + if (status & 0x100000) { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE); + saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma); + } else { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->top, PAGE_SIZE); + saa_writel(SAA7134_RS_BA1(5), saa->top_dma); + } +} + +static int saa7134_go7007_stream_start(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&dev->pci->dev, saa->top_dma)) + return -ENOMEM; + saa->bottom_dma = dma_map_page(&dev->pci->dev, + virt_to_page(saa->bottom), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&dev->pci->dev, saa->bottom_dma)) { + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + return -ENOMEM; + } + + saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000); + saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200); + + /* Set HPI interface for video */ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Enable TS interface */ + saa_writeb(SAA7134_TS_PARALLEL, 0xe6); + + /* Reset TS interface */ + saa_setb(SAA7134_TS_SERIAL1, 0x01); + saa_clearb(SAA7134_TS_SERIAL1, 0x01); + + /* Set up transfer block size */ + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); + saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); + saa_writeb(SAA7134_TS_DMA1, 0); + saa_writeb(SAA7134_TS_DMA2, 0); + + /* Enable video streaming mode */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); + + saa_writel(SAA7134_RS_BA1(5), saa->top_dma); + saa_writel(SAA7134_RS_BA2(5), saa->bottom_dma); + saa_writel(SAA7134_RS_PITCH(5), 128); + saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX); + + /* Enable TS FIFO */ + saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Enable DMA IRQ */ + saa_setl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + return 0; +} + +static int saa7134_go7007_stream_stop(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev; + + if (!saa) + return -EINVAL; + dev = saa->dev; + if (!dev) + return -EINVAL; + + /* Shut down TS FIFO */ + saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Disable DMA IRQ */ + saa_clearl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + /* Disable TS interface */ + saa_clearb(SAA7134_TS_PARALLEL, 0x80); + + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + + return 0; +} + +static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u16 status_reg; + int i; + +#ifdef GO7007_HPI_DEBUG + pr_debug("saa7134-go7007: DownloadBuffer sending %d bytes\n", len); +#endif + + while (len > 0) { + i = len > 64 ? 64 : len; + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + while (i-- > 0) { + saa_writeb(SAA7134_GPIO_GPSTATUS0, *data); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + ++data; + --len; + } + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0002)) + break; + } + if (i == 100) { + pr_err("saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + } + return 0; +} + +static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { + .interface_reset = saa7134_go7007_interface_reset, + .write_interrupt = saa7134_go7007_write_interrupt, + .read_interrupt = saa7134_go7007_read_interrupt, + .stream_start = saa7134_go7007_stream_start, + .stream_stop = saa7134_go7007_stream_stop, + .send_firmware = saa7134_go7007_send_firmware, +}; +MODULE_FIRMWARE("go7007/go7007tv.bin"); + +/* --------------------------------------------------------------------------*/ + +static int saa7134_go7007_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ +#if 0 + struct saa7134_go7007 *saa = to_state(sd); + struct saa7134_dev *dev = saa->dev; + + return saa7134_s_std_internal(dev, NULL, norm); +#else + return 0; +#endif +} + +static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = { + .s_std = saa7134_go7007_s_std, +}; + +static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = { + .video = &saa7134_go7007_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + + +/********************* Add/remove functions *********************/ + +static int saa7134_go7007_init(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + struct v4l2_subdev *sd; + + pr_debug("saa7134-go7007: probing new SAA713X board\n"); + + go = go7007_alloc(&board_voyager, &dev->pci->dev); + if (go == NULL) + return -ENOMEM; + + saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); + if (saa == NULL) { + kfree(go); + return -ENOMEM; + } + + go->board_id = GO7007_BOARDID_PCI_VOYAGER; + snprintf(go->bus_info, sizeof(go->bus_info), "PCI:%s", pci_name(dev->pci)); + strlcpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); + go->hpi_ops = &saa7134_go7007_hpi_ops; + go->hpi_context = saa; + saa->dev = dev; + + /* Init the subdevice interface */ + sd = &saa->sd; + v4l2_subdev_init(sd, &saa7134_go7007_sd_ops); + v4l2_set_subdevdata(sd, saa); + strncpy(sd->name, "saa7134-go7007", sizeof(sd->name)); + + /* Allocate a couple pages for receiving the compressed stream */ + saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->top) + goto allocfail; + saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->bottom) + goto allocfail; + + /* Boot the GO7007 */ + if (go7007_boot_encoder(go, go->board_info->flags & + GO7007_BOARD_USE_ONBOARD_I2C) < 0) + goto allocfail; + + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go, go->board_info->num_i2c_devs) < 0) + goto allocfail; + + /* Register the subdevice interface with the go7007 device */ + if (v4l2_device_register_subdev(&go->v4l2_dev, sd) < 0) + pr_info("saa7134-go7007: register subdev failed\n"); + + dev->empress_dev = &go->vdev; + + go->status = STATUS_ONLINE; + return 0; + +allocfail: + if (saa->top) + free_page((unsigned long)saa->top); + if (saa->bottom) + free_page((unsigned long)saa->bottom); + kfree(saa); + kfree(go); + return -ENOMEM; +} + +static int saa7134_go7007_fini(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + + if (NULL == dev->empress_dev) + return 0; + + go = video_get_drvdata(dev->empress_dev); + if (go->audio_enabled) + go7007_snd_remove(go); + + saa = go->hpi_context; + go->status = STATUS_SHUTDOWN; + free_page((unsigned long)saa->top); + free_page((unsigned long)saa->bottom); + v4l2_device_unregister_subdev(&saa->sd); + kfree(saa); + video_unregister_device(&go->vdev); + + v4l2_device_put(&go->v4l2_dev); + dev->empress_dev = NULL; + + return 0; +} + +static struct saa7134_mpeg_ops saa7134_go7007_ops = { + .type = SAA7134_MPEG_GO7007, + .init = saa7134_go7007_init, + .fini = saa7134_go7007_fini, + .irq_ts_done = saa7134_go7007_irq_ts_done, +}; + +static int __init saa7134_go7007_mod_init(void) +{ + return saa7134_ts_register(&saa7134_go7007_ops); +} + +static void __exit saa7134_go7007_mod_cleanup(void) +{ + saa7134_ts_unregister(&saa7134_go7007_ops); +} + +module_init(saa7134_go7007_mod_init); +module_exit(saa7134_go7007_mod_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index c06dbe17a87f..4f0b1012e4f3 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); /* ------------------------------------------------------------------ */ -#define VBI_LINE_COUNT 16 +#define VBI_LINE_COUNT 17 #define VBI_LINE_LENGTH 2048 #define VBI_SCALE 0x200 diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 0cfa2ca6a32a..fc4a427cb51f 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -201,7 +201,7 @@ static struct saa7134_format formats[] = { .video_v_start = 24, \ .video_v_stop = 311, \ .vbi_v_start_0 = 7, \ - .vbi_v_stop_0 = 22, \ + .vbi_v_stop_0 = 23, \ .vbi_v_start_1 = 319, \ .src_timing = 4 diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index e47edd4b57ce..1a82dd07205b 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -338,6 +338,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_ASUSTeK_PS3_100 190 #define SAA7134_BOARD_HAWELL_HW_9004V1 191 #define SAA7134_BOARD_AVERMEDIA_A706 192 +#define SAA7134_BOARD_WIS_VOYAGER 193 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -368,6 +369,7 @@ enum saa7134_mpeg_type { SAA7134_MPEG_UNUSED, SAA7134_MPEG_EMPRESS, SAA7134_MPEG_DVB, + SAA7134_MPEG_GO7007, }; enum saa7134_mpeg_ts_type { @@ -407,6 +409,7 @@ struct saa7134_board { #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) #define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) #define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) +#define card_is_go7007(dev) (SAA7134_MPEG_GO7007 == saa7134_boards[dev->board].mpeg) #define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) #define card(dev) (saa7134_boards[dev->board]) #define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) @@ -522,6 +525,8 @@ struct saa7134_mpeg_ops { int (*init)(struct saa7134_dev *dev); int (*fini)(struct saa7134_dev *dev); void (*signal_change)(struct saa7134_dev *dev); + void (*irq_ts_done)(struct saa7134_dev *dev, + unsigned long status); }; /* global device status */ diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c index e042963d377d..4f3b1dd18ba4 100644 --- a/drivers/media/pci/saa7164/saa7164-api.c +++ b/drivers/media/pci/saa7164/saa7164-api.c @@ -680,7 +680,6 @@ static int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val) int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) { struct saa7164_dev *dev = port->dev; - int ret = 0; u8 agc_disable; dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std); @@ -733,7 +732,7 @@ int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */ msleep(100); - return ret; + return 0; } /* Ensure the dif is in the correct state for the operating mode diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 1bf06970ca3e..cc1be8a7a451 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -52,7 +52,7 @@ unsigned int saa_debug; module_param_named(debug, saa_debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); -unsigned int fw_debug; +static unsigned int fw_debug; module_param(fw_debug, int, 0644); MODULE_PARM_DESC(fw_debug, "Firmware debug level def:2"); @@ -72,7 +72,7 @@ static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -unsigned int print_histogram = 64; +static unsigned int print_histogram = 64; module_param(print_histogram, int, 0644); MODULE_PARM_DESC(print_histogram, "print histogram values once"); @@ -80,7 +80,7 @@ unsigned int crc_checking = 1; module_param(crc_checking, int, 0644); MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); -unsigned int guard_checking = 1; +static unsigned int guard_checking = 1; module_param(guard_checking, int, 0644); MODULE_PARM_DESC(guard_checking, "enable dma sanity checking for buffer overruns"); diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index d9e06a6bf1eb..0fb91dc7ca73 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -1,6 +1,7 @@ config VIDEO_SOLO6X10 tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" depends on PCI && VIDEO_DEV && SND && I2C + depends on HAS_DMA select BITREVERSE select FONT_SUPPORT select FONT_8x16 diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index 5ea9cac03968..11c98f0625e4 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -172,7 +172,7 @@ static void solo_vout_config(struct solo_dev *solo_dev) static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, u16 val, int reg_size) { - u16 *buf; + __le16 *buf; const int n = 64, size = n * sizeof(*buf); int i, ret = 0; @@ -211,7 +211,7 @@ int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, { const unsigned size = sizeof(u16) * 64; u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; - u16 *buf; + __le16 *buf; int x, y; int ret = 0; diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index af40b3aba410..da25ce4a6952 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -100,7 +100,7 @@ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) return retval; } -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) +__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc) { int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); unsigned short retval = 0; @@ -117,11 +117,11 @@ unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc) solo_eeprom_reg_write(solo_dev, ~EE_CS); - return retval; + return (__force __be16)retval; } int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data) + __be16 data) { int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN); unsigned int retval; @@ -130,7 +130,7 @@ int solo_eeprom_write(struct solo_dev *solo_dev, int loc, solo_eeprom_cmd(solo_dev, write_cmd); for (i = 15; i >= 0; i--) { - unsigned int dataval = (data >> i) & 1; + unsigned int dataval = ((__force unsigned)data >> i) & 1; solo_eeprom_reg_write(solo_dev, EE_ENB); solo_eeprom_reg_write(solo_dev, diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index c6154b00fcbd..72017b7f0a75 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -394,9 +394,9 @@ int solo_osd_print(struct solo_enc_dev *solo_enc); /* EEPROM commands */ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en); -unsigned short solo_eeprom_read(struct solo_dev *solo_dev, int loc); +__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc); int solo_eeprom_write(struct solo_dev *solo_dev, int loc, - unsigned short data); + __be16 data); /* JPEG Qp functions */ void solo_s_jpeg_qp(struct solo_dev *solo_dev, unsigned int ch, diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 03130157db83..f6f30abc088b 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,6 +1,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 + depends on HAS_DMA select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 365bd21301ba..22450f583da1 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -152,7 +152,7 @@ struct sta2x11_vip { int tcount, bcount; int overflow; - void *iomem; /* I/O Memory */ + void __iomem *iomem; /* I/O Memory */ struct vip_config *config; }; diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 0dcb8cd77676..7b83151ed6c4 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -1,8 +1,12 @@ +config DVB_AV7110_IR + bool + config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C select TTPCI_EEPROM select VIDEO_SAA7146_VV + select DVB_AV7110_IR if INPUT_EVDEV=y || INPUT_EVDEV=DVB_AV7110 depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile index 98905963ff08..49f71b1eaf14 100644 --- a/drivers/media/pci/ttpci/Makefile +++ b/drivers/media/pci/ttpci/Makefile @@ -5,7 +5,7 @@ dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o -ifdef CONFIG_INPUT_EVDEV +ifdef CONFIG_DVB_AV7110_IR dvb-ttpci-objs += av7110_ir.o endif diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index f38329d29daa..c1f0617a6973 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -235,7 +235,7 @@ static void recover_arm(struct av7110 *av7110) restart_feeds(av7110); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_check_ir_config(av7110, true); #endif } @@ -268,7 +268,7 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_check_ir_config(av7110, false); #endif @@ -2725,7 +2725,7 @@ static int av7110_attach(struct saa7146_dev* dev, mutex_init(&av7110->ioctl_mutex); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_ir_init(av7110); #endif printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); @@ -2768,7 +2768,7 @@ static int av7110_detach(struct saa7146_dev* saa) struct av7110 *av7110 = saa->ext_priv; dprintk(4, "%p\n", av7110); -#if IS_ENABLED(CONFIG_INPUT_EVDEV) +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_ir_exit(av7110); #endif if (budgetpatch || av7110->full_ts) { diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig new file mode 100644 index 000000000000..5425ba1e320d --- /dev/null +++ b/drivers/media/pci/tw68/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_TW68 + tristate "Techwell tw68x Video For Linux" + depends on VIDEO_DEV && PCI && VIDEO_V4L2 + select I2C_ALGOBIT + select VIDEOBUF2_DMA_SG + ---help--- + Support for Techwell tw68xx based frame grabber boards. + + To compile this driver as a module, choose M here: the + module will be called tw68. diff --git a/drivers/media/pci/tw68/Makefile b/drivers/media/pci/tw68/Makefile new file mode 100644 index 000000000000..3d02f28b14fb --- /dev/null +++ b/drivers/media/pci/tw68/Makefile @@ -0,0 +1,3 @@ +tw68-objs := tw68-core.o tw68-video.o tw68-risc.o + +obj-$(CONFIG_VIDEO_TW68) += tw68.o diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c new file mode 100644 index 000000000000..a6fb48cf7aae --- /dev/null +++ b/drivers/media/pci/tw68/tw68-core.c @@ -0,0 +1,434 @@ +/* + * tw68-core.c + * Core functions for the Techwell 68xx driver + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/sound.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/pm.h> + +#include <media/v4l2-dev.h> +#include "tw68.h" +#include "tw68-reg.h" + +MODULE_DESCRIPTION("v4l2 driver module for tw6800 based video capture cards"); +MODULE_AUTHOR("William M. Brack"); +MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); +MODULE_LICENSE("GPL"); + +static unsigned int latency = UNSET; +module_param(latency, int, 0444); +MODULE_PARM_DESC(latency, "pci latency timer"); + +static unsigned int video_nr[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET }; +module_param_array(video_nr, int, NULL, 0444); +MODULE_PARM_DESC(video_nr, "video device number"); + +static unsigned int card[] = {[0 ... (TW68_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static atomic_t tw68_instance = ATOMIC_INIT(0); + +/* ------------------------------------------------------------------ */ + +/* + * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * the PCI ID database up to date. Note that the entries must be + * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. + */ +static const struct pci_device_id tw68_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6800)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6801)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6804)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_1)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_2)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_3)}, + {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_6816_4)}, + {0,} +}; + +/* ------------------------------------------------------------------ */ + + +/* + * The device is given a "soft reset". According to the specifications, + * after this "all register content remain unchanged", so we also write + * to all specified registers manually as well (mostly to manufacturer's + * specified reset values) + */ +static int tw68_hw_init1(struct tw68_dev *dev) +{ + /* Assure all interrupts are disabled */ + tw_writel(TW68_INTMASK, 0); /* 020 */ + /* Clear any pending interrupts */ + tw_writel(TW68_INTSTAT, 0xffffffff); /* 01C */ + /* Stop risc processor, set default buffer level */ + tw_writel(TW68_DMAC, 0x1600); + + tw_writeb(TW68_ACNTL, 0x80); /* 218 soft reset */ + msleep(100); + + tw_writeb(TW68_INFORM, 0x40); /* 208 mux0, 27mhz xtal */ + tw_writeb(TW68_OPFORM, 0x04); /* 20C analog line-lock */ + tw_writeb(TW68_HSYNC, 0); /* 210 color-killer high sens */ + tw_writeb(TW68_ACNTL, 0x42); /* 218 int vref #2, chroma adc off */ + + tw_writeb(TW68_CROP_HI, 0x02); /* 21C Hactive m.s. bits */ + tw_writeb(TW68_VDELAY_LO, 0x12);/* 220 Mfg specified reset value */ + tw_writeb(TW68_VACTIVE_LO, 0xf0); + tw_writeb(TW68_HDELAY_LO, 0x0f); + tw_writeb(TW68_HACTIVE_LO, 0xd0); + + tw_writeb(TW68_CNTRL1, 0xcd); /* 230 Wide Chroma BPF B/W + * Secam reduction, Adap comb for + * NTSC, Op Mode 1 */ + + tw_writeb(TW68_VSCALE_LO, 0); /* 234 */ + tw_writeb(TW68_SCALE_HI, 0x11); /* 238 */ + tw_writeb(TW68_HSCALE_LO, 0); /* 23c */ + tw_writeb(TW68_BRIGHT, 0); /* 240 */ + tw_writeb(TW68_CONTRAST, 0x5c); /* 244 */ + tw_writeb(TW68_SHARPNESS, 0x51);/* 248 */ + tw_writeb(TW68_SAT_U, 0x80); /* 24C */ + tw_writeb(TW68_SAT_V, 0x80); /* 250 */ + tw_writeb(TW68_HUE, 0x00); /* 254 */ + + /* TODO - Check that none of these are set by control defaults */ + tw_writeb(TW68_SHARP2, 0x53); /* 258 Mfg specified reset val */ + tw_writeb(TW68_VSHARP, 0x80); /* 25C Sharpness Coring val 8 */ + tw_writeb(TW68_CORING, 0x44); /* 260 CTI and Vert Peak coring */ + tw_writeb(TW68_CNTRL2, 0x00); /* 268 No power saving enabled */ + tw_writeb(TW68_SDT, 0x07); /* 270 Enable shadow reg, auto-det */ + tw_writeb(TW68_SDTR, 0x7f); /* 274 All stds recog, don't start */ + tw_writeb(TW68_CLMPG, 0x50); /* 280 Clamp end at 40 sys clocks */ + tw_writeb(TW68_IAGC, 0x22); /* 284 Mfg specified reset val */ + tw_writeb(TW68_AGCGAIN, 0xf0); /* 288 AGC gain when loop disabled */ + tw_writeb(TW68_PEAKWT, 0xd8); /* 28C White peak threshold */ + tw_writeb(TW68_CLMPL, 0x3c); /* 290 Y channel clamp level */ +/* tw_writeb(TW68_SYNCT, 0x38);*/ /* 294 Sync amplitude */ + tw_writeb(TW68_SYNCT, 0x30); /* 294 Sync amplitude */ + tw_writeb(TW68_MISSCNT, 0x44); /* 298 Horiz sync, VCR detect sens */ + tw_writeb(TW68_PCLAMP, 0x28); /* 29C Clamp pos from PLL sync */ + /* Bit DETV of VCNTL1 helps sync multi cams/chip board */ + tw_writeb(TW68_VCNTL1, 0x04); /* 2A0 */ + tw_writeb(TW68_VCNTL2, 0); /* 2A4 */ + tw_writeb(TW68_CKILL, 0x68); /* 2A8 Mfg specified reset val */ + tw_writeb(TW68_COMB, 0x44); /* 2AC Mfg specified reset val */ + tw_writeb(TW68_LDLY, 0x30); /* 2B0 Max positive luma delay */ + tw_writeb(TW68_MISC1, 0x14); /* 2B4 Mfg specified reset val */ + tw_writeb(TW68_LOOP, 0xa5); /* 2B8 Mfg specified reset val */ + tw_writeb(TW68_MISC2, 0xe0); /* 2BC Enable colour killer */ + tw_writeb(TW68_MVSN, 0); /* 2C0 */ + tw_writeb(TW68_CLMD, 0x05); /* 2CC slice level auto, clamp med. */ + tw_writeb(TW68_IDCNTL, 0); /* 2D0 Writing zero to this register + * selects NTSC ID detection, + * but doesn't change the + * sensitivity (which has a reset + * value of 1E). Since we are + * not doing auto-detection, it + * has no real effect */ + tw_writeb(TW68_CLCNTL1, 0); /* 2D4 */ + tw_writel(TW68_VBIC, 0x03); /* 010 */ + tw_writel(TW68_CAP_CTL, 0x03); /* 040 Enable both even & odd flds */ + tw_writel(TW68_DMAC, 0x2000); /* patch set had 0x2080 */ + tw_writel(TW68_TESTREG, 0); /* 02C */ + + /* + * Some common boards, especially inexpensive single-chip models, + * use the GPIO bits 0-3 to control an on-board video-output mux. + * For these boards, we need to set up the GPIO register into + * "normal" mode, set bits 0-3 as output, and then set those bits + * zero. + * + * Eventually, it would be nice if we could identify these boards + * uniquely, and only do this initialisation if the board has been + * identify. For the moment, however, it shouldn't hurt anything + * to do these steps. + */ + tw_writel(TW68_GPIOC, 0); /* Set the GPIO to "normal", no ints */ + tw_writel(TW68_GPOE, 0x0f); /* Set bits 0-3 to "output" */ + tw_writel(TW68_GPDATA, 0); /* Set all bits to low state */ + + /* Initialize the device control structures */ + mutex_init(&dev->lock); + spin_lock_init(&dev->slock); + + /* Initialize any subsystems */ + tw68_video_init1(dev); + return 0; +} + +static irqreturn_t tw68_irq(int irq, void *dev_id) +{ + struct tw68_dev *dev = dev_id; + u32 status, orig; + int loop; + + status = orig = tw_readl(TW68_INTSTAT) & dev->pci_irqmask; + /* Check if anything to do */ + if (0 == status) + return IRQ_NONE; /* Nope - return */ + for (loop = 0; loop < 10; loop++) { + if (status & dev->board_virqmask) /* video interrupt */ + tw68_irq_video_done(dev, status); + status = tw_readl(TW68_INTSTAT) & dev->pci_irqmask; + if (0 == status) + return IRQ_HANDLED; + } + dev_dbg(&dev->pci->dev, "%s: **** INTERRUPT NOT HANDLED - clearing mask (orig 0x%08x, cur 0x%08x)", + dev->name, orig, tw_readl(TW68_INTSTAT)); + dev_dbg(&dev->pci->dev, "%s: pci_irqmask 0x%08x; board_virqmask 0x%08x ****\n", + dev->name, dev->pci_irqmask, dev->board_virqmask); + tw_clearl(TW68_INTMASK, dev->pci_irqmask); + return IRQ_HANDLED; +} + +static int tw68_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct tw68_dev *dev; + int vidnr = -1; + int err; + + dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + dev->instance = v4l2_device_set_name(&dev->v4l2_dev, "tw68", + &tw68_instance); + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + return err; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail1; + } + + dev->name = dev->v4l2_dev.name; + + if (UNSET != latency) { + pr_info("%s: setting pci latency timer to %d\n", + dev->name, latency); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + } + + /* print pci info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + pr_info("%s: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n", + dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat, (u64)pci_resource_start(pci_dev, 0)); + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { + pr_info("%s: Oops: no 32bit PCI DMA ???\n", dev->name); + err = -EIO; + goto fail1; + } + + switch (pci_id->device) { + case PCI_DEVICE_ID_6800: /* TW6800 */ + dev->vdecoder = TW6800; + dev->board_virqmask = TW68_VID_INTS; + break; + case PCI_DEVICE_ID_6801: /* Video decoder for TW6802 */ + dev->vdecoder = TW6801; + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + case PCI_DEVICE_ID_6804: /* Video decoder for TW6804 */ + dev->vdecoder = TW6804; + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + default: + dev->vdecoder = TWXXXX; /* To be announced */ + dev->board_virqmask = TW68_VID_INTS | TW68_VID_INTSX; + break; + } + + /* get mmio */ + if (!request_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0), + dev->name)) { + err = -EBUSY; + pr_err("%s: can't get MMIO memory @ 0x%llx\n", + dev->name, + (unsigned long long)pci_resource_start(pci_dev, 0)); + goto fail1; + } + dev->lmmio = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + dev->bmmio = (__u8 __iomem *)dev->lmmio; + if (NULL == dev->lmmio) { + err = -EIO; + pr_err("%s: can't ioremap() MMIO memory\n", + dev->name); + goto fail2; + } + /* initialize hardware #1 */ + /* Then do any initialisation wanted before interrupts are on */ + tw68_hw_init1(dev); + + /* get irq */ + err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + pr_err("%s: can't get IRQ %d\n", + dev->name, pci_dev->irq); + goto fail3; + } + + /* + * Now do remainder of initialisation, first for + * things unique for this card, then for general board + */ + if (dev->instance < TW68_MAXBOARDS) + vidnr = video_nr[dev->instance]; + /* initialise video function first */ + err = tw68_video_init2(dev, vidnr); + if (err < 0) { + pr_err("%s: can't register video device\n", + dev->name); + goto fail4; + } + tw_setl(TW68_INTMASK, dev->pci_irqmask); + + pr_info("%s: registered device %s\n", + dev->name, video_device_node_name(&dev->vdev)); + + return 0; + +fail4: + video_unregister_device(&dev->vdev); +fail3: + iounmap(dev->lmmio); +fail2: + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); +fail1: + v4l2_device_unregister(&dev->v4l2_dev); + return err; +} + +static void tw68_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = + container_of(v4l2_dev, struct tw68_dev, v4l2_dev); + + /* shutdown subsystems */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + tw_writel(TW68_INTMASK, 0); + + /* unregister */ + video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + + /* release resources */ + iounmap(dev->lmmio); + release_mem_region(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + + v4l2_device_unregister(&dev->v4l2_dev); +} + +#ifdef CONFIG_PM + +static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = container_of(v4l2_dev, + struct tw68_dev, v4l2_dev); + + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + dev->pci_irqmask &= ~TW68_VID_INTS; + tw_writel(TW68_INTMASK, 0); + + synchronize_irq(pci_dev->irq); + + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); + vb2_discard_done(&dev->vidq); + + return 0; +} + +static int tw68_resume(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct tw68_dev *dev = container_of(v4l2_dev, + struct tw68_dev, v4l2_dev); + struct tw68_buf *buf; + unsigned long flags; + + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + /* Do things that are done in tw68_initdev , + except of initializing memory structures.*/ + + msleep(100); + + tw68_set_tvnorm_hw(dev); + + /*resume unfinished buffer(s)*/ + spin_lock_irqsave(&dev->slock, flags); + buf = container_of(dev->active.next, struct tw68_buf, list); + + tw68_video_start_dma(dev, buf); + + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} +#endif + +/* ----------------------------------------------------------- */ + +static struct pci_driver tw68_pci_driver = { + .name = "tw68", + .id_table = tw68_pci_tbl, + .probe = tw68_initdev, + .remove = tw68_finidev, +#ifdef CONFIG_PM + .suspend = tw68_suspend, + .resume = tw68_resume +#endif +}; + +module_pci_driver(tw68_pci_driver); diff --git a/drivers/media/pci/tw68/tw68-reg.h b/drivers/media/pci/tw68/tw68-reg.h new file mode 100644 index 000000000000..f60b3a896fa7 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-reg.h @@ -0,0 +1,195 @@ +/* + * tw68-reg.h - TW68xx register offsets + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. +*/ + +#ifndef _TW68_REG_H_ +#define _TW68_REG_H_ + +/* ---------------------------------------------------------------------- */ +#define TW68_DMAC 0x000 +#define TW68_DMAP_SA 0x004 +#define TW68_DMAP_EXE 0x008 +#define TW68_DMAP_PP 0x00c +#define TW68_VBIC 0x010 +#define TW68_SBUSC 0x014 +#define TW68_SBUSSD 0x018 +#define TW68_INTSTAT 0x01C +#define TW68_INTMASK 0x020 +#define TW68_GPIOC 0x024 +#define TW68_GPOE 0x028 +#define TW68_TESTREG 0x02C +#define TW68_SBUSRD 0x030 +#define TW68_SBUS_TRIG 0x034 +#define TW68_CAP_CTL 0x040 +#define TW68_SUBSYS 0x054 +#define TW68_I2C_RST 0x064 +#define TW68_VBIINST 0x06C +/* define bits in FIFO and DMAP Control reg */ +#define TW68_DMAP_EN (1 << 0) +#define TW68_FIFO_EN (1 << 1) +/* define the Interrupt Status Register bits */ +#define TW68_SBDONE (1 << 0) +#define TW68_DMAPI (1 << 1) +#define TW68_GPINT (1 << 2) +#define TW68_FFOF (1 << 3) +#define TW68_FDMIS (1 << 4) +#define TW68_DMAPERR (1 << 5) +#define TW68_PABORT (1 << 6) +#define TW68_SBDONE2 (1 << 12) +#define TW68_SBERR2 (1 << 13) +#define TW68_PPERR (1 << 14) +#define TW68_FFERR (1 << 15) +#define TW68_DET50 (1 << 16) +#define TW68_FLOCK (1 << 17) +#define TW68_CCVALID (1 << 18) +#define TW68_VLOCK (1 << 19) +#define TW68_FIELD (1 << 20) +#define TW68_SLOCK (1 << 21) +#define TW68_HLOCK (1 << 22) +#define TW68_VDLOSS (1 << 23) +#define TW68_SBERR (1 << 24) +/* define the i2c control register bits */ +#define TW68_SBMODE (0) +#define TW68_WREN (1) +#define TW68_SSCLK (6) +#define TW68_SSDAT (7) +#define TW68_SBCLK (8) +#define TW68_WDLEN (16) +#define TW68_RDLEN (20) +#define TW68_SBRW (24) +#define TW68_SBDEV (25) + +#define TW68_SBMODE_B (1 << TW68_SBMODE) +#define TW68_WREN_B (1 << TW68_WREN) +#define TW68_SSCLK_B (1 << TW68_SSCLK) +#define TW68_SSDAT_B (1 << TW68_SSDAT) +#define TW68_SBRW_B (1 << TW68_SBRW) + +#define TW68_GPDATA 0x100 +#define TW68_STATUS1 0x204 +#define TW68_INFORM 0x208 +#define TW68_OPFORM 0x20C +#define TW68_HSYNC 0x210 +#define TW68_ACNTL 0x218 +#define TW68_CROP_HI 0x21C +#define TW68_VDELAY_LO 0x220 +#define TW68_VACTIVE_LO 0x224 +#define TW68_HDELAY_LO 0x228 +#define TW68_HACTIVE_LO 0x22C +#define TW68_CNTRL1 0x230 +#define TW68_VSCALE_LO 0x234 +#define TW68_SCALE_HI 0x238 +#define TW68_HSCALE_LO 0x23C +#define TW68_BRIGHT 0x240 +#define TW68_CONTRAST 0x244 +#define TW68_SHARPNESS 0x248 +#define TW68_SAT_U 0x24C +#define TW68_SAT_V 0x250 +#define TW68_HUE 0x254 +#define TW68_SHARP2 0x258 +#define TW68_VSHARP 0x25C +#define TW68_CORING 0x260 +#define TW68_VBICNTL 0x264 +#define TW68_CNTRL2 0x268 +#define TW68_CC_DATA 0x26C +#define TW68_SDT 0x270 +#define TW68_SDTR 0x274 +#define TW68_RESERV2 0x278 +#define TW68_RESERV3 0x27C +#define TW68_CLMPG 0x280 +#define TW68_IAGC 0x284 +#define TW68_AGCGAIN 0x288 +#define TW68_PEAKWT 0x28C +#define TW68_CLMPL 0x290 +#define TW68_SYNCT 0x294 +#define TW68_MISSCNT 0x298 +#define TW68_PCLAMP 0x29C +#define TW68_VCNTL1 0x2A0 +#define TW68_VCNTL2 0x2A4 +#define TW68_CKILL 0x2A8 +#define TW68_COMB 0x2AC +#define TW68_LDLY 0x2B0 +#define TW68_MISC1 0x2B4 +#define TW68_LOOP 0x2B8 +#define TW68_MISC2 0x2BC +#define TW68_MVSN 0x2C0 +#define TW68_STATUS2 0x2C4 +#define TW68_HFREF 0x2C8 +#define TW68_CLMD 0x2CC +#define TW68_IDCNTL 0x2D0 +#define TW68_CLCNTL1 0x2D4 + +/* Audio */ +#define TW68_ACKI1 0x300 +#define TW68_ACKI2 0x304 +#define TW68_ACKI3 0x308 +#define TW68_ACKN1 0x30C +#define TW68_ACKN2 0x310 +#define TW68_ACKN3 0x314 +#define TW68_SDIV 0x318 +#define TW68_LRDIV 0x31C +#define TW68_ACCNTL 0x320 + +#define TW68_VSCTL 0x3B8 +#define TW68_CHROMAGVAL 0x3BC + +#define TW68_F2CROP_HI 0x3DC +#define TW68_F2VDELAY_LO 0x3E0 +#define TW68_F2VACTIVE_LO 0x3E4 +#define TW68_F2HDELAY_LO 0x3E8 +#define TW68_F2HACTIVE_LO 0x3EC +#define TW68_F2CNT 0x3F0 +#define TW68_F2VSCALE_LO 0x3F4 +#define TW68_F2SCALE_HI 0x3F8 +#define TW68_F2HSCALE_LO 0x3FC + +#define RISC_INT_BIT 0x08000000 +#define RISC_SYNCO 0xC0000000 +#define RISC_SYNCE 0xD0000000 +#define RISC_JUMP 0xB0000000 +#define RISC_LINESTART 0x90000000 +#define RISC_INLINE 0xA0000000 + +#define VideoFormatNTSC 0 +#define VideoFormatNTSCJapan 0 +#define VideoFormatPALBDGHI 1 +#define VideoFormatSECAM 2 +#define VideoFormatNTSC443 3 +#define VideoFormatPALM 4 +#define VideoFormatPALN 5 +#define VideoFormatPALNC 5 +#define VideoFormatPAL60 6 +#define VideoFormatAuto 7 + +#define ColorFormatRGB32 0x00 +#define ColorFormatRGB24 0x10 +#define ColorFormatRGB16 0x20 +#define ColorFormatRGB15 0x30 +#define ColorFormatYUY2 0x40 +#define ColorFormatBSWAP 0x04 +#define ColorFormatWSWAP 0x08 +#define ColorFormatGamma 0x80 +#endif diff --git a/drivers/media/pci/tw68/tw68-risc.c b/drivers/media/pci/tw68/tw68-risc.c new file mode 100644 index 000000000000..7439db212a69 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-risc.c @@ -0,0 +1,230 @@ +/* + * tw68_risc.c + * Part of the device driver for Techwell 68xx based cards + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include "tw68.h" + +/** + * @rp pointer to current risc program position + * @sglist pointer to "scatter-gather list" of buffer pointers + * @offset offset to target memory buffer + * @sync_line 0 -> no sync, 1 -> odd sync, 2 -> even sync + * @bpl number of bytes per scan line + * @padding number of bytes of padding to add + * @lines number of lines in field + * @jump insert a jump at the start + */ +static __le32 *tw68_risc_field(__le32 *rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines, bool jump) +{ + struct scatterlist *sg; + unsigned int line, todo, done; + + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = 0; + } + + /* sync instruction */ + if (sync_line == 1) + *(rp++) = cpu_to_le32(RISC_SYNCO); + else + *(rp++) = cpu_to_le32(RISC_SYNCE); + *(rp++) = 0; + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + /* calculate next starting position */ + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg = sg_next(sg); + } + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = cpu_to_le32(RISC_LINESTART | + /* (offset<<12) |*/ bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + offset += bpl; + } else { + /* + * scanline needs to be split. Put the start in + * whatever memory remains using RISC_LINESTART, + * then the remainder into following addresses + * given by the scatter-gather list. + */ + todo = bpl; /* one full line to be done */ + /* first fragment */ + done = (sg_dma_len(sg) - offset); + *(rp++) = cpu_to_le32(RISC_LINESTART | + (7 << 24) | + done); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + todo -= done; + sg = sg_next(sg); + /* succeeding fragments have no offset */ + while (todo > sg_dma_len(sg)) { + *(rp++) = cpu_to_le32(RISC_INLINE | + (done << 12) | + sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + todo -= sg_dma_len(sg); + sg = sg_next(sg); + done += sg_dma_len(sg); + } + if (todo) { + /* final chunk - offset 0, count 'todo' */ + *(rp++) = cpu_to_le32(RISC_INLINE | + (done << 12) | + todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + } + offset = todo; + } + offset += padding; + } + + return rp; +} + +/** + * tw68_risc_buffer + * + * This routine is called by tw68-video. It allocates + * memory for the dma controller "program" and then fills in that + * memory with the appropriate "instructions". + * + * @pci_dev structure with info about the pci + * slot which our device is in. + * @risc structure with info about the memory + * used for our controller program. + * @sglist scatter-gather list entry + * @top_offset offset within the risc program area for the + * first odd frame line + * @bottom_offset offset within the risc program area for the + * first even frame line + * @bpl number of data bytes per scan line + * @padding number of extra bytes to add at end of line + * @lines number of scan lines + */ +int tw68_risc_buffer(struct pci_dev *pci, + struct tw68_buf *buf, + struct scatterlist *sglist, + unsigned int top_offset, + unsigned int bottom_offset, + unsigned int bpl, + unsigned int padding, + unsigned int lines) +{ + u32 instructions, fields; + __le32 *rp; + + fields = 0; + if (UNSET != top_offset) + fields++; + if (UNSET != bottom_offset) + fields++; + /* + * estimate risc mem: worst case is one write per page border + + * one write per scan line + syncs + 2 jumps (all 2 dwords). + * Padding can cause next bpl to start close to a page border. + * First DMA region may be smaller than PAGE_SIZE + */ + instructions = fields * (1 + (((bpl + padding) * lines) / + PAGE_SIZE) + lines) + 4; + buf->size = instructions * 8; + buf->cpu = pci_alloc_consistent(pci, buf->size, &buf->dma); + if (buf->cpu == NULL) + return -ENOMEM; + + /* write risc instructions */ + rp = buf->cpu; + if (UNSET != top_offset) /* generates SYNCO */ + rp = tw68_risc_field(rp, sglist, top_offset, 1, + bpl, padding, lines, true); + if (UNSET != bottom_offset) /* generates SYNCE */ + rp = tw68_risc_field(rp, sglist, bottom_offset, 2, + bpl, padding, lines, top_offset == UNSET); + + /* save pointer to jmp instruction address */ + buf->jmp = rp; + buf->cpu[1] = cpu_to_le32(buf->dma + 8); + /* assure risc buffer hasn't overflowed */ + BUG_ON((buf->jmp - buf->cpu + 2) * sizeof(buf->cpu[0]) > buf->size); + return 0; +} + +#if 0 +/* ------------------------------------------------------------------ */ +/* debug helper code */ + +static void tw68_risc_decode(u32 risc, u32 addr) +{ +#define RISC_OP(reg) (((reg) >> 28) & 7) + static struct instr_details { + char *name; + u8 has_data_type; + u8 has_byte_info; + u8 has_addr; + } instr[8] = { + [RISC_OP(RISC_SYNCO)] = {"syncOdd", 0, 0, 0}, + [RISC_OP(RISC_SYNCE)] = {"syncEven", 0, 0, 0}, + [RISC_OP(RISC_JUMP)] = {"jump", 0, 0, 1}, + [RISC_OP(RISC_LINESTART)] = {"lineStart", 1, 1, 1}, + [RISC_OP(RISC_INLINE)] = {"inline", 1, 1, 1}, + }; + u32 p; + + p = RISC_OP(risc); + if (!(risc & 0x80000000) || !instr[p].name) { + pr_debug("0x%08x [ INVALID ]\n", risc); + return; + } + pr_debug("0x%08x %-9s IRQ=%d", + risc, instr[p].name, (risc >> 27) & 1); + if (instr[p].has_data_type) + pr_debug(" Type=%d", (risc >> 24) & 7); + if (instr[p].has_byte_info) + pr_debug(" Start=0x%03x Count=%03u", + (risc >> 12) & 0xfff, risc & 0xfff); + if (instr[p].has_addr) + pr_debug(" StartAddr=0x%08x", addr); + pr_debug("\n"); +} + +void tw68_risc_program_dump(struct tw68_core *core, struct tw68_buf *buf) +{ + const __le32 *addr; + + pr_debug("%s: risc_program_dump: risc=%p, buf->cpu=0x%p, buf->jmp=0x%p\n", + core->name, buf, buf->cpu, buf->jmp); + for (addr = buf->cpu; addr <= buf->jmp; addr += 2) + tw68_risc_decode(*addr, *(addr+1)); +} +#endif diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c new file mode 100644 index 000000000000..5c94ac7c88d9 --- /dev/null +++ b/drivers/media/pci/tw68/tw68-video.c @@ -0,0 +1,1051 @@ +/* + * tw68 functions to handle video data + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/module.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-sg.h> + +#include "tw68.h" +#include "tw68-reg.h" + +/* ------------------------------------------------------------------ */ +/* data structs for video */ +/* + * FIXME - + * Note that the saa7134 has formats, e.g. YUV420, which are classified + * as "planar". These affect overlay mode, and are flagged with a field + * ".planar" in the format. Do we need to implement this in this driver? + */ +static const struct tw68_format formats[] = { + { + .name = "15 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB555, + .depth = 16, + .twformat = ColorFormatRGB15, + }, { + .name = "15 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB555X, + .depth = 16, + .twformat = ColorFormatRGB15 | ColorFormatBSWAP, + }, { + .name = "16 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .twformat = ColorFormatRGB16, + }, { + .name = "16 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .twformat = ColorFormatRGB16 | ColorFormatBSWAP, + }, { + .name = "24 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + .twformat = ColorFormatRGB24, + }, { + .name = "24 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + .twformat = ColorFormatRGB24 | ColorFormatBSWAP, + }, { + .name = "32 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR32, + .depth = 32, + .twformat = ColorFormatRGB32, + }, { + .name = "32 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .twformat = ColorFormatRGB32 | ColorFormatBSWAP | + ColorFormatWSWAP, + }, { + .name = "4:2:2 packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .twformat = ColorFormatYUY2, + }, { + .name = "4:2:2 packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .twformat = ColorFormatYUY2 | ColorFormatBSWAP, + } +}; +#define FORMATS ARRAY_SIZE(formats) + +#define NORM_625_50 \ + .h_delay = 3, \ + .h_delay0 = 133, \ + .h_start = 0, \ + .h_stop = 719, \ + .v_delay = 24, \ + .vbi_v_start_0 = 7, \ + .vbi_v_stop_0 = 22, \ + .video_v_start = 24, \ + .video_v_stop = 311, \ + .vbi_v_start_1 = 319 + +#define NORM_525_60 \ + .h_delay = 8, \ + .h_delay0 = 138, \ + .h_start = 0, \ + .h_stop = 719, \ + .v_delay = 22, \ + .vbi_v_start_0 = 10, \ + .vbi_v_stop_0 = 21, \ + .video_v_start = 22, \ + .video_v_stop = 262, \ + .vbi_v_start_1 = 273 + +/* + * The following table is searched by tw68_s_std, first for a specific + * match, then for an entry which contains the desired id. The table + * entries should therefore be ordered in ascending order of specificity. + */ +static const struct tw68_tvnorm tvnorms[] = { + { + .name = "PAL", /* autodetect */ + .id = V4L2_STD_PAL, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPALBDGHI, + }, { + .name = "NTSC", + .id = V4L2_STD_NTSC, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0x89, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + .format = VideoFormatNTSC, + }, { + .name = "SECAM", + .id = V4L2_STD_SECAM, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + .format = VideoFormatSECAM, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0xb9, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + .format = VideoFormatPALM, + }, { + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0xa1, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPALNC, + }, { + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + .h_delay = 186, + .h_start = 0, + .h_stop = 719, + .v_delay = 26, + .video_v_start = 23, + .video_v_stop = 262, + .vbi_v_start_0 = 10, + .vbi_v_stop_0 = 21, + .vbi_v_start_1 = 273, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + .format = VideoFormatPAL60, + } +}; +#define TVNORMS ARRAY_SIZE(tvnorms) + +static const struct tw68_format *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < FORMATS; i++) + if (formats[i].fourcc == fourcc) + return formats+i; + return NULL; +} + + +/* ------------------------------------------------------------------ */ +/* + * Note that the cropping rectangles are described in terms of a single + * frame, i.e. line positions are only 1/2 the interlaced equivalent + */ +static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm) +{ + if (norm != dev->tvnorm) { + dev->width = 720; + dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576; + dev->tvnorm = norm; + tw68_set_tvnorm_hw(dev); + } +} + +/* + * tw68_set_scale + * + * Scaling and Cropping for video decoding + * + * We are working with 3 values for horizontal and vertical - scale, + * delay and active. + * + * HACTIVE represent the actual number of pixels in the "usable" image, + * before scaling. HDELAY represents the number of pixels skipped + * between the start of the horizontal sync and the start of the image. + * HSCALE is calculated using the formula + * HSCALE = (HACTIVE / (#pixels desired)) * 256 + * + * The vertical registers are similar, except based upon the total number + * of lines in the image, and the first line of the image (i.e. ignoring + * vertical sync and VBI). + * + * Note that the number of bytes reaching the FIFO (and hence needing + * to be processed by the DMAP program) is completely dependent upon + * these values, especially HSCALE. + * + * Parameters: + * @dev pointer to the device structure, needed for + * getting current norm (as well as debug print) + * @width actual image width (from user buffer) + * @height actual image height + * @field indicates Top, Bottom or Interlaced + */ +static int tw68_set_scale(struct tw68_dev *dev, unsigned int width, + unsigned int height, enum v4l2_field field) +{ + const struct tw68_tvnorm *norm = dev->tvnorm; + /* set individually for debugging clarity */ + int hactive, hdelay, hscale; + int vactive, vdelay, vscale; + int comb; + + if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */ + height /= 2; /* we must set for 1-frame */ + + pr_debug("%s: width=%d, height=%d, both=%d\n" + " tvnorm h_delay=%d, h_start=%d, h_stop=%d, " + "v_delay=%d, v_start=%d, v_stop=%d\n" , __func__, + width, height, V4L2_FIELD_HAS_BOTH(field), + norm->h_delay, norm->h_start, norm->h_stop, + norm->v_delay, norm->video_v_start, + norm->video_v_stop); + + switch (dev->vdecoder) { + case TW6800: + hdelay = norm->h_delay0; + break; + default: + hdelay = norm->h_delay; + break; + } + + hdelay += norm->h_start; + hactive = norm->h_stop - norm->h_start + 1; + + hscale = (hactive * 256) / (width); + + vdelay = norm->v_delay; + vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start; + vscale = (vactive * 256) / height; + + pr_debug("%s: %dx%d [%s%s,%s]\n", __func__, + width, height, + V4L2_FIELD_HAS_TOP(field) ? "T" : "", + V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", + v4l2_norm_to_name(dev->tvnorm->id)); + pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; " + "vactive=%d, vdelay=%d, vscale=%d\n", __func__, + hactive, hdelay, hscale, vactive, vdelay, vscale); + + comb = ((vdelay & 0x300) >> 2) | + ((vactive & 0x300) >> 4) | + ((hdelay & 0x300) >> 6) | + ((hactive & 0x300) >> 8); + pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, " + "VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n", + __func__, comb, vdelay, vactive, hdelay, hactive); + tw_writeb(TW68_CROP_HI, comb); + tw_writeb(TW68_VDELAY_LO, vdelay & 0xff); + tw_writeb(TW68_VACTIVE_LO, vactive & 0xff); + tw_writeb(TW68_HDELAY_LO, hdelay & 0xff); + tw_writeb(TW68_HACTIVE_LO, hactive & 0xff); + + comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8); + pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, " + "HSCALE_LO=%02x\n", __func__, comb, vscale, hscale); + tw_writeb(TW68_SCALE_HI, comb); + tw_writeb(TW68_VSCALE_LO, vscale); + tw_writeb(TW68_HSCALE_LO, hscale); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf) +{ + /* Set cropping and scaling */ + tw68_set_scale(dev, dev->width, dev->height, dev->field); + /* + * Set start address for RISC program. Note that if the DMAP + * processor is currently running, it must be stopped before + * a new address can be set. + */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN); + tw_writel(TW68_DMAP_SA, buf->dma); + /* Clear any pending interrupts */ + tw_writel(TW68_INTSTAT, dev->board_virqmask); + /* Enable the risc engine and the fifo */ + tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat | + ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN); + dev->pci_irqmask |= dev->board_virqmask; + tw_setl(TW68_INTMASK, dev->pci_irqmask); + return 0; +} + +/* ------------------------------------------------------------------ */ + +/* calc max # of buffers from size (must not exceed the 4MB virtual + * address space per DMA channel) */ +static int tw68_buffer_count(unsigned int size, unsigned int count) +{ + unsigned int maxcount; + + maxcount = (4 * 1024 * 1024) / roundup(size, PAGE_SIZE); + if (count > maxcount) + count = maxcount; + return count; +} + +/* ------------------------------------------------------------- */ +/* vb2 queue operations */ + +static int tw68_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + unsigned tot_bufs = q->num_buffers + *num_buffers; + + sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3; + /* + * We allow create_bufs, but only if the sizeimage is the same as the + * current sizeimage. The tw68_buffer_count calculation becomes quite + * difficult otherwise. + */ + if (fmt && fmt->fmt.pix.sizeimage < sizes[0]) + return -EINVAL; + *num_planes = 1; + if (tot_bufs < 2) + tot_bufs = 2; + tot_bufs = tw68_buffer_count(sizes[0], tot_bufs); + *num_buffers = tot_bufs - q->num_buffers; + + return 0; +} + +/* + * The risc program for each buffers works as follows: it starts with a simple + * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the + * buffer follows and at the end we have a JUMP back to the start + 8 (skipping + * the initial JUMP). + * + * This is the program of the first buffer to be queued if the active list is + * empty and it just keeps DMAing this buffer without generating any interrupts. + * + * If a new buffer is added then the initial JUMP in the program generates an + * interrupt as well which signals that the previous buffer has been DMAed + * successfully and that it can be returned to userspace. + * + * It also sets the final jump of the previous buffer to the start of the new + * buffer, thus chaining the new buffer into the DMA chain. This is a single + * atomic u32 write, so there is no race condition. + * + * The end-result of all this that you only get an interrupt when a buffer + * is ready, so the control flow is very easy. + */ +static void tw68_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct tw68_buf *prev; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + /* append a 'JUMP to start of buffer' to the buffer risc program */ + buf->jmp[0] = cpu_to_le32(RISC_JUMP); + buf->jmp[1] = cpu_to_le32(buf->dma + 8); + + if (!list_empty(&dev->active)) { + prev = list_entry(dev->active.prev, struct tw68_buf, list); + buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT); + prev->jmp[1] = cpu_to_le32(buf->dma); + } + list_add_tail(&buf->list, &dev->active); + spin_unlock_irqrestore(&dev->slock, flags); +} + +/* + * buffer_prepare + * + * Set the ancilliary information into the buffer structure. This + * includes generating the necessary risc program if it hasn't already + * been done for the current buffer format. + * The structure fh contains the details of the format requested by the + * user - type, width, height and #fields. This is compared with the + * last format set for the current buffer. If they differ, the risc + * code (which controls the filling of the buffer) is (re-)generated. + */ +static int tw68_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0); + unsigned size, bpl; + int rc; + + size = (dev->width * dev->height * dev->fmt->depth) >> 3; + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + vb2_set_plane_payload(vb, 0, size); + + rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + if (!rc) + return -EIO; + + bpl = (dev->width * dev->fmt->depth) >> 3; + switch (dev->field) { + case V4L2_FIELD_TOP: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, UNSET, bpl, 0, dev->height); + break; + case V4L2_FIELD_BOTTOM: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + UNSET, 0, bpl, 0, dev->height); + break; + case V4L2_FIELD_SEQ_TB: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, bpl * (dev->height >> 1), + bpl, 0, dev->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + bpl * (dev->height >> 1), 0, + bpl, 0, dev->height >> 1); + break; + case V4L2_FIELD_INTERLACED: + default: + tw68_risc_buffer(dev->pci, buf, dma->sgl, + 0, bpl, bpl, bpl, dev->height >> 1); + break; + } + return 0; +} + +static void tw68_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct tw68_dev *dev = vb2_get_drv_priv(vq); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0); + struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb); + + dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + + pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma); +} + +static int tw68_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + struct tw68_buf *buf = + container_of(dev->active.next, struct tw68_buf, list); + + dev->seqnr = 0; + tw68_video_start_dma(dev, buf); + return 0; +} + +static void tw68_stop_streaming(struct vb2_queue *q) +{ + struct tw68_dev *dev = vb2_get_drv_priv(q); + + /* Stop risc & fifo */ + tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN); + while (!list_empty(&dev->active)) { + struct tw68_buf *buf = + container_of(dev->active.next, struct tw68_buf, list); + + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } +} + +static struct vb2_ops tw68_video_qops = { + .queue_setup = tw68_queue_setup, + .buf_queue = tw68_buf_queue, + .buf_prepare = tw68_buf_prepare, + .buf_finish = tw68_buf_finish, + .start_streaming = tw68_start_streaming, + .stop_streaming = tw68_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ */ + +static int tw68_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tw68_dev *dev = + container_of(ctrl->handler, struct tw68_dev, hdl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + tw_writeb(TW68_BRIGHT, ctrl->val); + break; + case V4L2_CID_HUE: + tw_writeb(TW68_HUE, ctrl->val); + break; + case V4L2_CID_CONTRAST: + tw_writeb(TW68_CONTRAST, ctrl->val); + break; + case V4L2_CID_SATURATION: + tw_writeb(TW68_SAT_U, ctrl->val); + tw_writeb(TW68_SAT_V, ctrl->val); + break; + case V4L2_CID_COLOR_KILLER: + if (ctrl->val) + tw_andorb(TW68_MISC2, 0xe0, 0xe0); + else + tw_andorb(TW68_MISC2, 0xe0, 0x00); + break; + case V4L2_CID_CHROMA_AGC: + if (ctrl->val) + tw_andorb(TW68_LOOP, 0x30, 0x20); + else + tw_andorb(TW68_LOOP, 0x30, 0x00); + break; + } + return 0; +} + +/* ------------------------------------------------------------------ */ + +/* + * Note that this routine returns what is stored in the fh structure, and + * does not interrogate any of the device registers. + */ +static int tw68_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = dev->field; + f->fmt.pix.pixelformat = dev->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * (dev->fmt->depth)) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + return 0; +} + +static int tw68_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + const struct tw68_format *fmt; + enum v4l2_field field; + unsigned int maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxh = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_SEQ_TB: + maxh = maxh * 2; + break; + default: + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + break; + } + + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > 720) + f->fmt.pix.width = 720; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * (fmt->depth)) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +/* + * Note that tw68_s_fmt_vid_cap sets the information into the fh structure, + * and it will be used for all future new buffers. However, there could be + * some number of buffers on the "active" chain which will be filled before + * the change takes place. + */ +static int tw68_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tw68_dev *dev = video_drvdata(file); + int err; + + err = tw68_try_fmt_vid_cap(file, priv, f); + if (0 != err) + return err; + + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; + return 0; +} + +static int tw68_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tw68_dev *dev = video_drvdata(file); + unsigned int n; + + n = i->index; + if (n >= TW68_INPUT_MAX) + return -EINVAL; + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + snprintf(i->name, sizeof(i->name), "Composite %d", n); + + /* If the query is for the current input, get live data */ + if (n == dev->input) { + int v1 = tw_readb(TW68_STATUS1); + int v2 = tw_readb(TW68_MVSN); + + if (0 != (v1 & (1 << 7))) + i->status |= V4L2_IN_ST_NO_SYNC; + if (0 != (v1 & (1 << 6))) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (0 != (v1 & (1 << 2))) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (0 != (v1 & 1 << 1)) + i->status |= V4L2_IN_ST_NO_COLOR; + if (0 != (v2 & (1 << 2))) + i->status |= V4L2_IN_ST_MACROVISION; + } + i->std = video_devdata(file)->tvnorms; + return 0; +} + +static int tw68_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tw68_dev *dev = video_drvdata(file); + + *i = dev->input; + return 0; +} + +static int tw68_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (i >= TW68_INPUT_MAX) + return -EINVAL; + dev->input = i; + tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2); + return 0; +} + +static int tw68_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tw68_dev *dev = video_drvdata(file); + + strcpy(cap->driver, "tw68"); + strlcpy(cap->card, "Techwell Capture Card", + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->device_caps = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct tw68_dev *dev = video_drvdata(file); + unsigned int i; + + if (vb2_is_busy(&dev->vidq)) + return -EBUSY; + + /* Look for match on complete norm id (may have mult bits) */ + for (i = 0; i < TVNORMS; i++) { + if (id == tvnorms[i].id) + break; + } + + /* If no exact match, look for norm which contains this one */ + if (i == TVNORMS) { + for (i = 0; i < TVNORMS; i++) + if (id & tvnorms[i].id) + break; + } + /* If still not matched, give up */ + if (i == TVNORMS) + return -EINVAL; + + set_tvnorm(dev, &tvnorms[i]); /* do the actual setting */ + return 0; +} + +static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct tw68_dev *dev = video_drvdata(file); + + *id = dev->tvnorm->id; + return 0; +} + +static int tw68_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +/* + * Used strictly for internal development and debugging, this routine + * prints out the current register contents for the tw68xx device. + */ +static void tw68_dump_regs(struct tw68_dev *dev) +{ + unsigned char line[80]; + int i, j, k; + unsigned char *cptr; + + pr_info("Full dump of TW68 registers:\n"); + /* First we do the PCI regs, 8 4-byte regs per line */ + for (i = 0; i < 0x100; i += 32) { + cptr = line; + cptr += sprintf(cptr, "%03x ", i); + /* j steps through the next 4 words */ + for (j = i; j < i + 16; j += 4) + cptr += sprintf(cptr, "%08x ", tw_readl(j)); + *cptr++ = ' '; + for (; j < i + 32; j += 4) + cptr += sprintf(cptr, "%08x ", tw_readl(j)); + *cptr++ = '\n'; + *cptr = 0; + pr_info("%s", line); + } + /* Next the control regs, which are single-byte, address mod 4 */ + while (i < 0x400) { + cptr = line; + cptr += sprintf(cptr, "%03x ", i); + /* Print out 4 groups of 4 bytes */ + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + cptr += sprintf(cptr, "%02x ", + tw_readb(i)); + i += 4; + } + *cptr++ = ' '; + } + *cptr++ = '\n'; + *cptr = 0; + pr_info("%s", line); + } +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct tw68_dev *dev = video_drvdata(file); + + tw68_dump_regs(dev); + return v4l2_ctrl_log_status(file, priv); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (reg->size == 1) + reg->val = tw_readb(reg->reg); + else + reg->val = tw_readl(reg->reg); + return 0; +} + +static int vidioc_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) +{ + struct tw68_dev *dev = video_drvdata(file); + + if (reg->size == 1) + tw_writeb(reg->reg, reg->val); + else + tw_writel(reg->reg & 0xffff, reg->val); + return 0; +} +#endif + +static const struct v4l2_ctrl_ops tw68_ctrl_ops = { + .s_ctrl = tw68_s_ctrl, +}; + +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = tw68_querycap, + .vidioc_enum_fmt_vid_cap = tw68_enum_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_s_std = tw68_s_std, + .vidioc_g_std = tw68_g_std, + .vidioc_enum_input = tw68_enum_input, + .vidioc_g_input = tw68_g_input, + .vidioc_s_input = tw68_s_input, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_fmt_vid_cap = tw68_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = tw68_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = tw68_s_fmt_vid_cap, + .vidioc_log_status = vidioc_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device tw68_video_template = { + .name = "tw68_video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TW68_NORMS, +}; + +/* ------------------------------------------------------------------ */ +/* exported stuff */ +void tw68_set_tvnorm_hw(struct tw68_dev *dev) +{ + tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format); +} + +int tw68_video_init1(struct tw68_dev *dev) +{ + struct v4l2_ctrl_handler *hdl = &dev->hdl; + + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 20); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 100); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + /* NTSC only */ + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; + } + dev->v4l2_dev.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + return 0; +} + +int tw68_video_init2(struct tw68_dev *dev, int video_nr) +{ + int ret; + + set_tvnorm(dev, &tvnorms[0]); + + dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + dev->width = 720; + dev->height = 576; + dev->field = V4L2_FIELD_INTERLACED; + + INIT_LIST_HEAD(&dev->active); + dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF; + dev->vidq.ops = &tw68_video_qops; + dev->vidq.mem_ops = &vb2_dma_sg_memops; + dev->vidq.drv_priv = dev; + dev->vidq.gfp_flags = __GFP_DMA32; + dev->vidq.buf_struct_size = sizeof(struct tw68_buf); + dev->vidq.lock = &dev->lock; + dev->vidq.min_buffers_needed = 2; + ret = vb2_queue_init(&dev->vidq); + if (ret) + return ret; + dev->vdev = tw68_video_template; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->lock; + dev->vdev.queue = &dev->vidq; + video_set_drvdata(&dev->vdev, dev); + return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); +} + +/* + * tw68_irq_video_done + */ +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status) +{ + __u32 reg; + + /* reset interrupts handled by this routine */ + tw_writel(TW68_INTSTAT, status); + /* + * Check most likely first + * + * DMAPI shows we have reached the end of the risc code + * for the current buffer. + */ + if (status & TW68_DMAPI) { + struct tw68_buf *buf; + + spin_lock(&dev->slock); + buf = list_entry(dev->active.next, struct tw68_buf, list); + list_del(&buf->list); + spin_unlock(&dev->slock); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.field = dev->field; + buf->vb.v4l2_buf.sequence = dev->seqnr++; + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + status &= ~(TW68_DMAPI); + if (0 == status) + return; + } + if (status & (TW68_VLOCK | TW68_HLOCK)) + dev_dbg(&dev->pci->dev, "Lost sync\n"); + if (status & TW68_PABORT) + dev_err(&dev->pci->dev, "PABORT interrupt\n"); + if (status & TW68_DMAPERR) + dev_err(&dev->pci->dev, "DMAPERR interrupt\n"); + /* + * On TW6800, FDMIS is apparently generated if video input is switched + * during operation. Therefore, it is not enabled for that chip. + */ + if (status & TW68_FDMIS) + dev_dbg(&dev->pci->dev, "FDMIS interrupt\n"); + if (status & TW68_FFOF) { + /* probably a logic error */ + reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN; + tw_clearl(TW68_DMAC, TW68_FIFO_EN); + dev_dbg(&dev->pci->dev, "FFOF interrupt\n"); + tw_setl(TW68_DMAC, reg); + } + if (status & TW68_FFERR) + dev_dbg(&dev->pci->dev, "FFERR interrupt\n"); +} diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h new file mode 100644 index 000000000000..2c8abe26b13b --- /dev/null +++ b/drivers/media/pci/tw68/tw68.h @@ -0,0 +1,231 @@ +/* + * tw68 driver common header file + * + * Much of this code is derived from the cx88 and sa7134 drivers, which + * were in turn derived from the bt87x driver. The original work was by + * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab, + * Hans Verkuil, Andy Walls and many others. Their work is gratefully + * acknowledged. Full credit goes to them - any problems within this code + * are mine. + * + * Copyright (C) 2009 William M. Brack + * + * Refactored and updated to the latest v4l core frameworks: + * + * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl> + * + * 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. + */ + +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/videodev2.h> +#include <linux/notifier.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-dma-sg.h> + +#include "tw68-reg.h" + +#define UNSET (-1U) + +/* system vendor and device ID's */ +#define PCI_VENDOR_ID_TECHWELL 0x1797 +#define PCI_DEVICE_ID_6800 0x6800 +#define PCI_DEVICE_ID_6801 0x6801 +#define PCI_DEVICE_ID_AUDIO2 0x6802 +#define PCI_DEVICE_ID_TS3 0x6803 +#define PCI_DEVICE_ID_6804 0x6804 +#define PCI_DEVICE_ID_AUDIO5 0x6805 +#define PCI_DEVICE_ID_TS6 0x6806 + +/* tw6816 based cards */ +#define PCI_DEVICE_ID_6816_1 0x6810 +#define PCI_DEVICE_ID_6816_2 0x6811 +#define PCI_DEVICE_ID_6816_3 0x6812 +#define PCI_DEVICE_ID_6816_4 0x6813 + +#define TW68_NORMS ( \ + V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_Nc | V4L2_STD_PAL_60) + +#define TW68_VID_INTS (TW68_FFERR | TW68_PABORT | TW68_DMAPERR | \ + TW68_FFOF | TW68_DMAPI) +/* TW6800 chips have trouble with these, so we don't set them for that chip */ +#define TW68_VID_INTSX (TW68_FDMIS | TW68_HLOCK | TW68_VLOCK) + +#define TW68_I2C_INTS (TW68_SBERR | TW68_SBDONE | TW68_SBERR2 | \ + TW68_SBDONE2) + +enum tw68_decoder_type { + TW6800, + TW6801, + TW6804, + TWXXXX, +}; + +/* ----------------------------------------------------------- */ +/* static data */ + +struct tw68_tvnorm { + char *name; + v4l2_std_id id; + + /* video decoder */ + u32 sync_control; + u32 luma_control; + u32 chroma_ctrl1; + u32 chroma_gain; + u32 chroma_ctrl2; + u32 vgate_misc; + + /* video scaler */ + u32 h_delay; + u32 h_delay0; /* for TW6800 */ + u32 h_start; + u32 h_stop; + u32 v_delay; + u32 video_v_start; + u32 video_v_stop; + u32 vbi_v_start_0; + u32 vbi_v_stop_0; + u32 vbi_v_start_1; + + /* Techwell specific */ + u32 format; +}; + +struct tw68_format { + char *name; + u32 fourcc; + u32 depth; + u32 twformat; +}; + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define TW68_BOARD_NOAUTO UNSET +#define TW68_BOARD_UNKNOWN 0 +#define TW68_BOARD_GENERIC_6802 1 + +#define TW68_MAXBOARDS 16 +#define TW68_INPUT_MAX 4 + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +struct tw68_dev; /* forward delclaration */ + +/* buffer for one video/vbi/ts frame */ +struct tw68_buf { + struct vb2_buffer vb; + struct list_head list; + + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct tw68_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 twformat; +}; + +/* global device status */ +struct tw68_dev { + struct mutex lock; + spinlock_t slock; + u16 instance; + struct v4l2_device v4l2_dev; + + /* various device info */ + enum tw68_decoder_type vdecoder; + struct video_device vdev; + struct v4l2_ctrl_handler hdl; + + /* pci i/o */ + char *name; + struct pci_dev *pci; + unsigned char pci_rev, pci_lat; + u32 __iomem *lmmio; + u8 __iomem *bmmio; + u32 pci_irqmask; + /* The irq mask to be used will depend upon the chip type */ + u32 board_virqmask; + + /* video capture */ + const struct tw68_format *fmt; + unsigned width, height; + unsigned seqnr; + unsigned field; + struct vb2_queue vidq; + struct list_head active; + + /* various v4l controls */ + const struct tw68_tvnorm *tvnorm; /* video */ + + int input; +}; + +/* ----------------------------------------------------------- */ + +#define tw_readl(reg) readl(dev->lmmio + ((reg) >> 2)) +#define tw_readb(reg) readb(dev->bmmio + (reg)) +#define tw_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) +#define tw_writeb(reg, value) writeb((value), dev->bmmio + (reg)) + +#define tw_andorl(reg, mask, value) \ + writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+((reg)>>2)) +#define tw_andorb(reg, mask, value) \ + writeb((readb(dev->bmmio + (reg)) & ~(mask)) |\ + ((value) & (mask)), dev->bmmio+(reg)) +#define tw_setl(reg, bit) tw_andorl((reg), (bit), (bit)) +#define tw_setb(reg, bit) tw_andorb((reg), (bit), (bit)) +#define tw_clearl(reg, bit) \ + writel((readl(dev->lmmio + ((reg) >> 2)) & ~(bit)), \ + dev->lmmio + ((reg) >> 2)) +#define tw_clearb(reg, bit) \ + writeb((readb(dev->bmmio+(reg)) & ~(bit)), \ + dev->bmmio + (reg)) + +#define tw_wait(us) { udelay(us); } + +/* ----------------------------------------------------------- */ +/* tw68-video.c */ + +void tw68_set_tvnorm_hw(struct tw68_dev *dev); + +int tw68_video_init1(struct tw68_dev *dev); +int tw68_video_init2(struct tw68_dev *dev, int video_nr); +void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status); +int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf); + +/* ----------------------------------------------------------- */ +/* tw68-risc.c */ + +int tw68_risc_buffer(struct pci_dev *pci, struct tw68_buf *buf, + struct scatterlist *sglist, unsigned int top_offset, + unsigned int bottom_offset, unsigned int bpl, + unsigned int padding, unsigned int lines); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index bf34b93f23ee..b6801e035ea4 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -682,7 +682,7 @@ set_videobus_dir (struct zoran *zr, switch (zr->card.type) { case LML33: case LML33R10: - if (lml33dpath == 0) + if (!lml33dpath) GPIO(zr, 5, val); else GPIO(zr, 5, 1); |