2 * TW5864 driver - core functions
4 * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/init.h>
18 #include <linux/list.h>
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/slab.h>
22 #include <linux/kmod.h>
23 #include <linux/sound.h>
24 #include <linux/interrupt.h>
25 #include <linux/delay.h>
26 #include <linux/dma-mapping.h>
28 #include <linux/pci_ids.h>
29 #include <linux/jiffies.h>
31 #include <media/v4l2-dev.h>
34 #include "tw5864-reg.h"
36 MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices");
37 MODULE_AUTHOR("Bluecherry Maintainers <maintainers@bluecherrydvr.com>");
38 MODULE_AUTHOR("Andrey Utkin <andrey.utkin@corp.bluecherry.net>");
39 MODULE_LICENSE("GPL");
42 * BEWARE OF KNOWN ISSUES WITH VIDEO QUALITY
44 * This driver was developed by Bluecherry LLC by deducing behaviour of
45 * original manufacturer's driver, from both source code and execution traces.
46 * It is known that there are some artifacts on output video with this driver:
47 * - on all known hardware samples: random pixels of wrong color (mostly
48 * white, red or blue) appearing and disappearing on sequences of P-frames;
49 * - on some hardware samples (known with H.264 core version e006:2800):
50 * total madness on P-frames: blocks of wrong luminance; blocks of wrong
51 * colors "creeping" across the picture.
52 * There is a workaround for both issues: avoid P-frames by setting GOP size
53 * to 1. To do that, run this command on device files created by this driver:
55 * v4l2-ctl --device /dev/videoX --set-ctrl=video_gop_size=1
57 * These issues are not decoding errors; all produced H.264 streams are decoded
58 * properly. Streams without P-frames don't have these artifacts so it's not
59 * analog-to-digital conversion issues nor internal memory errors; we conclude
60 * it's internal H.264 encoder issues.
61 * We cannot even check the original driver's behaviour because it has never
62 * worked properly at all in our development environment. So these issues may
63 * be actually related to firmware or hardware. However it may be that there's
64 * just some more register settings missing in the driver which would please
66 * Manufacturer didn't help much on our inquiries, but feel free to disturb
67 * again the support of Intersil (owner of former Techwell).
70 /* take first free /dev/videoX indexes by default */
71 static unsigned int video_nr
[] = {[0 ... (TW5864_INPUTS
- 1)] = -1 };
73 module_param_array(video_nr
, int, NULL
, 0444);
74 MODULE_PARM_DESC(video_nr
, "video devices numbers array");
77 * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps
78 * the PCI ID database up to date. Note that the entries must be
79 * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
81 static const struct pci_device_id tw5864_pci_tbl
[] = {
82 {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL
, PCI_DEVICE_ID_TECHWELL_5864
)},
86 void tw5864_irqmask_apply(struct tw5864_dev
*dev
)
88 tw_writel(TW5864_INTR_ENABLE_L
, dev
->irqmask
& 0xffff);
89 tw_writel(TW5864_INTR_ENABLE_H
, (dev
->irqmask
>> 16));
92 static void tw5864_interrupts_disable(struct tw5864_dev
*dev
)
96 spin_lock_irqsave(&dev
->slock
, flags
);
98 tw5864_irqmask_apply(dev
);
99 spin_unlock_irqrestore(&dev
->slock
, flags
);
102 static void tw5864_timer_isr(struct tw5864_dev
*dev
);
103 static void tw5864_h264_isr(struct tw5864_dev
*dev
);
105 static irqreturn_t
tw5864_isr(int irq
, void *dev_id
)
107 struct tw5864_dev
*dev
= dev_id
;
110 status
= tw_readl(TW5864_INTR_STATUS_L
) |
111 tw_readl(TW5864_INTR_STATUS_H
) << 16;
115 tw_writel(TW5864_INTR_CLR_L
, 0xffff);
116 tw_writel(TW5864_INTR_CLR_H
, 0xffff);
118 if (status
& TW5864_INTR_VLC_DONE
)
119 tw5864_h264_isr(dev
);
121 if (status
& TW5864_INTR_TIMER
)
122 tw5864_timer_isr(dev
);
124 if (!(status
& (TW5864_INTR_TIMER
| TW5864_INTR_VLC_DONE
))) {
125 dev_dbg(&dev
->pci
->dev
, "Unknown interrupt, status 0x%08X\n",
132 static void tw5864_h264_isr(struct tw5864_dev
*dev
)
134 int channel
= tw_readl(TW5864_DSP
) & TW5864_DSP_ENC_CHN
;
135 struct tw5864_input
*input
= &dev
->inputs
[channel
];
136 int cur_frame_index
, next_frame_index
;
137 struct tw5864_h264_frame
*cur_frame
, *next_frame
;
140 spin_lock_irqsave(&dev
->slock
, flags
);
142 cur_frame_index
= dev
->h264_buf_w_index
;
143 next_frame_index
= (cur_frame_index
+ 1) % H264_BUF_CNT
;
144 cur_frame
= &dev
->h264_buf
[cur_frame_index
];
145 next_frame
= &dev
->h264_buf
[next_frame_index
];
147 if (next_frame_index
!= dev
->h264_buf_r_index
) {
148 cur_frame
->vlc_len
= tw_readl(TW5864_VLC_LENGTH
) << 2;
149 cur_frame
->checksum
= tw_readl(TW5864_VLC_CRC_REG
);
150 cur_frame
->input
= input
;
151 cur_frame
->timestamp
= ktime_get_ns();
152 cur_frame
->seqno
= input
->frame_seqno
;
153 cur_frame
->gop_seqno
= input
->frame_gop_seqno
;
155 dev
->h264_buf_w_index
= next_frame_index
;
156 tasklet_schedule(&dev
->tasklet
);
158 cur_frame
= next_frame
;
160 spin_lock(&input
->slock
);
161 input
->frame_seqno
++;
162 input
->frame_gop_seqno
++;
163 if (input
->frame_gop_seqno
>= input
->gop
)
164 input
->frame_gop_seqno
= 0;
165 spin_unlock(&input
->slock
);
167 dev_err(&dev
->pci
->dev
,
168 "Skipped frame on input %d because all buffers busy\n",
172 dev
->encoder_busy
= 0;
174 spin_unlock_irqrestore(&dev
->slock
, flags
);
176 tw_writel(TW5864_VLC_STREAM_BASE_ADDR
, cur_frame
->vlc
.dma_addr
);
177 tw_writel(TW5864_MV_STREAM_BASE_ADDR
, cur_frame
->mv
.dma_addr
);
179 /* Additional ack for this interrupt */
180 tw_writel(TW5864_VLC_DSP_INTR
, 0x00000001);
181 tw_writel(TW5864_PCI_INTR_STATUS
, TW5864_VLC_DONE_INTR
);
184 static void tw5864_input_deadline_update(struct tw5864_input
*input
)
186 input
->new_frame_deadline
= jiffies
+ msecs_to_jiffies(1000);
189 static void tw5864_timer_isr(struct tw5864_dev
*dev
)
195 /* Additional ack for this interrupt */
196 tw_writel(TW5864_PCI_INTR_STATUS
, TW5864_TIMER_INTR
);
198 spin_lock_irqsave(&dev
->slock
, flags
);
199 encoder_busy
= dev
->encoder_busy
;
200 spin_unlock_irqrestore(&dev
->slock
, flags
);
206 * Traversing inputs in round-robin fashion, starting from next to the
209 for (i
= 0; i
< TW5864_INPUTS
; i
++) {
210 int next_input
= (i
+ dev
->next_input
) % TW5864_INPUTS
;
211 struct tw5864_input
*input
= &dev
->inputs
[next_input
];
212 int raw_buf_id
; /* id of internal buf with last raw frame */
214 spin_lock_irqsave(&input
->slock
, flags
);
218 /* Check if new raw frame is available */
219 raw_buf_id
= tw_mask_shift_readl(TW5864_SENIF_ORG_FRM_PTR1
, 0x3,
222 if (input
->buf_id
!= raw_buf_id
) {
223 input
->buf_id
= raw_buf_id
;
224 tw5864_input_deadline_update(input
);
225 spin_unlock_irqrestore(&input
->slock
, flags
);
227 spin_lock_irqsave(&dev
->slock
, flags
);
228 dev
->encoder_busy
= 1;
229 dev
->next_input
= (next_input
+ 1) % TW5864_INPUTS
;
230 spin_unlock_irqrestore(&dev
->slock
, flags
);
232 tw5864_request_encoded_frame(input
);
236 /* No new raw frame; check if channel is stuck */
237 if (time_is_after_jiffies(input
->new_frame_deadline
)) {
238 /* If stuck, request new raw frames again */
239 tw_mask_shift_writel(TW5864_ENC_BUF_PTR_REC1
, 0x3,
240 2 * input
->nr
, input
->buf_id
+ 3);
241 tw5864_input_deadline_update(input
);
244 spin_unlock_irqrestore(&input
->slock
, flags
);
248 static int tw5864_initdev(struct pci_dev
*pci_dev
,
249 const struct pci_device_id
*pci_id
)
251 struct tw5864_dev
*dev
;
254 dev
= devm_kzalloc(&pci_dev
->dev
, sizeof(*dev
), GFP_KERNEL
);
258 snprintf(dev
->name
, sizeof(dev
->name
), "tw5864:%s", pci_name(pci_dev
));
260 err
= v4l2_device_register(&pci_dev
->dev
, &dev
->v4l2_dev
);
266 err
= pci_enable_device(pci_dev
);
268 dev_err(&dev
->pci
->dev
, "pci_enable_device() failed\n");
272 pci_set_master(pci_dev
);
274 err
= pci_set_dma_mask(pci_dev
, DMA_BIT_MASK(32));
276 dev_err(&dev
->pci
->dev
, "32 bit PCI DMA is not supported\n");
281 err
= pci_request_regions(pci_dev
, dev
->name
);
283 dev_err(&dev
->pci
->dev
, "Cannot request regions for MMIO\n");
286 dev
->mmio
= pci_ioremap_bar(pci_dev
, 0);
289 dev_err(&dev
->pci
->dev
, "can't ioremap() MMIO memory\n");
293 spin_lock_init(&dev
->slock
);
295 dev_info(&pci_dev
->dev
, "TW5864 hardware version: %04x\n",
296 tw_readl(TW5864_HW_VERSION
));
297 dev_info(&pci_dev
->dev
, "TW5864 H.264 core version: %04x:%04x\n",
298 tw_readl(TW5864_H264REV
),
299 tw_readl(TW5864_UNDECLARED_H264REV_PART2
));
301 err
= tw5864_video_init(dev
, video_nr
);
306 err
= devm_request_irq(&pci_dev
->dev
, pci_dev
->irq
, tw5864_isr
,
307 IRQF_SHARED
, "tw5864", dev
);
309 dev_err(&dev
->pci
->dev
, "can't get IRQ %d\n", pci_dev
->irq
);
313 dev_info(&pci_dev
->dev
, "Note: there are known video quality issues. For details\n");
314 dev_info(&pci_dev
->dev
, "see the comment in drivers/media/pci/tw5864/tw5864-core.c.\n");
319 tw5864_video_fini(dev
);
323 pci_release_regions(pci_dev
);
325 pci_disable_device(pci_dev
);
327 v4l2_device_unregister(&dev
->v4l2_dev
);
331 static void tw5864_finidev(struct pci_dev
*pci_dev
)
333 struct v4l2_device
*v4l2_dev
= pci_get_drvdata(pci_dev
);
334 struct tw5864_dev
*dev
=
335 container_of(v4l2_dev
, struct tw5864_dev
, v4l2_dev
);
337 /* shutdown subsystems */
338 tw5864_interrupts_disable(dev
);
341 tw5864_video_fini(dev
);
343 /* release resources */
345 release_mem_region(pci_resource_start(pci_dev
, 0),
346 pci_resource_len(pci_dev
, 0));
348 v4l2_device_unregister(&dev
->v4l2_dev
);
349 devm_kfree(&pci_dev
->dev
, dev
);
352 static struct pci_driver tw5864_pci_driver
= {
354 .id_table
= tw5864_pci_tbl
,
355 .probe
= tw5864_initdev
,
356 .remove
= tw5864_finidev
,
359 module_pci_driver(tw5864_pci_driver
);