Commit | Line | Data |
---|---|---|
ff1d5c2f MB |
1 | /* |
2 | ||
3 | bt8xx GPIO abuser | |
4 | ||
5 | Copyright (C) 2008 Michael Buesch <mb@bu3sch.de> | |
6 | ||
7 | Please do _only_ contact the people listed _above_ with issues related to this driver. | |
8 | All the other people listed below are not related to this driver. Their names | |
9 | are only here, because this driver is derived from the bt848 driver. | |
10 | ||
11 | ||
12 | Derived from the bt848 driver: | |
13 | ||
14 | Copyright (C) 1996,97,98 Ralph Metzler | |
15 | & Marcus Metzler | |
16 | (c) 1999-2002 Gerd Knorr | |
17 | ||
18 | some v4l2 code lines are taken from Justin's bttv2 driver which is | |
19 | (c) 2000 Justin Schoeman | |
20 | ||
21 | V4L1 removal from: | |
22 | (c) 2005-2006 Nickolay V. Shmyrev | |
23 | ||
24 | Fixes to be fully V4L2 compliant by | |
25 | (c) 2006 Mauro Carvalho Chehab | |
26 | ||
27 | Cropping and overscan support | |
28 | Copyright (C) 2005, 2006 Michael H. Schimek | |
29 | Sponsored by OPQ Systems AB | |
30 | ||
31 | This program is free software; you can redistribute it and/or modify | |
32 | it under the terms of the GNU General Public License as published by | |
33 | the Free Software Foundation; either version 2 of the License, or | |
34 | (at your option) any later version. | |
35 | ||
36 | This program is distributed in the hope that it will be useful, | |
37 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
39 | GNU General Public License for more details. | |
40 | ||
41 | You should have received a copy of the GNU General Public License | |
42 | along with this program; if not, write to the Free Software | |
43 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
44 | */ | |
45 | ||
46 | #include <linux/module.h> | |
47 | #include <linux/pci.h> | |
48 | #include <linux/spinlock.h> | |
d120c17f | 49 | #include <linux/gpio.h> |
ff1d5c2f MB |
50 | |
51 | /* Steal the hardware definitions from the bttv driver. */ | |
52 | #include "../media/video/bt8xx/bt848.h" | |
53 | ||
54 | ||
55 | #define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ | |
56 | ||
57 | ||
58 | struct bt8xxgpio { | |
59 | spinlock_t lock; | |
60 | ||
61 | void __iomem *mmio; | |
62 | struct pci_dev *pdev; | |
63 | struct gpio_chip gpio; | |
64 | ||
65 | #ifdef CONFIG_PM | |
66 | u32 saved_outen; | |
67 | u32 saved_data; | |
68 | #endif | |
69 | }; | |
70 | ||
71 | #define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) | |
72 | #define bgread(adr) readl(bg->mmio+(adr)) | |
73 | ||
74 | ||
75 | static int modparam_gpiobase = -1/* dynamic */; | |
76 | module_param_named(gpiobase, modparam_gpiobase, int, 0444); | |
77 | MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); | |
78 | ||
79 | ||
80 | static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | |
81 | { | |
82 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | |
83 | unsigned long flags; | |
84 | u32 outen, data; | |
85 | ||
86 | spin_lock_irqsave(&bg->lock, flags); | |
87 | ||
88 | data = bgread(BT848_GPIO_DATA); | |
89 | data &= ~(1 << nr); | |
90 | bgwrite(data, BT848_GPIO_DATA); | |
91 | ||
92 | outen = bgread(BT848_GPIO_OUT_EN); | |
93 | outen &= ~(1 << nr); | |
94 | bgwrite(outen, BT848_GPIO_OUT_EN); | |
95 | ||
96 | spin_unlock_irqrestore(&bg->lock, flags); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | |
102 | { | |
103 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | |
104 | unsigned long flags; | |
105 | u32 val; | |
106 | ||
107 | spin_lock_irqsave(&bg->lock, flags); | |
108 | val = bgread(BT848_GPIO_DATA); | |
109 | spin_unlock_irqrestore(&bg->lock, flags); | |
110 | ||
111 | return !!(val & (1 << nr)); | |
112 | } | |
113 | ||
114 | static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, | |
115 | unsigned nr, int val) | |
116 | { | |
117 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | |
118 | unsigned long flags; | |
119 | u32 outen, data; | |
120 | ||
121 | spin_lock_irqsave(&bg->lock, flags); | |
122 | ||
123 | outen = bgread(BT848_GPIO_OUT_EN); | |
124 | outen |= (1 << nr); | |
125 | bgwrite(outen, BT848_GPIO_OUT_EN); | |
126 | ||
127 | data = bgread(BT848_GPIO_DATA); | |
128 | if (val) | |
129 | data |= (1 << nr); | |
130 | else | |
131 | data &= ~(1 << nr); | |
132 | bgwrite(data, BT848_GPIO_DATA); | |
133 | ||
134 | spin_unlock_irqrestore(&bg->lock, flags); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, | |
140 | unsigned nr, int val) | |
141 | { | |
142 | struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); | |
143 | unsigned long flags; | |
144 | u32 data; | |
145 | ||
146 | spin_lock_irqsave(&bg->lock, flags); | |
147 | ||
148 | data = bgread(BT848_GPIO_DATA); | |
149 | if (val) | |
150 | data |= (1 << nr); | |
151 | else | |
152 | data &= ~(1 << nr); | |
153 | bgwrite(data, BT848_GPIO_DATA); | |
154 | ||
155 | spin_unlock_irqrestore(&bg->lock, flags); | |
156 | } | |
157 | ||
158 | static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) | |
159 | { | |
160 | struct gpio_chip *c = &bg->gpio; | |
161 | ||
3e274bd0 | 162 | c->label = dev_name(&bg->pdev->dev); |
ff1d5c2f MB |
163 | c->owner = THIS_MODULE; |
164 | c->direction_input = bt8xxgpio_gpio_direction_input; | |
165 | c->get = bt8xxgpio_gpio_get; | |
166 | c->direction_output = bt8xxgpio_gpio_direction_output; | |
167 | c->set = bt8xxgpio_gpio_set; | |
168 | c->dbg_show = NULL; | |
169 | c->base = modparam_gpiobase; | |
170 | c->ngpio = BT8XXGPIO_NR_GPIOS; | |
171 | c->can_sleep = 0; | |
172 | } | |
173 | ||
174 | static int bt8xxgpio_probe(struct pci_dev *dev, | |
175 | const struct pci_device_id *pci_id) | |
176 | { | |
177 | struct bt8xxgpio *bg; | |
178 | int err; | |
179 | ||
180 | bg = kzalloc(sizeof(*bg), GFP_KERNEL); | |
181 | if (!bg) | |
182 | return -ENOMEM; | |
183 | ||
184 | bg->pdev = dev; | |
185 | spin_lock_init(&bg->lock); | |
186 | ||
187 | err = pci_enable_device(dev); | |
188 | if (err) { | |
189 | printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); | |
190 | goto err_freebg; | |
191 | } | |
192 | if (!request_mem_region(pci_resource_start(dev, 0), | |
193 | pci_resource_len(dev, 0), | |
194 | "bt8xxgpio")) { | |
195 | printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", | |
196 | (unsigned long long)pci_resource_start(dev, 0)); | |
197 | err = -EBUSY; | |
198 | goto err_disable; | |
199 | } | |
200 | pci_set_master(dev); | |
201 | pci_set_drvdata(dev, bg); | |
202 | ||
203 | bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); | |
204 | if (!bg->mmio) { | |
205 | printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); | |
206 | err = -EIO; | |
207 | goto err_release_mem; | |
208 | } | |
209 | ||
210 | /* Disable interrupts */ | |
211 | bgwrite(0, BT848_INT_MASK); | |
212 | ||
213 | /* gpio init */ | |
214 | bgwrite(0, BT848_GPIO_DMA_CTL); | |
215 | bgwrite(0, BT848_GPIO_REG_INP); | |
216 | bgwrite(0, BT848_GPIO_OUT_EN); | |
217 | ||
218 | bt8xxgpio_gpio_setup(bg); | |
219 | err = gpiochip_add(&bg->gpio); | |
220 | if (err) { | |
221 | printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); | |
222 | goto err_release_mem; | |
223 | } | |
224 | ||
225 | printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", | |
226 | bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); | |
227 | ||
228 | return 0; | |
229 | ||
230 | err_release_mem: | |
231 | release_mem_region(pci_resource_start(dev, 0), | |
232 | pci_resource_len(dev, 0)); | |
233 | pci_set_drvdata(dev, NULL); | |
234 | err_disable: | |
235 | pci_disable_device(dev); | |
236 | err_freebg: | |
237 | kfree(bg); | |
238 | ||
239 | return err; | |
240 | } | |
241 | ||
242 | static void bt8xxgpio_remove(struct pci_dev *pdev) | |
243 | { | |
244 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
245 | ||
246 | gpiochip_remove(&bg->gpio); | |
247 | ||
248 | bgwrite(0, BT848_INT_MASK); | |
249 | bgwrite(~0x0, BT848_INT_STAT); | |
250 | bgwrite(0x0, BT848_GPIO_OUT_EN); | |
251 | ||
252 | iounmap(bg->mmio); | |
253 | release_mem_region(pci_resource_start(pdev, 0), | |
254 | pci_resource_len(pdev, 0)); | |
255 | pci_disable_device(pdev); | |
256 | ||
257 | pci_set_drvdata(pdev, NULL); | |
258 | kfree(bg); | |
259 | } | |
260 | ||
261 | #ifdef CONFIG_PM | |
262 | static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) | |
263 | { | |
264 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
265 | unsigned long flags; | |
266 | ||
267 | spin_lock_irqsave(&bg->lock, flags); | |
268 | ||
269 | bg->saved_outen = bgread(BT848_GPIO_OUT_EN); | |
270 | bg->saved_data = bgread(BT848_GPIO_DATA); | |
271 | ||
272 | bgwrite(0, BT848_INT_MASK); | |
273 | bgwrite(~0x0, BT848_INT_STAT); | |
274 | bgwrite(0x0, BT848_GPIO_OUT_EN); | |
275 | ||
276 | spin_unlock_irqrestore(&bg->lock, flags); | |
277 | ||
278 | pci_save_state(pdev); | |
279 | pci_disable_device(pdev); | |
280 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int bt8xxgpio_resume(struct pci_dev *pdev) | |
286 | { | |
287 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
288 | unsigned long flags; | |
289 | int err; | |
290 | ||
291 | pci_set_power_state(pdev, 0); | |
292 | err = pci_enable_device(pdev); | |
293 | if (err) | |
294 | return err; | |
295 | pci_restore_state(pdev); | |
296 | ||
297 | spin_lock_irqsave(&bg->lock, flags); | |
298 | ||
299 | bgwrite(0, BT848_INT_MASK); | |
300 | bgwrite(0, BT848_GPIO_DMA_CTL); | |
301 | bgwrite(0, BT848_GPIO_REG_INP); | |
302 | bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); | |
303 | bgwrite(bg->saved_data & bg->saved_outen, | |
304 | BT848_GPIO_DATA); | |
305 | ||
306 | spin_unlock_irqrestore(&bg->lock, flags); | |
307 | ||
308 | return 0; | |
309 | } | |
310 | #else | |
311 | #define bt8xxgpio_suspend NULL | |
312 | #define bt8xxgpio_resume NULL | |
313 | #endif /* CONFIG_PM */ | |
314 | ||
315 | static struct pci_device_id bt8xxgpio_pci_tbl[] = { | |
316 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, | |
317 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, | |
318 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, | |
319 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, | |
320 | { 0, }, | |
321 | }; | |
322 | MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); | |
323 | ||
324 | static struct pci_driver bt8xxgpio_pci_driver = { | |
325 | .name = "bt8xxgpio", | |
326 | .id_table = bt8xxgpio_pci_tbl, | |
327 | .probe = bt8xxgpio_probe, | |
328 | .remove = bt8xxgpio_remove, | |
329 | .suspend = bt8xxgpio_suspend, | |
330 | .resume = bt8xxgpio_resume, | |
331 | }; | |
332 | ||
59f6a6c6 | 333 | static int __init bt8xxgpio_init(void) |
ff1d5c2f MB |
334 | { |
335 | return pci_register_driver(&bt8xxgpio_pci_driver); | |
336 | } | |
337 | module_init(bt8xxgpio_init) | |
338 | ||
59f6a6c6 | 339 | static void __exit bt8xxgpio_exit(void) |
ff1d5c2f MB |
340 | { |
341 | pci_unregister_driver(&bt8xxgpio_pci_driver); | |
342 | } | |
343 | module_exit(bt8xxgpio_exit) | |
344 | ||
345 | MODULE_LICENSE("GPL"); | |
346 | MODULE_AUTHOR("Michael Buesch"); | |
347 | MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); |