// 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. 
 | 
 */ 
 | 
  
 | 
#include <linux/init.h> 
 | 
#include <linux/interrupt.h> 
 | 
#include <linux/delay.h> 
 | 
#include <linux/kernel.h> 
 | 
#include <linux/module.h> 
 | 
#include <linux/pci_ids.h> 
 | 
#include <linux/slab.h> 
 | 
#include <linux/timer.h> 
 | 
  
 | 
#include "tw686x.h" 
 | 
#include "tw686x-regs.h" 
 | 
  
 | 
/* 
 | 
 * 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"); 
 | 
  
 | 
static unsigned int dma_mode = TW686X_DMA_MODE_MEMCPY; 
 | 
static const char *dma_mode_name(unsigned int 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"; 
 | 
    } 
 | 
} 
 | 
  
 | 
static int tw686x_dma_mode_get(char *buffer, const struct kernel_param *kp) 
 | 
{ 
 | 
    return sprintf(buffer, "%s", dma_mode_name(dma_mode)); 
 | 
} 
 | 
  
 | 
static int tw686x_dma_mode_set(const char *val, const struct kernel_param *kp) 
 | 
{ 
 | 
    if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY))) 
 | 
        dma_mode = TW686X_DMA_MODE_MEMCPY; 
 | 
    else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_CONTIG))) 
 | 
        dma_mode = TW686X_DMA_MODE_CONTIG; 
 | 
    else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_SG))) 
 | 
        dma_mode = TW686X_DMA_MODE_SG; 
 | 
    else 
 | 
        return -EINVAL; 
 | 
    return 0; 
 | 
} 
 | 
module_param_call(dma_mode, tw686x_dma_mode_set, tw686x_dma_mode_get, 
 | 
          &dma_mode, S_IRUGO|S_IWUSR); 
 | 
MODULE_PARM_DESC(dma_mode, "DMA operation mode (memcpy/contig/sg, default=memcpy)"); 
 | 
  
 | 
void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel) 
 | 
{ 
 | 
    u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); 
 | 
    u32 dma_cmd = reg_read(dev, DMA_CMD); 
 | 
  
 | 
    dma_en &= ~BIT(channel); 
 | 
    dma_cmd &= ~BIT(channel); 
 | 
  
 | 
    /* Must remove it from pending too */ 
 | 
    dev->pending_dma_en &= ~BIT(channel); 
 | 
    dev->pending_dma_cmd &= ~BIT(channel); 
 | 
  
 | 
    /* Stop DMA if no channels are enabled */ 
 | 
    if (!dma_en) 
 | 
        dma_cmd = 0; 
 | 
    reg_write(dev, DMA_CHANNEL_ENABLE, dma_en); 
 | 
    reg_write(dev, DMA_CMD, dma_cmd); 
 | 
} 
 | 
  
 | 
void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel) 
 | 
{ 
 | 
    u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); 
 | 
    u32 dma_cmd = reg_read(dev, DMA_CMD); 
 | 
  
 | 
    dev->pending_dma_en |= dma_en | BIT(channel); 
 | 
    dev->pending_dma_cmd |= dma_cmd | DMA_CMD_ENABLE | BIT(channel); 
 | 
} 
 | 
  
 | 
/* 
 | 
 * 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). 
 | 
 */ 
 | 
static void tw686x_dma_delay(struct timer_list *t) 
 | 
{ 
 | 
    struct tw686x_dev *dev = from_timer(dev, t, dma_delay_timer); 
 | 
    unsigned long flags; 
 | 
  
 | 
    spin_lock_irqsave(&dev->lock, flags); 
 | 
  
 | 
    reg_write(dev, DMA_CHANNEL_ENABLE, dev->pending_dma_en); 
 | 
    reg_write(dev, DMA_CMD, dev->pending_dma_cmd); 
 | 
    dev->pending_dma_en = 0; 
 | 
    dev->pending_dma_cmd = 0; 
 | 
  
 | 
    spin_unlock_irqrestore(&dev->lock, flags); 
 | 
} 
 | 
  
 | 
static void tw686x_reset_channels(struct tw686x_dev *dev, unsigned int ch_mask) 
 | 
{ 
 | 
    u32 dma_en, dma_cmd; 
 | 
  
 | 
    dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); 
 | 
    dma_cmd = reg_read(dev, DMA_CMD); 
 | 
  
 | 
    /* 
 | 
     * Save pending register status, the timer will 
 | 
     * restore them. 
 | 
     */ 
 | 
    dev->pending_dma_en |= dma_en; 
 | 
    dev->pending_dma_cmd |= dma_cmd; 
 | 
  
 | 
    /* Disable the reset channels */ 
 | 
    reg_write(dev, DMA_CHANNEL_ENABLE, dma_en & ~ch_mask); 
 | 
  
 | 
    if ((dma_en & ~ch_mask) == 0) { 
 | 
        dev_dbg(&dev->pci_dev->dev, "reset: stopping DMA\n"); 
 | 
        dma_cmd &= ~DMA_CMD_ENABLE; 
 | 
    } 
 | 
    reg_write(dev, DMA_CMD, dma_cmd & ~ch_mask); 
 | 
} 
 | 
  
 | 
