Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  TW5864 driver - core functions
0004  *
0005  *  Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com>
0006  */
0007 
0008 #include <linux/init.h>
0009 #include <linux/list.h>
0010 #include <linux/module.h>
0011 #include <linux/kernel.h>
0012 #include <linux/slab.h>
0013 #include <linux/kmod.h>
0014 #include <linux/sound.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/delay.h>
0017 #include <linux/dma-mapping.h>
0018 #include <linux/pm.h>
0019 #include <linux/pci_ids.h>
0020 #include <linux/jiffies.h>
0021 #include <asm/dma.h>
0022 #include <media/v4l2-dev.h>
0023 
0024 #include "tw5864.h"
0025 #include "tw5864-reg.h"
0026 
0027 MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices");
0028 MODULE_AUTHOR("Bluecherry Maintainers <maintainers@bluecherrydvr.com>");
0029 MODULE_AUTHOR("Andrey Utkin <andrey.utkin@corp.bluecherry.net>");
0030 MODULE_LICENSE("GPL");
0031 
0032 /*
0033  * BEWARE OF KNOWN ISSUES WITH VIDEO QUALITY
0034  *
0035  * This driver was developed by Bluecherry LLC by deducing behaviour of
0036  * original manufacturer's driver, from both source code and execution traces.
0037  * It is known that there are some artifacts on output video with this driver:
0038  *  - on all known hardware samples: random pixels of wrong color (mostly
0039  *    white, red or blue) appearing and disappearing on sequences of P-frames;
0040  *  - on some hardware samples (known with H.264 core version e006:2800):
0041  *    total madness on P-frames: blocks of wrong luminance; blocks of wrong
0042  *    colors "creeping" across the picture.
0043  * There is a workaround for both issues: avoid P-frames by setting GOP size
0044  * to 1. To do that, run this command on device files created by this driver:
0045  *
0046  * v4l2-ctl --device /dev/videoX --set-ctrl=video_gop_size=1
0047  *
0048  * These issues are not decoding errors; all produced H.264 streams are decoded
0049  * properly. Streams without P-frames don't have these artifacts so it's not
0050  * analog-to-digital conversion issues nor internal memory errors; we conclude
0051  * it's internal H.264 encoder issues.
0052  * We cannot even check the original driver's behaviour because it has never
0053  * worked properly at all in our development environment. So these issues may
0054  * be actually related to firmware or hardware. However it may be that there's
0055  * just some more register settings missing in the driver which would please
0056  * the hardware.
0057  * Manufacturer didn't help much on our inquiries, but feel free to disturb
0058  * again the support of Intersil (owner of former Techwell).
0059  */
0060 
0061 /* take first free /dev/videoX indexes by default */
0062 static unsigned int video_nr[] = {[0 ... (TW5864_INPUTS - 1)] = -1 };
0063 
0064 module_param_array(video_nr, int, NULL, 0444);
0065 MODULE_PARM_DESC(video_nr, "video devices numbers array");
0066 
0067 /*
0068  * Please add any new PCI IDs to: https://pci-ids.ucw.cz.  This keeps
0069  * the PCI ID database up to date.  Note that the entries must be
0070  * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs.
0071  */
0072 static const struct pci_device_id tw5864_pci_tbl[] = {
0073     {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_5864)},
0074     {0,}
0075 };
0076 
0077 void tw5864_irqmask_apply(struct tw5864_dev *dev)
0078 {
0079     tw_writel(TW5864_INTR_ENABLE_L, dev->irqmask & 0xffff);
0080     tw_writel(TW5864_INTR_ENABLE_H, (dev->irqmask >> 16));
0081 }
0082 
0083 static void tw5864_interrupts_disable(struct tw5864_dev *dev)
0084 {
0085     unsigned long flags;
0086 
0087     spin_lock_irqsave(&dev->slock, flags);
0088     dev->irqmask = 0;
0089     tw5864_irqmask_apply(dev);
0090     spin_unlock_irqrestore(&dev->slock, flags);
0091 }
0092 
0093 static void tw5864_timer_isr(struct tw5864_dev *dev);
0094 static void tw5864_h264_isr(struct tw5864_dev *dev);
0095 
0096 static irqreturn_t tw5864_isr(int irq, void *dev_id)
0097 {
0098     struct tw5864_dev *dev = dev_id;
0099     u32 status;
0100 
0101     status = tw_readl(TW5864_INTR_STATUS_L) |
0102         tw_readl(TW5864_INTR_STATUS_H) << 16;
0103     if (!status)
0104         return IRQ_NONE;
0105 
0106     tw_writel(TW5864_INTR_CLR_L, 0xffff);
0107     tw_writel(TW5864_INTR_CLR_H, 0xffff);
0108 
0109     if (status & TW5864_INTR_VLC_DONE)
0110         tw5864_h264_isr(dev);
0111 
0112     if (status & TW5864_INTR_TIMER)
0113         tw5864_timer_isr(dev);
0114 
0115     if (!(status & (TW5864_INTR_TIMER | TW5864_INTR_VLC_DONE))) {
0116         dev_dbg(&dev->pci->dev, "Unknown interrupt, status 0x%08X\n",
0117             status);
0118     }
0119 
0120     return IRQ_HANDLED;
0121 }
0122 
0123 static void tw5864_h264_isr(struct tw5864_dev *dev)
0124 {
0125     int channel = tw_readl(TW5864_DSP) & TW5864_DSP_ENC_CHN;
0126     struct tw5864_input *input = &dev->inputs[channel];
0127     int cur_frame_index, next_frame_index;
0128     struct tw5864_h264_frame *cur_frame, *next_frame;
0129     unsigned long flags;
0130 
0131     spin_lock_irqsave(&dev->slock, flags);
0132 
0133     cur_frame_index = dev->h264_buf_w_index;
0134     next_frame_index = (cur_frame_index + 1) % H264_BUF_CNT;
0135     cur_frame = &dev->h264_buf[cur_frame_index];
0136     next_frame = &dev->h264_buf[next_frame_index];
0137 
0138     if (next_frame_index != dev->h264_buf_r_index) {
0139         cur_frame->vlc_len = tw_readl(TW5864_VLC_LENGTH) << 2;
0140         cur_frame->checksum = tw_readl(TW5864_VLC_CRC_REG);
0141         cur_frame->input = input;
0142         cur_frame->timestamp = ktime_get_ns();
0143         cur_frame->seqno = input->frame_seqno;
0144         cur_frame->gop_seqno = input->frame_gop_seqno;
0145 
0146         dev->h264_buf_w_index = next_frame_index;
0147         tasklet_schedule(&dev->tasklet);
0148 
0149         cur_frame = next_frame;
0150 
0151         spin_lock(&input->slock);
0152         input->frame_seqno++;
0153         input->frame_gop_seqno++;
0154         if (input->frame_gop_seqno >= input->gop)
0155             input->frame_gop_seqno = 0;
0156         spin_unlock(&input->slock);
0157     } else {
0158         dev_err(&dev->pci->dev,
0159             "Skipped frame on input %d because all buffers busy\n",
0160             channel);
0161     }
0162 
0163     dev->encoder_busy = 0;
0164 
0165     spin_unlock_irqrestore(&dev->slock, flags);
0166 
0167     tw_writel(TW5864_VLC_STREAM_BASE_ADDR, cur_frame->vlc.dma_addr);
0168     tw_writel(TW5864_MV_STREAM_BASE_ADDR, cur_frame->mv.dma_addr);
0169 
0170     /* Additional ack for this interrupt */
0171     tw_writel(TW5864_VLC_DSP_INTR, 0x00000001);
0172     tw_writel(TW5864_PCI_INTR_STATUS, TW5864_VLC_DONE_INTR);
0173 }
0174 
0175 static void tw5864_input_deadline_update(struct tw5864_input *input)
0176 {
0177     input->new_frame_deadline = jiffies + msecs_to_jiffies(1000);
0178 }
0179 
0180 static void tw5864_timer_isr(struct tw5864_dev *dev)
0181 {
0182     unsigned long flags;
0183     int i;
0184     int encoder_busy;
0185 
0186     /* Additional ack for this interrupt */
0187     tw_writel(TW5864_PCI_INTR_STATUS, TW5864_TIMER_INTR);
0188 
0189     spin_lock_irqsave(&dev->slock, flags);
0190     encoder_busy = dev->encoder_busy;
0191     spin_unlock_irqrestore(&dev->slock, flags);
0192 
0193     if (encoder_busy)
0194         return;
0195 
0196     /*
0197      * Traversing inputs in round-robin fashion, starting from next to the
0198      * last processed one
0199      */
0200     for (i = 0; i < TW5864_INPUTS; i++) {
0201         int next_input = (i + dev->next_input) % TW5864_INPUTS;
0202         struct tw5864_input *input = &dev->inputs[next_input];
0203         int raw_buf_id; /* id of internal buf with last raw frame */
0204 
0205         spin_lock_irqsave(&input->slock, flags);
0206         if (!input->enabled)
0207             goto next;
0208 
0209         /* Check if new raw frame is available */
0210         raw_buf_id = tw_mask_shift_readl(TW5864_SENIF_ORG_FRM_PTR1, 0x3,
0211                          2 * input->nr);
0212 
0213         if (input->buf_id != raw_buf_id) {
0214             input->buf_id = raw_buf_id;
0215             tw5864_input_deadline_update(input);
0216             spin_unlock_irqrestore(&input->slock, flags);
0217 
0218             spin_lock_irqsave(&dev->slock, flags);
0219             dev->encoder_busy = 1;
0220             dev->next_input = (next_input + 1) % TW5864_INPUTS;
0221             spin_unlock_irqrestore(&dev->slock, flags);
0222 
0223             tw5864_request_encoded_frame(input);
0224             break;
0225         }
0226 
0227         /* No new raw frame; check if channel is stuck */
0228         if (time_is_after_jiffies(input->new_frame_deadline)) {
0229             /* If stuck, request new raw frames again */
0230             tw_mask_shift_writel(TW5864_ENC_BUF_PTR_REC1, 0x3,
0231                          2 * input->nr, input->buf_id + 3);
0232             tw5864_input_deadline_update(input);
0233         }
0234 next:
0235         spin_unlock_irqrestore(&input->slock, flags);
0236     }
0237 }
0238 
0239 static int tw5864_initdev(struct pci_dev *pci_dev,
0240               const struct pci_device_id *pci_id)
0241 {
0242     struct tw5864_dev *dev;
0243     int err;
0244 
0245     dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev), GFP_KERNEL);
0246     if (!dev)
0247         return -ENOMEM;
0248 
0249     snprintf(dev->name, sizeof(dev->name), "tw5864:%s", pci_name(pci_dev));
0250 
0251     err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
0252     if (err)
0253         return err;
0254 
0255     /* pci init */
0256     dev->pci = pci_dev;
0257     err = pcim_enable_device(pci_dev);
0258     if (err) {
0259         dev_err(&dev->pci->dev, "pcim_enable_device() failed\n");
0260         goto unreg_v4l2;
0261     }
0262 
0263     pci_set_master(pci_dev);
0264 
0265     err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
0266     if (err) {
0267         dev_err(&dev->pci->dev, "32 bit PCI DMA is not supported\n");
0268         goto unreg_v4l2;
0269     }
0270 
0271     /* get mmio */
0272     err = pcim_iomap_regions(pci_dev, BIT(0), dev->name);
0273     if (err) {
0274         dev_err(&dev->pci->dev, "Cannot request regions for MMIO\n");
0275         goto unreg_v4l2;
0276     }
0277     dev->mmio = pcim_iomap_table(pci_dev)[0];
0278 
0279     spin_lock_init(&dev->slock);
0280 
0281     dev_info(&pci_dev->dev, "TW5864 hardware version: %04x\n",
0282          tw_readl(TW5864_HW_VERSION));
0283     dev_info(&pci_dev->dev, "TW5864 H.264 core version: %04x:%04x\n",
0284          tw_readl(TW5864_H264REV),
0285          tw_readl(TW5864_UNDECLARED_H264REV_PART2));
0286 
0287     err = tw5864_video_init(dev, video_nr);
0288     if (err)
0289         goto unreg_v4l2;
0290 
0291     /* get irq */
0292     err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw5864_isr,
0293                    IRQF_SHARED, "tw5864", dev);
0294     if (err < 0) {
0295         dev_err(&dev->pci->dev, "can't get IRQ %d\n", pci_dev->irq);
0296         goto fini_video;
0297     }
0298 
0299     dev_info(&pci_dev->dev, "Note: there are known video quality issues. For details\n");
0300     dev_info(&pci_dev->dev, "see the comment in drivers/media/pci/tw5864/tw5864-core.c.\n");
0301 
0302     return 0;
0303 
0304 fini_video:
0305     tw5864_video_fini(dev);
0306 unreg_v4l2:
0307     v4l2_device_unregister(&dev->v4l2_dev);
0308     return err;
0309 }
0310 
0311 static void tw5864_finidev(struct pci_dev *pci_dev)
0312 {
0313     struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
0314     struct tw5864_dev *dev =
0315         container_of(v4l2_dev, struct tw5864_dev, v4l2_dev);
0316 
0317     /* shutdown subsystems */
0318     tw5864_interrupts_disable(dev);
0319 
0320     /* unregister */
0321     tw5864_video_fini(dev);
0322 
0323     v4l2_device_unregister(&dev->v4l2_dev);
0324 }
0325 
0326 static struct pci_driver tw5864_pci_driver = {
0327     .name = "tw5864",
0328     .id_table = tw5864_pci_tbl,
0329     .probe = tw5864_initdev,
0330     .remove = tw5864_finidev,
0331 };
0332 
0333 module_pci_driver(tw5864_pci_driver);