diff options
author | Jonathan Corbet <corbet@lwn.net> | 2009-12-01 22:39:57 -0500 |
---|---|---|
committer | Jonathan Corbet <corbet@lwn.net> | 2010-05-07 19:16:02 -0400 |
commit | 7e0de022680f7899d33141f3ab5724a704f5669a (patch) | |
tree | 237054ee8d0676b9a327f0e3cdd1a6773cb3eb2d /drivers/video | |
parent | 24b4d82e4715841848a499534ed5cb7db3d6bca3 (diff) |
viafb: add a driver for GPIO lines
This is a simple gpiolib driver giving access to the GPIO lines in the
VIA framebuffer system. A simple mechanism exists for switching lines
between GPIO and I2C, but it's only compile-time for now.
Cc: ScottFang@viatech.com.cn
Cc: JosephChan@via.com.tw
Cc: Harald Welte <laforge@gnumonks.org>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 2 | ||||
-rw-r--r-- | drivers/video/via/Makefile | 5 | ||||
-rw-r--r-- | drivers/video/via/via-core.c | 7 | ||||
-rw-r--r-- | drivers/video/via/via-gpio.c | 268 | ||||
-rw-r--r-- | drivers/video/via/via-gpio.h | 15 |
5 files changed, 296 insertions, 1 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6e16244f3ed1..22c1662de964 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1511,6 +1511,7 @@ config FB_VIA | |||
1511 | select FB_CFB_IMAGEBLIT | 1511 | select FB_CFB_IMAGEBLIT |
1512 | select I2C_ALGOBIT | 1512 | select I2C_ALGOBIT |
1513 | select I2C | 1513 | select I2C |
1514 | select GPIOLIB | ||
1514 | help | 1515 | help |
1515 | This is the frame buffer device driver for Graphics chips of VIA | 1516 | This is the frame buffer device driver for Graphics chips of VIA |
1516 | UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ | 1517 | UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ |
@@ -1520,6 +1521,7 @@ config FB_VIA | |||
1520 | 1521 | ||
1521 | To compile this driver as a module, choose M here: the | 1522 | To compile this driver as a module, choose M here: the |
1522 | module will be called viafb. | 1523 | module will be called viafb. |
1524 | |||
1523 | config FB_NEOMAGIC | 1525 | config FB_NEOMAGIC |
1524 | tristate "NeoMagic display support" | 1526 | tristate "NeoMagic display support" |
1525 | depends on FB && PCI | 1527 | depends on FB && PCI |
diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile index aec3f8b24a96..8c42a42c3dbb 100644 --- a/drivers/video/via/Makefile +++ b/drivers/video/via/Makefile | |||
@@ -4,4 +4,7 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_FB_VIA) += viafb.o | 5 | obj-$(CONFIG_FB_VIA) += viafb.o |
6 | 6 | ||
7 | viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o via_utility.o vt1636.o global.o tblDPASetting.o viamode.o tbl1636.o via-core.o | 7 | viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \ |
8 | via_utility.o vt1636.o global.o tblDPASetting.o viamode.o tbl1636.o \ | ||
9 | via-core.o via-gpio.o | ||
10 | |||
diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index b77cd5c2fc96..806237e8b4a4 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c | |||
@@ -9,6 +9,7 @@ | |||
9 | */ | 9 | */ |
10 | #include "via-core.h" | 10 | #include "via-core.h" |
11 | #include "via_i2c.h" | 11 | #include "via_i2c.h" |
12 | #include "via-gpio.h" | ||
12 | #include "global.h" | 13 | #include "global.h" |
13 | 14 | ||
14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
@@ -221,6 +222,11 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, | |||
221 | ret = via_fb_pci_probe(&global_dev); | 222 | ret = via_fb_pci_probe(&global_dev); |
222 | if (ret) | 223 | if (ret) |
223 | goto out_i2c; | 224 | goto out_i2c; |
225 | /* | ||
226 | * Create the GPIOs. We continue whether or not this succeeds; | ||
227 | * the framebuffer might be useful even without GPIO ports. | ||
228 | */ | ||
229 | ret = viafb_create_gpios(&global_dev, adap_configs); | ||
224 | return 0; | 230 | return 0; |
225 | 231 | ||
226 | out_i2c: | 232 | out_i2c: |
@@ -234,6 +240,7 @@ out_disable: | |||
234 | 240 | ||
235 | static void __devexit via_pci_remove(struct pci_dev *pdev) | 241 | static void __devexit via_pci_remove(struct pci_dev *pdev) |
236 | { | 242 | { |
243 | viafb_destroy_gpios(); | ||
237 | viafb_delete_i2c_busses(); | 244 | viafb_delete_i2c_busses(); |
238 | via_fb_pci_remove(pdev); | 245 | via_fb_pci_remove(pdev); |
239 | via_pci_teardown_mmio(&global_dev); | 246 | via_pci_teardown_mmio(&global_dev); |
diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c new file mode 100644 index 000000000000..e119d2103730 --- /dev/null +++ b/drivers/video/via/via-gpio.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* | ||
2 | * Support for viafb GPIO ports. | ||
3 | * | ||
4 | * Copyright 2009 Jonathan Corbet <corbet@lwn.net> | ||
5 | * Distributable under version 2 of the GNU General Public License. | ||
6 | */ | ||
7 | |||
8 | #include <linux/spinlock.h> | ||
9 | #include <linux/gpio.h> | ||
10 | #include "via-core.h" | ||
11 | #include "via-gpio.h" | ||
12 | #include "global.h" | ||
13 | |||
14 | /* | ||
15 | * The ports we know about. Note that the port-25 gpios are not | ||
16 | * mentioned in the datasheet. | ||
17 | */ | ||
18 | |||
19 | struct viafb_gpio { | ||
20 | char *vg_name; /* Data sheet name */ | ||
21 | u16 vg_io_port; | ||
22 | u8 vg_port_index; | ||
23 | int vg_mask_shift; | ||
24 | }; | ||
25 | |||
26 | static struct viafb_gpio viafb_all_gpios[] = { | ||
27 | { | ||
28 | .vg_name = "VGPIO0", /* Guess - not in datasheet */ | ||
29 | .vg_io_port = VIASR, | ||
30 | .vg_port_index = 0x25, | ||
31 | .vg_mask_shift = 1 | ||
32 | }, | ||
33 | { | ||
34 | .vg_name = "VGPIO1", | ||
35 | .vg_io_port = VIASR, | ||
36 | .vg_port_index = 0x25, | ||
37 | .vg_mask_shift = 0 | ||
38 | }, | ||
39 | { | ||
40 | .vg_name = "VGPIO2", /* aka DISPCLKI0 */ | ||
41 | .vg_io_port = VIASR, | ||
42 | .vg_port_index = 0x2c, | ||
43 | .vg_mask_shift = 1 | ||
44 | }, | ||
45 | { | ||
46 | .vg_name = "VGPIO3", /* aka DISPCLKO0 */ | ||
47 | .vg_io_port = VIASR, | ||
48 | .vg_port_index = 0x2c, | ||
49 | .vg_mask_shift = 0 | ||
50 | }, | ||
51 | { | ||
52 | .vg_name = "VGPIO4", /* DISPCLKI1 */ | ||
53 | .vg_io_port = VIASR, | ||
54 | .vg_port_index = 0x3d, | ||
55 | .vg_mask_shift = 1 | ||
56 | }, | ||
57 | { | ||
58 | .vg_name = "VGPIO5", /* DISPCLKO1 */ | ||
59 | .vg_io_port = VIASR, | ||
60 | .vg_port_index = 0x3d, | ||
61 | .vg_mask_shift = 0 | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | #define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios) | ||
66 | |||
67 | /* | ||
68 | * This structure controls the active GPIOs, which may be a subset | ||
69 | * of those which are known. | ||
70 | */ | ||
71 | |||
72 | struct viafb_gpio_cfg { | ||
73 | struct gpio_chip gpio_chip; | ||
74 | struct viafb_dev *vdev; | ||
75 | struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; | ||
76 | char *gpio_names[VIAFB_NUM_GPIOS]; | ||
77 | }; | ||
78 | |||
79 | /* | ||
80 | * GPIO access functions | ||
81 | */ | ||
82 | static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, | ||
83 | int value) | ||
84 | { | ||
85 | struct viafb_gpio_cfg *cfg = container_of(chip, | ||
86 | struct viafb_gpio_cfg, | ||
87 | gpio_chip); | ||
88 | u8 reg; | ||
89 | struct viafb_gpio *gpio; | ||
90 | unsigned long flags; | ||
91 | |||
92 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
93 | gpio = cfg->active_gpios[nr]; | ||
94 | reg = viafb_read_reg(VIASR, gpio->vg_port_index); | ||
95 | reg |= 0x40 << gpio->vg_mask_shift; /* output enable */ | ||
96 | if (value) | ||
97 | reg |= 0x10 << gpio->vg_mask_shift; | ||
98 | else | ||
99 | reg &= ~(0x10 << gpio->vg_mask_shift); | ||
100 | viafb_write_reg(gpio->vg_port_index, VIASR, reg); | ||
101 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
102 | } | ||
103 | |||
104 | static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr, | ||
105 | int value) | ||
106 | { | ||
107 | via_gpio_set(chip, nr, value); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Set the input direction. I'm not sure this is right; we should | ||
113 | * be able to do input without disabling output. | ||
114 | */ | ||
115 | static int via_gpio_dir_input(struct gpio_chip *chip, unsigned int nr) | ||
116 | { | ||
117 | struct viafb_gpio_cfg *cfg = container_of(chip, | ||
118 | struct viafb_gpio_cfg, | ||
119 | gpio_chip); | ||
120 | struct viafb_gpio *gpio; | ||
121 | unsigned long flags; | ||
122 | |||
123 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
124 | gpio = cfg->active_gpios[nr]; | ||
125 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, | ||
126 | 0x40 << gpio->vg_mask_shift); | ||
127 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) | ||
132 | { | ||
133 | struct viafb_gpio_cfg *cfg = container_of(chip, | ||
134 | struct viafb_gpio_cfg, | ||
135 | gpio_chip); | ||
136 | u8 reg; | ||
137 | struct viafb_gpio *gpio; | ||
138 | unsigned long flags; | ||
139 | |||
140 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
141 | gpio = cfg->active_gpios[nr]; | ||
142 | reg = viafb_read_reg(VIASR, gpio->vg_port_index); | ||
143 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
144 | return reg & (0x04 << gpio->vg_mask_shift); | ||
145 | } | ||
146 | |||
147 | |||
148 | static struct viafb_gpio_cfg gpio_config = { | ||
149 | .gpio_chip = { | ||
150 | .label = "VIAFB onboard GPIO", | ||
151 | .owner = THIS_MODULE, | ||
152 | .direction_output = via_gpio_dir_out, | ||
153 | .set = via_gpio_set, | ||
154 | .direction_input = via_gpio_dir_input, | ||
155 | .get = via_gpio_get, | ||
156 | .base = -1, | ||
157 | .ngpio = 0, | ||
158 | .can_sleep = 0 | ||
159 | } | ||
160 | }; | ||
161 | |||
162 | /* | ||
163 | * Manage the software enable bit. | ||
164 | */ | ||
165 | static void viafb_gpio_enable(struct viafb_gpio *gpio) | ||
166 | { | ||
167 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0x02, 0x02); | ||
168 | } | ||
169 | |||
170 | static void viafb_gpio_disable(struct viafb_gpio *gpio) | ||
171 | { | ||
172 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, 0x02); | ||
173 | } | ||
174 | |||
175 | |||
176 | |||
177 | |||
178 | int viafb_create_gpios(struct viafb_dev *vdev, | ||
179 | const struct via_port_cfg *port_cfg) | ||
180 | { | ||
181 | int i, ngpio = 0, ret; | ||
182 | struct viafb_gpio *gpio; | ||
183 | unsigned long flags; | ||
184 | |||
185 | /* | ||
186 | * Set up entries for all GPIOs which have been configured to | ||
187 | * operate as such (as opposed to as i2c ports). | ||
188 | */ | ||
189 | for (i = 0; i < VIAFB_NUM_PORTS; i++) { | ||
190 | if (port_cfg[i].mode != VIA_MODE_GPIO) | ||
191 | continue; | ||
192 | for (gpio = viafb_all_gpios; | ||
193 | gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) | ||
194 | if (gpio->vg_port_index == port_cfg[i].ioport_index) { | ||
195 | gpio_config.active_gpios[ngpio] = gpio; | ||
196 | gpio_config.gpio_names[ngpio] = gpio->vg_name; | ||
197 | ngpio++; | ||
198 | } | ||
199 | } | ||
200 | gpio_config.gpio_chip.ngpio = ngpio; | ||
201 | gpio_config.gpio_chip.names = gpio_config.gpio_names; | ||
202 | gpio_config.vdev = vdev; | ||
203 | if (ngpio == 0) { | ||
204 | printk(KERN_INFO "viafb: no GPIOs configured\n"); | ||
205 | return 0; | ||
206 | } | ||
207 | /* | ||
208 | * Enable the ports. They come in pairs, with a single | ||
209 | * enable bit for both. | ||
210 | */ | ||
211 | spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
212 | for (i = 0; i < ngpio; i += 2) | ||
213 | viafb_gpio_enable(gpio_config.active_gpios[i]); | ||
214 | spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
215 | /* | ||
216 | * Get registered. | ||
217 | */ | ||
218 | gpio_config.gpio_chip.base = -1; /* Dynamic */ | ||
219 | ret = gpiochip_add(&gpio_config.gpio_chip); | ||
220 | if (ret) { | ||
221 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); | ||
222 | gpio_config.gpio_chip.ngpio = 0; | ||
223 | } | ||
224 | return ret; | ||
225 | /* Port enable ? */ | ||
226 | } | ||
227 | |||
228 | |||
229 | int viafb_destroy_gpios(void) | ||
230 | { | ||
231 | unsigned long flags; | ||
232 | int ret = 0, i; | ||
233 | |||
234 | spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
235 | /* | ||
236 | * Get unregistered. | ||
237 | */ | ||
238 | if (gpio_config.gpio_chip.ngpio > 0) { | ||
239 | ret = gpiochip_remove(&gpio_config.gpio_chip); | ||
240 | if (ret) { /* Somebody still using it? */ | ||
241 | printk(KERN_ERR "Viafb: GPIO remove failed\n"); | ||
242 | goto out; | ||
243 | } | ||
244 | } | ||
245 | /* | ||
246 | * Disable the ports. | ||
247 | */ | ||
248 | for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) | ||
249 | viafb_gpio_disable(gpio_config.active_gpios[i]); | ||
250 | gpio_config.gpio_chip.ngpio = 0; | ||
251 | out: | ||
252 | spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Look up a specific gpio and return the number it was assigned. | ||
258 | */ | ||
259 | int viafb_gpio_lookup(const char *name) | ||
260 | { | ||
261 | int i; | ||
262 | |||
263 | for (i = 0; i < gpio_config.gpio_chip.ngpio; i++) | ||
264 | if (!strcmp(name, gpio_config.active_gpios[i]->vg_name)) | ||
265 | return gpio_config.gpio_chip.base + i; | ||
266 | return -1; | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(viafb_gpio_lookup); | ||
diff --git a/drivers/video/via/via-gpio.h b/drivers/video/via/via-gpio.h new file mode 100644 index 000000000000..7b53f966eb4b --- /dev/null +++ b/drivers/video/via/via-gpio.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Support for viafb GPIO ports. | ||
3 | * | ||
4 | * Copyright 2009 Jonathan Corbet <corbet@lwn.net> | ||
5 | * Distributable under version 2 of the GNU General Public License. | ||
6 | */ | ||
7 | |||
8 | #ifndef __VIA_GPIO_H__ | ||
9 | #define __VIA_GPIO_H__ | ||
10 | |||
11 | extern int viafb_create_gpios(struct viafb_dev *vdev, | ||
12 | const struct via_port_cfg *port_cfg); | ||
13 | extern int viafb_destroy_gpios(void); | ||
14 | extern int viafb_gpio_lookup(const char *name); | ||
15 | #endif | ||