diff options
Diffstat (limited to 'drivers/video/intelfb')
-rw-r--r-- | drivers/video/intelfb/Makefile | 4 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfb.h | 79 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfb_i2c.c | 200 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbdrv.c | 64 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbhw.c | 136 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbhw.h | 25 |
6 files changed, 505 insertions, 3 deletions
diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile index 722d21d6e5cd..6c782d3ae1be 100644 --- a/drivers/video/intelfb/Makefile +++ b/drivers/video/intelfb/Makefile | |||
@@ -1,6 +1,8 @@ | |||
1 | obj-$(CONFIG_FB_INTEL) += intelfb.o | 1 | obj-$(CONFIG_FB_INTEL) += intelfb.o |
2 | 2 | ||
3 | intelfb-objs := intelfbdrv.o intelfbhw.o | 3 | intelfb-y := intelfbdrv.o intelfbhw.o |
4 | intelfb-$(CONFIG_FB_INTEL_I2C) += intelfb_i2c.o | ||
5 | intelfb-objs := $(intelfb-y) | ||
4 | 6 | ||
5 | ifdef CONFIG_FB_INTEL_DEBUG | 7 | ifdef CONFIG_FB_INTEL_DEBUG |
6 | #EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP | 8 | #EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP |
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index e290d7460e1b..80b94c19a9fa 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h | |||
@@ -6,6 +6,10 @@ | |||
6 | #include <linux/agp_backend.h> | 6 | #include <linux/agp_backend.h> |
7 | #include <linux/fb.h> | 7 | #include <linux/fb.h> |
8 | 8 | ||
9 | #ifdef CONFIG_FB_INTEL_I2C | ||
10 | #include <linux/i2c.h> | ||
11 | #include <linux/i2c-algo-bit.h> | ||
12 | #endif | ||
9 | 13 | ||
10 | /*** Version/name ***/ | 14 | /*** Version/name ***/ |
11 | #define INTELFB_VERSION "0.9.4" | 15 | #define INTELFB_VERSION "0.9.4" |
@@ -115,6 +119,29 @@ | |||
115 | /* Intel agpgart driver */ | 119 | /* Intel agpgart driver */ |
116 | #define AGP_PHYSICAL_MEMORY 2 | 120 | #define AGP_PHYSICAL_MEMORY 2 |
117 | 121 | ||
122 | /* store information about an Ixxx DVO */ | ||
123 | /* The i830->i865 use multiple DVOs with multiple i2cs */ | ||
124 | /* the i915, i945 have a single sDVO i2c bus - which is different */ | ||
125 | #define MAX_OUTPUTS 6 | ||
126 | |||
127 | /* these are outputs from the chip - integrated only | ||
128 | external chips are via DVO or SDVO output */ | ||
129 | #define INTELFB_OUTPUT_UNUSED 0 | ||
130 | #define INTELFB_OUTPUT_ANALOG 1 | ||
131 | #define INTELFB_OUTPUT_DVO 2 | ||
132 | #define INTELFB_OUTPUT_SDVO 3 | ||
133 | #define INTELFB_OUTPUT_LVDS 4 | ||
134 | #define INTELFB_OUTPUT_TVOUT 5 | ||
135 | |||
136 | #define INTELFB_DVO_CHIP_NONE 0 | ||
137 | #define INTELFB_DVO_CHIP_LVDS 1 | ||
138 | #define INTELFB_DVO_CHIP_TMDS 2 | ||
139 | #define INTELFB_DVO_CHIP_TVOUT 4 | ||
140 | |||
141 | #define INTELFB_OUTPUT_PIPE_NC 0 | ||
142 | #define INTELFB_OUTPUT_PIPE_A 1 | ||
143 | #define INTELFB_OUTPUT_PIPE_B 2 | ||
144 | |||
118 | /*** Data Types ***/ | 145 | /*** Data Types ***/ |
119 | 146 | ||
120 | /* supported chipsets */ | 147 | /* supported chipsets */ |
@@ -195,6 +222,10 @@ struct intelfb_hwstate { | |||
195 | u32 mem_mode; | 222 | u32 mem_mode; |
196 | u32 fw_blc_0; | 223 | u32 fw_blc_0; |
197 | u32 fw_blc_1; | 224 | u32 fw_blc_1; |
225 | u16 hwstam; | ||
226 | u16 ier; | ||
227 | u16 iir; | ||
228 | u16 imr; | ||
198 | }; | 229 | }; |
199 | 230 | ||
200 | struct intelfb_heap_data { | 231 | struct intelfb_heap_data { |
@@ -204,6 +235,33 @@ struct intelfb_heap_data { | |||
204 | u32 size; // in bytes | 235 | u32 size; // in bytes |
205 | }; | 236 | }; |
206 | 237 | ||
238 | #ifdef CONFIG_FB_INTEL_I2C | ||
239 | struct intelfb_i2c_chan { | ||
240 | struct intelfb_info *dinfo; | ||
241 | u32 reg; | ||
242 | struct i2c_adapter adapter; | ||
243 | struct i2c_algo_bit_data algo; | ||
244 | }; | ||
245 | #endif | ||
246 | |||
247 | struct intelfb_output_rec { | ||
248 | int type; | ||
249 | int pipe; | ||
250 | int flags; | ||
251 | |||
252 | #ifdef CONFIG_FB_INTEL_I2C | ||
253 | struct intelfb_i2c_chan i2c_bus; | ||
254 | struct intelfb_i2c_chan ddc_bus; | ||
255 | #endif | ||
256 | }; | ||
257 | |||
258 | struct intelfb_vsync { | ||
259 | wait_queue_head_t wait; | ||
260 | unsigned int count; | ||
261 | int pan_display; | ||
262 | u32 pan_offset; | ||
263 | }; | ||
264 | |||
207 | struct intelfb_info { | 265 | struct intelfb_info { |
208 | struct fb_info *info; | 266 | struct fb_info *info; |
209 | struct fb_ops *fbops; | 267 | struct fb_ops *fbops; |
@@ -220,7 +278,7 @@ struct intelfb_info { | |||
220 | u8 fbmem_gart; | 278 | u8 fbmem_gart; |
221 | 279 | ||
222 | /* mtrr support */ | 280 | /* mtrr support */ |
223 | u32 mtrr_reg; | 281 | int mtrr_reg; |
224 | u32 has_mtrr; | 282 | u32 has_mtrr; |
225 | 283 | ||
226 | /* heap data */ | 284 | /* heap data */ |
@@ -267,6 +325,12 @@ struct intelfb_info { | |||
267 | int fixed_mode; | 325 | int fixed_mode; |
268 | int ring_active; | 326 | int ring_active; |
269 | int flag; | 327 | int flag; |
328 | unsigned long irq_flags; | ||
329 | int open; | ||
330 | |||
331 | /* vsync */ | ||
332 | struct intelfb_vsync vsync; | ||
333 | spinlock_t int_lock; | ||
270 | 334 | ||
271 | /* hw cursor */ | 335 | /* hw cursor */ |
272 | int cursor_on; | 336 | int cursor_on; |
@@ -285,12 +349,25 @@ struct intelfb_info { | |||
285 | 349 | ||
286 | /* index into plls */ | 350 | /* index into plls */ |
287 | int pll_index; | 351 | int pll_index; |
352 | |||
353 | /* outputs */ | ||
354 | int num_outputs; | ||
355 | struct intelfb_output_rec output[MAX_OUTPUTS]; | ||
288 | }; | 356 | }; |
289 | 357 | ||
290 | #define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM)) | 358 | #define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM)) |
291 | 359 | ||
360 | #ifndef FBIO_WAITFORVSYNC | ||
361 | #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) | ||
362 | #endif | ||
363 | |||
292 | /*** function prototypes ***/ | 364 | /*** function prototypes ***/ |
293 | 365 | ||
294 | extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var); | 366 | extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var); |
295 | 367 | ||
368 | #ifdef CONFIG_FB_INTEL_I2C | ||
369 | extern void intelfb_create_i2c_busses(struct intelfb_info *dinfo); | ||
370 | extern void intelfb_delete_i2c_busses(struct intelfb_info *dinfo); | ||
371 | #endif | ||
372 | |||
296 | #endif /* _INTELFB_H */ | 373 | #endif /* _INTELFB_H */ |
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c new file mode 100644 index 000000000000..c1113d6e941d --- /dev/null +++ b/drivers/video/intelfb/intelfb_i2c.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /************************************************************************** | ||
2 | |||
3 | Copyright 2006 Dave Airlie <airlied@linux.ie> | ||
4 | |||
5 | All Rights Reserved. | ||
6 | |||
7 | Permission is hereby granted, free of charge, to any person obtaining a | ||
8 | copy of this software and associated documentation files (the "Software"), | ||
9 | to deal in the Software without restriction, including without limitation | ||
10 | on the rights to use, copy, modify, merge, publish, distribute, sub | ||
11 | license, and/or sell copies of the Software, and to permit persons to whom | ||
12 | the Software is furnished to do so, subject to the following conditions: | ||
13 | |||
14 | The above copyright notice and this permission notice (including the next | ||
15 | paragraph) shall be included in all copies or substantial portions of the | ||
16 | Software. | ||
17 | |||
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
21 | THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
22 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
24 | USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | |||
26 | **************************************************************************/ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/fb.h> | ||
35 | |||
36 | #include <linux/i2c.h> | ||
37 | #include <linux/i2c-id.h> | ||
38 | #include <linux/i2c-algo-bit.h> | ||
39 | |||
40 | #include <asm/io.h> | ||
41 | |||
42 | #include "intelfb.h" | ||
43 | #include "intelfbhw.h" | ||
44 | |||
45 | /* bit locations in the registers */ | ||
46 | #define SCL_DIR_MASK 0x0001 | ||
47 | #define SCL_DIR 0x0002 | ||
48 | #define SCL_VAL_MASK 0x0004 | ||
49 | #define SCL_VAL_OUT 0x0008 | ||
50 | #define SCL_VAL_IN 0x0010 | ||
51 | #define SDA_DIR_MASK 0x0100 | ||
52 | #define SDA_DIR 0x0200 | ||
53 | #define SDA_VAL_MASK 0x0400 | ||
54 | #define SDA_VAL_OUT 0x0800 | ||
55 | #define SDA_VAL_IN 0x1000 | ||
56 | |||
57 | static void intelfb_gpio_setscl(void *data, int state) | ||
58 | { | ||
59 | struct intelfb_i2c_chan *chan = data; | ||
60 | struct intelfb_info *dinfo = chan->dinfo; | ||
61 | u32 val; | ||
62 | |||
63 | OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); | ||
64 | val = INREG(chan->reg); | ||
65 | } | ||
66 | |||
67 | static void intelfb_gpio_setsda(void *data, int state) | ||
68 | { | ||
69 | struct intelfb_i2c_chan *chan = data; | ||
70 | struct intelfb_info *dinfo = chan->dinfo; | ||
71 | u32 val; | ||
72 | |||
73 | OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); | ||
74 | val = INREG(chan->reg); | ||
75 | } | ||
76 | |||
77 | static int intelfb_gpio_getscl(void *data) | ||
78 | { | ||
79 | struct intelfb_i2c_chan *chan = data; | ||
80 | struct intelfb_info *dinfo = chan->dinfo; | ||
81 | u32 val; | ||
82 | |||
83 | OUTREG(chan->reg, SCL_DIR_MASK); | ||
84 | OUTREG(chan->reg, 0); | ||
85 | val = INREG(chan->reg); | ||
86 | return ((val & SCL_VAL_IN) != 0); | ||
87 | } | ||
88 | |||
89 | static int intelfb_gpio_getsda(void *data) | ||
90 | { | ||
91 | struct intelfb_i2c_chan *chan = data; | ||
92 | struct intelfb_info *dinfo = chan->dinfo; | ||
93 | u32 val; | ||
94 | |||
95 | OUTREG(chan->reg, SDA_DIR_MASK); | ||
96 | OUTREG(chan->reg, 0); | ||
97 | val = INREG(chan->reg); | ||
98 | return ((val & SDA_VAL_IN) != 0); | ||
99 | } | ||
100 | |||
101 | static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, | ||
102 | struct intelfb_i2c_chan *chan, | ||
103 | const u32 reg, const char *name) | ||
104 | { | ||
105 | int rc; | ||
106 | |||
107 | chan->dinfo = dinfo; | ||
108 | chan->reg = reg; | ||
109 | snprintf(chan->adapter.name, I2C_NAME_SIZE, "intelfb %s", name); | ||
110 | chan->adapter.owner = THIS_MODULE; | ||
111 | chan->adapter.id = I2C_HW_B_INTELFB; | ||
112 | chan->adapter.algo_data = &chan->algo; | ||
113 | chan->adapter.dev.parent = &chan->dinfo->pdev->dev; | ||
114 | chan->algo.setsda = intelfb_gpio_setsda; | ||
115 | chan->algo.setscl = intelfb_gpio_setscl; | ||
116 | chan->algo.getsda = intelfb_gpio_getsda; | ||
117 | chan->algo.getscl = intelfb_gpio_getscl; | ||
118 | chan->algo.udelay = 40; | ||
119 | chan->algo.timeout = 20; | ||
120 | chan->algo.data = chan; | ||
121 | |||
122 | i2c_set_adapdata(&chan->adapter, chan); | ||
123 | |||
124 | /* Raise SCL and SDA */ | ||
125 | intelfb_gpio_setsda(chan, 1); | ||
126 | intelfb_gpio_setscl(chan, 1); | ||
127 | udelay(20); | ||
128 | |||
129 | rc = i2c_bit_add_bus(&chan->adapter); | ||
130 | if (rc == 0) | ||
131 | DBG_MSG("I2C bus %s registered.\n", name); | ||
132 | else | ||
133 | WRN_MSG("Failed to register I2C bus %s.\n", name); | ||
134 | return rc; | ||
135 | } | ||
136 | |||
137 | void intelfb_create_i2c_busses(struct intelfb_info *dinfo) | ||
138 | { | ||
139 | int i = 0; | ||
140 | |||
141 | /* everyone has at least a single analog output */ | ||
142 | dinfo->num_outputs = 1; | ||
143 | dinfo->output[i].type = INTELFB_OUTPUT_ANALOG; | ||
144 | |||
145 | /* setup the DDC bus for analog output */ | ||
146 | intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, "CRTDDC_A"); | ||
147 | i++; | ||
148 | |||
149 | /* need to add the output busses for each device | ||
150 | - this function is very incomplete | ||
151 | - i915GM has LVDS and TVOUT for example | ||
152 | */ | ||
153 | switch(dinfo->chipset) { | ||
154 | case INTEL_830M: | ||
155 | case INTEL_845G: | ||
156 | case INTEL_855GM: | ||
157 | case INTEL_865G: | ||
158 | dinfo->output[i].type = INTELFB_OUTPUT_DVO; | ||
159 | intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOD, "DVODDC_D"); | ||
160 | intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "DVOI2C_E"); | ||
161 | i++; | ||
162 | break; | ||
163 | case INTEL_915G: | ||
164 | case INTEL_915GM: | ||
165 | /* has some LVDS + tv-out */ | ||
166 | case INTEL_945G: | ||
167 | case INTEL_945GM: | ||
168 | /* SDVO ports have a single control bus - 2 devices */ | ||
169 | dinfo->output[i].type = INTELFB_OUTPUT_SDVO; | ||
170 | intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "SDVOCTRL_E"); | ||
171 | /* TODO: initialize the SDVO */ | ||
172 | // I830SDVOInit(pScrn, i, DVOB); | ||
173 | i++; | ||
174 | |||
175 | /* set up SDVOC */ | ||
176 | dinfo->output[i].type = INTELFB_OUTPUT_SDVO; | ||
177 | dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus; | ||
178 | /* TODO: initialize the SDVO */ | ||
179 | // I830SDVOInit(pScrn, i, DVOC); | ||
180 | i++; | ||
181 | break; | ||
182 | } | ||
183 | dinfo->num_outputs = i; | ||
184 | } | ||
185 | |||
186 | void intelfb_delete_i2c_busses(struct intelfb_info *dinfo) | ||
187 | { | ||
188 | int i; | ||
189 | |||
190 | for (i = 0; i < MAX_OUTPUTS; i++) { | ||
191 | if (dinfo->output[i].i2c_bus.dinfo) { | ||
192 | i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter); | ||
193 | dinfo->output[i].i2c_bus.dinfo = NULL; | ||
194 | } | ||
195 | if (dinfo->output[i].ddc_bus.dinfo) { | ||
196 | i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter); | ||
197 | dinfo->output[i].ddc_bus.dinfo = NULL; | ||
198 | } | ||
199 | } | ||
200 | } | ||
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 06af89d44a0d..6f9de04193d2 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c | |||
@@ -136,6 +136,8 @@ | |||
136 | static void __devinit get_initial_mode(struct intelfb_info *dinfo); | 136 | static void __devinit get_initial_mode(struct intelfb_info *dinfo); |
137 | static void update_dinfo(struct intelfb_info *dinfo, | 137 | static void update_dinfo(struct intelfb_info *dinfo, |
138 | struct fb_var_screeninfo *var); | 138 | struct fb_var_screeninfo *var); |
139 | static int intelfb_open(struct fb_info *info, int user); | ||
140 | static int intelfb_release(struct fb_info *info, int user); | ||
139 | static int intelfb_check_var(struct fb_var_screeninfo *var, | 141 | static int intelfb_check_var(struct fb_var_screeninfo *var, |
140 | struct fb_info *info); | 142 | struct fb_info *info); |
141 | static int intelfb_set_par(struct fb_info *info); | 143 | static int intelfb_set_par(struct fb_info *info); |
@@ -194,6 +196,8 @@ static int num_registered = 0; | |||
194 | /* fb ops */ | 196 | /* fb ops */ |
195 | static struct fb_ops intel_fb_ops = { | 197 | static struct fb_ops intel_fb_ops = { |
196 | .owner = THIS_MODULE, | 198 | .owner = THIS_MODULE, |
199 | .fb_open = intelfb_open, | ||
200 | .fb_release = intelfb_release, | ||
197 | .fb_check_var = intelfb_check_var, | 201 | .fb_check_var = intelfb_check_var, |
198 | .fb_set_par = intelfb_set_par, | 202 | .fb_set_par = intelfb_set_par, |
199 | .fb_setcolreg = intelfb_setcolreg, | 203 | .fb_setcolreg = intelfb_setcolreg, |
@@ -446,6 +450,8 @@ cleanup(struct intelfb_info *dinfo) | |||
446 | if (!dinfo) | 450 | if (!dinfo) |
447 | return; | 451 | return; |
448 | 452 | ||
453 | intelfbhw_disable_irq(dinfo); | ||
454 | |||
449 | fb_dealloc_cmap(&dinfo->info->cmap); | 455 | fb_dealloc_cmap(&dinfo->info->cmap); |
450 | kfree(dinfo->info->pixmap.addr); | 456 | kfree(dinfo->info->pixmap.addr); |
451 | 457 | ||
@@ -467,6 +473,11 @@ cleanup(struct intelfb_info *dinfo) | |||
467 | agp_free_memory(dinfo->gtt_ring_mem); | 473 | agp_free_memory(dinfo->gtt_ring_mem); |
468 | } | 474 | } |
469 | 475 | ||
476 | #ifdef CONFIG_FB_INTEL_I2C | ||
477 | /* un-register I2C bus */ | ||
478 | intelfb_delete_i2c_busses(dinfo); | ||
479 | #endif | ||
480 | |||
470 | if (dinfo->mmio_base) | 481 | if (dinfo->mmio_base) |
471 | iounmap((void __iomem *)dinfo->mmio_base); | 482 | iounmap((void __iomem *)dinfo->mmio_base); |
472 | if (dinfo->aperture.virtual) | 483 | if (dinfo->aperture.virtual) |
@@ -844,6 +855,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
844 | if (bailearly == 5) | 855 | if (bailearly == 5) |
845 | bailout(dinfo); | 856 | bailout(dinfo); |
846 | 857 | ||
858 | #ifdef CONFIG_FB_INTEL_I2C | ||
859 | /* register I2C bus */ | ||
860 | intelfb_create_i2c_busses(dinfo); | ||
861 | #endif | ||
862 | |||
847 | if (bailearly == 6) | 863 | if (bailearly == 6) |
848 | bailout(dinfo); | 864 | bailout(dinfo); |
849 | 865 | ||
@@ -888,6 +904,13 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
888 | } | 904 | } |
889 | 905 | ||
890 | dinfo->registered = 1; | 906 | dinfo->registered = 1; |
907 | dinfo->open = 0; | ||
908 | |||
909 | init_waitqueue_head(&dinfo->vsync.wait); | ||
910 | spin_lock_init(&dinfo->int_lock); | ||
911 | dinfo->irq_flags = 0; | ||
912 | dinfo->vsync.pan_display = 0; | ||
913 | dinfo->vsync.pan_offset = 0; | ||
891 | 914 | ||
892 | return 0; | 915 | return 0; |
893 | 916 | ||
@@ -1188,6 +1211,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var) | |||
1188 | ***************************************************************/ | 1211 | ***************************************************************/ |
1189 | 1212 | ||
1190 | static int | 1213 | static int |
1214 | intelfb_open(struct fb_info *info, int user) | ||
1215 | { | ||
1216 | struct intelfb_info *dinfo = GET_DINFO(info); | ||
1217 | |||
1218 | if (user) { | ||
1219 | dinfo->open++; | ||
1220 | } | ||
1221 | |||
1222 | return 0; | ||
1223 | } | ||
1224 | |||
1225 | static int | ||
1226 | intelfb_release(struct fb_info *info, int user) | ||
1227 | { | ||
1228 | struct intelfb_info *dinfo = GET_DINFO(info); | ||
1229 | |||
1230 | if (user) { | ||
1231 | dinfo->open--; | ||
1232 | msleep(1); | ||
1233 | if (!dinfo->open) { | ||
1234 | intelfbhw_disable_irq(dinfo); | ||
1235 | } | ||
1236 | } | ||
1237 | |||
1238 | return 0; | ||
1239 | } | ||
1240 | |||
1241 | static int | ||
1191 | intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 1242 | intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
1192 | { | 1243 | { |
1193 | int change_var = 0; | 1244 | int change_var = 0; |
@@ -1433,6 +1484,19 @@ static int | |||
1433 | intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) | 1484 | intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) |
1434 | { | 1485 | { |
1435 | int retval = 0; | 1486 | int retval = 0; |
1487 | struct intelfb_info *dinfo = GET_DINFO(info); | ||
1488 | u32 pipe = 0; | ||
1489 | |||
1490 | switch (cmd) { | ||
1491 | case FBIO_WAITFORVSYNC: | ||
1492 | if (get_user(pipe, (__u32 __user *)arg)) | ||
1493 | return -EFAULT; | ||
1494 | |||
1495 | retval = intelfbhw_wait_for_vsync(dinfo, pipe); | ||
1496 | break; | ||
1497 | default: | ||
1498 | break; | ||
1499 | } | ||
1436 | 1500 | ||
1437 | return retval; | 1501 | return retval; |
1438 | } | 1502 | } |
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 2a9322f9cfdc..f887f1efd3fe 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/pci.h> | 32 | #include <linux/pci.h> |
33 | #include <linux/vmalloc.h> | 33 | #include <linux/vmalloc.h> |
34 | #include <linux/pagemap.h> | 34 | #include <linux/pagemap.h> |
35 | #include <linux/interrupt.h> | ||
35 | 36 | ||
36 | #include <asm/io.h> | 37 | #include <asm/io.h> |
37 | 38 | ||
@@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | |||
368 | 369 | ||
369 | offset += dinfo->fb.offset << 12; | 370 | offset += dinfo->fb.offset << 12; |
370 | 371 | ||
371 | OUTREG(DSPABASE, offset); | 372 | dinfo->vsync.pan_offset = offset; |
373 | if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) { | ||
374 | dinfo->vsync.pan_display = 1; | ||
375 | } else { | ||
376 | dinfo->vsync.pan_display = 0; | ||
377 | OUTREG(DSPABASE, offset); | ||
378 | } | ||
372 | 379 | ||
373 | return 0; | 380 | return 0; |
374 | } | 381 | } |
@@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, | |||
585 | hw->fw_blc_0 = INREG(FW_BLC_0); | 592 | hw->fw_blc_0 = INREG(FW_BLC_0); |
586 | hw->fw_blc_1 = INREG(FW_BLC_1); | 593 | hw->fw_blc_1 = INREG(FW_BLC_1); |
587 | 594 | ||
595 | hw->hwstam = INREG16(HWSTAM); | ||
596 | hw->ier = INREG16(IER); | ||
597 | hw->iir = INREG16(IIR); | ||
598 | hw->imr = INREG16(IMR); | ||
599 | |||
588 | return 0; | 600 | return 0; |
589 | } | 601 | } |
590 | 602 | ||
@@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd | |||
613 | return vco / p; | 625 | return vco / p; |
614 | } | 626 | } |
615 | 627 | ||
628 | #if REGDUMP | ||
616 | static void | 629 | static void |
617 | intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2) | 630 | intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2) |
618 | { | 631 | { |
@@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2) | |||
638 | *o_p1 = p1; | 651 | *o_p1 = p1; |
639 | *o_p2 = p2; | 652 | *o_p2 = p2; |
640 | } | 653 | } |
654 | #endif | ||
641 | 655 | ||
642 | 656 | ||
643 | void | 657 | void |
@@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) | |||
794 | printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); | 808 | printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); |
795 | printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); | 809 | printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); |
796 | 810 | ||
811 | printk(" HWSTAM 0x%04x\n", hw->hwstam); | ||
812 | printk(" IER 0x%04x\n", hw->ier); | ||
813 | printk(" IIR 0x%04x\n", hw->iir); | ||
814 | printk(" IMR 0x%04x\n", hw->imr); | ||
797 | printk("hw state dump end\n"); | 815 | printk("hw state dump end\n"); |
798 | #endif | 816 | #endif |
799 | } | 817 | } |
@@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) { | |||
1932 | addr += 16; | 1950 | addr += 16; |
1933 | } | 1951 | } |
1934 | } | 1952 | } |
1953 | |||
1954 | static irqreturn_t | ||
1955 | intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) { | ||
1956 | int handled = 0; | ||
1957 | u16 tmp; | ||
1958 | struct intelfb_info *dinfo = (struct intelfb_info *)dev_id; | ||
1959 | |||
1960 | spin_lock(&dinfo->int_lock); | ||
1961 | |||
1962 | tmp = INREG16(IIR); | ||
1963 | tmp &= VSYNC_PIPE_A_INTERRUPT; | ||
1964 | |||
1965 | if (tmp == 0) { | ||
1966 | spin_unlock(&dinfo->int_lock); | ||
1967 | return IRQ_RETVAL(handled); | ||
1968 | } | ||
1969 | |||
1970 | OUTREG16(IIR, tmp); | ||
1971 | |||
1972 | if (tmp & VSYNC_PIPE_A_INTERRUPT) { | ||
1973 | dinfo->vsync.count++; | ||
1974 | if (dinfo->vsync.pan_display) { | ||
1975 | dinfo->vsync.pan_display = 0; | ||
1976 | OUTREG(DSPABASE, dinfo->vsync.pan_offset); | ||
1977 | } | ||
1978 | wake_up_interruptible(&dinfo->vsync.wait); | ||
1979 | handled = 1; | ||
1980 | } | ||
1981 | |||
1982 | spin_unlock(&dinfo->int_lock); | ||
1983 | |||
1984 | return IRQ_RETVAL(handled); | ||
1985 | } | ||
1986 | |||
1987 | int | ||
1988 | intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) { | ||
1989 | |||
1990 | if (!test_and_set_bit(0, &dinfo->irq_flags)) { | ||
1991 | if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) { | ||
1992 | clear_bit(0, &dinfo->irq_flags); | ||
1993 | return -EINVAL; | ||
1994 | } | ||
1995 | |||
1996 | spin_lock_irq(&dinfo->int_lock); | ||
1997 | OUTREG16(HWSTAM, 0xfffe); | ||
1998 | OUTREG16(IMR, 0x0); | ||
1999 | OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT); | ||
2000 | spin_unlock_irq(&dinfo->int_lock); | ||
2001 | } else if (reenable) { | ||
2002 | u16 ier; | ||
2003 | |||
2004 | spin_lock_irq(&dinfo->int_lock); | ||
2005 | ier = INREG16(IER); | ||
2006 | if ((ier & VSYNC_PIPE_A_INTERRUPT)) { | ||
2007 | DBG_MSG("someone disabled the IRQ [%08X]\n", ier); | ||
2008 | OUTREG(IER, VSYNC_PIPE_A_INTERRUPT); | ||
2009 | } | ||
2010 | spin_unlock_irq(&dinfo->int_lock); | ||
2011 | } | ||
2012 | return 0; | ||
2013 | } | ||
2014 | |||
2015 | void | ||
2016 | intelfbhw_disable_irq(struct intelfb_info *dinfo) { | ||
2017 | u16 tmp; | ||
2018 | |||
2019 | if (test_and_clear_bit(0, &dinfo->irq_flags)) { | ||
2020 | if (dinfo->vsync.pan_display) { | ||
2021 | dinfo->vsync.pan_display = 0; | ||
2022 | OUTREG(DSPABASE, dinfo->vsync.pan_offset); | ||
2023 | } | ||
2024 | spin_lock_irq(&dinfo->int_lock); | ||
2025 | OUTREG16(HWSTAM, 0xffff); | ||
2026 | OUTREG16(IMR, 0xffff); | ||
2027 | OUTREG16(IER, 0x0); | ||
2028 | |||
2029 | tmp = INREG16(IIR); | ||
2030 | OUTREG16(IIR, tmp); | ||
2031 | spin_unlock_irq(&dinfo->int_lock); | ||
2032 | |||
2033 | free_irq(dinfo->pdev->irq, dinfo); | ||
2034 | } | ||
2035 | } | ||
2036 | |||
2037 | int | ||
2038 | intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) { | ||
2039 | struct intelfb_vsync *vsync; | ||
2040 | unsigned int count; | ||
2041 | int ret; | ||
2042 | |||
2043 | switch (pipe) { | ||
2044 | case 0: | ||
2045 | vsync = &dinfo->vsync; | ||
2046 | break; | ||
2047 | default: | ||
2048 | return -ENODEV; | ||
2049 | } | ||
2050 | |||
2051 | ret = intelfbhw_enable_irq(dinfo, 0); | ||
2052 | if (ret) { | ||
2053 | return ret; | ||
2054 | } | ||
2055 | |||
2056 | count = vsync->count; | ||
2057 | ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10); | ||
2058 | if (ret < 0) { | ||
2059 | return ret; | ||
2060 | } | ||
2061 | if (ret == 0) { | ||
2062 | intelfbhw_enable_irq(dinfo, 1); | ||
2063 | DBG_MSG("wait_for_vsync timed out!\n"); | ||
2064 | return -ETIMEDOUT; | ||
2065 | } | ||
2066 | |||
2067 | return 0; | ||
2068 | } | ||
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h index 10acda098b71..8c54ba8fbdda 100644 --- a/drivers/video/intelfb/intelfbhw.h +++ b/drivers/video/intelfb/intelfbhw.h | |||
@@ -88,6 +88,19 @@ | |||
88 | #define INSTDONE 0x2090 | 88 | #define INSTDONE 0x2090 |
89 | #define PRI_RING_EMPTY 1 | 89 | #define PRI_RING_EMPTY 1 |
90 | 90 | ||
91 | #define HWSTAM 0x2098 | ||
92 | #define IER 0x20A0 | ||
93 | #define IIR 0x20A4 | ||
94 | #define IMR 0x20A8 | ||
95 | #define VSYNC_PIPE_A_INTERRUPT (1 << 7) | ||
96 | #define PIPE_A_EVENT_INTERRUPT (1 << 4) | ||
97 | #define VSYNC_PIPE_B_INTERRUPT (1 << 5) | ||
98 | #define PIPE_B_EVENT_INTERRUPT (1 << 4) | ||
99 | #define HOST_PORT_EVENT_INTERRUPT (1 << 3) | ||
100 | #define CAPTURE_EVENT_INTERRUPT (1 << 2) | ||
101 | #define USER_DEFINED_INTERRUPT (1 << 1) | ||
102 | #define BREAKPOINT_INTERRUPT 1 | ||
103 | |||
91 | #define INSTPM 0x20c0 | 104 | #define INSTPM 0x20c0 |
92 | #define SYNC_FLUSH_ENABLE (1 << 5) | 105 | #define SYNC_FLUSH_ENABLE (1 << 5) |
93 | 106 | ||
@@ -113,6 +126,12 @@ | |||
113 | #define FW_DISPC_BL_SHIFT 8 | 126 | #define FW_DISPC_BL_SHIFT 8 |
114 | #define FW_DISPC_BL_MASK 0x7 | 127 | #define FW_DISPC_BL_MASK 0x7 |
115 | 128 | ||
129 | #define GPIOA 0x5010 | ||
130 | #define GPIOB 0x5014 | ||
131 | #define GPIOC 0x5018 // this may be external DDC on i830 | ||
132 | #define GPIOD 0x501C // this is DVO DDC | ||
133 | #define GPIOE 0x5020 // this is DVO i2C | ||
134 | #define GPIOF 0x5024 | ||
116 | 135 | ||
117 | /* PLL registers */ | 136 | /* PLL registers */ |
118 | #define VGA0_DIVISOR 0x06000 | 137 | #define VGA0_DIVISOR 0x06000 |
@@ -468,9 +487,12 @@ | |||
468 | 487 | ||
469 | /* I/O macros */ | 488 | /* I/O macros */ |
470 | #define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr))) | 489 | #define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr))) |
490 | #define INREG16(addr) readw((u16 __iomem *)(dinfo->mmio_base + (addr))) | ||
471 | #define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr))) | 491 | #define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr))) |
472 | #define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \ | 492 | #define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \ |
473 | (addr))) | 493 | (addr))) |
494 | #define OUTREG16(addr, val) writew((val),(u16 __iomem *)(dinfo->mmio_base + \ | ||
495 | (addr))) | ||
474 | #define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \ | 496 | #define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \ |
475 | (addr))) | 497 | (addr))) |
476 | 498 | ||
@@ -545,5 +567,8 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, | |||
545 | extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, | 567 | extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, |
546 | int height, u8 *data); | 568 | int height, u8 *data); |
547 | extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); | 569 | extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); |
570 | extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable); | ||
571 | extern void intelfbhw_disable_irq(struct intelfb_info *dinfo); | ||
572 | extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe); | ||
548 | 573 | ||
549 | #endif /* _INTELFBHW_H */ | 574 | #endif /* _INTELFBHW_H */ |