// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar * * Based on original driver by Krzysztof Ha?asa: * Copyright (C) 2015 Industrial Research Institute for Automation * and Measurements PIAP * * Notes * ----- * * 1. Under stress-testing, it has been observed that the PCIe link * goes down, without reason. Therefore, the driver takes special care * to allow device hot-unplugging. * * 2. TW686X devices are capable of setting a few different DMA modes, * including: scatter-gather, field and frame modes. However, * under stress testings it has been found that the machine can * freeze completely if DMA registers are programmed while streaming * is active. * * Therefore, driver implements a dma_mode called 'memcpy' which * avoids cycling the DMA buffers, and insteads allocates extra DMA buffers * and then copies into vmalloc'ed user buffers. * * In addition to this, when streaming is on, the driver tries to access * hardware registers as infrequently as possible. This is done by using * a timer to limit the rate at which DMA is reset on DMA channels error.
*/
/* * This module parameter allows to control the DMA_TIMER_INTERVAL value. * The DMA_TIMER_INTERVAL register controls the minimum DMA interrupt * time span (iow, the maximum DMA interrupt rate) thus allowing for * IRQ coalescing. * * The chip datasheet does not mention a time unit for this value, so * users wanting fine-grain control over the interrupt rate should * determine the desired value through testing.
*/ static u32 dma_interval = 0x00098968;
module_param(dma_interval, int, 0444);
MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host");
staticunsignedint dma_mode = TW686X_DMA_MODE_MEMCPY; staticconstchar *dma_mode_name(unsignedint mode)
{ switch (mode) { case TW686X_DMA_MODE_MEMCPY: return"memcpy"; case TW686X_DMA_MODE_CONTIG: return"contig"; case TW686X_DMA_MODE_SG: return"sg"; default: return"unknown";
}
}
/* * The purpose of this awful hack is to avoid enabling the DMA * channels "too fast" which makes some TW686x devices very * angry and freeze the CPU (see note 1).
*/ staticvoid tw686x_dma_delay(struct timer_list *t)
{ struct tw686x_dev *dev = timer_container_of(dev, t, dma_delay_timer); unsignedlong flags;
/* * This must be set right before initializing v4l2_dev. * It's used to release resources after the last handle * held is released.
*/
dev->v4l2_dev.release = tw686x_dev_release;
err = tw686x_video_init(dev); if (err) {
dev_err(&pci_dev->dev, "can't register video\n"); goto iounmap;
}
err = tw686x_audio_init(dev); if (err)
dev_warn(&pci_dev->dev, "can't register audio\n");
err = request_irq(pci_dev->irq, tw686x_irq, IRQF_SHARED,
dev->name, dev); if (err < 0) {
dev_err(&pci_dev->dev, "unable to request interrupt\n"); goto tw686x_free;
}
/* * Setting pci_dev to NULL allows to detect hardware is no longer * available and will be used by vb2_ops. This is required because * the device sometimes hot-unplugs itself as the result of a PCIe * link down. * The lock is really important here.
*/
spin_lock_irqsave(&dev->lock, flags);
dev->pci_dev = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
/* * This calls tw686x_dev_release if it's the last reference. * Otherwise, release is postponed until there are no users left.
*/
v4l2_device_put(&dev->v4l2_dev);
}
/* * On TW6864 and TW6868, all channels share the pair of video DMA SG tables, * with 10-bit start_idx and end_idx determining start and end of frame buffer * for particular channel. * TW6868 with all its 8 channels would be problematic (only 127 SG entries per * channel) but we support only 4 channels on this chip anyway (the first * 4 channels are driven with internal video decoder, the other 4 would require * an external TW286x part). * * On TW6865 and TW6869, each channel has its own DMA SG table, with indexes * starting with 0. Both chips have complete sets of internal video decoders * (respectively 4 or 8-channel). * * All chips have separate SG tables for two video frames.
*/
/* driver_data is number of A/V channels */ staticconststruct pci_device_id tw686x_pci_tbl[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6864),
.driver_data = 4
},
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6865), /* not tested */
.driver_data = 4 | TYPE_SECOND_GEN
}, /* * TW6868 supports 8 A/V channels with an external TW2865 chip; * not supported by the driver.
*/
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6868), /* not tested */
.driver_data = 4
},
{
PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6869),
.driver_data = 8 | TYPE_SECOND_GEN},
{}
};
MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl);
MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]");
MODULE_AUTHOR("Ezequiel Garcia ");
MODULE_AUTHOR("Krzysztof Ha?asa ");
MODULE_LICENSE("GPL v2");
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.