📄 cafe_ccic.c
字号:
* This template device holds all of those v4l2 methods; we * clone it for specific real devices. */static const struct file_operations cafe_v4l_fops = { .owner = THIS_MODULE, .open = cafe_v4l_open, .release = cafe_v4l_release, .read = cafe_v4l_read, .poll = cafe_v4l_poll, .mmap = cafe_v4l_mmap, .ioctl = video_ioctl2, .llseek = no_llseek,};static struct video_device cafe_v4l_template = { .name = "cafe", .type = VFL_TYPE_GRABBER, .type2 = VID_TYPE_CAPTURE, .minor = -1, /* Get one dynamically */ .tvnorms = V4L2_STD_NTSC_M, .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ .fops = &cafe_v4l_fops, .release = cafe_v4l_dev_release, .vidioc_querycap = cafe_vidioc_querycap, .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap, .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap, .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap, .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap, .vidioc_enum_input = cafe_vidioc_enum_input, .vidioc_g_input = cafe_vidioc_g_input, .vidioc_s_input = cafe_vidioc_s_input, .vidioc_s_std = cafe_vidioc_s_std, .vidioc_reqbufs = cafe_vidioc_reqbufs, .vidioc_querybuf = cafe_vidioc_querybuf, .vidioc_qbuf = cafe_vidioc_qbuf, .vidioc_dqbuf = cafe_vidioc_dqbuf, .vidioc_streamon = cafe_vidioc_streamon, .vidioc_streamoff = cafe_vidioc_streamoff, .vidioc_queryctrl = cafe_vidioc_queryctrl, .vidioc_g_ctrl = cafe_vidioc_g_ctrl, .vidioc_s_ctrl = cafe_vidioc_s_ctrl, .vidioc_g_parm = cafe_vidioc_g_parm, .vidioc_s_parm = cafe_vidioc_s_parm,};/* ---------------------------------------------------------------------- *//* * Interrupt handler stuff */static void cafe_frame_tasklet(unsigned long data){ struct cafe_camera *cam = (struct cafe_camera *) data; int i; unsigned long flags; struct cafe_sio_buffer *sbuf; spin_lock_irqsave(&cam->dev_lock, flags); for (i = 0; i < cam->nbufs; i++) { int bufno = cam->next_buf; if (bufno < 0) { /* "will never happen" */ cam_err(cam, "No valid bufs in tasklet!\n"); break; } if (++(cam->next_buf) >= cam->nbufs) cam->next_buf = 0; if (! test_bit(bufno, &cam->flags)) continue; if (list_empty(&cam->sb_avail)) break; /* Leave it valid, hope for better later */ clear_bit(bufno, &cam->flags); sbuf = list_entry(cam->sb_avail.next, struct cafe_sio_buffer, list); /* * Drop the lock during the big copy. This *should* be safe... */ spin_unlock_irqrestore(&cam->dev_lock, flags); memcpy(sbuf->buffer, cam->dma_bufs[bufno], cam->pix_format.sizeimage); sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; spin_lock_irqsave(&cam->dev_lock, flags); list_move_tail(&sbuf->list, &cam->sb_full); } if (! list_empty(&cam->sb_full)) wake_up(&cam->iowait); spin_unlock_irqrestore(&cam->dev_lock, flags);}static void cafe_frame_complete(struct cafe_camera *cam, int frame){ /* * Basic frame housekeeping. */ if (test_bit(frame, &cam->flags) && printk_ratelimit()) cam_err(cam, "Frame overrun on %d, frames lost\n", frame); set_bit(frame, &cam->flags); clear_bit(CF_DMA_ACTIVE, &cam->flags); if (cam->next_buf < 0) cam->next_buf = frame; cam->buf_seq[frame] = ++(cam->sequence); switch (cam->state) { /* * If in single read mode, try going speculative. */ case S_SINGLEREAD: cam->state = S_SPECREAD; cam->specframes = 0; wake_up(&cam->iowait); break; /* * If we are already doing speculative reads, and nobody is * reading them, just stop. */ case S_SPECREAD: if (++(cam->specframes) >= cam->nbufs) { cafe_ctlr_stop(cam); cafe_ctlr_irq_disable(cam); cam->state = S_IDLE; } wake_up(&cam->iowait); break; /* * For the streaming case, we defer the real work to the * camera tasklet. * * FIXME: if the application is not consuming the buffers, * we should eventually put things on hold and restart in * vidioc_dqbuf(). */ case S_STREAMING: tasklet_schedule(&cam->s_tasklet); break; default: cam_err(cam, "Frame interrupt in non-operational state\n"); break; }}static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs){ unsigned int frame; cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */ /* * Handle any frame completions. There really should * not be more than one of these, or we have fallen * far behind. */ for (frame = 0; frame < cam->nbufs; frame++) if (irqs & (IRQ_EOF0 << frame)) cafe_frame_complete(cam, frame); /* * If a frame starts, note that we have DMA active. This * code assumes that we won't get multiple frame interrupts * at once; may want to rethink that. */ if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) set_bit(CF_DMA_ACTIVE, &cam->flags);}static irqreturn_t cafe_irq(int irq, void *data){ struct cafe_camera *cam = data; unsigned int irqs; spin_lock(&cam->dev_lock); irqs = cafe_reg_read(cam, REG_IRQSTAT); if ((irqs & ALLIRQS) == 0) { spin_unlock(&cam->dev_lock); return IRQ_NONE; } if (irqs & FRAMEIRQS) cafe_frame_irq(cam, irqs); if (irqs & TWSIIRQS) { cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS); wake_up(&cam->smbus_wait); } spin_unlock(&cam->dev_lock); return IRQ_HANDLED;}/* -------------------------------------------------------------------------- */#ifdef CONFIG_VIDEO_ADV_DEBUG/* * Debugfs stuff. */static char cafe_debug_buf[1024];static struct dentry *cafe_dfs_root;static void cafe_dfs_setup(void){ cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL); if (IS_ERR(cafe_dfs_root)) { cafe_dfs_root = NULL; /* Never mind */ printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n"); }}static void cafe_dfs_shutdown(void){ if (cafe_dfs_root) debugfs_remove(cafe_dfs_root);}static int cafe_dfs_open(struct inode *inode, struct file *file){ file->private_data = inode->i_private; return 0;}static ssize_t cafe_dfs_read_regs(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct cafe_camera *cam = file->private_data; char *s = cafe_debug_buf; int offset; for (offset = 0; offset < 0x44; offset += 4) s += sprintf(s, "%02x: %08x\n", offset, cafe_reg_read(cam, offset)); for (offset = 0x88; offset <= 0x90; offset += 4) s += sprintf(s, "%02x: %08x\n", offset, cafe_reg_read(cam, offset)); for (offset = 0xb4; offset <= 0xbc; offset += 4) s += sprintf(s, "%02x: %08x\n", offset, cafe_reg_read(cam, offset)); for (offset = 0x3000; offset <= 0x300c; offset += 4) s += sprintf(s, "%04x: %08x\n", offset, cafe_reg_read(cam, offset)); return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, s - cafe_debug_buf);}static const struct file_operations cafe_dfs_reg_ops = { .owner = THIS_MODULE, .read = cafe_dfs_read_regs, .open = cafe_dfs_open};static ssize_t cafe_dfs_read_cam(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct cafe_camera *cam = file->private_data; char *s = cafe_debug_buf; int offset; if (! cam->sensor) return -EINVAL; for (offset = 0x0; offset < 0x8a; offset++) { u8 v; cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v); s += sprintf(s, "%02x: %02x\n", offset, v); } return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, s - cafe_debug_buf);}static const struct file_operations cafe_dfs_cam_ops = { .owner = THIS_MODULE, .read = cafe_dfs_read_cam, .open = cafe_dfs_open};static void cafe_dfs_cam_setup(struct cafe_camera *cam){ char fname[40]; if (!cafe_dfs_root) return; sprintf(fname, "regs-%d", cam->v4ldev.minor); cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, cam, &cafe_dfs_reg_ops); sprintf(fname, "cam-%d", cam->v4ldev.minor); cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, cam, &cafe_dfs_cam_ops);}static void cafe_dfs_cam_shutdown(struct cafe_camera *cam){ if (! IS_ERR(cam->dfs_regs)) debugfs_remove(cam->dfs_regs); if (! IS_ERR(cam->dfs_cam_regs)) debugfs_remove(cam->dfs_cam_regs);}#else#define cafe_dfs_setup()#define cafe_dfs_shutdown()#define cafe_dfs_cam_setup(cam)#define cafe_dfs_cam_shutdown(cam)#endif /* CONFIG_VIDEO_ADV_DEBUG *//* ------------------------------------------------------------------------*//* * PCI interface stuff. */static int cafe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id){ int ret; u16 classword; struct cafe_camera *cam; /* * Make sure we have a camera here - we'll get calls for * the other cafe devices as well. */ pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword); if (classword != PCI_CLASS_MULTIMEDIA_VIDEO) return -ENODEV; /* * Start putting together one of our big camera structures. */ ret = -ENOMEM; cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); if (cam == NULL) goto out; mutex_init(&cam->s_mutex); mutex_lock(&cam->s_mutex); spin_lock_init(&cam->dev_lock); cam->state = S_NOTREADY; cafe_set_config_needed(cam, 1); init_waitqueue_head(&cam->smbus_wait); init_waitqueue_head(&cam->iowait); cam->pdev = pdev; cam->pix_format = cafe_def_pix_format; INIT_LIST_HEAD(&cam->dev_list); INIT_LIST_HEAD(&cam->sb_avail); INIT_LIST_HEAD(&cam->sb_full); tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam); /* * Get set up on the PCI bus. */ ret = pci_enable_device(pdev); if (ret) goto out_free; pci_set_master(pdev); ret = -EIO; cam->regs = pci_iomap(pdev, 0, 0); if (! cam->regs) { printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); goto out_free; } ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); if (ret) goto out_iounmap; /* * Initialize the controller and leave it powered up. It will * stay that way until the sensor driver shows up. */ cafe_ctlr_init(cam); cafe_ctlr_power_up(cam); /* * Set up I2C/SMBUS communications. We have to drop the mutex here * because the sensor could attach in this call chain, leading to * unsightly deadlocks. */ mutex_unlock(&cam->s_mutex); /* attach can deadlock */ ret = cafe_smbus_setup(cam); if (ret) goto out_freeirq; /* * Get the v4l2 setup done. */ mutex_lock(&cam->s_mutex); cam->v4ldev = cafe_v4l_template; cam->v4ldev.debug = 0;// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; cam->v4ldev.dev = &pdev->dev; ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; /* * If so requested, try to get our DMA buffers now. */ if (!alloc_bufs_at_read) { if (cafe_alloc_dma_bufs(cam, 1)) cam_warn(cam, "Unable to alloc DMA buffers at load" " will try again later."); } cafe_dfs_cam_setup(cam); mutex_unlock(&cam->s_mutex); cafe_add_dev(cam); return 0; out_smbus: cafe_smbus_shutdown(cam); out_freeirq: cafe_ctlr_power_down(cam); free_irq(pdev->irq, cam); out_iounmap: pci_iounmap(pdev, cam->regs); out_free: kfree(cam); out: return ret;}/* * Shut down an initialized device */static void cafe_shutdown(struct cafe_camera *cam){/* FIXME: Make sure we take care of everything here */ cafe_dfs_cam_shutdown(cam); if (cam->n_sbufs > 0) /* What if they are still mapped? Shouldn't be, but... */ cafe_free_sio_buffers(cam); cafe_remove_dev(cam); cafe_ctlr_stop_dma(cam); cafe_ctlr_power_down(cam); cafe_smbus_shutdown(cam); cafe_free_dma_bufs(cam); free_irq(cam->pdev->irq, cam); pci_iounmap(cam->pdev, cam->regs); video_unregister_device(&cam->v4ldev); /* kfree(cam); done in v4l_release () */}static void cafe_pci_remove(struct pci_dev *pdev){ struct cafe_camera *cam = cafe_find_by_pdev(pdev); if (cam == NULL) { printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); return; } mutex_lock(&cam->s_mutex); if (cam->users > 0) cam_warn(cam, "Removing a device with users!\n"); cafe_shutdown(cam);/* No unlock - it no longer exists */}#ifdef CONFIG_PM/* * Basic power management. */static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state){ struct cafe_camera *cam = cafe_find_by_pdev(pdev); int ret; enum cafe_state cstate; ret = pci_save_state(pdev); if (ret) return ret; cstate = cam->state; /* HACK - stop_dma sets to idle */ cafe_ctlr_stop_dma(cam); cafe_ctlr_power_down(cam); pci_disable_device(pdev); cam->state = cstate; return 0;}static int cafe_pci_resume(struct pci_dev *pdev){ struct cafe_camera *cam = cafe_find_by_pdev(pdev); int ret = 0; ret = pci_restore_state(pdev); if (ret) return ret; ret = pci_enable_device(pdev); if (ret) { cam_warn(cam, "Unable to re-enable device on resume!\n"); return ret; } cafe_ctlr_init(cam); cafe_ctlr_power_down(cam); mutex_lock(&cam->s_mutex); if (cam->users > 0) { cafe_ctlr_power_up(cam); __cafe_cam_reset(cam); } mutex_unlock(&cam->s_mutex); set_bit(CF_CONFIG_NEEDED, &cam->flags); if (cam->state == S_SPECREAD) cam->state = S_IDLE; /* Don't bother restarting */ else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING) ret = cafe_read_setup(cam, cam->state); return ret;}#endif /* CONFIG_PM */static struct pci_device_id cafe_ids[] = { { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ { 0, }};MODULE_DEVICE_TABLE(pci, cafe_ids);static struct pci_driver cafe_pci_driver = { .name = "cafe1000-ccic", .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove,#ifdef CONFIG_PM .suspend = cafe_pci_suspend, .resume = cafe_pci_resume,#endif};static int __init cafe_init(void){ int ret; printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", CAFE_VERSION); cafe_dfs_setup(); ret = pci_register_driver(&cafe_pci_driver); if (ret) { printk(KERN_ERR "Unable to register cafe_ccic driver\n"); goto out; } request_module("ov7670"); /* FIXME want something more general */ ret = 0; out: return ret;}static void __exit cafe_exit(void){ pci_unregister_driver(&cafe_pci_driver); cafe_dfs_shutdown();}module_init(cafe_init);module_exit(cafe_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -