Path: blob/master/drivers/media/common/saa7146_fops.c
15112 views
#include <media/saa7146_vv.h>12/****************************************************************************/3/* resource management functions, shamelessly stolen from saa7134 driver */45int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)6{7struct saa7146_dev *dev = fh->dev;8struct saa7146_vv *vv = dev->vv_data;910if (fh->resources & bit) {11DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));12/* have it already allocated */13return 1;14}1516/* is it free? */17if (vv->resources & bit) {18DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));19/* no, someone else uses it */20return 0;21}22/* it's free, grab it */23fh->resources |= bit;24vv->resources |= bit;25DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));26return 1;27}2829void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)30{31struct saa7146_dev *dev = fh->dev;32struct saa7146_vv *vv = dev->vv_data;3334BUG_ON((fh->resources & bits) != bits);3536fh->resources &= ~bits;37vv->resources &= ~bits;38DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));39}404142/********************************************************************************/43/* common dma functions */4445void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,46struct saa7146_buf *buf)47{48struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);49DEB_EE(("dev:%p, buf:%p\n",dev,buf));5051BUG_ON(in_interrupt());5253videobuf_waiton(q, &buf->vb, 0, 0);54videobuf_dma_unmap(q->dev, dma);55videobuf_dma_free(dma);56buf->vb.state = VIDEOBUF_NEEDS_INIT;57}585960/********************************************************************************/61/* common buffer functions */6263int saa7146_buffer_queue(struct saa7146_dev *dev,64struct saa7146_dmaqueue *q,65struct saa7146_buf *buf)66{67assert_spin_locked(&dev->slock);68DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));6970BUG_ON(!q);7172if (NULL == q->curr) {73q->curr = buf;74DEB_D(("immediately activating buffer %p\n", buf));75buf->activate(dev,buf,NULL);76} else {77list_add_tail(&buf->vb.queue,&q->queue);78buf->vb.state = VIDEOBUF_QUEUED;79DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));80}81return 0;82}8384void saa7146_buffer_finish(struct saa7146_dev *dev,85struct saa7146_dmaqueue *q,86int state)87{88assert_spin_locked(&dev->slock);89DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));90DEB_EE(("q->curr:%p\n",q->curr));9192BUG_ON(!q->curr);9394/* finish current buffer */95if (NULL == q->curr) {96DEB_D(("aiii. no current buffer\n"));97return;98}99100q->curr->vb.state = state;101do_gettimeofday(&q->curr->vb.ts);102wake_up(&q->curr->vb.done);103104q->curr = NULL;105}106107void saa7146_buffer_next(struct saa7146_dev *dev,108struct saa7146_dmaqueue *q, int vbi)109{110struct saa7146_buf *buf,*next = NULL;111112BUG_ON(!q);113114DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));115116assert_spin_locked(&dev->slock);117if (!list_empty(&q->queue)) {118/* activate next one from queue */119buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);120list_del(&buf->vb.queue);121if (!list_empty(&q->queue))122next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);123q->curr = buf;124DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));125buf->activate(dev,buf,next);126} else {127DEB_INT(("no next buffer. stopping.\n"));128if( 0 != vbi ) {129/* turn off video-dma3 */130saa7146_write(dev,MC1, MASK_20);131} else {132/* nothing to do -- just prevent next video-dma1 transfer133by lowering the protection address */134135// fixme: fix this for vflip != 0136137saa7146_write(dev, PROT_ADDR1, 0);138saa7146_write(dev, MC2, (MASK_02|MASK_18));139140/* write the address of the rps-program */141saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);142/* turn on rps */143saa7146_write(dev, MC1, (MASK_12 | MASK_28));144145/*146printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));147printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));148printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));149printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));150printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));151printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));152*/153}154del_timer(&q->timeout);155}156}157158void saa7146_buffer_timeout(unsigned long data)159{160struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;161struct saa7146_dev *dev = q->dev;162unsigned long flags;163164DEB_EE(("dev:%p, dmaq:%p\n", dev, q));165166spin_lock_irqsave(&dev->slock,flags);167if (q->curr) {168DEB_D(("timeout on %p\n", q->curr));169saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);170}171172/* we don't restart the transfer here like other drivers do. when173a streaming capture is disabled, the timeout function will be174called for the current buffer. if we activate the next buffer now,175we mess up our capture logic. if a timeout occurs on another buffer,176then something is seriously broken before, so no need to buffer the177next capture IMHO... */178/*179saa7146_buffer_next(dev,q);180*/181spin_unlock_irqrestore(&dev->slock,flags);182}183184/********************************************************************************/185/* file operations */186187static int fops_open(struct file *file)188{189struct video_device *vdev = video_devdata(file);190struct saa7146_dev *dev = video_drvdata(file);191struct saa7146_fh *fh = NULL;192int result = 0;193194enum v4l2_buf_type type;195196DEB_EE(("file:%p, dev:%s\n", file, video_device_node_name(vdev)));197198if (mutex_lock_interruptible(&saa7146_devices_lock))199return -ERESTARTSYS;200201DEB_D(("using: %p\n",dev));202203type = vdev->vfl_type == VFL_TYPE_GRABBER204? V4L2_BUF_TYPE_VIDEO_CAPTURE205: V4L2_BUF_TYPE_VBI_CAPTURE;206207/* check if an extension is registered */208if( NULL == dev->ext ) {209DEB_S(("no extension registered for this device.\n"));210result = -ENODEV;211goto out;212}213214/* allocate per open data */215fh = kzalloc(sizeof(*fh),GFP_KERNEL);216if (NULL == fh) {217DEB_S(("cannot allocate memory for per open data.\n"));218result = -ENOMEM;219goto out;220}221222file->private_data = fh;223fh->dev = dev;224fh->type = type;225226if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {227DEB_S(("initializing vbi...\n"));228if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)229result = saa7146_vbi_uops.open(dev,file);230if (dev->ext_vv_data->vbi_fops.open)231dev->ext_vv_data->vbi_fops.open(file);232} else {233DEB_S(("initializing video...\n"));234result = saa7146_video_uops.open(dev,file);235}236237if (0 != result) {238goto out;239}240241if( 0 == try_module_get(dev->ext->module)) {242result = -EINVAL;243goto out;244}245246result = 0;247out:248if (fh && result != 0) {249kfree(fh);250file->private_data = NULL;251}252mutex_unlock(&saa7146_devices_lock);253return result;254}255256static int fops_release(struct file *file)257{258struct saa7146_fh *fh = file->private_data;259struct saa7146_dev *dev = fh->dev;260261DEB_EE(("file:%p\n", file));262263if (mutex_lock_interruptible(&saa7146_devices_lock))264return -ERESTARTSYS;265266if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {267if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)268saa7146_vbi_uops.release(dev,file);269if (dev->ext_vv_data->vbi_fops.release)270dev->ext_vv_data->vbi_fops.release(file);271} else {272saa7146_video_uops.release(dev,file);273}274275module_put(dev->ext->module);276file->private_data = NULL;277kfree(fh);278279mutex_unlock(&saa7146_devices_lock);280281return 0;282}283284static int fops_mmap(struct file *file, struct vm_area_struct * vma)285{286struct saa7146_fh *fh = file->private_data;287struct videobuf_queue *q;288289switch (fh->type) {290case V4L2_BUF_TYPE_VIDEO_CAPTURE: {291DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));292q = &fh->video_q;293break;294}295case V4L2_BUF_TYPE_VBI_CAPTURE: {296DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));297q = &fh->vbi_q;298break;299}300default:301BUG();302return 0;303}304305return videobuf_mmap_mapper(q,vma);306}307308static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)309{310struct saa7146_fh *fh = file->private_data;311struct videobuf_buffer *buf = NULL;312struct videobuf_queue *q;313314DEB_EE(("file:%p, poll:%p\n",file, wait));315316if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {317if( 0 == fh->vbi_q.streaming )318return videobuf_poll_stream(file, &fh->vbi_q, wait);319q = &fh->vbi_q;320} else {321DEB_D(("using video queue.\n"));322q = &fh->video_q;323}324325if (!list_empty(&q->stream))326buf = list_entry(q->stream.next, struct videobuf_buffer, stream);327328if (!buf) {329DEB_D(("buf == NULL!\n"));330return POLLERR;331}332333poll_wait(file, &buf->done, wait);334if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {335DEB_D(("poll succeeded!\n"));336return POLLIN|POLLRDNORM;337}338339DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));340return 0;341}342343static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)344{345struct saa7146_fh *fh = file->private_data;346347switch (fh->type) {348case V4L2_BUF_TYPE_VIDEO_CAPTURE: {349// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));350return saa7146_video_uops.read(file,data,count,ppos);351}352case V4L2_BUF_TYPE_VBI_CAPTURE: {353// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));354if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)355return saa7146_vbi_uops.read(file,data,count,ppos);356else357return -EINVAL;358}359break;360default:361BUG();362return 0;363}364}365366static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)367{368struct saa7146_fh *fh = file->private_data;369370switch (fh->type) {371case V4L2_BUF_TYPE_VIDEO_CAPTURE:372return -EINVAL;373case V4L2_BUF_TYPE_VBI_CAPTURE:374if (fh->dev->ext_vv_data->vbi_fops.write)375return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);376else377return -EINVAL;378default:379BUG();380return -EINVAL;381}382}383384static const struct v4l2_file_operations video_fops =385{386.owner = THIS_MODULE,387.open = fops_open,388.release = fops_release,389.read = fops_read,390.write = fops_write,391.poll = fops_poll,392.mmap = fops_mmap,393.unlocked_ioctl = video_ioctl2,394};395396static void vv_callback(struct saa7146_dev *dev, unsigned long status)397{398u32 isr = status;399400DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));401402if (0 != (isr & (MASK_27))) {403DEB_INT(("irq: RPS0 (0x%08x).\n",isr));404saa7146_video_uops.irq_done(dev,isr);405}406407if (0 != (isr & (MASK_28))) {408u32 mc2 = saa7146_read(dev, MC2);409if( 0 != (mc2 & MASK_15)) {410DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));411wake_up(&dev->vv_data->vbi_wq);412saa7146_write(dev,MC2, MASK_31);413return;414}415DEB_INT(("irq: RPS1 (0x%08x).\n",isr));416saa7146_vbi_uops.irq_done(dev,isr);417}418}419420int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)421{422struct saa7146_vv *vv;423int err;424425err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);426if (err)427return err;428429vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);430if (vv == NULL) {431ERR(("out of memory. aborting.\n"));432return -ENOMEM;433}434ext_vv->ops = saa7146_video_ioctl_ops;435ext_vv->core_ops = &saa7146_video_ioctl_ops;436437DEB_EE(("dev:%p\n",dev));438439/* set default values for video parts of the saa7146 */440saa7146_write(dev, BCS_CTRL, 0x80400040);441442/* enable video-port pins */443saa7146_write(dev, MC1, (MASK_10 | MASK_26));444445/* save per-device extension data (one extension can446handle different devices that might need different447configuration data) */448dev->ext_vv_data = ext_vv;449450vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);451if( NULL == vv->d_clipping.cpu_addr ) {452ERR(("out of memory. aborting.\n"));453kfree(vv);454return -1;455}456memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);457458saa7146_video_uops.init(dev,vv);459if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)460saa7146_vbi_uops.init(dev,vv);461462dev->vv_data = vv;463dev->vv_callback = &vv_callback;464465return 0;466}467EXPORT_SYMBOL_GPL(saa7146_vv_init);468469int saa7146_vv_release(struct saa7146_dev* dev)470{471struct saa7146_vv *vv = dev->vv_data;472473DEB_EE(("dev:%p\n",dev));474475v4l2_device_unregister(&dev->v4l2_dev);476pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);477kfree(vv);478dev->vv_data = NULL;479dev->vv_callback = NULL;480481return 0;482}483EXPORT_SYMBOL_GPL(saa7146_vv_release);484485int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,486char *name, int type)487{488struct video_device *vfd;489int err;490int i;491492DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));493494// released by vfd->release495vfd = video_device_alloc();496if (vfd == NULL)497return -ENOMEM;498499vfd->fops = &video_fops;500vfd->ioctl_ops = &dev->ext_vv_data->ops;501vfd->release = video_device_release;502vfd->lock = &dev->v4l2_lock;503vfd->tvnorms = 0;504for (i = 0; i < dev->ext_vv_data->num_stds; i++)505vfd->tvnorms |= dev->ext_vv_data->stds[i].id;506strlcpy(vfd->name, name, sizeof(vfd->name));507video_set_drvdata(vfd, dev);508509err = video_register_device(vfd, type, -1);510if (err < 0) {511ERR(("cannot register v4l2 device. skipping.\n"));512video_device_release(vfd);513return err;514}515516INFO(("%s: registered device %s [v4l2]\n",517dev->name, video_device_node_name(vfd)));518519*vid = vfd;520return 0;521}522EXPORT_SYMBOL_GPL(saa7146_register_device);523524int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)525{526DEB_EE(("dev:%p\n",dev));527528video_unregister_device(*vid);529*vid = NULL;530531return 0;532}533EXPORT_SYMBOL_GPL(saa7146_unregister_device);534535static int __init saa7146_vv_init_module(void)536{537return 0;538}539540541static void __exit saa7146_vv_cleanup_module(void)542{543}544545module_init(saa7146_vv_init_module);546module_exit(saa7146_vv_cleanup_module);547548MODULE_AUTHOR("Michael Hunold <[email protected]>");549MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");550MODULE_LICENSE("GPL");551552553