1b285192aSMauro Carvalho Chehab /* 2b285192aSMauro Carvalho Chehab * Driver for the Conexant CX25821 PCIe bridge 3b285192aSMauro Carvalho Chehab * 4b285192aSMauro Carvalho Chehab * Copyright (C) 2009 Conexant Systems Inc. 5b285192aSMauro Carvalho Chehab * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> 6b285192aSMauro Carvalho Chehab * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver 7b285192aSMauro Carvalho Chehab * Parts adapted/taken from Eduardo Moscoso Rubino 8b285192aSMauro Carvalho Chehab * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com> 9b285192aSMauro Carvalho Chehab * 10b285192aSMauro Carvalho Chehab * 11b285192aSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 12b285192aSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 13b285192aSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 14b285192aSMauro Carvalho Chehab * (at your option) any later version. 15b285192aSMauro Carvalho Chehab * 16b285192aSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 17b285192aSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 18b285192aSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19b285192aSMauro Carvalho Chehab * 20b285192aSMauro Carvalho Chehab * GNU General Public License for more details. 21b285192aSMauro Carvalho Chehab * 22b285192aSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 23b285192aSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 24b285192aSMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25b285192aSMauro Carvalho Chehab */ 26b285192aSMauro Carvalho Chehab 27b285192aSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 28b285192aSMauro Carvalho Chehab 29b285192aSMauro Carvalho Chehab #include "cx25821-video.h" 30b285192aSMauro Carvalho Chehab 31b285192aSMauro Carvalho Chehab MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); 32b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); 33b285192aSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 34b285192aSMauro Carvalho Chehab 35b285192aSMauro Carvalho Chehab static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; 36b285192aSMauro Carvalho Chehab 37b285192aSMauro Carvalho Chehab module_param_array(video_nr, int, NULL, 0444); 38b285192aSMauro Carvalho Chehab 39b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "video device numbers"); 40b285192aSMauro Carvalho Chehab 41b285192aSMauro Carvalho Chehab static unsigned int video_debug = VIDEO_DEBUG; 42b285192aSMauro Carvalho Chehab module_param(video_debug, int, 0644); 43b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); 44b285192aSMauro Carvalho Chehab 45b285192aSMauro Carvalho Chehab static unsigned int irq_debug; 46b285192aSMauro Carvalho Chehab module_param(irq_debug, int, 0644); 47b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); 48b285192aSMauro Carvalho Chehab 4995c232a2SHans Verkuil static unsigned int vid_limit = 16; 50b285192aSMauro Carvalho Chehab module_param(vid_limit, int, 0644); 51b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); 52b285192aSMauro Carvalho Chehab 53b285192aSMauro Carvalho Chehab #define FORMAT_FLAGS_PACKED 0x01 54b285192aSMauro Carvalho Chehab 5595c232a2SHans Verkuil static const struct cx25821_fmt formats[] = { 56b285192aSMauro Carvalho Chehab { 57b285192aSMauro Carvalho Chehab .name = "4:1:1, packed, Y41P", 58b285192aSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_Y41P, 59b285192aSMauro Carvalho Chehab .depth = 12, 60b285192aSMauro Carvalho Chehab .flags = FORMAT_FLAGS_PACKED, 61b285192aSMauro Carvalho Chehab }, { 62b285192aSMauro Carvalho Chehab .name = "4:2:2, packed, YUYV", 63b285192aSMauro Carvalho Chehab .fourcc = V4L2_PIX_FMT_YUYV, 64b285192aSMauro Carvalho Chehab .depth = 16, 65b285192aSMauro Carvalho Chehab .flags = FORMAT_FLAGS_PACKED, 66b285192aSMauro Carvalho Chehab }, 67b285192aSMauro Carvalho Chehab }; 68b285192aSMauro Carvalho Chehab 6995c232a2SHans Verkuil static const struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) 70b285192aSMauro Carvalho Chehab { 71b285192aSMauro Carvalho Chehab unsigned int i; 72b285192aSMauro Carvalho Chehab 73b285192aSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(formats); i++) 74b285192aSMauro Carvalho Chehab if (formats[i].fourcc == fourcc) 75b285192aSMauro Carvalho Chehab return formats + i; 76b285192aSMauro Carvalho Chehab return NULL; 77b285192aSMauro Carvalho Chehab } 78b285192aSMauro Carvalho Chehab 79b285192aSMauro Carvalho Chehab void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, 80b285192aSMauro Carvalho Chehab u32 count) 81b285192aSMauro Carvalho Chehab { 82b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf; 83b285192aSMauro Carvalho Chehab int bc; 84b285192aSMauro Carvalho Chehab 85b285192aSMauro Carvalho Chehab for (bc = 0;; bc++) { 86b285192aSMauro Carvalho Chehab if (list_empty(&q->active)) { 87b285192aSMauro Carvalho Chehab dprintk(1, "bc=%d (=0: active empty)\n", bc); 88b285192aSMauro Carvalho Chehab break; 89b285192aSMauro Carvalho Chehab } 90b285192aSMauro Carvalho Chehab 91b285192aSMauro Carvalho Chehab buf = list_entry(q->active.next, struct cx25821_buffer, 92b285192aSMauro Carvalho Chehab vb.queue); 93b285192aSMauro Carvalho Chehab 94b285192aSMauro Carvalho Chehab /* count comes from the hw and it is 16bit wide -- 95b285192aSMauro Carvalho Chehab * this trick handles wrap-arounds correctly for 96b285192aSMauro Carvalho Chehab * up to 32767 buffers in flight... */ 97b285192aSMauro Carvalho Chehab if ((s16) (count - buf->count) < 0) 98b285192aSMauro Carvalho Chehab break; 99b285192aSMauro Carvalho Chehab 1008e6057b5SSakari Ailus v4l2_get_timestamp(&buf->vb.ts); 101b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_DONE; 102b285192aSMauro Carvalho Chehab list_del(&buf->vb.queue); 103b285192aSMauro Carvalho Chehab wake_up(&buf->vb.done); 104b285192aSMauro Carvalho Chehab } 105b285192aSMauro Carvalho Chehab 106b285192aSMauro Carvalho Chehab if (list_empty(&q->active)) 107b285192aSMauro Carvalho Chehab del_timer(&q->timeout); 108b285192aSMauro Carvalho Chehab else 109b285192aSMauro Carvalho Chehab mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); 110b285192aSMauro Carvalho Chehab if (bc != 1) 111b285192aSMauro Carvalho Chehab pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); 112b285192aSMauro Carvalho Chehab } 113b285192aSMauro Carvalho Chehab 114b285192aSMauro Carvalho Chehab int cx25821_start_video_dma(struct cx25821_dev *dev, 115b285192aSMauro Carvalho Chehab struct cx25821_dmaqueue *q, 116b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf, 117bfef0d35SHans Verkuil const struct sram_channel *channel) 118b285192aSMauro Carvalho Chehab { 119b285192aSMauro Carvalho Chehab int tmp = 0; 120b285192aSMauro Carvalho Chehab 121b285192aSMauro Carvalho Chehab /* setup fifo + format */ 122b285192aSMauro Carvalho Chehab cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma); 123b285192aSMauro Carvalho Chehab 124b285192aSMauro Carvalho Chehab /* reset counter */ 125b285192aSMauro Carvalho Chehab cx_write(channel->gpcnt_ctl, 3); 126b285192aSMauro Carvalho Chehab q->count = 1; 127b285192aSMauro Carvalho Chehab 128b285192aSMauro Carvalho Chehab /* enable irq */ 129b285192aSMauro Carvalho Chehab cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); 130b285192aSMauro Carvalho Chehab cx_set(channel->int_msk, 0x11); 131b285192aSMauro Carvalho Chehab 132b285192aSMauro Carvalho Chehab /* start dma */ 133b285192aSMauro Carvalho Chehab cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */ 134b285192aSMauro Carvalho Chehab 135b285192aSMauro Carvalho Chehab /* make sure upstream setting if any is reversed */ 136b285192aSMauro Carvalho Chehab tmp = cx_read(VID_CH_MODE_SEL); 137b285192aSMauro Carvalho Chehab cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); 138b285192aSMauro Carvalho Chehab 139b285192aSMauro Carvalho Chehab return 0; 140b285192aSMauro Carvalho Chehab } 141b285192aSMauro Carvalho Chehab 142dafc456cSMauro Carvalho Chehab static int cx25821_restart_video_queue(struct cx25821_dev *dev, 143b285192aSMauro Carvalho Chehab struct cx25821_dmaqueue *q, 144bfef0d35SHans Verkuil const struct sram_channel *channel) 145b285192aSMauro Carvalho Chehab { 146b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf, *prev; 147b285192aSMauro Carvalho Chehab struct list_head *item; 148b285192aSMauro Carvalho Chehab 149b285192aSMauro Carvalho Chehab if (!list_empty(&q->active)) { 150b285192aSMauro Carvalho Chehab buf = list_entry(q->active.next, struct cx25821_buffer, 151b285192aSMauro Carvalho Chehab vb.queue); 152b285192aSMauro Carvalho Chehab 153b285192aSMauro Carvalho Chehab cx25821_start_video_dma(dev, q, buf, channel); 154b285192aSMauro Carvalho Chehab 155b285192aSMauro Carvalho Chehab list_for_each(item, &q->active) { 156b285192aSMauro Carvalho Chehab buf = list_entry(item, struct cx25821_buffer, vb.queue); 157b285192aSMauro Carvalho Chehab buf->count = q->count++; 158b285192aSMauro Carvalho Chehab } 159b285192aSMauro Carvalho Chehab 160b285192aSMauro Carvalho Chehab mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); 161b285192aSMauro Carvalho Chehab return 0; 162b285192aSMauro Carvalho Chehab } 163b285192aSMauro Carvalho Chehab 164b285192aSMauro Carvalho Chehab prev = NULL; 165b285192aSMauro Carvalho Chehab for (;;) { 166b285192aSMauro Carvalho Chehab if (list_empty(&q->queued)) 167b285192aSMauro Carvalho Chehab return 0; 168b285192aSMauro Carvalho Chehab 169b285192aSMauro Carvalho Chehab buf = list_entry(q->queued.next, struct cx25821_buffer, 170b285192aSMauro Carvalho Chehab vb.queue); 171b285192aSMauro Carvalho Chehab 172b285192aSMauro Carvalho Chehab if (NULL == prev) { 173b285192aSMauro Carvalho Chehab list_move_tail(&buf->vb.queue, &q->active); 174b285192aSMauro Carvalho Chehab cx25821_start_video_dma(dev, q, buf, channel); 175b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_ACTIVE; 176b285192aSMauro Carvalho Chehab buf->count = q->count++; 177b285192aSMauro Carvalho Chehab mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); 178b285192aSMauro Carvalho Chehab } else if (prev->vb.width == buf->vb.width && 179b285192aSMauro Carvalho Chehab prev->vb.height == buf->vb.height && 180b285192aSMauro Carvalho Chehab prev->fmt == buf->fmt) { 181b285192aSMauro Carvalho Chehab list_move_tail(&buf->vb.queue, &q->active); 182b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_ACTIVE; 183b285192aSMauro Carvalho Chehab buf->count = q->count++; 184b285192aSMauro Carvalho Chehab prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); 185b285192aSMauro Carvalho Chehab prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ 186b285192aSMauro Carvalho Chehab } else { 187b285192aSMauro Carvalho Chehab return 0; 188b285192aSMauro Carvalho Chehab } 189b285192aSMauro Carvalho Chehab prev = buf; 190b285192aSMauro Carvalho Chehab } 191b285192aSMauro Carvalho Chehab } 192b285192aSMauro Carvalho Chehab 193dafc456cSMauro Carvalho Chehab static void cx25821_vid_timeout(unsigned long data) 194b285192aSMauro Carvalho Chehab { 195b285192aSMauro Carvalho Chehab struct cx25821_data *timeout_data = (struct cx25821_data *)data; 196b285192aSMauro Carvalho Chehab struct cx25821_dev *dev = timeout_data->dev; 197bfef0d35SHans Verkuil const struct sram_channel *channel = timeout_data->channel; 1982efe2cc4SHans Verkuil struct cx25821_dmaqueue *q = &dev->channels[channel->i].dma_vidq; 199b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf; 200b285192aSMauro Carvalho Chehab unsigned long flags; 201b285192aSMauro Carvalho Chehab 202b285192aSMauro Carvalho Chehab /* cx25821_sram_channel_dump(dev, channel); */ 203b285192aSMauro Carvalho Chehab cx_clear(channel->dma_ctl, 0x11); 204b285192aSMauro Carvalho Chehab 205b285192aSMauro Carvalho Chehab spin_lock_irqsave(&dev->slock, flags); 206b285192aSMauro Carvalho Chehab while (!list_empty(&q->active)) { 207b285192aSMauro Carvalho Chehab buf = list_entry(q->active.next, struct cx25821_buffer, 208b285192aSMauro Carvalho Chehab vb.queue); 209b285192aSMauro Carvalho Chehab list_del(&buf->vb.queue); 210b285192aSMauro Carvalho Chehab 211b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_ERROR; 212b285192aSMauro Carvalho Chehab wake_up(&buf->vb.done); 213b285192aSMauro Carvalho Chehab } 214b285192aSMauro Carvalho Chehab 215b285192aSMauro Carvalho Chehab cx25821_restart_video_queue(dev, q, channel); 216b285192aSMauro Carvalho Chehab spin_unlock_irqrestore(&dev->slock, flags); 217b285192aSMauro Carvalho Chehab } 218b285192aSMauro Carvalho Chehab 219b285192aSMauro Carvalho Chehab int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) 220b285192aSMauro Carvalho Chehab { 221b285192aSMauro Carvalho Chehab u32 count = 0; 222b285192aSMauro Carvalho Chehab int handled = 0; 223b285192aSMauro Carvalho Chehab u32 mask; 224bfef0d35SHans Verkuil const struct sram_channel *channel = dev->channels[chan_num].sram_channels; 225b285192aSMauro Carvalho Chehab 226b285192aSMauro Carvalho Chehab mask = cx_read(channel->int_msk); 227b285192aSMauro Carvalho Chehab if (0 == (status & mask)) 228b285192aSMauro Carvalho Chehab return handled; 229b285192aSMauro Carvalho Chehab 230b285192aSMauro Carvalho Chehab cx_write(channel->int_stat, status); 231b285192aSMauro Carvalho Chehab 232b285192aSMauro Carvalho Chehab /* risc op code error */ 233b285192aSMauro Carvalho Chehab if (status & (1 << 16)) { 234b285192aSMauro Carvalho Chehab pr_warn("%s, %s: video risc op code error\n", 235b285192aSMauro Carvalho Chehab dev->name, channel->name); 236b285192aSMauro Carvalho Chehab cx_clear(channel->dma_ctl, 0x11); 237b285192aSMauro Carvalho Chehab cx25821_sram_channel_dump(dev, channel); 238b285192aSMauro Carvalho Chehab } 239b285192aSMauro Carvalho Chehab 240b285192aSMauro Carvalho Chehab /* risc1 y */ 241b285192aSMauro Carvalho Chehab if (status & FLD_VID_DST_RISC1) { 242b285192aSMauro Carvalho Chehab spin_lock(&dev->slock); 243b285192aSMauro Carvalho Chehab count = cx_read(channel->gpcnt); 2442efe2cc4SHans Verkuil cx25821_video_wakeup(dev, &dev->channels[channel->i].dma_vidq, 245b285192aSMauro Carvalho Chehab count); 246b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 247b285192aSMauro Carvalho Chehab handled++; 248b285192aSMauro Carvalho Chehab } 249b285192aSMauro Carvalho Chehab 250b285192aSMauro Carvalho Chehab /* risc2 y */ 251b285192aSMauro Carvalho Chehab if (status & 0x10) { 252b285192aSMauro Carvalho Chehab dprintk(2, "stopper video\n"); 253b285192aSMauro Carvalho Chehab spin_lock(&dev->slock); 254b285192aSMauro Carvalho Chehab cx25821_restart_video_queue(dev, 2552efe2cc4SHans Verkuil &dev->channels[channel->i].dma_vidq, channel); 256b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 257b285192aSMauro Carvalho Chehab handled++; 258b285192aSMauro Carvalho Chehab } 259b285192aSMauro Carvalho Chehab return handled; 260b285192aSMauro Carvalho Chehab } 261b285192aSMauro Carvalho Chehab 26295c232a2SHans Verkuil static int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, 263b285192aSMauro Carvalho Chehab unsigned int *size) 264b285192aSMauro Carvalho Chehab { 2652efe2cc4SHans Verkuil struct cx25821_channel *chan = q->priv_data; 266b285192aSMauro Carvalho Chehab 2672efe2cc4SHans Verkuil *size = chan->fmt->depth * chan->width * chan->height >> 3; 268b285192aSMauro Carvalho Chehab 269b285192aSMauro Carvalho Chehab if (0 == *count) 270b285192aSMauro Carvalho Chehab *count = 32; 271b285192aSMauro Carvalho Chehab 272b285192aSMauro Carvalho Chehab if (*size * *count > vid_limit * 1024 * 1024) 273b285192aSMauro Carvalho Chehab *count = (vid_limit * 1024 * 1024) / *size; 274b285192aSMauro Carvalho Chehab 275b285192aSMauro Carvalho Chehab return 0; 276b285192aSMauro Carvalho Chehab } 277b285192aSMauro Carvalho Chehab 27895c232a2SHans Verkuil static int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, 279b285192aSMauro Carvalho Chehab enum v4l2_field field) 280b285192aSMauro Carvalho Chehab { 2812efe2cc4SHans Verkuil struct cx25821_channel *chan = q->priv_data; 2822efe2cc4SHans Verkuil struct cx25821_dev *dev = chan->dev; 283b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf = 284b285192aSMauro Carvalho Chehab container_of(vb, struct cx25821_buffer, vb); 285b285192aSMauro Carvalho Chehab int rc, init_buffer = 0; 286b285192aSMauro Carvalho Chehab u32 line0_offset; 287b285192aSMauro Carvalho Chehab struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); 288b285192aSMauro Carvalho Chehab int bpl_local = LINE_SIZE_D1; 289b285192aSMauro Carvalho Chehab 2902efe2cc4SHans Verkuil BUG_ON(NULL == chan->fmt); 2912efe2cc4SHans Verkuil if (chan->width < 48 || chan->width > 720 || 2922efe2cc4SHans Verkuil chan->height < 32 || chan->height > 576) 293b285192aSMauro Carvalho Chehab return -EINVAL; 294b285192aSMauro Carvalho Chehab 2952efe2cc4SHans Verkuil buf->vb.size = (chan->width * chan->height * chan->fmt->depth) >> 3; 296b285192aSMauro Carvalho Chehab 297b285192aSMauro Carvalho Chehab if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) 298b285192aSMauro Carvalho Chehab return -EINVAL; 299b285192aSMauro Carvalho Chehab 3002efe2cc4SHans Verkuil if (buf->fmt != chan->fmt || 3012efe2cc4SHans Verkuil buf->vb.width != chan->width || 3022efe2cc4SHans Verkuil buf->vb.height != chan->height || buf->vb.field != field) { 3032efe2cc4SHans Verkuil buf->fmt = chan->fmt; 3042efe2cc4SHans Verkuil buf->vb.width = chan->width; 3052efe2cc4SHans Verkuil buf->vb.height = chan->height; 306b285192aSMauro Carvalho Chehab buf->vb.field = field; 307b285192aSMauro Carvalho Chehab init_buffer = 1; 308b285192aSMauro Carvalho Chehab } 309b285192aSMauro Carvalho Chehab 310b285192aSMauro Carvalho Chehab if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { 311b285192aSMauro Carvalho Chehab init_buffer = 1; 312b285192aSMauro Carvalho Chehab rc = videobuf_iolock(q, &buf->vb, NULL); 313b285192aSMauro Carvalho Chehab if (0 != rc) { 314b285192aSMauro Carvalho Chehab printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); 315b285192aSMauro Carvalho Chehab goto fail; 316b285192aSMauro Carvalho Chehab } 317b285192aSMauro Carvalho Chehab } 318b285192aSMauro Carvalho Chehab 319b285192aSMauro Carvalho Chehab dprintk(1, "init_buffer=%d\n", init_buffer); 320b285192aSMauro Carvalho Chehab 321b285192aSMauro Carvalho Chehab if (init_buffer) { 3228d125c50SHans Verkuil if (chan->pixel_formats == PIXEL_FRMT_411) 323b285192aSMauro Carvalho Chehab buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; 324b285192aSMauro Carvalho Chehab else 325b285192aSMauro Carvalho Chehab buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); 326b285192aSMauro Carvalho Chehab 3278d125c50SHans Verkuil if (chan->pixel_formats == PIXEL_FRMT_411) { 328b285192aSMauro Carvalho Chehab bpl_local = buf->bpl; 329b285192aSMauro Carvalho Chehab } else { 330b285192aSMauro Carvalho Chehab bpl_local = buf->bpl; /* Default */ 331b285192aSMauro Carvalho Chehab 3328d125c50SHans Verkuil if (chan->use_cif_resolution) { 333988f7b80SHans Verkuil if (dev->tvnorm & V4L2_STD_625_50) 334b285192aSMauro Carvalho Chehab bpl_local = 352 << 1; 335b285192aSMauro Carvalho Chehab else 3368d125c50SHans Verkuil bpl_local = chan->cif_width << 1; 337b285192aSMauro Carvalho Chehab } 338b285192aSMauro Carvalho Chehab } 339b285192aSMauro Carvalho Chehab 340b285192aSMauro Carvalho Chehab switch (buf->vb.field) { 341b285192aSMauro Carvalho Chehab case V4L2_FIELD_TOP: 342b285192aSMauro Carvalho Chehab cx25821_risc_buffer(dev->pci, &buf->risc, 343b285192aSMauro Carvalho Chehab dma->sglist, 0, UNSET, 344b285192aSMauro Carvalho Chehab buf->bpl, 0, buf->vb.height); 345b285192aSMauro Carvalho Chehab break; 346b285192aSMauro Carvalho Chehab case V4L2_FIELD_BOTTOM: 347b285192aSMauro Carvalho Chehab cx25821_risc_buffer(dev->pci, &buf->risc, 348b285192aSMauro Carvalho Chehab dma->sglist, UNSET, 0, 349b285192aSMauro Carvalho Chehab buf->bpl, 0, buf->vb.height); 350b285192aSMauro Carvalho Chehab break; 351b285192aSMauro Carvalho Chehab case V4L2_FIELD_INTERLACED: 352b285192aSMauro Carvalho Chehab /* All other formats are top field first */ 353b285192aSMauro Carvalho Chehab line0_offset = 0; 354b285192aSMauro Carvalho Chehab dprintk(1, "top field first\n"); 355b285192aSMauro Carvalho Chehab 356b285192aSMauro Carvalho Chehab cx25821_risc_buffer(dev->pci, &buf->risc, 357b285192aSMauro Carvalho Chehab dma->sglist, line0_offset, 358b285192aSMauro Carvalho Chehab bpl_local, bpl_local, bpl_local, 359b285192aSMauro Carvalho Chehab buf->vb.height >> 1); 360b285192aSMauro Carvalho Chehab break; 361b285192aSMauro Carvalho Chehab case V4L2_FIELD_SEQ_TB: 362b285192aSMauro Carvalho Chehab cx25821_risc_buffer(dev->pci, &buf->risc, 363b285192aSMauro Carvalho Chehab dma->sglist, 364b285192aSMauro Carvalho Chehab 0, buf->bpl * (buf->vb.height >> 1), 365b285192aSMauro Carvalho Chehab buf->bpl, 0, buf->vb.height >> 1); 366b285192aSMauro Carvalho Chehab break; 367b285192aSMauro Carvalho Chehab case V4L2_FIELD_SEQ_BT: 368b285192aSMauro Carvalho Chehab cx25821_risc_buffer(dev->pci, &buf->risc, 369b285192aSMauro Carvalho Chehab dma->sglist, 370b285192aSMauro Carvalho Chehab buf->bpl * (buf->vb.height >> 1), 0, 371b285192aSMauro Carvalho Chehab buf->bpl, 0, buf->vb.height >> 1); 372b285192aSMauro Carvalho Chehab break; 373b285192aSMauro Carvalho Chehab default: 374b285192aSMauro Carvalho Chehab BUG(); 375b285192aSMauro Carvalho Chehab } 376b285192aSMauro Carvalho Chehab } 377b285192aSMauro Carvalho Chehab 378b285192aSMauro Carvalho Chehab dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", 3792efe2cc4SHans Verkuil buf, buf->vb.i, chan->width, chan->height, chan->fmt->depth, 3802efe2cc4SHans Verkuil chan->fmt->name, (unsigned long)buf->risc.dma); 381b285192aSMauro Carvalho Chehab 382b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_PREPARED; 383b285192aSMauro Carvalho Chehab 384b285192aSMauro Carvalho Chehab return 0; 385b285192aSMauro Carvalho Chehab 386b285192aSMauro Carvalho Chehab fail: 387b285192aSMauro Carvalho Chehab cx25821_free_buffer(q, buf); 388b285192aSMauro Carvalho Chehab return rc; 389b285192aSMauro Carvalho Chehab } 390b285192aSMauro Carvalho Chehab 39195c232a2SHans Verkuil static void cx25821_buffer_release(struct videobuf_queue *q, 392b285192aSMauro Carvalho Chehab struct videobuf_buffer *vb) 393b285192aSMauro Carvalho Chehab { 394b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf = 395b285192aSMauro Carvalho Chehab container_of(vb, struct cx25821_buffer, vb); 396b285192aSMauro Carvalho Chehab 397b285192aSMauro Carvalho Chehab cx25821_free_buffer(q, buf); 398b285192aSMauro Carvalho Chehab } 399b285192aSMauro Carvalho Chehab 40095c232a2SHans Verkuil static int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) 401b285192aSMauro Carvalho Chehab { 4022efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 403b285192aSMauro Carvalho Chehab 4042efe2cc4SHans Verkuil return videobuf_mmap_mapper(&chan->vidq, vma); 405b285192aSMauro Carvalho Chehab } 406b285192aSMauro Carvalho Chehab 407b285192aSMauro Carvalho Chehab 408b285192aSMauro Carvalho Chehab static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) 409b285192aSMauro Carvalho Chehab { 410b285192aSMauro Carvalho Chehab struct cx25821_buffer *buf = 411b285192aSMauro Carvalho Chehab container_of(vb, struct cx25821_buffer, vb); 412b285192aSMauro Carvalho Chehab struct cx25821_buffer *prev; 4132efe2cc4SHans Verkuil struct cx25821_channel *chan = vq->priv_data; 4142efe2cc4SHans Verkuil struct cx25821_dev *dev = chan->dev; 4152efe2cc4SHans Verkuil struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq; 416b285192aSMauro Carvalho Chehab 417b285192aSMauro Carvalho Chehab /* add jump to stopper */ 418b285192aSMauro Carvalho Chehab buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); 419b285192aSMauro Carvalho Chehab buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); 420b285192aSMauro Carvalho Chehab buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ 421b285192aSMauro Carvalho Chehab 422b285192aSMauro Carvalho Chehab dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); 423b285192aSMauro Carvalho Chehab 424b285192aSMauro Carvalho Chehab if (!list_empty(&q->queued)) { 425b285192aSMauro Carvalho Chehab list_add_tail(&buf->vb.queue, &q->queued); 426b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_QUEUED; 427b285192aSMauro Carvalho Chehab dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, 428b285192aSMauro Carvalho Chehab buf->vb.i); 429b285192aSMauro Carvalho Chehab 430b285192aSMauro Carvalho Chehab } else if (list_empty(&q->active)) { 431b285192aSMauro Carvalho Chehab list_add_tail(&buf->vb.queue, &q->active); 4322efe2cc4SHans Verkuil cx25821_start_video_dma(dev, q, buf, chan->sram_channels); 433b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_ACTIVE; 434b285192aSMauro Carvalho Chehab buf->count = q->count++; 435b285192aSMauro Carvalho Chehab mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); 436b285192aSMauro Carvalho Chehab dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", 437b285192aSMauro Carvalho Chehab buf, buf->vb.i, buf->count, q->count); 438b285192aSMauro Carvalho Chehab } else { 439b285192aSMauro Carvalho Chehab prev = list_entry(q->active.prev, struct cx25821_buffer, 440b285192aSMauro Carvalho Chehab vb.queue); 441b285192aSMauro Carvalho Chehab if (prev->vb.width == buf->vb.width 442b285192aSMauro Carvalho Chehab && prev->vb.height == buf->vb.height 443b285192aSMauro Carvalho Chehab && prev->fmt == buf->fmt) { 444b285192aSMauro Carvalho Chehab list_add_tail(&buf->vb.queue, &q->active); 445b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_ACTIVE; 446b285192aSMauro Carvalho Chehab buf->count = q->count++; 447b285192aSMauro Carvalho Chehab prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); 448b285192aSMauro Carvalho Chehab 449b285192aSMauro Carvalho Chehab /* 64 bit bits 63-32 */ 450b285192aSMauro Carvalho Chehab prev->risc.jmp[2] = cpu_to_le32(0); 451b285192aSMauro Carvalho Chehab dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", 452b285192aSMauro Carvalho Chehab buf, buf->vb.i, buf->count); 453b285192aSMauro Carvalho Chehab 454b285192aSMauro Carvalho Chehab } else { 455b285192aSMauro Carvalho Chehab list_add_tail(&buf->vb.queue, &q->queued); 456b285192aSMauro Carvalho Chehab buf->vb.state = VIDEOBUF_QUEUED; 457b285192aSMauro Carvalho Chehab dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, 458b285192aSMauro Carvalho Chehab buf->vb.i); 459b285192aSMauro Carvalho Chehab } 460b285192aSMauro Carvalho Chehab } 461b285192aSMauro Carvalho Chehab 462b285192aSMauro Carvalho Chehab if (list_empty(&q->active)) 463b285192aSMauro Carvalho Chehab dprintk(2, "active queue empty!\n"); 464b285192aSMauro Carvalho Chehab } 465b285192aSMauro Carvalho Chehab 466b285192aSMauro Carvalho Chehab static struct videobuf_queue_ops cx25821_video_qops = { 467b285192aSMauro Carvalho Chehab .buf_setup = cx25821_buffer_setup, 468b285192aSMauro Carvalho Chehab .buf_prepare = cx25821_buffer_prepare, 469b285192aSMauro Carvalho Chehab .buf_queue = buffer_queue, 470b285192aSMauro Carvalho Chehab .buf_release = cx25821_buffer_release, 471b285192aSMauro Carvalho Chehab }; 472b285192aSMauro Carvalho Chehab 473b285192aSMauro Carvalho Chehab static ssize_t video_read(struct file *file, char __user * data, size_t count, 474b285192aSMauro Carvalho Chehab loff_t *ppos) 475b285192aSMauro Carvalho Chehab { 4768d125c50SHans Verkuil struct v4l2_fh *fh = file->private_data; 4772efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 4788d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 47984293f08SHans Verkuil int err = 0; 480b285192aSMauro Carvalho Chehab 481be178cb4SHans Verkuil if (mutex_lock_interruptible(&dev->lock)) 482be178cb4SHans Verkuil return -ERESTARTSYS; 48384293f08SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != fh) { 484be178cb4SHans Verkuil err = -EBUSY; 48584293f08SHans Verkuil goto unlock; 48684293f08SHans Verkuil } 48784293f08SHans Verkuil chan->streaming_fh = fh; 48884293f08SHans Verkuil 4892efe2cc4SHans Verkuil err = videobuf_read_one(&chan->vidq, data, count, ppos, 490b285192aSMauro Carvalho Chehab file->f_flags & O_NONBLOCK); 49184293f08SHans Verkuil unlock: 492be178cb4SHans Verkuil mutex_unlock(&dev->lock); 493be178cb4SHans Verkuil return err; 494b285192aSMauro Carvalho Chehab } 495b285192aSMauro Carvalho Chehab 496b285192aSMauro Carvalho Chehab static unsigned int video_poll(struct file *file, 497b285192aSMauro Carvalho Chehab struct poll_table_struct *wait) 498b285192aSMauro Carvalho Chehab { 4992efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 5008d125c50SHans Verkuil unsigned long req_events = poll_requested_events(wait); 5018d125c50SHans Verkuil unsigned int res = v4l2_ctrl_poll(file, wait); 502b285192aSMauro Carvalho Chehab 5038d125c50SHans Verkuil if (req_events & (POLLIN | POLLRDNORM)) 5048d125c50SHans Verkuil res |= videobuf_poll_stream(file, &chan->vidq, wait); 5058d125c50SHans Verkuil return res; 506b285192aSMauro Carvalho Chehab 50784293f08SHans Verkuil /* This doesn't belong in poll(). This can be done 50884293f08SHans Verkuil * much better with vb2. We keep this code here as a 50984293f08SHans Verkuil * reminder. 51084293f08SHans Verkuil if ((res & POLLIN) && buf->vb.state == VIDEOBUF_DONE) { 51184293f08SHans Verkuil struct cx25821_dev *dev = chan->dev; 512b285192aSMauro Carvalho Chehab 5132efe2cc4SHans Verkuil if (dev && chan->use_cif_resolution) { 514b285192aSMauro Carvalho Chehab u8 cam_id = *((char *)buf->vb.baddr + 3); 515b285192aSMauro Carvalho Chehab memcpy((char *)buf->vb.baddr, 5162efe2cc4SHans Verkuil (char *)buf->vb.baddr + (chan->width * 2), 5172efe2cc4SHans Verkuil (chan->width * 2)); 518b285192aSMauro Carvalho Chehab *((char *)buf->vb.baddr + 3) = cam_id; 519b285192aSMauro Carvalho Chehab } 520b285192aSMauro Carvalho Chehab } 52184293f08SHans Verkuil */ 522b285192aSMauro Carvalho Chehab } 523b285192aSMauro Carvalho Chehab 524b285192aSMauro Carvalho Chehab static int video_release(struct file *file) 525b285192aSMauro Carvalho Chehab { 5262efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 5278d125c50SHans Verkuil struct v4l2_fh *fh = file->private_data; 5282efe2cc4SHans Verkuil struct cx25821_dev *dev = chan->dev; 529bfef0d35SHans Verkuil const struct sram_channel *sram_ch = 530bfef0d35SHans Verkuil dev->channels[0].sram_channels; 531b285192aSMauro Carvalho Chehab 532be178cb4SHans Verkuil mutex_lock(&dev->lock); 533b285192aSMauro Carvalho Chehab /* stop the risc engine and fifo */ 534bfef0d35SHans Verkuil cx_write(sram_ch->dma_ctl, 0); /* FIFO and RISC disable */ 535b285192aSMauro Carvalho Chehab 536b285192aSMauro Carvalho Chehab /* stop video capture */ 53784293f08SHans Verkuil if (chan->streaming_fh == fh) { 5382efe2cc4SHans Verkuil videobuf_queue_cancel(&chan->vidq); 53984293f08SHans Verkuil chan->streaming_fh = NULL; 540b285192aSMauro Carvalho Chehab } 541b285192aSMauro Carvalho Chehab 5422efe2cc4SHans Verkuil if (chan->vidq.read_buf) { 5432efe2cc4SHans Verkuil cx25821_buffer_release(&chan->vidq, chan->vidq.read_buf); 5442efe2cc4SHans Verkuil kfree(chan->vidq.read_buf); 545b285192aSMauro Carvalho Chehab } 546b285192aSMauro Carvalho Chehab 5472efe2cc4SHans Verkuil videobuf_mmap_free(&chan->vidq); 54884293f08SHans Verkuil mutex_unlock(&dev->lock); 549b285192aSMauro Carvalho Chehab 5508d125c50SHans Verkuil return v4l2_fh_release(file); 551b285192aSMauro Carvalho Chehab } 552b285192aSMauro Carvalho Chehab 55395c232a2SHans Verkuil /* VIDEO IOCTLS */ 5544c1d0f73SHans Verkuil 5554c1d0f73SHans Verkuil static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 5564c1d0f73SHans Verkuil struct v4l2_fmtdesc *f) 5574c1d0f73SHans Verkuil { 5584c1d0f73SHans Verkuil if (unlikely(f->index >= ARRAY_SIZE(formats))) 5594c1d0f73SHans Verkuil return -EINVAL; 5604c1d0f73SHans Verkuil 5614c1d0f73SHans Verkuil strlcpy(f->description, formats[f->index].name, sizeof(f->description)); 5624c1d0f73SHans Verkuil f->pixelformat = formats[f->index].fourcc; 5634c1d0f73SHans Verkuil 5644c1d0f73SHans Verkuil return 0; 5654c1d0f73SHans Verkuil } 5664c1d0f73SHans Verkuil 56795c232a2SHans Verkuil static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, 56895c232a2SHans Verkuil struct v4l2_format *f) 56995c232a2SHans Verkuil { 5702efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 57195c232a2SHans Verkuil 5722efe2cc4SHans Verkuil f->fmt.pix.width = chan->width; 5732efe2cc4SHans Verkuil f->fmt.pix.height = chan->height; 5742efe2cc4SHans Verkuil f->fmt.pix.field = chan->vidq.field; 5752efe2cc4SHans Verkuil f->fmt.pix.pixelformat = chan->fmt->fourcc; 576988f7b80SHans Verkuil f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; 577988f7b80SHans Verkuil f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; 578988f7b80SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 579988f7b80SHans Verkuil f->fmt.pix.priv = 0; 58095c232a2SHans Verkuil 58195c232a2SHans Verkuil return 0; 58295c232a2SHans Verkuil } 58395c232a2SHans Verkuil 58495c232a2SHans Verkuil static int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, 58595c232a2SHans Verkuil struct v4l2_format *f) 58695c232a2SHans Verkuil { 587988f7b80SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 588988f7b80SHans Verkuil struct cx25821_dev *dev = chan->dev; 58995c232a2SHans Verkuil const struct cx25821_fmt *fmt; 590988f7b80SHans Verkuil enum v4l2_field field = f->fmt.pix.field; 59195c232a2SHans Verkuil unsigned int maxw, maxh; 592988f7b80SHans Verkuil unsigned w; 59395c232a2SHans Verkuil 59495c232a2SHans Verkuil fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); 59595c232a2SHans Verkuil if (NULL == fmt) 59695c232a2SHans Verkuil return -EINVAL; 59795c232a2SHans Verkuil maxw = 720; 598988f7b80SHans Verkuil maxh = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; 59995c232a2SHans Verkuil 600988f7b80SHans Verkuil w = f->fmt.pix.width; 601988f7b80SHans Verkuil if (field != V4L2_FIELD_BOTTOM) 60295c232a2SHans Verkuil field = V4L2_FIELD_TOP; 603988f7b80SHans Verkuil if (w < 352) { 604988f7b80SHans Verkuil w = 176; 605988f7b80SHans Verkuil f->fmt.pix.height = maxh / 4; 606988f7b80SHans Verkuil } else if (w < 720) { 607988f7b80SHans Verkuil w = 352; 608988f7b80SHans Verkuil f->fmt.pix.height = maxh / 2; 609988f7b80SHans Verkuil } else { 610988f7b80SHans Verkuil w = 720; 61195c232a2SHans Verkuil f->fmt.pix.height = maxh; 612988f7b80SHans Verkuil field = V4L2_FIELD_INTERLACED; 613988f7b80SHans Verkuil } 614988f7b80SHans Verkuil f->fmt.pix.field = field; 615988f7b80SHans Verkuil f->fmt.pix.width = w; 61695c232a2SHans Verkuil f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 61795c232a2SHans Verkuil f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 618988f7b80SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 619988f7b80SHans Verkuil f->fmt.pix.priv = 0; 62095c232a2SHans Verkuil 62195c232a2SHans Verkuil return 0; 62295c232a2SHans Verkuil } 623b285192aSMauro Carvalho Chehab 624b285192aSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 625b285192aSMauro Carvalho Chehab struct v4l2_format *f) 626b285192aSMauro Carvalho Chehab { 6272efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6288d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 629b285192aSMauro Carvalho Chehab int pix_format = PIXEL_FRMT_422; 630a6aa0dc4SHans Verkuil int err; 631b285192aSMauro Carvalho Chehab 632b285192aSMauro Carvalho Chehab err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); 633b285192aSMauro Carvalho Chehab 634b285192aSMauro Carvalho Chehab if (0 != err) 635b285192aSMauro Carvalho Chehab return err; 636b285192aSMauro Carvalho Chehab 6372efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); 6382efe2cc4SHans Verkuil chan->vidq.field = f->fmt.pix.field; 6392efe2cc4SHans Verkuil chan->width = f->fmt.pix.width; 6402efe2cc4SHans Verkuil chan->height = f->fmt.pix.height; 641b285192aSMauro Carvalho Chehab 642b285192aSMauro Carvalho Chehab if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) 643b285192aSMauro Carvalho Chehab pix_format = PIXEL_FRMT_411; 644b285192aSMauro Carvalho Chehab else 645988f7b80SHans Verkuil pix_format = PIXEL_FRMT_422; 646b285192aSMauro Carvalho Chehab 647b285192aSMauro Carvalho Chehab cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); 648b285192aSMauro Carvalho Chehab 649b285192aSMauro Carvalho Chehab /* check if cif resolution */ 6502efe2cc4SHans Verkuil if (chan->width == 320 || chan->width == 352) 6512efe2cc4SHans Verkuil chan->use_cif_resolution = 1; 652b285192aSMauro Carvalho Chehab else 6532efe2cc4SHans Verkuil chan->use_cif_resolution = 0; 654b285192aSMauro Carvalho Chehab 6552efe2cc4SHans Verkuil chan->cif_width = chan->width; 6562efe2cc4SHans Verkuil medusa_set_resolution(dev, chan->width, SRAM_CH00); 657b285192aSMauro Carvalho Chehab return 0; 658b285192aSMauro Carvalho Chehab } 659b285192aSMauro Carvalho Chehab 6604c1d0f73SHans Verkuil static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) 6614c1d0f73SHans Verkuil { 6624c1d0f73SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6634c1d0f73SHans Verkuil 6644c1d0f73SHans Verkuil if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) 6654c1d0f73SHans Verkuil return -EINVAL; 6664c1d0f73SHans Verkuil 6674c1d0f73SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != priv) 6684c1d0f73SHans Verkuil return -EBUSY; 6694c1d0f73SHans Verkuil chan->streaming_fh = priv; 6704c1d0f73SHans Verkuil 6714c1d0f73SHans Verkuil return videobuf_streamon(&chan->vidq); 6724c1d0f73SHans Verkuil } 6734c1d0f73SHans Verkuil 6744c1d0f73SHans Verkuil static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) 6754c1d0f73SHans Verkuil { 6764c1d0f73SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6774c1d0f73SHans Verkuil 6784c1d0f73SHans Verkuil if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) 6794c1d0f73SHans Verkuil return -EINVAL; 6804c1d0f73SHans Verkuil 6814c1d0f73SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != priv) 6824c1d0f73SHans Verkuil return -EBUSY; 6834c1d0f73SHans Verkuil if (chan->streaming_fh == NULL) 6844c1d0f73SHans Verkuil return 0; 6854c1d0f73SHans Verkuil 6864c1d0f73SHans Verkuil chan->streaming_fh = NULL; 6874c1d0f73SHans Verkuil return videobuf_streamoff(&chan->vidq); 6884c1d0f73SHans Verkuil } 6894c1d0f73SHans Verkuil 690b285192aSMauro Carvalho Chehab static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) 691b285192aSMauro Carvalho Chehab { 692b285192aSMauro Carvalho Chehab int ret_val = 0; 6932efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 694b285192aSMauro Carvalho Chehab 6952efe2cc4SHans Verkuil ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK); 6962efe2cc4SHans Verkuil p->sequence = chan->dma_vidq.count; 697b285192aSMauro Carvalho Chehab 698b285192aSMauro Carvalho Chehab return ret_val; 699b285192aSMauro Carvalho Chehab } 700b285192aSMauro Carvalho Chehab 701b285192aSMauro Carvalho Chehab static int vidioc_log_status(struct file *file, void *priv) 702b285192aSMauro Carvalho Chehab { 7038d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7048d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 7058d125c50SHans Verkuil const struct sram_channel *sram_ch = chan->sram_channels; 706b285192aSMauro Carvalho Chehab u32 tmp = 0; 707b285192aSMauro Carvalho Chehab 708b285192aSMauro Carvalho Chehab tmp = cx_read(sram_ch->dma_ctl); 709b285192aSMauro Carvalho Chehab pr_info("Video input 0 is %s\n", 710b285192aSMauro Carvalho Chehab (tmp & 0x11) ? "streaming" : "stopped"); 711b285192aSMauro Carvalho Chehab return 0; 712b285192aSMauro Carvalho Chehab } 713b285192aSMauro Carvalho Chehab 714b285192aSMauro Carvalho Chehab 71595c232a2SHans Verkuil static int cx25821_vidioc_querycap(struct file *file, void *priv, 716b285192aSMauro Carvalho Chehab struct v4l2_capability *cap) 717b285192aSMauro Carvalho Chehab { 7188d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7198d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 7203dd473caSHans Verkuil const u32 cap_input = V4L2_CAP_VIDEO_CAPTURE | 7213dd473caSHans Verkuil V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; 7220df13d99SHans Verkuil const u32 cap_output = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE; 723b285192aSMauro Carvalho Chehab 724b285192aSMauro Carvalho Chehab strcpy(cap->driver, "cx25821"); 725b285192aSMauro Carvalho Chehab strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); 726b285192aSMauro Carvalho Chehab sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); 7278d125c50SHans Verkuil if (chan->id >= VID_CHANNEL_NUM) 7283dd473caSHans Verkuil cap->device_caps = cap_output; 7293dd473caSHans Verkuil else 7303dd473caSHans Verkuil cap->device_caps = cap_input; 7310df13d99SHans Verkuil cap->capabilities = cap_input | cap_output | V4L2_CAP_DEVICE_CAPS; 732b285192aSMauro Carvalho Chehab return 0; 733b285192aSMauro Carvalho Chehab } 734b285192aSMauro Carvalho Chehab 73595c232a2SHans Verkuil static int cx25821_vidioc_reqbufs(struct file *file, void *priv, 736b285192aSMauro Carvalho Chehab struct v4l2_requestbuffers *p) 737b285192aSMauro Carvalho Chehab { 7382efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7392efe2cc4SHans Verkuil 7402efe2cc4SHans Verkuil return videobuf_reqbufs(&chan->vidq, p); 741b285192aSMauro Carvalho Chehab } 742b285192aSMauro Carvalho Chehab 74395c232a2SHans Verkuil static int cx25821_vidioc_querybuf(struct file *file, void *priv, 744b285192aSMauro Carvalho Chehab struct v4l2_buffer *p) 745b285192aSMauro Carvalho Chehab { 7462efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7472efe2cc4SHans Verkuil 7482efe2cc4SHans Verkuil return videobuf_querybuf(&chan->vidq, p); 749b285192aSMauro Carvalho Chehab } 750b285192aSMauro Carvalho Chehab 75195c232a2SHans Verkuil static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) 752b285192aSMauro Carvalho Chehab { 7532efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7542efe2cc4SHans Verkuil 7552efe2cc4SHans Verkuil return videobuf_qbuf(&chan->vidq, p); 756b285192aSMauro Carvalho Chehab } 757b285192aSMauro Carvalho Chehab 75895c232a2SHans Verkuil static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms) 75918c73af6SHans Verkuil { 7608d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 76118c73af6SHans Verkuil 7628d125c50SHans Verkuil *tvnorms = chan->dev->tvnorm; 76318c73af6SHans Verkuil return 0; 76418c73af6SHans Verkuil } 76518c73af6SHans Verkuil 766314527acSHans Verkuil int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms) 767b285192aSMauro Carvalho Chehab { 7688d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7698d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 770b285192aSMauro Carvalho Chehab 771314527acSHans Verkuil if (dev->tvnorm == tvnorms) 772b285192aSMauro Carvalho Chehab return 0; 773b285192aSMauro Carvalho Chehab 774a6aa0dc4SHans Verkuil dev->tvnorm = tvnorms; 775988f7b80SHans Verkuil chan->width = 720; 776988f7b80SHans Verkuil chan->height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; 777b285192aSMauro Carvalho Chehab 778b285192aSMauro Carvalho Chehab medusa_set_videostandard(dev); 779b285192aSMauro Carvalho Chehab 780b285192aSMauro Carvalho Chehab return 0; 781b285192aSMauro Carvalho Chehab } 782b285192aSMauro Carvalho Chehab 78395c232a2SHans Verkuil static int cx25821_vidioc_enum_input(struct file *file, void *priv, 78495c232a2SHans Verkuil struct v4l2_input *i) 785b285192aSMauro Carvalho Chehab { 786a6aa0dc4SHans Verkuil if (i->index) 787b285192aSMauro Carvalho Chehab return -EINVAL; 788b285192aSMauro Carvalho Chehab 789b285192aSMauro Carvalho Chehab i->type = V4L2_INPUT_TYPE_CAMERA; 790b285192aSMauro Carvalho Chehab i->std = CX25821_NORMS; 791a6aa0dc4SHans Verkuil strcpy(i->name, "Composite"); 792b285192aSMauro Carvalho Chehab return 0; 793b285192aSMauro Carvalho Chehab } 794b285192aSMauro Carvalho Chehab 79595c232a2SHans Verkuil static int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) 796b285192aSMauro Carvalho Chehab { 797a6aa0dc4SHans Verkuil *i = 0; 798b285192aSMauro Carvalho Chehab return 0; 799b285192aSMauro Carvalho Chehab } 800b285192aSMauro Carvalho Chehab 80195c232a2SHans Verkuil static int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) 802b285192aSMauro Carvalho Chehab { 803a6aa0dc4SHans Verkuil return i ? -EINVAL : 0; 804b285192aSMauro Carvalho Chehab } 805b285192aSMauro Carvalho Chehab 806f8d7ee70SHans Verkuil static int cx25821_s_ctrl(struct v4l2_ctrl *ctrl) 807b285192aSMauro Carvalho Chehab { 808f8d7ee70SHans Verkuil struct cx25821_channel *chan = 809f8d7ee70SHans Verkuil container_of(ctrl->handler, struct cx25821_channel, hdl); 810f8d7ee70SHans Verkuil struct cx25821_dev *dev = chan->dev; 811b285192aSMauro Carvalho Chehab 812f8d7ee70SHans Verkuil switch (ctrl->id) { 813b285192aSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 814f8d7ee70SHans Verkuil medusa_set_brightness(dev, ctrl->val, chan->id); 815b285192aSMauro Carvalho Chehab break; 816b285192aSMauro Carvalho Chehab case V4L2_CID_HUE: 817f8d7ee70SHans Verkuil medusa_set_hue(dev, ctrl->val, chan->id); 818b285192aSMauro Carvalho Chehab break; 819b285192aSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 820f8d7ee70SHans Verkuil medusa_set_contrast(dev, ctrl->val, chan->id); 821b285192aSMauro Carvalho Chehab break; 822b285192aSMauro Carvalho Chehab case V4L2_CID_SATURATION: 823f8d7ee70SHans Verkuil medusa_set_saturation(dev, ctrl->val, chan->id); 824b285192aSMauro Carvalho Chehab break; 825b285192aSMauro Carvalho Chehab default: 826f8d7ee70SHans Verkuil return -EINVAL; 827b285192aSMauro Carvalho Chehab } 828f8d7ee70SHans Verkuil return 0; 829b285192aSMauro Carvalho Chehab } 830b285192aSMauro Carvalho Chehab 8311f198870SHans Verkuil static int cx25821_vidioc_enum_output(struct file *file, void *priv, 8321f198870SHans Verkuil struct v4l2_output *o) 833b285192aSMauro Carvalho Chehab { 8341f198870SHans Verkuil if (o->index) 8351f198870SHans Verkuil return -EINVAL; 836b285192aSMauro Carvalho Chehab 8371f198870SHans Verkuil o->type = V4L2_INPUT_TYPE_CAMERA; 8381f198870SHans Verkuil o->std = CX25821_NORMS; 8391f198870SHans Verkuil strcpy(o->name, "Composite"); 840b285192aSMauro Carvalho Chehab return 0; 841b285192aSMauro Carvalho Chehab } 842b285192aSMauro Carvalho Chehab 8431f198870SHans Verkuil static int cx25821_vidioc_g_output(struct file *file, void *priv, unsigned int *o) 844b285192aSMauro Carvalho Chehab { 8451f198870SHans Verkuil *o = 0; 846b285192aSMauro Carvalho Chehab return 0; 847b285192aSMauro Carvalho Chehab } 848b285192aSMauro Carvalho Chehab 8491f198870SHans Verkuil static int cx25821_vidioc_s_output(struct file *file, void *priv, unsigned int o) 850b285192aSMauro Carvalho Chehab { 8511f198870SHans Verkuil return o ? -EINVAL : 0; 852b285192aSMauro Carvalho Chehab } 853b285192aSMauro Carvalho Chehab 854e90878abSHans Verkuil static int cx25821_vidioc_try_fmt_vid_out(struct file *file, void *priv, 855e90878abSHans Verkuil struct v4l2_format *f) 856e90878abSHans Verkuil { 857e90878abSHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 858e90878abSHans Verkuil struct cx25821_dev *dev = chan->dev; 859e90878abSHans Verkuil const struct cx25821_fmt *fmt; 860e90878abSHans Verkuil 861e90878abSHans Verkuil fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); 862e90878abSHans Verkuil if (NULL == fmt) 863e90878abSHans Verkuil return -EINVAL; 864e90878abSHans Verkuil f->fmt.pix.width = 720; 865e90878abSHans Verkuil f->fmt.pix.height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; 866e90878abSHans Verkuil f->fmt.pix.field = V4L2_FIELD_INTERLACED; 867e90878abSHans Verkuil f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 868e90878abSHans Verkuil f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 869e90878abSHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 870e90878abSHans Verkuil f->fmt.pix.priv = 0; 871e90878abSHans Verkuil return 0; 872e90878abSHans Verkuil } 873e90878abSHans Verkuil 874e90878abSHans Verkuil static int vidioc_s_fmt_vid_out(struct file *file, void *priv, 875e90878abSHans Verkuil struct v4l2_format *f) 876e90878abSHans Verkuil { 877e90878abSHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 878e90878abSHans Verkuil int err; 879e90878abSHans Verkuil 880e90878abSHans Verkuil err = cx25821_vidioc_try_fmt_vid_out(file, priv, f); 881e90878abSHans Verkuil 882e90878abSHans Verkuil if (0 != err) 883e90878abSHans Verkuil return err; 884e90878abSHans Verkuil 885e90878abSHans Verkuil chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); 886e90878abSHans Verkuil chan->vidq.field = f->fmt.pix.field; 887e90878abSHans Verkuil chan->width = f->fmt.pix.width; 888e90878abSHans Verkuil chan->height = f->fmt.pix.height; 889e90878abSHans Verkuil if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) 890e90878abSHans Verkuil chan->pixel_formats = PIXEL_FRMT_411; 891e90878abSHans Verkuil else 892e90878abSHans Verkuil chan->pixel_formats = PIXEL_FRMT_422; 893e90878abSHans Verkuil return 0; 894e90878abSHans Verkuil } 895e90878abSHans Verkuil 896*7087d31bSHans Verkuil static int video_out_release(struct file *file) 897*7087d31bSHans Verkuil { 898*7087d31bSHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 899*7087d31bSHans Verkuil struct cx25821_video_out_data *out = chan->out; 900*7087d31bSHans Verkuil struct cx25821_dev *dev = chan->dev; 901*7087d31bSHans Verkuil 902*7087d31bSHans Verkuil mutex_lock(&dev->lock); 903*7087d31bSHans Verkuil if ((chan->id == SRAM_CH09 || chan->id == SRAM_CH10) && out->_is_running) 904*7087d31bSHans Verkuil cx25821_stop_upstream_video(chan); 905*7087d31bSHans Verkuil mutex_unlock(&dev->lock); 906*7087d31bSHans Verkuil 907*7087d31bSHans Verkuil return v4l2_fh_release(file); 908*7087d31bSHans Verkuil } 909*7087d31bSHans Verkuil 910f8d7ee70SHans Verkuil static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { 911f8d7ee70SHans Verkuil .s_ctrl = cx25821_s_ctrl, 912f8d7ee70SHans Verkuil }; 913f8d7ee70SHans Verkuil 914b285192aSMauro Carvalho Chehab static const struct v4l2_file_operations video_fops = { 915b285192aSMauro Carvalho Chehab .owner = THIS_MODULE, 9168d125c50SHans Verkuil .open = v4l2_fh_open, 917b285192aSMauro Carvalho Chehab .release = video_release, 918b285192aSMauro Carvalho Chehab .read = video_read, 919b285192aSMauro Carvalho Chehab .poll = video_poll, 920b285192aSMauro Carvalho Chehab .mmap = cx25821_video_mmap, 9211f198870SHans Verkuil .unlocked_ioctl = video_ioctl2, 922b285192aSMauro Carvalho Chehab }; 923b285192aSMauro Carvalho Chehab 924b285192aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops video_ioctl_ops = { 925b285192aSMauro Carvalho Chehab .vidioc_querycap = cx25821_vidioc_querycap, 926b285192aSMauro Carvalho Chehab .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, 927b285192aSMauro Carvalho Chehab .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, 928b285192aSMauro Carvalho Chehab .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, 929b285192aSMauro Carvalho Chehab .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 930b285192aSMauro Carvalho Chehab .vidioc_reqbufs = cx25821_vidioc_reqbufs, 931b285192aSMauro Carvalho Chehab .vidioc_querybuf = cx25821_vidioc_querybuf, 932b285192aSMauro Carvalho Chehab .vidioc_qbuf = cx25821_vidioc_qbuf, 933b285192aSMauro Carvalho Chehab .vidioc_dqbuf = vidioc_dqbuf, 93418c73af6SHans Verkuil .vidioc_g_std = cx25821_vidioc_g_std, 935b285192aSMauro Carvalho Chehab .vidioc_s_std = cx25821_vidioc_s_std, 936b285192aSMauro Carvalho Chehab .vidioc_enum_input = cx25821_vidioc_enum_input, 937b285192aSMauro Carvalho Chehab .vidioc_g_input = cx25821_vidioc_g_input, 938b285192aSMauro Carvalho Chehab .vidioc_s_input = cx25821_vidioc_s_input, 939b285192aSMauro Carvalho Chehab .vidioc_streamon = vidioc_streamon, 940b285192aSMauro Carvalho Chehab .vidioc_streamoff = vidioc_streamoff, 941b285192aSMauro Carvalho Chehab .vidioc_log_status = vidioc_log_status, 9428d125c50SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 9438d125c50SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 944b285192aSMauro Carvalho Chehab }; 945b285192aSMauro Carvalho Chehab 946ffd3c233SHans Verkuil static const struct video_device cx25821_video_device = { 947ffd3c233SHans Verkuil .name = "cx25821-video", 948b285192aSMauro Carvalho Chehab .fops = &video_fops, 949467870caSHans Verkuil .release = video_device_release_empty, 950ffd3c233SHans Verkuil .minor = -1, 951b285192aSMauro Carvalho Chehab .ioctl_ops = &video_ioctl_ops, 952b285192aSMauro Carvalho Chehab .tvnorms = CX25821_NORMS, 953b285192aSMauro Carvalho Chehab }; 954ffd3c233SHans Verkuil 9551f198870SHans Verkuil static const struct v4l2_file_operations video_out_fops = { 9561f198870SHans Verkuil .owner = THIS_MODULE, 9571f198870SHans Verkuil .open = v4l2_fh_open, 958*7087d31bSHans Verkuil .release = video_out_release, 9591f198870SHans Verkuil .unlocked_ioctl = video_ioctl2, 9601f198870SHans Verkuil }; 9611f198870SHans Verkuil 9621f198870SHans Verkuil static const struct v4l2_ioctl_ops video_out_ioctl_ops = { 9631f198870SHans Verkuil .vidioc_querycap = cx25821_vidioc_querycap, 964e90878abSHans Verkuil .vidioc_enum_fmt_vid_out = cx25821_vidioc_enum_fmt_vid_cap, 965e90878abSHans Verkuil .vidioc_g_fmt_vid_out = cx25821_vidioc_g_fmt_vid_cap, 966e90878abSHans Verkuil .vidioc_try_fmt_vid_out = cx25821_vidioc_try_fmt_vid_out, 967e90878abSHans Verkuil .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, 9681f198870SHans Verkuil .vidioc_g_std = cx25821_vidioc_g_std, 9691f198870SHans Verkuil .vidioc_s_std = cx25821_vidioc_s_std, 9701f198870SHans Verkuil .vidioc_enum_output = cx25821_vidioc_enum_output, 9711f198870SHans Verkuil .vidioc_g_output = cx25821_vidioc_g_output, 9721f198870SHans Verkuil .vidioc_s_output = cx25821_vidioc_s_output, 9731f198870SHans Verkuil .vidioc_log_status = vidioc_log_status, 9741f198870SHans Verkuil }; 9751f198870SHans Verkuil 9761f198870SHans Verkuil static const struct video_device cx25821_video_out_device = { 9771f198870SHans Verkuil .name = "cx25821-video", 9781f198870SHans Verkuil .fops = &video_out_fops, 9791f198870SHans Verkuil .release = video_device_release_empty, 9801f198870SHans Verkuil .minor = -1, 9811f198870SHans Verkuil .ioctl_ops = &video_out_ioctl_ops, 9821f198870SHans Verkuil .tvnorms = CX25821_NORMS, 9831f198870SHans Verkuil }; 9841f198870SHans Verkuil 985ffd3c233SHans Verkuil void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) 986ffd3c233SHans Verkuil { 987ffd3c233SHans Verkuil cx_clear(PCI_INT_MSK, 1); 988ffd3c233SHans Verkuil 989467870caSHans Verkuil if (video_is_registered(&dev->channels[chan_num].vdev)) { 990467870caSHans Verkuil video_unregister_device(&dev->channels[chan_num].vdev); 991f8d7ee70SHans Verkuil v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); 992ffd3c233SHans Verkuil 993ffd3c233SHans Verkuil btcx_riscmem_free(dev->pci, 9942efe2cc4SHans Verkuil &dev->channels[chan_num].dma_vidq.stopper); 995ffd3c233SHans Verkuil } 996ffd3c233SHans Verkuil } 997ffd3c233SHans Verkuil 998ffd3c233SHans Verkuil int cx25821_video_register(struct cx25821_dev *dev) 999ffd3c233SHans Verkuil { 1000ffd3c233SHans Verkuil int err; 1001ffd3c233SHans Verkuil int i; 1002ffd3c233SHans Verkuil 1003be178cb4SHans Verkuil /* initial device configuration */ 1004a6aa0dc4SHans Verkuil dev->tvnorm = V4L2_STD_NTSC_M; 1005be178cb4SHans Verkuil 1006ffd3c233SHans Verkuil spin_lock_init(&dev->slock); 1007ffd3c233SHans Verkuil 10081f198870SHans Verkuil for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { 10092efe2cc4SHans Verkuil struct cx25821_channel *chan = &dev->channels[i]; 10102efe2cc4SHans Verkuil struct video_device *vdev = &chan->vdev; 10112efe2cc4SHans Verkuil struct v4l2_ctrl_handler *hdl = &chan->hdl; 10121f198870SHans Verkuil bool is_output = i > SRAM_CH08; 1013467870caSHans Verkuil 1014ffd3c233SHans Verkuil if (i == SRAM_CH08) /* audio channel */ 1015ffd3c233SHans Verkuil continue; 1016ffd3c233SHans Verkuil 10171f198870SHans Verkuil if (!is_output) { 1018f8d7ee70SHans Verkuil v4l2_ctrl_handler_init(hdl, 4); 1019f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1020f8d7ee70SHans Verkuil V4L2_CID_BRIGHTNESS, 0, 10000, 1, 6200); 1021f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1022f8d7ee70SHans Verkuil V4L2_CID_CONTRAST, 0, 10000, 1, 5000); 1023f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1024f8d7ee70SHans Verkuil V4L2_CID_SATURATION, 0, 10000, 1, 5000); 1025f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1026f8d7ee70SHans Verkuil V4L2_CID_HUE, 0, 10000, 1, 5000); 1027f8d7ee70SHans Verkuil if (hdl->error) { 1028f8d7ee70SHans Verkuil err = hdl->error; 1029f8d7ee70SHans Verkuil goto fail_unreg; 1030f8d7ee70SHans Verkuil } 1031be178cb4SHans Verkuil err = v4l2_ctrl_handler_setup(hdl); 1032be178cb4SHans Verkuil if (err) 1033be178cb4SHans Verkuil goto fail_unreg; 1034*7087d31bSHans Verkuil } else { 1035*7087d31bSHans Verkuil chan->out = &dev->vid_out_data[i - SRAM_CH09]; 1036*7087d31bSHans Verkuil chan->out->chan = chan; 10371f198870SHans Verkuil } 1038ffd3c233SHans Verkuil 10392efe2cc4SHans Verkuil cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper, 10402efe2cc4SHans Verkuil chan->sram_channels->dma_ctl, 0x11, 0); 1041ffd3c233SHans Verkuil 10422efe2cc4SHans Verkuil chan->sram_channels = &cx25821_sram_channels[i]; 10432efe2cc4SHans Verkuil chan->width = 720; 10442efe2cc4SHans Verkuil if (dev->tvnorm & V4L2_STD_625_50) 10452efe2cc4SHans Verkuil chan->height = 576; 10462efe2cc4SHans Verkuil else 10472efe2cc4SHans Verkuil chan->height = 480; 1048ffd3c233SHans Verkuil 10492efe2cc4SHans Verkuil if (chan->pixel_formats == PIXEL_FRMT_411) 10502efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_Y41P); 10512efe2cc4SHans Verkuil else 10522efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_YUYV); 1053ffd3c233SHans Verkuil 10542efe2cc4SHans Verkuil cx_write(chan->sram_channels->int_stat, 0xffffffff); 1055ffd3c233SHans Verkuil 10562efe2cc4SHans Verkuil INIT_LIST_HEAD(&chan->dma_vidq.active); 10572efe2cc4SHans Verkuil INIT_LIST_HEAD(&chan->dma_vidq.queued); 10582efe2cc4SHans Verkuil 10592efe2cc4SHans Verkuil chan->timeout_data.dev = dev; 10602efe2cc4SHans Verkuil chan->timeout_data.channel = &cx25821_sram_channels[i]; 10612efe2cc4SHans Verkuil chan->dma_vidq.timeout.function = cx25821_vid_timeout; 10622efe2cc4SHans Verkuil chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data; 10632efe2cc4SHans Verkuil init_timer(&chan->dma_vidq.timeout); 10642efe2cc4SHans Verkuil 10651f198870SHans Verkuil if (!is_output) 10662efe2cc4SHans Verkuil videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev, 10672efe2cc4SHans Verkuil &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, 10682efe2cc4SHans Verkuil V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), 10692efe2cc4SHans Verkuil chan, &dev->lock); 1070ffd3c233SHans Verkuil 1071ffd3c233SHans Verkuil /* register v4l devices */ 10721f198870SHans Verkuil *vdev = is_output ? cx25821_video_out_device : cx25821_video_device; 1073467870caSHans Verkuil vdev->v4l2_dev = &dev->v4l2_dev; 10741f198870SHans Verkuil if (!is_output) 1075f8d7ee70SHans Verkuil vdev->ctrl_handler = hdl; 10761f198870SHans Verkuil else 10771f198870SHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 1078be178cb4SHans Verkuil vdev->lock = &dev->lock; 10798d125c50SHans Verkuil set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); 1080467870caSHans Verkuil snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); 10812efe2cc4SHans Verkuil video_set_drvdata(vdev, chan); 1082ffd3c233SHans Verkuil 1083467870caSHans Verkuil err = video_register_device(vdev, VFL_TYPE_GRABBER, 1084467870caSHans Verkuil video_nr[dev->nr]); 1085ffd3c233SHans Verkuil 1086ffd3c233SHans Verkuil if (err < 0) 1087ffd3c233SHans Verkuil goto fail_unreg; 1088ffd3c233SHans Verkuil } 1089ffd3c233SHans Verkuil 1090ffd3c233SHans Verkuil /* set PCI interrupt */ 1091ffd3c233SHans Verkuil cx_set(PCI_INT_MSK, 0xff); 1092ffd3c233SHans Verkuil 1093ffd3c233SHans Verkuil return 0; 1094ffd3c233SHans Verkuil 1095ffd3c233SHans Verkuil fail_unreg: 1096467870caSHans Verkuil while (i >= 0) 1097467870caSHans Verkuil cx25821_video_unregister(dev, i--); 1098ffd3c233SHans Verkuil return err; 1099ffd3c233SHans Verkuil } 1100