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; 59166f93178SMauro Carvalho Chehab unsigned int 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; 597988f7b80SHans Verkuil maxh = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480; 59895c232a2SHans Verkuil 599988f7b80SHans Verkuil w = f->fmt.pix.width; 600988f7b80SHans Verkuil if (field != V4L2_FIELD_BOTTOM) 60195c232a2SHans Verkuil field = V4L2_FIELD_TOP; 602988f7b80SHans Verkuil if (w < 352) { 603988f7b80SHans Verkuil w = 176; 604988f7b80SHans Verkuil f->fmt.pix.height = maxh / 4; 605988f7b80SHans Verkuil } else if (w < 720) { 606988f7b80SHans Verkuil w = 352; 607988f7b80SHans Verkuil f->fmt.pix.height = maxh / 2; 608988f7b80SHans Verkuil } else { 609988f7b80SHans Verkuil w = 720; 61095c232a2SHans Verkuil f->fmt.pix.height = maxh; 611988f7b80SHans Verkuil field = V4L2_FIELD_INTERLACED; 612988f7b80SHans Verkuil } 613988f7b80SHans Verkuil f->fmt.pix.field = field; 614988f7b80SHans Verkuil f->fmt.pix.width = w; 61595c232a2SHans Verkuil f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 61695c232a2SHans Verkuil f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 617988f7b80SHans Verkuil f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 618988f7b80SHans Verkuil f->fmt.pix.priv = 0; 61995c232a2SHans Verkuil 62095c232a2SHans Verkuil return 0; 62195c232a2SHans Verkuil } 622b285192aSMauro Carvalho Chehab 623b285192aSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 624b285192aSMauro Carvalho Chehab struct v4l2_format *f) 625b285192aSMauro Carvalho Chehab { 6262efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6278d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 628b285192aSMauro Carvalho Chehab int pix_format = PIXEL_FRMT_422; 629a6aa0dc4SHans Verkuil int err; 630b285192aSMauro Carvalho Chehab 631b285192aSMauro Carvalho Chehab err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); 632b285192aSMauro Carvalho Chehab 633b285192aSMauro Carvalho Chehab if (0 != err) 634b285192aSMauro Carvalho Chehab return err; 635b285192aSMauro Carvalho Chehab 6362efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); 6372efe2cc4SHans Verkuil chan->vidq.field = f->fmt.pix.field; 6382efe2cc4SHans Verkuil chan->width = f->fmt.pix.width; 6392efe2cc4SHans Verkuil chan->height = f->fmt.pix.height; 640b285192aSMauro Carvalho Chehab 641b285192aSMauro Carvalho Chehab if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) 642b285192aSMauro Carvalho Chehab pix_format = PIXEL_FRMT_411; 643b285192aSMauro Carvalho Chehab else 644988f7b80SHans Verkuil pix_format = PIXEL_FRMT_422; 645b285192aSMauro Carvalho Chehab 646b285192aSMauro Carvalho Chehab cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); 647b285192aSMauro Carvalho Chehab 648b285192aSMauro Carvalho Chehab /* check if cif resolution */ 6492efe2cc4SHans Verkuil if (chan->width == 320 || chan->width == 352) 6502efe2cc4SHans Verkuil chan->use_cif_resolution = 1; 651b285192aSMauro Carvalho Chehab else 6522efe2cc4SHans Verkuil chan->use_cif_resolution = 0; 653b285192aSMauro Carvalho Chehab 6542efe2cc4SHans Verkuil chan->cif_width = chan->width; 6552efe2cc4SHans Verkuil medusa_set_resolution(dev, chan->width, SRAM_CH00); 656b285192aSMauro Carvalho Chehab return 0; 657b285192aSMauro Carvalho Chehab } 658b285192aSMauro Carvalho Chehab 6594c1d0f73SHans Verkuil static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) 6604c1d0f73SHans Verkuil { 6614c1d0f73SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6624c1d0f73SHans Verkuil 6634c1d0f73SHans Verkuil if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) 6644c1d0f73SHans Verkuil return -EINVAL; 6654c1d0f73SHans Verkuil 6664c1d0f73SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != priv) 6674c1d0f73SHans Verkuil return -EBUSY; 6684c1d0f73SHans Verkuil chan->streaming_fh = priv; 6694c1d0f73SHans Verkuil 6704c1d0f73SHans Verkuil return videobuf_streamon(&chan->vidq); 6714c1d0f73SHans Verkuil } 6724c1d0f73SHans Verkuil 6734c1d0f73SHans Verkuil static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) 6744c1d0f73SHans Verkuil { 6754c1d0f73SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 6764c1d0f73SHans Verkuil 6774c1d0f73SHans Verkuil if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) 6784c1d0f73SHans Verkuil return -EINVAL; 6794c1d0f73SHans Verkuil 6804c1d0f73SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != priv) 6814c1d0f73SHans Verkuil return -EBUSY; 6824c1d0f73SHans Verkuil if (chan->streaming_fh == NULL) 6834c1d0f73SHans Verkuil return 0; 6844c1d0f73SHans Verkuil 6854c1d0f73SHans Verkuil chan->streaming_fh = NULL; 6864c1d0f73SHans Verkuil return videobuf_streamoff(&chan->vidq); 6874c1d0f73SHans Verkuil } 6884c1d0f73SHans Verkuil 689b285192aSMauro Carvalho Chehab static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) 690b285192aSMauro Carvalho Chehab { 691b285192aSMauro Carvalho Chehab int ret_val = 0; 6922efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 693b285192aSMauro Carvalho Chehab 6942efe2cc4SHans Verkuil ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK); 6952efe2cc4SHans Verkuil p->sequence = chan->dma_vidq.count; 696b285192aSMauro Carvalho Chehab 697b285192aSMauro Carvalho Chehab return ret_val; 698b285192aSMauro Carvalho Chehab } 699b285192aSMauro Carvalho Chehab 700b285192aSMauro Carvalho Chehab static int vidioc_log_status(struct file *file, void *priv) 701b285192aSMauro Carvalho Chehab { 7028d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7038d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 7048d125c50SHans Verkuil const struct sram_channel *sram_ch = chan->sram_channels; 705b285192aSMauro Carvalho Chehab u32 tmp = 0; 706b285192aSMauro Carvalho Chehab 707b285192aSMauro Carvalho Chehab tmp = cx_read(sram_ch->dma_ctl); 708b285192aSMauro Carvalho Chehab pr_info("Video input 0 is %s\n", 709b285192aSMauro Carvalho Chehab (tmp & 0x11) ? "streaming" : "stopped"); 710b285192aSMauro Carvalho Chehab return 0; 711b285192aSMauro Carvalho Chehab } 712b285192aSMauro Carvalho Chehab 713b285192aSMauro Carvalho Chehab 71495c232a2SHans Verkuil static int cx25821_vidioc_querycap(struct file *file, void *priv, 715b285192aSMauro Carvalho Chehab struct v4l2_capability *cap) 716b285192aSMauro Carvalho Chehab { 7178d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7188d125c50SHans Verkuil struct cx25821_dev *dev = chan->dev; 7193dd473caSHans Verkuil const u32 cap_input = V4L2_CAP_VIDEO_CAPTURE | 7203dd473caSHans Verkuil V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; 7210df13d99SHans Verkuil const u32 cap_output = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE; 722b285192aSMauro Carvalho Chehab 723b285192aSMauro Carvalho Chehab strcpy(cap->driver, "cx25821"); 724b285192aSMauro Carvalho Chehab strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); 725b285192aSMauro Carvalho Chehab sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); 7268d125c50SHans Verkuil if (chan->id >= VID_CHANNEL_NUM) 7273dd473caSHans Verkuil cap->device_caps = cap_output; 7283dd473caSHans Verkuil else 7293dd473caSHans Verkuil cap->device_caps = cap_input; 7300df13d99SHans Verkuil cap->capabilities = cap_input | cap_output | V4L2_CAP_DEVICE_CAPS; 731b285192aSMauro Carvalho Chehab return 0; 732b285192aSMauro Carvalho Chehab } 733b285192aSMauro Carvalho Chehab 73495c232a2SHans Verkuil static int cx25821_vidioc_reqbufs(struct file *file, void *priv, 735b285192aSMauro Carvalho Chehab struct v4l2_requestbuffers *p) 736b285192aSMauro Carvalho Chehab { 7372efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7382efe2cc4SHans Verkuil 7392efe2cc4SHans Verkuil return videobuf_reqbufs(&chan->vidq, p); 740b285192aSMauro Carvalho Chehab } 741b285192aSMauro Carvalho Chehab 74295c232a2SHans Verkuil static int cx25821_vidioc_querybuf(struct file *file, void *priv, 743b285192aSMauro Carvalho Chehab struct v4l2_buffer *p) 744b285192aSMauro Carvalho Chehab { 7452efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7462efe2cc4SHans Verkuil 7472efe2cc4SHans Verkuil return videobuf_querybuf(&chan->vidq, p); 748b285192aSMauro Carvalho Chehab } 749b285192aSMauro Carvalho Chehab 75095c232a2SHans Verkuil static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) 751b285192aSMauro Carvalho Chehab { 7522efe2cc4SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 7532efe2cc4SHans Verkuil 7542efe2cc4SHans Verkuil return videobuf_qbuf(&chan->vidq, p); 755b285192aSMauro Carvalho Chehab } 756b285192aSMauro Carvalho Chehab 75795c232a2SHans Verkuil static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms) 75818c73af6SHans Verkuil { 7598d125c50SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 76018c73af6SHans Verkuil 7618d125c50SHans Verkuil *tvnorms = chan->dev->tvnorm; 76218c73af6SHans Verkuil return 0; 76318c73af6SHans Verkuil } 76418c73af6SHans Verkuil 765*a3f17af2SMauro Carvalho Chehab static int cx25821_vidioc_s_std(struct file *file, void *priv, 766*a3f17af2SMauro Carvalho Chehab 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 896ea3f7ac6SHans Verkuil static ssize_t video_write(struct file *file, const char __user *data, size_t count, 897ea3f7ac6SHans Verkuil loff_t *ppos) 898ea3f7ac6SHans Verkuil { 899ea3f7ac6SHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 900ea3f7ac6SHans Verkuil struct cx25821_dev *dev = chan->dev; 901ea3f7ac6SHans Verkuil struct v4l2_fh *fh = file->private_data; 902ea3f7ac6SHans Verkuil int err = 0; 903ea3f7ac6SHans Verkuil 904ea3f7ac6SHans Verkuil if (mutex_lock_interruptible(&dev->lock)) 905ea3f7ac6SHans Verkuil return -ERESTARTSYS; 906ea3f7ac6SHans Verkuil if (chan->streaming_fh && chan->streaming_fh != fh) { 907ea3f7ac6SHans Verkuil err = -EBUSY; 908ea3f7ac6SHans Verkuil goto unlock; 909ea3f7ac6SHans Verkuil } 910ea3f7ac6SHans Verkuil if (!chan->streaming_fh) { 911ea3f7ac6SHans Verkuil err = cx25821_vidupstream_init(chan, chan->pixel_formats); 912ea3f7ac6SHans Verkuil if (err) 913ea3f7ac6SHans Verkuil goto unlock; 914ea3f7ac6SHans Verkuil chan->streaming_fh = fh; 915ea3f7ac6SHans Verkuil } 916ea3f7ac6SHans Verkuil 917ea3f7ac6SHans Verkuil err = cx25821_write_frame(chan, data, count); 918ea3f7ac6SHans Verkuil count -= err; 919ea3f7ac6SHans Verkuil *ppos += err; 920ea3f7ac6SHans Verkuil 921ea3f7ac6SHans Verkuil unlock: 922ea3f7ac6SHans Verkuil mutex_unlock(&dev->lock); 923ea3f7ac6SHans Verkuil return err; 924ea3f7ac6SHans Verkuil } 925ea3f7ac6SHans Verkuil 9267087d31bSHans Verkuil static int video_out_release(struct file *file) 9277087d31bSHans Verkuil { 9287087d31bSHans Verkuil struct cx25821_channel *chan = video_drvdata(file); 9297087d31bSHans Verkuil struct cx25821_dev *dev = chan->dev; 930ea3f7ac6SHans Verkuil struct v4l2_fh *fh = file->private_data; 9317087d31bSHans Verkuil 9327087d31bSHans Verkuil mutex_lock(&dev->lock); 933ea3f7ac6SHans Verkuil if (chan->streaming_fh == fh) { 9347087d31bSHans Verkuil cx25821_stop_upstream_video(chan); 935ea3f7ac6SHans Verkuil chan->streaming_fh = NULL; 936ea3f7ac6SHans Verkuil } 9377087d31bSHans Verkuil mutex_unlock(&dev->lock); 9387087d31bSHans Verkuil 9397087d31bSHans Verkuil return v4l2_fh_release(file); 9407087d31bSHans Verkuil } 9417087d31bSHans Verkuil 942f8d7ee70SHans Verkuil static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { 943f8d7ee70SHans Verkuil .s_ctrl = cx25821_s_ctrl, 944f8d7ee70SHans Verkuil }; 945f8d7ee70SHans Verkuil 946b285192aSMauro Carvalho Chehab static const struct v4l2_file_operations video_fops = { 947b285192aSMauro Carvalho Chehab .owner = THIS_MODULE, 9488d125c50SHans Verkuil .open = v4l2_fh_open, 949b285192aSMauro Carvalho Chehab .release = video_release, 950b285192aSMauro Carvalho Chehab .read = video_read, 951b285192aSMauro Carvalho Chehab .poll = video_poll, 952b285192aSMauro Carvalho Chehab .mmap = cx25821_video_mmap, 9531f198870SHans Verkuil .unlocked_ioctl = video_ioctl2, 954b285192aSMauro Carvalho Chehab }; 955b285192aSMauro Carvalho Chehab 956b285192aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops video_ioctl_ops = { 957b285192aSMauro Carvalho Chehab .vidioc_querycap = cx25821_vidioc_querycap, 958b285192aSMauro Carvalho Chehab .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, 959b285192aSMauro Carvalho Chehab .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, 960b285192aSMauro Carvalho Chehab .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, 961b285192aSMauro Carvalho Chehab .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 962b285192aSMauro Carvalho Chehab .vidioc_reqbufs = cx25821_vidioc_reqbufs, 963b285192aSMauro Carvalho Chehab .vidioc_querybuf = cx25821_vidioc_querybuf, 964b285192aSMauro Carvalho Chehab .vidioc_qbuf = cx25821_vidioc_qbuf, 965b285192aSMauro Carvalho Chehab .vidioc_dqbuf = vidioc_dqbuf, 96618c73af6SHans Verkuil .vidioc_g_std = cx25821_vidioc_g_std, 967b285192aSMauro Carvalho Chehab .vidioc_s_std = cx25821_vidioc_s_std, 968b285192aSMauro Carvalho Chehab .vidioc_enum_input = cx25821_vidioc_enum_input, 969b285192aSMauro Carvalho Chehab .vidioc_g_input = cx25821_vidioc_g_input, 970b285192aSMauro Carvalho Chehab .vidioc_s_input = cx25821_vidioc_s_input, 971b285192aSMauro Carvalho Chehab .vidioc_streamon = vidioc_streamon, 972b285192aSMauro Carvalho Chehab .vidioc_streamoff = vidioc_streamoff, 973b285192aSMauro Carvalho Chehab .vidioc_log_status = vidioc_log_status, 9748d125c50SHans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 9758d125c50SHans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 976b285192aSMauro Carvalho Chehab }; 977b285192aSMauro Carvalho Chehab 978ffd3c233SHans Verkuil static const struct video_device cx25821_video_device = { 979ffd3c233SHans Verkuil .name = "cx25821-video", 980b285192aSMauro Carvalho Chehab .fops = &video_fops, 981467870caSHans Verkuil .release = video_device_release_empty, 982ffd3c233SHans Verkuil .minor = -1, 983b285192aSMauro Carvalho Chehab .ioctl_ops = &video_ioctl_ops, 984b285192aSMauro Carvalho Chehab .tvnorms = CX25821_NORMS, 985b285192aSMauro Carvalho Chehab }; 986ffd3c233SHans Verkuil 9871f198870SHans Verkuil static const struct v4l2_file_operations video_out_fops = { 9881f198870SHans Verkuil .owner = THIS_MODULE, 9891f198870SHans Verkuil .open = v4l2_fh_open, 990ea3f7ac6SHans Verkuil .write = video_write, 9917087d31bSHans Verkuil .release = video_out_release, 9921f198870SHans Verkuil .unlocked_ioctl = video_ioctl2, 9931f198870SHans Verkuil }; 9941f198870SHans Verkuil 9951f198870SHans Verkuil static const struct v4l2_ioctl_ops video_out_ioctl_ops = { 9961f198870SHans Verkuil .vidioc_querycap = cx25821_vidioc_querycap, 997e90878abSHans Verkuil .vidioc_enum_fmt_vid_out = cx25821_vidioc_enum_fmt_vid_cap, 998e90878abSHans Verkuil .vidioc_g_fmt_vid_out = cx25821_vidioc_g_fmt_vid_cap, 999e90878abSHans Verkuil .vidioc_try_fmt_vid_out = cx25821_vidioc_try_fmt_vid_out, 1000e90878abSHans Verkuil .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, 10011f198870SHans Verkuil .vidioc_g_std = cx25821_vidioc_g_std, 10021f198870SHans Verkuil .vidioc_s_std = cx25821_vidioc_s_std, 10031f198870SHans Verkuil .vidioc_enum_output = cx25821_vidioc_enum_output, 10041f198870SHans Verkuil .vidioc_g_output = cx25821_vidioc_g_output, 10051f198870SHans Verkuil .vidioc_s_output = cx25821_vidioc_s_output, 10061f198870SHans Verkuil .vidioc_log_status = vidioc_log_status, 10071f198870SHans Verkuil }; 10081f198870SHans Verkuil 10091f198870SHans Verkuil static const struct video_device cx25821_video_out_device = { 10101f198870SHans Verkuil .name = "cx25821-video", 10111f198870SHans Verkuil .fops = &video_out_fops, 10121f198870SHans Verkuil .release = video_device_release_empty, 10131f198870SHans Verkuil .minor = -1, 10141f198870SHans Verkuil .ioctl_ops = &video_out_ioctl_ops, 10151f198870SHans Verkuil .tvnorms = CX25821_NORMS, 10161f198870SHans Verkuil }; 10171f198870SHans Verkuil 1018ffd3c233SHans Verkuil void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) 1019ffd3c233SHans Verkuil { 1020ffd3c233SHans Verkuil cx_clear(PCI_INT_MSK, 1); 1021ffd3c233SHans Verkuil 1022467870caSHans Verkuil if (video_is_registered(&dev->channels[chan_num].vdev)) { 1023467870caSHans Verkuil video_unregister_device(&dev->channels[chan_num].vdev); 1024f8d7ee70SHans Verkuil v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); 1025ffd3c233SHans Verkuil 1026ffd3c233SHans Verkuil btcx_riscmem_free(dev->pci, 10272efe2cc4SHans Verkuil &dev->channels[chan_num].dma_vidq.stopper); 1028ffd3c233SHans Verkuil } 1029ffd3c233SHans Verkuil } 1030ffd3c233SHans Verkuil 1031ffd3c233SHans Verkuil int cx25821_video_register(struct cx25821_dev *dev) 1032ffd3c233SHans Verkuil { 1033ffd3c233SHans Verkuil int err; 1034ffd3c233SHans Verkuil int i; 1035ffd3c233SHans Verkuil 1036be178cb4SHans Verkuil /* initial device configuration */ 1037a6aa0dc4SHans Verkuil dev->tvnorm = V4L2_STD_NTSC_M; 1038be178cb4SHans Verkuil 1039ffd3c233SHans Verkuil spin_lock_init(&dev->slock); 1040ffd3c233SHans Verkuil 10411f198870SHans Verkuil for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { 10422efe2cc4SHans Verkuil struct cx25821_channel *chan = &dev->channels[i]; 10432efe2cc4SHans Verkuil struct video_device *vdev = &chan->vdev; 10442efe2cc4SHans Verkuil struct v4l2_ctrl_handler *hdl = &chan->hdl; 10451f198870SHans Verkuil bool is_output = i > SRAM_CH08; 1046467870caSHans Verkuil 1047ffd3c233SHans Verkuil if (i == SRAM_CH08) /* audio channel */ 1048ffd3c233SHans Verkuil continue; 1049ffd3c233SHans Verkuil 10501f198870SHans Verkuil if (!is_output) { 1051f8d7ee70SHans Verkuil v4l2_ctrl_handler_init(hdl, 4); 1052f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1053f8d7ee70SHans Verkuil V4L2_CID_BRIGHTNESS, 0, 10000, 1, 6200); 1054f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1055f8d7ee70SHans Verkuil V4L2_CID_CONTRAST, 0, 10000, 1, 5000); 1056f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1057f8d7ee70SHans Verkuil V4L2_CID_SATURATION, 0, 10000, 1, 5000); 1058f8d7ee70SHans Verkuil v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops, 1059f8d7ee70SHans Verkuil V4L2_CID_HUE, 0, 10000, 1, 5000); 1060f8d7ee70SHans Verkuil if (hdl->error) { 1061f8d7ee70SHans Verkuil err = hdl->error; 1062f8d7ee70SHans Verkuil goto fail_unreg; 1063f8d7ee70SHans Verkuil } 1064be178cb4SHans Verkuil err = v4l2_ctrl_handler_setup(hdl); 1065be178cb4SHans Verkuil if (err) 1066be178cb4SHans Verkuil goto fail_unreg; 10677087d31bSHans Verkuil } else { 10687087d31bSHans Verkuil chan->out = &dev->vid_out_data[i - SRAM_CH09]; 10697087d31bSHans Verkuil chan->out->chan = chan; 10701f198870SHans Verkuil } 1071ffd3c233SHans Verkuil 10722efe2cc4SHans Verkuil cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper, 10732efe2cc4SHans Verkuil chan->sram_channels->dma_ctl, 0x11, 0); 1074ffd3c233SHans Verkuil 10752efe2cc4SHans Verkuil chan->sram_channels = &cx25821_sram_channels[i]; 10762efe2cc4SHans Verkuil chan->width = 720; 10772efe2cc4SHans Verkuil if (dev->tvnorm & V4L2_STD_625_50) 10782efe2cc4SHans Verkuil chan->height = 576; 10792efe2cc4SHans Verkuil else 10802efe2cc4SHans Verkuil chan->height = 480; 1081ffd3c233SHans Verkuil 10822efe2cc4SHans Verkuil if (chan->pixel_formats == PIXEL_FRMT_411) 10832efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_Y41P); 10842efe2cc4SHans Verkuil else 10852efe2cc4SHans Verkuil chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_YUYV); 1086ffd3c233SHans Verkuil 10872efe2cc4SHans Verkuil cx_write(chan->sram_channels->int_stat, 0xffffffff); 1088ffd3c233SHans Verkuil 10892efe2cc4SHans Verkuil INIT_LIST_HEAD(&chan->dma_vidq.active); 10902efe2cc4SHans Verkuil INIT_LIST_HEAD(&chan->dma_vidq.queued); 10912efe2cc4SHans Verkuil 10922efe2cc4SHans Verkuil chan->timeout_data.dev = dev; 10932efe2cc4SHans Verkuil chan->timeout_data.channel = &cx25821_sram_channels[i]; 10942efe2cc4SHans Verkuil chan->dma_vidq.timeout.function = cx25821_vid_timeout; 10952efe2cc4SHans Verkuil chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data; 10962efe2cc4SHans Verkuil init_timer(&chan->dma_vidq.timeout); 10972efe2cc4SHans Verkuil 10981f198870SHans Verkuil if (!is_output) 10992efe2cc4SHans Verkuil videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev, 11002efe2cc4SHans Verkuil &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, 11012efe2cc4SHans Verkuil V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), 11022efe2cc4SHans Verkuil chan, &dev->lock); 1103ffd3c233SHans Verkuil 1104ffd3c233SHans Verkuil /* register v4l devices */ 11051f198870SHans Verkuil *vdev = is_output ? cx25821_video_out_device : cx25821_video_device; 1106467870caSHans Verkuil vdev->v4l2_dev = &dev->v4l2_dev; 11071f198870SHans Verkuil if (!is_output) 1108f8d7ee70SHans Verkuil vdev->ctrl_handler = hdl; 11091f198870SHans Verkuil else 11101f198870SHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 1111be178cb4SHans Verkuil vdev->lock = &dev->lock; 11128d125c50SHans Verkuil set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); 1113467870caSHans Verkuil snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); 11142efe2cc4SHans Verkuil video_set_drvdata(vdev, chan); 1115ffd3c233SHans Verkuil 1116467870caSHans Verkuil err = video_register_device(vdev, VFL_TYPE_GRABBER, 1117467870caSHans Verkuil video_nr[dev->nr]); 1118ffd3c233SHans Verkuil 1119ffd3c233SHans Verkuil if (err < 0) 1120ffd3c233SHans Verkuil goto fail_unreg; 1121ffd3c233SHans Verkuil } 1122ffd3c233SHans Verkuil 1123ffd3c233SHans Verkuil /* set PCI interrupt */ 1124ffd3c233SHans Verkuil cx_set(PCI_INT_MSK, 0xff); 1125ffd3c233SHans Verkuil 1126ffd3c233SHans Verkuil return 0; 1127ffd3c233SHans Verkuil 1128ffd3c233SHans Verkuil fail_unreg: 1129467870caSHans Verkuil while (i >= 0) 1130467870caSHans Verkuil cx25821_video_unregister(dev, i--); 1131ffd3c233SHans Verkuil return err; 1132ffd3c233SHans Verkuil } 1133