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