static irqreturn_t tw686x_irq(int irq, void *dev_id) 
 | 
{ 
 | 
    struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; 
 | 
    unsigned int video_requests, audio_requests, reset_ch; 
 | 
    u32 fifo_status, fifo_signal, fifo_ov, fifo_bad, fifo_errors; 
 | 
    u32 int_status, dma_en, video_en, pb_status; 
 | 
    unsigned long flags; 
 | 
  
 | 
    int_status = reg_read(dev, INT_STATUS); /* cleared on read */ 
 | 
    fifo_status = reg_read(dev, VIDEO_FIFO_STATUS); 
 | 
  
 | 
    /* INT_STATUS does not include FIFO_STATUS errors! */ 
 | 
    if (!int_status && !TW686X_FIFO_ERROR(fifo_status)) 
 | 
        return IRQ_NONE; 
 | 
  
 | 
    if (int_status & INT_STATUS_DMA_TOUT) { 
 | 
        dev_dbg(&dev->pci_dev->dev, 
 | 
            "DMA timeout. Resetting DMA for all channels\n"); 
 | 
        reset_ch = ~0; 
 | 
        goto reset_channels; 
 | 
    } 
 | 
  
 | 
    spin_lock_irqsave(&dev->lock, flags); 
 | 
    dma_en = reg_read(dev, DMA_CHANNEL_ENABLE); 
 | 
    spin_unlock_irqrestore(&dev->lock, flags); 
 | 
  
 | 
    video_en = dma_en & 0xff; 
 | 
    fifo_signal = ~(fifo_status & 0xff) & video_en; 
 | 
    fifo_ov = fifo_status >> 24; 
 | 
    fifo_bad = fifo_status >> 16; 
 | 
  
 | 
    /* Mask of channels with signal and FIFO errors */ 
 | 
    fifo_errors = fifo_signal & (fifo_ov | fifo_bad); 
 | 
  
 | 
    reset_ch = 0; 
 | 
    pb_status = reg_read(dev, PB_STATUS); 
 | 
  
 | 
    /* Coalesce video frame/error events */ 
 | 
    video_requests = (int_status & video_en) | fifo_errors; 
 | 
    audio_requests = (int_status & dma_en) >> 8; 
 | 
  
 | 
    if (video_requests) 
 | 
        tw686x_video_irq(dev, video_requests, pb_status, 
 | 
                 fifo_status, &reset_ch); 
 | 
    if (audio_requests) 
 | 
        tw686x_audio_irq(dev, audio_requests, pb_status); 
 | 
  
 | 
reset_channels: 
 | 
    if (reset_ch) { 
 | 
        spin_lock_irqsave(&dev->lock, flags); 
 | 
        tw686x_reset_channels(dev, reset_ch); 
 | 
        spin_unlock_irqrestore(&dev->lock, flags); 
 | 
        mod_timer(&dev->dma_delay_timer, 
 | 
              jiffies + msecs_to_jiffies(100)); 
 | 
    } 
 | 
  
 | 
    return IRQ_HANDLED; 
 | 
} 
 | 
  
 | 
static void tw686x_dev_release(struct v4l2_device *v4l2_dev) 
 | 
{ 
 | 
    struct tw686x_dev *dev = container_of(v4l2_dev, struct tw686x_dev, 
 | 
                          v4l2_dev); 
 | 
    unsigned int ch; 
 | 
  
 | 
    for (ch = 0; ch < max_channels(dev); ch++) 
 | 
        v4l2_ctrl_handler_free(&dev->video_channels[ch].ctrl_handler); 
 | 
  
 | 
    v4l2_device_unregister(&dev->v4l2_dev); 
 | 
  
 | 
    kfree(dev->audio_channels); 
 | 
    kfree(dev->video_channels); 
 | 
    kfree(dev); 
 | 
} 
 | 
  
 | 
