Commit | Line | Data |
---|---|---|
0ff59f31 KH |
1 | /* |
2 | * Copyright (C) 2015 Industrial Research Institute for Automation | |
3 | * and Measurements PIAP | |
4 | * | |
5 | * Written by Krzysztof Ha?asa. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of version 2 of the GNU General Public License | |
9 | * as published by the Free Software Foundation. | |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/slab.h> | |
17 | #include "tw686x-kh.h" | |
18 | #include "tw686x-kh-regs.h" | |
19 | ||
20 | static irqreturn_t tw686x_irq(int irq, void *dev_id) | |
21 | { | |
22 | struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; | |
23 | u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */ | |
24 | unsigned long flags; | |
25 | unsigned int handled = 0; | |
26 | ||
27 | if (int_status) { | |
28 | spin_lock_irqsave(&dev->irq_lock, flags); | |
29 | dev->dma_requests |= int_status; | |
30 | spin_unlock_irqrestore(&dev->irq_lock, flags); | |
31 | ||
32 | if (int_status & 0xFF0000FF) | |
e3a900a8 | 33 | handled = tw686x_kh_video_irq(dev); |
0ff59f31 KH |
34 | } |
35 | ||
36 | return IRQ_RETVAL(handled); | |
37 | } | |
38 | ||
39 | static int tw686x_probe(struct pci_dev *pci_dev, | |
40 | const struct pci_device_id *pci_id) | |
41 | { | |
42 | struct tw686x_dev *dev; | |
43 | int err; | |
44 | ||
45 | dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev) + | |
46 | (pci_id->driver_data & TYPE_MAX_CHANNELS) * | |
47 | sizeof(dev->video_channels[0]), GFP_KERNEL); | |
48 | if (!dev) | |
49 | return -ENOMEM; | |
50 | ||
51 | sprintf(dev->name, "TW%04X", pci_dev->device); | |
52 | dev->type = pci_id->driver_data; | |
53 | ||
54 | pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, | |
55 | pci_name(pci_dev), pci_dev->irq, | |
56 | (unsigned long)pci_resource_start(pci_dev, 0)); | |
57 | ||
58 | dev->pci_dev = pci_dev; | |
59 | if (pcim_enable_device(pci_dev)) | |
60 | return -EIO; | |
61 | ||
62 | pci_set_master(pci_dev); | |
63 | ||
64 | if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { | |
65 | pr_err("%s: 32-bit PCI DMA not supported\n", dev->name); | |
66 | return -EIO; | |
67 | } | |
68 | ||
69 | err = pci_request_regions(pci_dev, dev->name); | |
70 | if (err < 0) { | |
71 | pr_err("%s: Unable to get MMIO region\n", dev->name); | |
72 | return err; | |
73 | } | |
74 | ||
75 | dev->mmio = pci_ioremap_bar(pci_dev, 0); | |
76 | if (!dev->mmio) { | |
77 | pr_err("%s: Unable to remap MMIO region\n", dev->name); | |
78 | return -EIO; | |
79 | } | |
80 | ||
81 | reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */ | |
82 | mdelay(1); | |
83 | ||
84 | reg_write(dev, SRST[0], 0x3F); | |
85 | if (max_channels(dev) > 4) | |
86 | reg_write(dev, SRST[1], 0x3F); | |
87 | reg_write(dev, DMA_CMD, 0); | |
88 | reg_write(dev, DMA_CHANNEL_ENABLE, 0); | |
89 | reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0); | |
90 | reg_write(dev, DMA_TIMER_INTERVAL, 0x38000); | |
91 | reg_write(dev, DMA_CONFIG, 0xFFFFFF04); | |
92 | ||
93 | spin_lock_init(&dev->irq_lock); | |
94 | ||
95 | err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq, | |
96 | IRQF_SHARED, dev->name, dev); | |
97 | if (err < 0) { | |
98 | pr_err("%s: Unable to get IRQ\n", dev->name); | |
99 | return err; | |
100 | } | |
101 | ||
e3a900a8 | 102 | err = tw686x_kh_video_init(dev); |
0ff59f31 KH |
103 | if (err) |
104 | return err; | |
105 | ||
106 | pci_set_drvdata(pci_dev, dev); | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static void tw686x_remove(struct pci_dev *pci_dev) | |
111 | { | |
112 | struct tw686x_dev *dev = pci_get_drvdata(pci_dev); | |
113 | ||
e3a900a8 | 114 | tw686x_kh_video_free(dev); |
0ff59f31 KH |
115 | } |
116 | ||
117 | /* driver_data is number of A/V channels */ | |
118 | static const struct pci_device_id tw686x_pci_tbl[] = { | |
119 | {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4}, | |
120 | /* not tested */ | |
121 | {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN}, | |
122 | /* TW6868 supports 8 A/V channels with an external TW2865 chip - | |
123 | not supported by the driver */ | |
124 | {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */ | |
125 | {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN}, | |
126 | {} | |
127 | }; | |
128 | ||
129 | static struct pci_driver tw686x_pci_driver = { | |
130 | .name = "tw686x-kh", | |
131 | .id_table = tw686x_pci_tbl, | |
132 | .probe = tw686x_probe, | |
133 | .remove = tw686x_remove, | |
134 | }; | |
135 | ||
136 | MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); | |
137 | MODULE_AUTHOR("Krzysztof Halasa"); | |
138 | MODULE_LICENSE("GPL v2"); | |
139 | MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); | |
140 | module_pci_driver(tw686x_pci_driver); |