600 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/***************************************************************************3 * Copyright (C) 2006-2010 by Marin Mitov *4 * mitov@issp.bas.bg *5 * *6 * *7 ***************************************************************************/8 9#include <linux/module.h>10#include <linux/stringify.h>11#include <linux/delay.h>12#include <linux/kthread.h>13#include <linux/slab.h>14#include <media/v4l2-dev.h>15#include <media/v4l2-ioctl.h>16#include <media/v4l2-common.h>17#include <media/videobuf2-dma-contig.h>18 19#include "dt3155.h"20 21#define DT3155_DEVICE_ID 0x122322 23/**24 * read_i2c_reg - reads an internal i2c register25 *26 * @addr: dt3155 mmio base address27 * @index: index (internal address) of register to read28 * @data: pointer to byte the read data will be placed in29 *30 * returns: zero on success or error code31 *32 * This function starts reading the specified (by index) register33 * and busy waits for the process to finish. The result is placed34 * in a byte pointed by data.35 */36static int read_i2c_reg(void __iomem *addr, u8 index, u8 *data)37{38 u32 tmp = index;39 40 iowrite32((tmp << 17) | IIC_READ, addr + IIC_CSR2);41 udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */42 if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)43 return -EIO; /* error: NEW_CYCLE not cleared */44 tmp = ioread32(addr + IIC_CSR1);45 if (tmp & DIRECT_ABORT) {46 /* reset DIRECT_ABORT bit */47 iowrite32(DIRECT_ABORT, addr + IIC_CSR1);48 return -EIO; /* error: DIRECT_ABORT set */49 }50 *data = tmp >> 24;51 return 0;52}53 54/**55 * write_i2c_reg - writes to an internal i2c register56 *57 * @addr: dt3155 mmio base address58 * @index: index (internal address) of register to read59 * @data: data to be written60 *61 * returns: zero on success or error code62 *63 * This function starts writing the specified (by index) register64 * and busy waits for the process to finish.65 */66static int write_i2c_reg(void __iomem *addr, u8 index, u8 data)67{68 u32 tmp = index;69 70 iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2);71 udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */72 if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)73 return -EIO; /* error: NEW_CYCLE not cleared */74 if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {75 /* reset DIRECT_ABORT bit */76 iowrite32(DIRECT_ABORT, addr + IIC_CSR1);77 return -EIO; /* error: DIRECT_ABORT set */78 }79 return 0;80}81 82/**83 * write_i2c_reg_nowait - writes to an internal i2c register84 *85 * @addr: dt3155 mmio base address86 * @index: index (internal address) of register to read87 * @data: data to be written88 *89 * This function starts writing the specified (by index) register90 * and then returns.91 */92static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data)93{94 u32 tmp = index;95 96 iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2);97}98 99/**100 * wait_i2c_reg - waits the read/write to finish101 *102 * @addr: dt3155 mmio base address103 *104 * returns: zero on success or error code105 *106 * This function waits reading/writing to finish.107 */108static int wait_i2c_reg(void __iomem *addr)109{110 if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)111 udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */112 if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)113 return -EIO; /* error: NEW_CYCLE not cleared */114 if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {115 /* reset DIRECT_ABORT bit */116 iowrite32(DIRECT_ABORT, addr + IIC_CSR1);117 return -EIO; /* error: DIRECT_ABORT set */118 }119 return 0;120}121 122static int123dt3155_queue_setup(struct vb2_queue *vq,124 unsigned int *nbuffers, unsigned int *num_planes,125 unsigned int sizes[], struct device *alloc_devs[])126 127{128 struct dt3155_priv *pd = vb2_get_drv_priv(vq);129 unsigned size = pd->width * pd->height;130 131 if (*num_planes)132 return sizes[0] < size ? -EINVAL : 0;133 *num_planes = 1;134 sizes[0] = size;135 return 0;136}137 138static int dt3155_buf_prepare(struct vb2_buffer *vb)139{140 struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);141 142 vb2_set_plane_payload(vb, 0, pd->width * pd->height);143 return 0;144}145 146static int dt3155_start_streaming(struct vb2_queue *q, unsigned count)147{148 struct dt3155_priv *pd = vb2_get_drv_priv(q);149 struct vb2_buffer *vb = &pd->curr_buf->vb2_buf;150 dma_addr_t dma_addr;151 152 pd->sequence = 0;153 dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);154 iowrite32(dma_addr, pd->regs + EVEN_DMA_START);155 iowrite32(dma_addr + pd->width, pd->regs + ODD_DMA_START);156 iowrite32(pd->width, pd->regs + EVEN_DMA_STRIDE);157 iowrite32(pd->width, pd->regs + ODD_DMA_STRIDE);158 /* enable interrupts, clear all irq flags */159 iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |160 FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR);161 iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |162 FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD,163 pd->regs + CSR1);164 wait_i2c_reg(pd->regs);165 write_i2c_reg(pd->regs, CONFIG, pd->config);166 write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE);167 write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE);168 169 /* start the board */170 write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD);171 return 0;172}173 174static void dt3155_stop_streaming(struct vb2_queue *q)175{176 struct dt3155_priv *pd = vb2_get_drv_priv(q);177 struct vb2_buffer *vb;178 179 spin_lock_irq(&pd->lock);180 /* stop the board */181 write_i2c_reg_nowait(pd->regs, CSR2, pd->csr2);182 iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |183 FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1);184 /* disable interrupts, clear all irq flags */185 iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR);186 spin_unlock_irq(&pd->lock);187 188 /*189 * It is not clear whether the DMA stops at once or whether it190 * will finish the current frame or field first. To be on the191 * safe side we wait a bit.192 */193 msleep(45);194 195 spin_lock_irq(&pd->lock);196 if (pd->curr_buf) {197 vb2_buffer_done(&pd->curr_buf->vb2_buf, VB2_BUF_STATE_ERROR);198 pd->curr_buf = NULL;199 }200 201 while (!list_empty(&pd->dmaq)) {202 vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry);203 list_del(&vb->done_entry);204 vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);205 }206 spin_unlock_irq(&pd->lock);207}208 209static void dt3155_buf_queue(struct vb2_buffer *vb)210{211 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);212 struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);213 214 /* pd->vidq.streaming = 1 when dt3155_buf_queue() is invoked */215 spin_lock_irq(&pd->lock);216 if (pd->curr_buf)217 list_add_tail(&vb->done_entry, &pd->dmaq);218 else219 pd->curr_buf = vbuf;220 spin_unlock_irq(&pd->lock);221}222 223static const struct vb2_ops q_ops = {224 .queue_setup = dt3155_queue_setup,225 .wait_prepare = vb2_ops_wait_prepare,226 .wait_finish = vb2_ops_wait_finish,227 .buf_prepare = dt3155_buf_prepare,228 .start_streaming = dt3155_start_streaming,229 .stop_streaming = dt3155_stop_streaming,230 .buf_queue = dt3155_buf_queue,231};232 233static irqreturn_t dt3155_irq_handler_even(int irq, void *dev_id)234{235 struct dt3155_priv *ipd = dev_id;236 struct vb2_buffer *ivb;237 dma_addr_t dma_addr;238 u32 tmp;239 240 tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD);241 if (!tmp)242 return IRQ_NONE; /* not our irq */243 if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) {244 iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START,245 ipd->regs + INT_CSR);246 return IRQ_HANDLED; /* start of field irq */247 }248 tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD);249 if (tmp) {250 iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |251 FLD_DN_ODD | FLD_DN_EVEN |252 CAP_CONT_EVEN | CAP_CONT_ODD,253 ipd->regs + CSR1);254 }255 256 spin_lock(&ipd->lock);257 if (ipd->curr_buf && !list_empty(&ipd->dmaq)) {258 ipd->curr_buf->vb2_buf.timestamp = ktime_get_ns();259 ipd->curr_buf->sequence = ipd->sequence++;260 ipd->curr_buf->field = V4L2_FIELD_NONE;261 vb2_buffer_done(&ipd->curr_buf->vb2_buf, VB2_BUF_STATE_DONE);262 263 ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);264 list_del(&ivb->done_entry);265 ipd->curr_buf = to_vb2_v4l2_buffer(ivb);266 dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);267 iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);268 iowrite32(dma_addr + ipd->width, ipd->regs + ODD_DMA_START);269 iowrite32(ipd->width, ipd->regs + EVEN_DMA_STRIDE);270 iowrite32(ipd->width, ipd->regs + ODD_DMA_STRIDE);271 }272 273 /* enable interrupts, clear all irq flags */274 iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |275 FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);276 spin_unlock(&ipd->lock);277 return IRQ_HANDLED;278}279 280static const struct v4l2_file_operations dt3155_fops = {281 .owner = THIS_MODULE,282 .open = v4l2_fh_open,283 .release = vb2_fop_release,284 .unlocked_ioctl = video_ioctl2,285 .read = vb2_fop_read,286 .mmap = vb2_fop_mmap,287 .poll = vb2_fop_poll288};289 290static int dt3155_querycap(struct file *filp, void *p,291 struct v4l2_capability *cap)292{293 strscpy(cap->driver, DT3155_NAME, sizeof(cap->driver));294 strscpy(cap->card, DT3155_NAME " frame grabber", sizeof(cap->card));295 return 0;296}297 298static int dt3155_enum_fmt_vid_cap(struct file *filp,299 void *p, struct v4l2_fmtdesc *f)300{301 if (f->index)302 return -EINVAL;303 f->pixelformat = V4L2_PIX_FMT_GREY;304 return 0;305}306 307static int dt3155_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)308{309 struct dt3155_priv *pd = video_drvdata(filp);310 311 f->fmt.pix.width = pd->width;312 f->fmt.pix.height = pd->height;313 f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;314 f->fmt.pix.field = V4L2_FIELD_NONE;315 f->fmt.pix.bytesperline = f->fmt.pix.width;316 f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height;317 f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;318 return 0;319}320 321static int dt3155_g_std(struct file *filp, void *p, v4l2_std_id *norm)322{323 struct dt3155_priv *pd = video_drvdata(filp);324 325 *norm = pd->std;326 return 0;327}328 329static int dt3155_s_std(struct file *filp, void *p, v4l2_std_id norm)330{331 struct dt3155_priv *pd = video_drvdata(filp);332 333 if (pd->std == norm)334 return 0;335 if (vb2_is_busy(&pd->vidq))336 return -EBUSY;337 pd->std = norm;338 if (pd->std & V4L2_STD_525_60) {339 pd->csr2 = VT_60HZ;340 pd->width = 640;341 pd->height = 480;342 } else {343 pd->csr2 = VT_50HZ;344 pd->width = 768;345 pd->height = 576;346 }347 return 0;348}349 350static int dt3155_enum_input(struct file *filp, void *p,351 struct v4l2_input *input)352{353 if (input->index > 3)354 return -EINVAL;355 if (input->index)356 snprintf(input->name, sizeof(input->name), "VID%d",357 input->index);358 else359 strscpy(input->name, "J2/VID0", sizeof(input->name));360 input->type = V4L2_INPUT_TYPE_CAMERA;361 input->std = V4L2_STD_ALL;362 input->status = 0;363 return 0;364}365 366static int dt3155_g_input(struct file *filp, void *p, unsigned int *i)367{368 struct dt3155_priv *pd = video_drvdata(filp);369 370 *i = pd->input;371 return 0;372}373 374static int dt3155_s_input(struct file *filp, void *p, unsigned int i)375{376 struct dt3155_priv *pd = video_drvdata(filp);377 378 if (i > 3)379 return -EINVAL;380 pd->input = i;381 write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);382 write_i2c_reg(pd->regs, AD_CMD, (i << 6) | (i << 4) | SYNC_LVL_3);383 return 0;384}385 386static const struct v4l2_ioctl_ops dt3155_ioctl_ops = {387 .vidioc_querycap = dt3155_querycap,388 .vidioc_enum_fmt_vid_cap = dt3155_enum_fmt_vid_cap,389 .vidioc_try_fmt_vid_cap = dt3155_fmt_vid_cap,390 .vidioc_g_fmt_vid_cap = dt3155_fmt_vid_cap,391 .vidioc_s_fmt_vid_cap = dt3155_fmt_vid_cap,392 .vidioc_reqbufs = vb2_ioctl_reqbufs,393 .vidioc_create_bufs = vb2_ioctl_create_bufs,394 .vidioc_querybuf = vb2_ioctl_querybuf,395 .vidioc_expbuf = vb2_ioctl_expbuf,396 .vidioc_qbuf = vb2_ioctl_qbuf,397 .vidioc_dqbuf = vb2_ioctl_dqbuf,398 .vidioc_streamon = vb2_ioctl_streamon,399 .vidioc_streamoff = vb2_ioctl_streamoff,400 .vidioc_g_std = dt3155_g_std,401 .vidioc_s_std = dt3155_s_std,402 .vidioc_enum_input = dt3155_enum_input,403 .vidioc_g_input = dt3155_g_input,404 .vidioc_s_input = dt3155_s_input,405};406 407static int dt3155_init_board(struct dt3155_priv *pd)408{409 struct pci_dev *pdev = pd->pdev;410 int i;411 u8 tmp = 0;412 413 pci_set_master(pdev); /* dt3155 needs it */414 415 /* resetting the adapter */416 iowrite32(ADDR_ERR_ODD | ADDR_ERR_EVEN | FLD_CRPT_ODD | FLD_CRPT_EVEN |417 FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1);418 msleep(20);419 420 /* initializing adapter registers */421 iowrite32(FIFO_EN | SRST, pd->regs + CSR1);422 iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT);423 iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT);424 iowrite32(0x00000020, pd->regs + FIFO_TRIGGER);425 iowrite32(0x00000103, pd->regs + XFER_MODE);426 iowrite32(0, pd->regs + RETRY_WAIT_CNT);427 iowrite32(0, pd->regs + INT_CSR);428 iowrite32(1, pd->regs + EVEN_FLD_MASK);429 iowrite32(1, pd->regs + ODD_FLD_MASK);430 iowrite32(0, pd->regs + MASK_LENGTH);431 iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT);432 iowrite32(0x01010101, pd->regs + IIC_CLK_DUR);433 434 /* verifying that we have a DT3155 board (not just a SAA7116 chip) */435 read_i2c_reg(pd->regs, DT_ID, &tmp);436 if (tmp != DT3155_ID)437 return -ENODEV;438 439 /* initialize AD LUT */440 write_i2c_reg(pd->regs, AD_ADDR, 0);441 for (i = 0; i < 256; i++)442 write_i2c_reg(pd->regs, AD_LUT, i);443 444 /* initialize ADC references */445 /* FIXME: pos_ref & neg_ref depend on VT_50HZ */446 write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);447 write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);448 write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF);449 write_i2c_reg(pd->regs, AD_CMD, 34);450 write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF);451 write_i2c_reg(pd->regs, AD_CMD, 0);452 453 /* initialize PM LUT */454 write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM);455 for (i = 0; i < 256; i++) {456 write_i2c_reg(pd->regs, PM_LUT_ADDR, i);457 write_i2c_reg(pd->regs, PM_LUT_DATA, i);458 }459 write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL);460 for (i = 0; i < 256; i++) {461 write_i2c_reg(pd->regs, PM_LUT_ADDR, i);462 write_i2c_reg(pd->regs, PM_LUT_DATA, i);463 }464 write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */465 466 /* select channel 1 for input and set sync level */467 write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);468 write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);469 470 /* disable all irqs, clear all irq flags */471 iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,472 pd->regs + INT_CSR);473 474 return 0;475}476 477static const struct video_device dt3155_vdev = {478 .name = DT3155_NAME,479 .fops = &dt3155_fops,480 .ioctl_ops = &dt3155_ioctl_ops,481 .minor = -1,482 .release = video_device_release_empty,483 .tvnorms = V4L2_STD_ALL,484 .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |485 V4L2_CAP_READWRITE,486};487 488static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)489{490 int err;491 struct dt3155_priv *pd;492 493 err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));494 if (err)495 return -ENODEV;496 pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);497 if (!pd)498 return -ENOMEM;499 500 err = v4l2_device_register(&pdev->dev, &pd->v4l2_dev);501 if (err)502 return err;503 pd->vdev = dt3155_vdev;504 pd->vdev.v4l2_dev = &pd->v4l2_dev;505 video_set_drvdata(&pd->vdev, pd); /* for use in video_fops */506 pd->pdev = pdev;507 pd->std = V4L2_STD_625_50;508 pd->csr2 = VT_50HZ;509 pd->width = 768;510 pd->height = 576;511 INIT_LIST_HEAD(&pd->dmaq);512 mutex_init(&pd->mux);513 pd->vdev.lock = &pd->mux; /* for locking v4l2_file_operations */514 pd->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;515 pd->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;516 pd->vidq.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;517 pd->vidq.ops = &q_ops;518 pd->vidq.mem_ops = &vb2_dma_contig_memops;519 pd->vidq.drv_priv = pd;520 pd->vidq.min_queued_buffers = 2;521 pd->vidq.gfp_flags = GFP_DMA32;522 pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */523 pd->vidq.dev = &pdev->dev;524 pd->vdev.queue = &pd->vidq;525 err = vb2_queue_init(&pd->vidq);526 if (err < 0)527 goto err_v4l2_dev_unreg;528 spin_lock_init(&pd->lock);529 pd->config = ACQ_MODE_EVEN;530 err = pci_enable_device(pdev);531 if (err)532 goto err_v4l2_dev_unreg;533 err = pci_request_region(pdev, 0, pci_name(pdev));534 if (err)535 goto err_pci_disable;536 pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0));537 if (!pd->regs) {538 err = -ENOMEM;539 goto err_free_reg;540 }541 err = dt3155_init_board(pd);542 if (err)543 goto err_iounmap;544 err = request_irq(pd->pdev->irq, dt3155_irq_handler_even,545 IRQF_SHARED, DT3155_NAME, pd);546 if (err)547 goto err_iounmap;548 err = video_register_device(&pd->vdev, VFL_TYPE_VIDEO, -1);549 if (err)550 goto err_free_irq;551 dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor);552 return 0; /* success */553 554err_free_irq:555 free_irq(pd->pdev->irq, pd);556err_iounmap:557 pci_iounmap(pdev, pd->regs);558err_free_reg:559 pci_release_region(pdev, 0);560err_pci_disable:561 pci_disable_device(pdev);562err_v4l2_dev_unreg:563 v4l2_device_unregister(&pd->v4l2_dev);564 return err;565}566 567static void dt3155_remove(struct pci_dev *pdev)568{569 struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);570 struct dt3155_priv *pd = container_of(v4l2_dev, struct dt3155_priv,571 v4l2_dev);572 573 vb2_video_unregister_device(&pd->vdev);574 free_irq(pd->pdev->irq, pd);575 v4l2_device_unregister(&pd->v4l2_dev);576 pci_iounmap(pdev, pd->regs);577 pci_release_region(pdev, 0);578 pci_disable_device(pdev);579}580 581static const struct pci_device_id pci_ids[] = {582 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) },583 { 0, /* zero marks the end */ },584};585MODULE_DEVICE_TABLE(pci, pci_ids);586 587static struct pci_driver pci_driver = {588 .name = DT3155_NAME,589 .id_table = pci_ids,590 .probe = dt3155_probe,591 .remove = dt3155_remove,592};593 594module_pci_driver(pci_driver);595 596MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");597MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");598MODULE_VERSION(DT3155_VERSION);599MODULE_LICENSE("GPL");600