| /* |
| * Copyright (C) 2015 Industrial Research Institute for Automation |
| * and Measurements PIAP |
| * |
| * Written by Krzysztof Ha?asa. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of version 2 of the GNU General Public License |
| * as published by the Free Software Foundation. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include "tw686x-kh.h" |
| #include "tw686x-kh-regs.h" |
| |
| static irqreturn_t tw686x_irq(int irq, void *dev_id) |
| { |
| struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; |
| u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */ |
| unsigned long flags; |
| unsigned int handled = 0; |
| |
| if (int_status) { |
| spin_lock_irqsave(&dev->irq_lock, flags); |
| dev->dma_requests |= int_status; |
| spin_unlock_irqrestore(&dev->irq_lock, flags); |
| |
| if (int_status & 0xFF0000FF) |
| handled = tw686x_kh_video_irq(dev); |
| } |
| |
| return IRQ_RETVAL(handled); |
| } |
| |
| static int tw686x_probe(struct pci_dev *pci_dev, |
| const struct pci_device_id *pci_id) |
| { |
| struct tw686x_dev *dev; |
| int err; |
| |
| dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev) + |
| (pci_id->driver_data & TYPE_MAX_CHANNELS) * |
| sizeof(dev->video_channels[0]), GFP_KERNEL); |
| if (!dev) |
| return -ENOMEM; |
| |
| sprintf(dev->name, "TW%04X", pci_dev->device); |
| dev->type = pci_id->driver_data; |
| |
| pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, |
| pci_name(pci_dev), pci_dev->irq, |
| (unsigned long)pci_resource_start(pci_dev, 0)); |
| |
| dev->pci_dev = pci_dev; |
| if (pcim_enable_device(pci_dev)) |
| return -EIO; |
| |
| pci_set_master(pci_dev); |
| |
| if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { |
| pr_err("%s: 32-bit PCI DMA not supported\n", dev->name); |
| return -EIO; |
| } |
| |
| err = pci_request_regions(pci_dev, dev->name); |
| if (err < 0) { |
| pr_err("%s: Unable to get MMIO region\n", dev->name); |
| return err; |
| } |
| |
| dev->mmio = pci_ioremap_bar(pci_dev, 0); |
| if (!dev->mmio) { |
| pr_err("%s: Unable to remap MMIO region\n", dev->name); |
| return -EIO; |
| } |
| |
| reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */ |
| mdelay(1); |
| |
| reg_write(dev, SRST[0], 0x3F); |
| if (max_channels(dev) > 4) |
| reg_write(dev, SRST[1], 0x3F); |
| reg_write(dev, DMA_CMD, 0); |
| reg_write(dev, DMA_CHANNEL_ENABLE, 0); |
| reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0); |
| reg_write(dev, DMA_TIMER_INTERVAL, 0x38000); |
| reg_write(dev, DMA_CONFIG, 0xFFFFFF04); |
| |
| spin_lock_init(&dev->irq_lock); |
| |
| err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq, |
| IRQF_SHARED, dev->name, dev); |
| if (err < 0) { |
| pr_err("%s: Unable to get IRQ\n", dev->name); |
| return err; |
| } |
| |
| err = tw686x_kh_video_init(dev); |
| if (err) |
| return err; |
| |
| pci_set_drvdata(pci_dev, dev); |
| return 0; |
| } |
| |
| static void tw686x_remove(struct pci_dev *pci_dev) |
| { |
| struct tw686x_dev *dev = pci_get_drvdata(pci_dev); |
| |
| tw686x_kh_video_free(dev); |
| } |
| |
| /* driver_data is number of A/V channels */ |
| static const struct pci_device_id tw686x_pci_tbl[] = { |
| {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4}, |
| /* not tested */ |
| {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN}, |
| /* TW6868 supports 8 A/V channels with an external TW2865 chip - |
| not supported by the driver */ |
| {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */ |
| {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN}, |
| {} |
| }; |
| |
| static struct pci_driver tw686x_pci_driver = { |
| .name = "tw686x-kh", |
| .id_table = tw686x_pci_tbl, |
| .probe = tw686x_probe, |
| .remove = tw686x_remove, |
| }; |
| |
| MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); |
| MODULE_AUTHOR("Krzysztof Halasa"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); |
| module_pci_driver(tw686x_pci_driver); |