static int tw686x_probe(struct pci_dev *pci_dev, 
 | 
            const struct pci_device_id *pci_id) 
 | 
{ 
 | 
    struct tw686x_dev *dev; 
 | 
    int err; 
 | 
  
 | 
    dev = kzalloc(sizeof(*dev), GFP_KERNEL); 
 | 
    if (!dev) 
 | 
        return -ENOMEM; 
 | 
    dev->type = pci_id->driver_data; 
 | 
    dev->dma_mode = dma_mode; 
 | 
    sprintf(dev->name, "tw%04X", pci_dev->device); 
 | 
  
 | 
    dev->video_channels = kcalloc(max_channels(dev), 
 | 
        sizeof(*dev->video_channels), GFP_KERNEL); 
 | 
    if (!dev->video_channels) { 
 | 
        err = -ENOMEM; 
 | 
        goto free_dev; 
 | 
    } 
 | 
  
 | 
    dev->audio_channels = kcalloc(max_channels(dev), 
 | 
        sizeof(*dev->audio_channels), GFP_KERNEL); 
 | 
    if (!dev->audio_channels) { 
 | 
        err = -ENOMEM; 
 | 
        goto free_video; 
 | 
    } 
 | 
  
 | 
    pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx (%s mode)\n", dev->name, 
 | 
        pci_name(pci_dev), pci_dev->irq, 
 | 
        (unsigned long)pci_resource_start(pci_dev, 0), 
 | 
        dma_mode_name(dma_mode)); 
 | 
  
 | 
    dev->pci_dev = pci_dev; 
 | 
    if (pci_enable_device(pci_dev)) { 
 | 
        err = -EIO; 
 | 
        goto free_audio; 
 | 
    } 
 | 
  
 | 
    pci_set_master(pci_dev); 
 | 
    err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); 
 | 
    if (err) { 
 | 
        dev_err(&pci_dev->dev, "32-bit PCI DMA not supported\n"); 
 | 
        err = -EIO; 
 | 
        goto disable_pci; 
 | 
    } 
 | 
  
 | 
    err = pci_request_regions(pci_dev, dev->name); 
 | 
    if (err) { 
 | 
        dev_err(&pci_dev->dev, "unable to request PCI region\n"); 
 | 
        goto disable_pci; 
 | 
    } 
 | 
  
 | 
    dev->mmio = pci_ioremap_bar(pci_dev, 0); 
 | 
    if (!dev->mmio) { 
 | 
        dev_err(&pci_dev->dev, "unable to remap PCI region\n"); 
 | 
        err = -ENOMEM; 
 | 
        goto free_region; 
 | 
    } 
 | 
  
 | 
    /* Reset all subsystems */ 
 | 
    reg_write(dev, SYS_SOFT_RST, 0x0f); 
 | 
    mdelay(1); 
 | 
  
 | 
    reg_write(dev, SRST[0], 0x3f); 
 | 
    if (max_channels(dev) > 4) 
 | 
        reg_write(dev, SRST[1], 0x3f); 
 | 
  
 | 
    /* Disable the DMA engine */ 
 | 
    reg_write(dev, DMA_CMD, 0); 
 | 
    reg_write(dev, DMA_CHANNEL_ENABLE, 0); 
 | 
  
 | 
    /* Enable DMA FIFO overflow and pointer check */ 
 | 
    reg_write(dev, DMA_CONFIG, 0xffffff04); 
 | 
    reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x140c8584); 
 | 
    reg_write(dev, DMA_TIMER_INTERVAL, dma_interval); 
 | 
  
 | 
    spin_lock_init(&dev->lock); 
 | 
  
 | 
    timer_setup(&dev->dma_delay_timer, tw686x_dma_delay, 0); 
 | 
  
 | 
    /* 
 | 
     * 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 iounmap; 
 | 
    } 
 | 
  
 | 
    pci_set_drvdata(pci_dev, dev); 
 | 
    return 0; 
 | 
  
 | 
iounmap: 
 | 
    pci_iounmap(pci_dev, dev->mmio); 
 | 
free_region: 
 | 
    pci_release_regions(pci_dev); 
 | 
disable_pci: 
 | 
    pci_disable_device(pci_dev); 
 | 
free_audio: 
 | 
    kfree(dev->audio_channels); 
 | 
free_video: 
 | 
    kfree(dev->video_channels); 
 | 
free_dev: 
 | 
    kfree(dev); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static void tw686x_remove(struct pci_dev *pci_dev) 
 | 
{ 
 | 
    struct tw686x_dev *dev = pci_get_drvdata(pci_dev); 
 | 
    unsigned long flags; 
 | 
  
 | 
    /* This guarantees the IRQ handler is no longer running, 
 | 
     * which means we can kiss good-bye some resources. 
 | 
     */ 
 | 
    free_irq(pci_dev->irq, dev); 
 | 
  
 | 
    tw686x_video_free(dev); 
 | 
    tw686x_audio_free(dev); 
 | 
    del_timer_sync(&dev->dma_delay_timer); 
 | 
  
 | 
    pci_iounmap(pci_dev, dev->mmio); 
 | 
    pci_release_regions(pci_dev); 
 | 
    pci_disable_device(pci_dev); 
 | 
  
 | 
    /* 
 | 
     * 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 */ 
 | 
static const struct 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); 
 | 
  
 | 
static struct pci_driver tw686x_pci_driver = { 
 | 
    .name = "tw686x", 
 | 
    .id_table = tw686x_pci_tbl, 
 | 
    .probe = tw686x_probe, 
 | 
    .remove = tw686x_remove, 
 | 
}; 
 | 
module_pci_driver(tw686x_pci_driver); 
 | 
  
 | 
MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); 
 | 
MODULE_AUTHOR("Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>"); 
 | 
MODULE_AUTHOR("Krzysztof Ha?asa <khalasa@piap.pl>"); 
 | 
MODULE_LICENSE("GPL v2"); 
 |