18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/*************************************************************************** 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2010 by Marin Mitov * 48c2ecf20Sopenharmony_ci * mitov@issp.bas.bg * 58c2ecf20Sopenharmony_ci * * 68c2ecf20Sopenharmony_ci * * 78c2ecf20Sopenharmony_ci ***************************************************************************/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/stringify.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/kthread.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 178c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "dt3155.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DT3155_DEVICE_ID 0x1223 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * read_i2c_reg - reads an internal i2c register 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * @addr: dt3155 mmio base address 278c2ecf20Sopenharmony_ci * @index: index (internal address) of register to read 288c2ecf20Sopenharmony_ci * @data: pointer to byte the read data will be placed in 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * returns: zero on success or error code 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * This function starts reading the specified (by index) register 338c2ecf20Sopenharmony_ci * and busy waits for the process to finish. The result is placed 348c2ecf20Sopenharmony_ci * in a byte pointed by data. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic int read_i2c_reg(void __iomem *addr, u8 index, u8 *data) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci u32 tmp = index; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci iowrite32((tmp << 17) | IIC_READ, addr + IIC_CSR2); 418c2ecf20Sopenharmony_ci udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */ 428c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) 438c2ecf20Sopenharmony_ci return -EIO; /* error: NEW_CYCLE not cleared */ 448c2ecf20Sopenharmony_ci tmp = ioread32(addr + IIC_CSR1); 458c2ecf20Sopenharmony_ci if (tmp & DIRECT_ABORT) { 468c2ecf20Sopenharmony_ci /* reset DIRECT_ABORT bit */ 478c2ecf20Sopenharmony_ci iowrite32(DIRECT_ABORT, addr + IIC_CSR1); 488c2ecf20Sopenharmony_ci return -EIO; /* error: DIRECT_ABORT set */ 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci *data = tmp >> 24; 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * write_i2c_reg - writes to an internal i2c register 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * @addr: dt3155 mmio base address 588c2ecf20Sopenharmony_ci * @index: index (internal address) of register to read 598c2ecf20Sopenharmony_ci * @data: data to be written 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * returns: zero on success or error code 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * This function starts writing the specified (by index) register 648c2ecf20Sopenharmony_ci * and busy waits for the process to finish. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic int write_i2c_reg(void __iomem *addr, u8 index, u8 data) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u32 tmp = index; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2); 718c2ecf20Sopenharmony_ci udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ 728c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) 738c2ecf20Sopenharmony_ci return -EIO; /* error: NEW_CYCLE not cleared */ 748c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { 758c2ecf20Sopenharmony_ci /* reset DIRECT_ABORT bit */ 768c2ecf20Sopenharmony_ci iowrite32(DIRECT_ABORT, addr + IIC_CSR1); 778c2ecf20Sopenharmony_ci return -EIO; /* error: DIRECT_ABORT set */ 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * write_i2c_reg_nowait - writes to an internal i2c register 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * @addr: dt3155 mmio base address 868c2ecf20Sopenharmony_ci * @index: index (internal address) of register to read 878c2ecf20Sopenharmony_ci * @data: data to be written 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * This function starts writing the specified (by index) register 908c2ecf20Sopenharmony_ci * and then returns. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 tmp = index; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci iowrite32((tmp << 17) | IIC_WRITE | data, addr + IIC_CSR2); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/** 1008c2ecf20Sopenharmony_ci * wait_i2c_reg - waits the read/write to finish 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * @addr: dt3155 mmio base address 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * returns: zero on success or error code 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * This function waits reading/writing to finish. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic int wait_i2c_reg(void __iomem *addr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) 1118c2ecf20Sopenharmony_ci udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ 1128c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) 1138c2ecf20Sopenharmony_ci return -EIO; /* error: NEW_CYCLE not cleared */ 1148c2ecf20Sopenharmony_ci if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { 1158c2ecf20Sopenharmony_ci /* reset DIRECT_ABORT bit */ 1168c2ecf20Sopenharmony_ci iowrite32(DIRECT_ABORT, addr + IIC_CSR1); 1178c2ecf20Sopenharmony_ci return -EIO; /* error: DIRECT_ABORT set */ 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int 1238c2ecf20Sopenharmony_cidt3155_queue_setup(struct vb2_queue *vq, 1248c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *num_planes, 1258c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct dt3155_priv *pd = vb2_get_drv_priv(vq); 1298c2ecf20Sopenharmony_ci unsigned size = pd->width * pd->height; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 2) 1328c2ecf20Sopenharmony_ci *nbuffers = 2 - vq->num_buffers; 1338c2ecf20Sopenharmony_ci if (*num_planes) 1348c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 1358c2ecf20Sopenharmony_ci *num_planes = 1; 1368c2ecf20Sopenharmony_ci sizes[0] = size; 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int dt3155_buf_prepare(struct vb2_buffer *vb) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, pd->width * pd->height); 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int dt3155_start_streaming(struct vb2_queue *q, unsigned count) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct dt3155_priv *pd = vb2_get_drv_priv(q); 1518c2ecf20Sopenharmony_ci struct vb2_buffer *vb = &pd->curr_buf->vb2_buf; 1528c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci pd->sequence = 0; 1558c2ecf20Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 1568c2ecf20Sopenharmony_ci iowrite32(dma_addr, pd->regs + EVEN_DMA_START); 1578c2ecf20Sopenharmony_ci iowrite32(dma_addr + pd->width, pd->regs + ODD_DMA_START); 1588c2ecf20Sopenharmony_ci iowrite32(pd->width, pd->regs + EVEN_DMA_STRIDE); 1598c2ecf20Sopenharmony_ci iowrite32(pd->width, pd->regs + ODD_DMA_STRIDE); 1608c2ecf20Sopenharmony_ci /* enable interrupts, clear all irq flags */ 1618c2ecf20Sopenharmony_ci iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | 1628c2ecf20Sopenharmony_ci FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); 1638c2ecf20Sopenharmony_ci iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | 1648c2ecf20Sopenharmony_ci FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD, 1658c2ecf20Sopenharmony_ci pd->regs + CSR1); 1668c2ecf20Sopenharmony_ci wait_i2c_reg(pd->regs); 1678c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, CONFIG, pd->config); 1688c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); 1698c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* start the board */ 1728c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void dt3155_stop_streaming(struct vb2_queue *q) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct dt3155_priv *pd = vb2_get_drv_priv(q); 1798c2ecf20Sopenharmony_ci struct vb2_buffer *vb; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spin_lock_irq(&pd->lock); 1828c2ecf20Sopenharmony_ci /* stop the board */ 1838c2ecf20Sopenharmony_ci write_i2c_reg_nowait(pd->regs, CSR2, pd->csr2); 1848c2ecf20Sopenharmony_ci iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | 1858c2ecf20Sopenharmony_ci FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1); 1868c2ecf20Sopenharmony_ci /* disable interrupts, clear all irq flags */ 1878c2ecf20Sopenharmony_ci iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); 1888c2ecf20Sopenharmony_ci spin_unlock_irq(&pd->lock); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * It is not clear whether the DMA stops at once or whether it 1928c2ecf20Sopenharmony_ci * will finish the current frame or field first. To be on the 1938c2ecf20Sopenharmony_ci * safe side we wait a bit. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci msleep(45); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci spin_lock_irq(&pd->lock); 1988c2ecf20Sopenharmony_ci if (pd->curr_buf) { 1998c2ecf20Sopenharmony_ci vb2_buffer_done(&pd->curr_buf->vb2_buf, VB2_BUF_STATE_ERROR); 2008c2ecf20Sopenharmony_ci pd->curr_buf = NULL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci while (!list_empty(&pd->dmaq)) { 2048c2ecf20Sopenharmony_ci vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry); 2058c2ecf20Sopenharmony_ci list_del(&vb->done_entry); 2068c2ecf20Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci spin_unlock_irq(&pd->lock); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void dt3155_buf_queue(struct vb2_buffer *vb) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 2148c2ecf20Sopenharmony_ci struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* pd->vidq.streaming = 1 when dt3155_buf_queue() is invoked */ 2178c2ecf20Sopenharmony_ci spin_lock_irq(&pd->lock); 2188c2ecf20Sopenharmony_ci if (pd->curr_buf) 2198c2ecf20Sopenharmony_ci list_add_tail(&vb->done_entry, &pd->dmaq); 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci pd->curr_buf = vbuf; 2228c2ecf20Sopenharmony_ci spin_unlock_irq(&pd->lock); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic const struct vb2_ops q_ops = { 2268c2ecf20Sopenharmony_ci .queue_setup = dt3155_queue_setup, 2278c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 2288c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 2298c2ecf20Sopenharmony_ci .buf_prepare = dt3155_buf_prepare, 2308c2ecf20Sopenharmony_ci .start_streaming = dt3155_start_streaming, 2318c2ecf20Sopenharmony_ci .stop_streaming = dt3155_stop_streaming, 2328c2ecf20Sopenharmony_ci .buf_queue = dt3155_buf_queue, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic irqreturn_t dt3155_irq_handler_even(int irq, void *dev_id) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct dt3155_priv *ipd = dev_id; 2388c2ecf20Sopenharmony_ci struct vb2_buffer *ivb; 2398c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2408c2ecf20Sopenharmony_ci u32 tmp; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD); 2438c2ecf20Sopenharmony_ci if (!tmp) 2448c2ecf20Sopenharmony_ci return IRQ_NONE; /* not our irq */ 2458c2ecf20Sopenharmony_ci if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) { 2468c2ecf20Sopenharmony_ci iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START, 2478c2ecf20Sopenharmony_ci ipd->regs + INT_CSR); 2488c2ecf20Sopenharmony_ci return IRQ_HANDLED; /* start of field irq */ 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD); 2518c2ecf20Sopenharmony_ci if (tmp) { 2528c2ecf20Sopenharmony_ci iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | 2538c2ecf20Sopenharmony_ci FLD_DN_ODD | FLD_DN_EVEN | 2548c2ecf20Sopenharmony_ci CAP_CONT_EVEN | CAP_CONT_ODD, 2558c2ecf20Sopenharmony_ci ipd->regs + CSR1); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_lock(&ipd->lock); 2598c2ecf20Sopenharmony_ci if (ipd->curr_buf && !list_empty(&ipd->dmaq)) { 2608c2ecf20Sopenharmony_ci ipd->curr_buf->vb2_buf.timestamp = ktime_get_ns(); 2618c2ecf20Sopenharmony_ci ipd->curr_buf->sequence = ipd->sequence++; 2628c2ecf20Sopenharmony_ci ipd->curr_buf->field = V4L2_FIELD_NONE; 2638c2ecf20Sopenharmony_ci vb2_buffer_done(&ipd->curr_buf->vb2_buf, VB2_BUF_STATE_DONE); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry); 2668c2ecf20Sopenharmony_ci list_del(&ivb->done_entry); 2678c2ecf20Sopenharmony_ci ipd->curr_buf = to_vb2_v4l2_buffer(ivb); 2688c2ecf20Sopenharmony_ci dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0); 2698c2ecf20Sopenharmony_ci iowrite32(dma_addr, ipd->regs + EVEN_DMA_START); 2708c2ecf20Sopenharmony_ci iowrite32(dma_addr + ipd->width, ipd->regs + ODD_DMA_START); 2718c2ecf20Sopenharmony_ci iowrite32(ipd->width, ipd->regs + EVEN_DMA_STRIDE); 2728c2ecf20Sopenharmony_ci iowrite32(ipd->width, ipd->regs + ODD_DMA_STRIDE); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* enable interrupts, clear all irq flags */ 2768c2ecf20Sopenharmony_ci iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | 2778c2ecf20Sopenharmony_ci FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); 2788c2ecf20Sopenharmony_ci spin_unlock(&ipd->lock); 2798c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations dt3155_fops = { 2838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2848c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 2858c2ecf20Sopenharmony_ci .release = vb2_fop_release, 2868c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 2878c2ecf20Sopenharmony_ci .read = vb2_fop_read, 2888c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 2898c2ecf20Sopenharmony_ci .poll = vb2_fop_poll 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int dt3155_querycap(struct file *filp, void *p, 2938c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci strscpy(cap->driver, DT3155_NAME, sizeof(cap->driver)); 2988c2ecf20Sopenharmony_ci strscpy(cap->card, DT3155_NAME " frame grabber", sizeof(cap->card)); 2998c2ecf20Sopenharmony_ci sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev)); 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int dt3155_enum_fmt_vid_cap(struct file *filp, 3048c2ecf20Sopenharmony_ci void *p, struct v4l2_fmtdesc *f) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci if (f->index) 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_GREY; 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int dt3155_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci f->fmt.pix.width = pd->width; 3178c2ecf20Sopenharmony_ci f->fmt.pix.height = pd->height; 3188c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; 3198c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 3208c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = f->fmt.pix.width; 3218c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height; 3228c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int dt3155_g_std(struct file *filp, void *p, v4l2_std_id *norm) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci *norm = pd->std; 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int dt3155_s_std(struct file *filp, void *p, v4l2_std_id norm) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (pd->std == norm) 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci if (vb2_is_busy(&pd->vidq)) 3418c2ecf20Sopenharmony_ci return -EBUSY; 3428c2ecf20Sopenharmony_ci pd->std = norm; 3438c2ecf20Sopenharmony_ci if (pd->std & V4L2_STD_525_60) { 3448c2ecf20Sopenharmony_ci pd->csr2 = VT_60HZ; 3458c2ecf20Sopenharmony_ci pd->width = 640; 3468c2ecf20Sopenharmony_ci pd->height = 480; 3478c2ecf20Sopenharmony_ci } else { 3488c2ecf20Sopenharmony_ci pd->csr2 = VT_50HZ; 3498c2ecf20Sopenharmony_ci pd->width = 768; 3508c2ecf20Sopenharmony_ci pd->height = 576; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int dt3155_enum_input(struct file *filp, void *p, 3568c2ecf20Sopenharmony_ci struct v4l2_input *input) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci if (input->index > 3) 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci if (input->index) 3618c2ecf20Sopenharmony_ci snprintf(input->name, sizeof(input->name), "VID%d", 3628c2ecf20Sopenharmony_ci input->index); 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci strscpy(input->name, "J2/VID0", sizeof(input->name)); 3658c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 3668c2ecf20Sopenharmony_ci input->std = V4L2_STD_ALL; 3678c2ecf20Sopenharmony_ci input->status = 0; 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int dt3155_g_input(struct file *filp, void *p, unsigned int *i) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci *i = pd->input; 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int dt3155_s_input(struct file *filp, void *p, unsigned int i) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct dt3155_priv *pd = video_drvdata(filp); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (i > 3) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci pd->input = i; 3868c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); 3878c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_CMD, (i << 6) | (i << 4) | SYNC_LVL_3); 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops dt3155_ioctl_ops = { 3928c2ecf20Sopenharmony_ci .vidioc_querycap = dt3155_querycap, 3938c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = dt3155_enum_fmt_vid_cap, 3948c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = dt3155_fmt_vid_cap, 3958c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = dt3155_fmt_vid_cap, 3968c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = dt3155_fmt_vid_cap, 3978c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 3988c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 3998c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 4008c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 4018c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 4028c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 4038c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 4048c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 4058c2ecf20Sopenharmony_ci .vidioc_g_std = dt3155_g_std, 4068c2ecf20Sopenharmony_ci .vidioc_s_std = dt3155_s_std, 4078c2ecf20Sopenharmony_ci .vidioc_enum_input = dt3155_enum_input, 4088c2ecf20Sopenharmony_ci .vidioc_g_input = dt3155_g_input, 4098c2ecf20Sopenharmony_ci .vidioc_s_input = dt3155_s_input, 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int dt3155_init_board(struct dt3155_priv *pd) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct pci_dev *pdev = pd->pdev; 4158c2ecf20Sopenharmony_ci int i; 4168c2ecf20Sopenharmony_ci u8 tmp = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci pci_set_master(pdev); /* dt3155 needs it */ 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* resetting the adapter */ 4218c2ecf20Sopenharmony_ci iowrite32(ADDR_ERR_ODD | ADDR_ERR_EVEN | FLD_CRPT_ODD | FLD_CRPT_EVEN | 4228c2ecf20Sopenharmony_ci FLD_DN_ODD | FLD_DN_EVEN, pd->regs + CSR1); 4238c2ecf20Sopenharmony_ci msleep(20); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* initializing adapter registers */ 4268c2ecf20Sopenharmony_ci iowrite32(FIFO_EN | SRST, pd->regs + CSR1); 4278c2ecf20Sopenharmony_ci iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); 4288c2ecf20Sopenharmony_ci iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); 4298c2ecf20Sopenharmony_ci iowrite32(0x00000020, pd->regs + FIFO_TRIGGER); 4308c2ecf20Sopenharmony_ci iowrite32(0x00000103, pd->regs + XFER_MODE); 4318c2ecf20Sopenharmony_ci iowrite32(0, pd->regs + RETRY_WAIT_CNT); 4328c2ecf20Sopenharmony_ci iowrite32(0, pd->regs + INT_CSR); 4338c2ecf20Sopenharmony_ci iowrite32(1, pd->regs + EVEN_FLD_MASK); 4348c2ecf20Sopenharmony_ci iowrite32(1, pd->regs + ODD_FLD_MASK); 4358c2ecf20Sopenharmony_ci iowrite32(0, pd->regs + MASK_LENGTH); 4368c2ecf20Sopenharmony_ci iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT); 4378c2ecf20Sopenharmony_ci iowrite32(0x01010101, pd->regs + IIC_CLK_DUR); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* verifying that we have a DT3155 board (not just a SAA7116 chip) */ 4408c2ecf20Sopenharmony_ci read_i2c_reg(pd->regs, DT_ID, &tmp); 4418c2ecf20Sopenharmony_ci if (tmp != DT3155_ID) 4428c2ecf20Sopenharmony_ci return -ENODEV; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* initialize AD LUT */ 4458c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, 0); 4468c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 4478c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_LUT, i); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* initialize ADC references */ 4508c2ecf20Sopenharmony_ci /* FIXME: pos_ref & neg_ref depend on VT_50HZ */ 4518c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); 4528c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); 4538c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF); 4548c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_CMD, 34); 4558c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF); 4568c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_CMD, 0); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* initialize PM LUT */ 4598c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM); 4608c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 4618c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, PM_LUT_ADDR, i); 4628c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, PM_LUT_DATA, i); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL); 4658c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 4668c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, PM_LUT_ADDR, i); 4678c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, PM_LUT_DATA, i); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* select channel 1 for input and set sync level */ 4728c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); 4738c2ecf20Sopenharmony_ci write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* disable all irqs, clear all irq flags */ 4768c2ecf20Sopenharmony_ci iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, 4778c2ecf20Sopenharmony_ci pd->regs + INT_CSR); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct video_device dt3155_vdev = { 4838c2ecf20Sopenharmony_ci .name = DT3155_NAME, 4848c2ecf20Sopenharmony_ci .fops = &dt3155_fops, 4858c2ecf20Sopenharmony_ci .ioctl_ops = &dt3155_ioctl_ops, 4868c2ecf20Sopenharmony_ci .minor = -1, 4878c2ecf20Sopenharmony_ci .release = video_device_release_empty, 4888c2ecf20Sopenharmony_ci .tvnorms = V4L2_STD_ALL, 4898c2ecf20Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 4908c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE, 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci struct dt3155_priv *pd; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 4998c2ecf20Sopenharmony_ci if (err) 5008c2ecf20Sopenharmony_ci return -ENODEV; 5018c2ecf20Sopenharmony_ci pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); 5028c2ecf20Sopenharmony_ci if (!pd) 5038c2ecf20Sopenharmony_ci return -ENOMEM; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci err = v4l2_device_register(&pdev->dev, &pd->v4l2_dev); 5068c2ecf20Sopenharmony_ci if (err) 5078c2ecf20Sopenharmony_ci return err; 5088c2ecf20Sopenharmony_ci pd->vdev = dt3155_vdev; 5098c2ecf20Sopenharmony_ci pd->vdev.v4l2_dev = &pd->v4l2_dev; 5108c2ecf20Sopenharmony_ci video_set_drvdata(&pd->vdev, pd); /* for use in video_fops */ 5118c2ecf20Sopenharmony_ci pd->pdev = pdev; 5128c2ecf20Sopenharmony_ci pd->std = V4L2_STD_625_50; 5138c2ecf20Sopenharmony_ci pd->csr2 = VT_50HZ; 5148c2ecf20Sopenharmony_ci pd->width = 768; 5158c2ecf20Sopenharmony_ci pd->height = 576; 5168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pd->dmaq); 5178c2ecf20Sopenharmony_ci mutex_init(&pd->mux); 5188c2ecf20Sopenharmony_ci pd->vdev.lock = &pd->mux; /* for locking v4l2_file_operations */ 5198c2ecf20Sopenharmony_ci pd->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 5208c2ecf20Sopenharmony_ci pd->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 5218c2ecf20Sopenharmony_ci pd->vidq.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 5228c2ecf20Sopenharmony_ci pd->vidq.ops = &q_ops; 5238c2ecf20Sopenharmony_ci pd->vidq.mem_ops = &vb2_dma_contig_memops; 5248c2ecf20Sopenharmony_ci pd->vidq.drv_priv = pd; 5258c2ecf20Sopenharmony_ci pd->vidq.min_buffers_needed = 2; 5268c2ecf20Sopenharmony_ci pd->vidq.gfp_flags = GFP_DMA32; 5278c2ecf20Sopenharmony_ci pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */ 5288c2ecf20Sopenharmony_ci pd->vidq.dev = &pdev->dev; 5298c2ecf20Sopenharmony_ci pd->vdev.queue = &pd->vidq; 5308c2ecf20Sopenharmony_ci err = vb2_queue_init(&pd->vidq); 5318c2ecf20Sopenharmony_ci if (err < 0) 5328c2ecf20Sopenharmony_ci goto err_v4l2_dev_unreg; 5338c2ecf20Sopenharmony_ci spin_lock_init(&pd->lock); 5348c2ecf20Sopenharmony_ci pd->config = ACQ_MODE_EVEN; 5358c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 5368c2ecf20Sopenharmony_ci if (err) 5378c2ecf20Sopenharmony_ci goto err_v4l2_dev_unreg; 5388c2ecf20Sopenharmony_ci err = pci_request_region(pdev, 0, pci_name(pdev)); 5398c2ecf20Sopenharmony_ci if (err) 5408c2ecf20Sopenharmony_ci goto err_pci_disable; 5418c2ecf20Sopenharmony_ci pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0)); 5428c2ecf20Sopenharmony_ci if (!pd->regs) { 5438c2ecf20Sopenharmony_ci err = -ENOMEM; 5448c2ecf20Sopenharmony_ci goto err_free_reg; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci err = dt3155_init_board(pd); 5478c2ecf20Sopenharmony_ci if (err) 5488c2ecf20Sopenharmony_ci goto err_iounmap; 5498c2ecf20Sopenharmony_ci err = request_irq(pd->pdev->irq, dt3155_irq_handler_even, 5508c2ecf20Sopenharmony_ci IRQF_SHARED, DT3155_NAME, pd); 5518c2ecf20Sopenharmony_ci if (err) 5528c2ecf20Sopenharmony_ci goto err_iounmap; 5538c2ecf20Sopenharmony_ci err = video_register_device(&pd->vdev, VFL_TYPE_VIDEO, -1); 5548c2ecf20Sopenharmony_ci if (err) 5558c2ecf20Sopenharmony_ci goto err_free_irq; 5568c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor); 5578c2ecf20Sopenharmony_ci return 0; /* success */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cierr_free_irq: 5608c2ecf20Sopenharmony_ci free_irq(pd->pdev->irq, pd); 5618c2ecf20Sopenharmony_cierr_iounmap: 5628c2ecf20Sopenharmony_ci pci_iounmap(pdev, pd->regs); 5638c2ecf20Sopenharmony_cierr_free_reg: 5648c2ecf20Sopenharmony_ci pci_release_region(pdev, 0); 5658c2ecf20Sopenharmony_cierr_pci_disable: 5668c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5678c2ecf20Sopenharmony_cierr_v4l2_dev_unreg: 5688c2ecf20Sopenharmony_ci v4l2_device_unregister(&pd->v4l2_dev); 5698c2ecf20Sopenharmony_ci return err; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic void dt3155_remove(struct pci_dev *pdev) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); 5758c2ecf20Sopenharmony_ci struct dt3155_priv *pd = container_of(v4l2_dev, struct dt3155_priv, 5768c2ecf20Sopenharmony_ci v4l2_dev); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci vb2_video_unregister_device(&pd->vdev); 5798c2ecf20Sopenharmony_ci free_irq(pd->pdev->irq, pd); 5808c2ecf20Sopenharmony_ci v4l2_device_unregister(&pd->v4l2_dev); 5818c2ecf20Sopenharmony_ci pci_iounmap(pdev, pd->regs); 5828c2ecf20Sopenharmony_ci pci_release_region(pdev, 0); 5838c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic const struct pci_device_id pci_ids[] = { 5878c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) }, 5888c2ecf20Sopenharmony_ci { 0, /* zero marks the end */ }, 5898c2ecf20Sopenharmony_ci}; 5908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_ids); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic struct pci_driver pci_driver = { 5938c2ecf20Sopenharmony_ci .name = DT3155_NAME, 5948c2ecf20Sopenharmony_ci .id_table = pci_ids, 5958c2ecf20Sopenharmony_ci .probe = dt3155_probe, 5968c2ecf20Sopenharmony_ci .remove = dt3155_remove, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cimodule_pci_driver(pci_driver); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber"); 6028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>"); 6038c2ecf20Sopenharmony_ciMODULE_VERSION(DT3155_VERSION); 6048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 605