diff options
Diffstat (limited to 'drivers/video/matrox/i2c-matroxfb.c')
-rw-r--r-- | drivers/video/matrox/i2c-matroxfb.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c new file mode 100644 index 000000000000..57abbae5520f --- /dev/null +++ b/drivers/video/matrox/i2c-matroxfb.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. | ||
4 | * | ||
5 | * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> | ||
6 | * | ||
7 | * Version: 1.64 2002/06/10 | ||
8 | * | ||
9 | * See matroxfb_base.c for contributors. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include "matroxfb_base.h" | ||
14 | #include "matroxfb_maven.h" | ||
15 | #include <linux/i2c.h> | ||
16 | #include <linux/i2c-algo-bit.h> | ||
17 | |||
18 | /* MGA-TVO I2C for G200, G400 */ | ||
19 | #define MAT_CLK 0x20 | ||
20 | #define MAT_DATA 0x10 | ||
21 | /* primary head DDC for Mystique(?), G100, G200, G400 */ | ||
22 | #define DDC1_CLK 0x08 | ||
23 | #define DDC1_DATA 0x02 | ||
24 | /* primary head DDC for Millennium, Millennium II */ | ||
25 | #define DDC1B_CLK 0x10 | ||
26 | #define DDC1B_DATA 0x04 | ||
27 | /* secondary head DDC for G400 */ | ||
28 | #define DDC2_CLK 0x04 | ||
29 | #define DDC2_DATA 0x01 | ||
30 | |||
31 | /******************************************************/ | ||
32 | |||
33 | struct matroxfb_dh_maven_info { | ||
34 | struct i2c_bit_adapter maven; | ||
35 | struct i2c_bit_adapter ddc1; | ||
36 | struct i2c_bit_adapter ddc2; | ||
37 | }; | ||
38 | |||
39 | static int matroxfb_read_gpio(struct matrox_fb_info* minfo) { | ||
40 | unsigned long flags; | ||
41 | int v; | ||
42 | |||
43 | matroxfb_DAC_lock_irqsave(flags); | ||
44 | v = matroxfb_DAC_in(PMINFO DAC_XGENIODATA); | ||
45 | matroxfb_DAC_unlock_irqrestore(flags); | ||
46 | return v; | ||
47 | } | ||
48 | |||
49 | static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { | ||
50 | unsigned long flags; | ||
51 | int v; | ||
52 | |||
53 | matroxfb_DAC_lock_irqsave(flags); | ||
54 | v = (matroxfb_DAC_in(PMINFO DAC_XGENIOCTRL) & mask) | val; | ||
55 | matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, v); | ||
56 | /* We must reset GENIODATA very often... XFree plays with this register */ | ||
57 | matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0x00); | ||
58 | matroxfb_DAC_unlock_irqrestore(flags); | ||
59 | } | ||
60 | |||
61 | /* software I2C functions */ | ||
62 | static inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) { | ||
63 | if (state) | ||
64 | state = 0; | ||
65 | else | ||
66 | state = mask; | ||
67 | matroxfb_set_gpio(minfo, ~mask, state); | ||
68 | } | ||
69 | |||
70 | static void matroxfb_gpio_setsda(void* data, int state) { | ||
71 | struct i2c_bit_adapter* b = data; | ||
72 | matroxfb_i2c_set(b->minfo, b->mask.data, state); | ||
73 | } | ||
74 | |||
75 | static void matroxfb_gpio_setscl(void* data, int state) { | ||
76 | struct i2c_bit_adapter* b = data; | ||
77 | matroxfb_i2c_set(b->minfo, b->mask.clock, state); | ||
78 | } | ||
79 | |||
80 | static int matroxfb_gpio_getsda(void* data) { | ||
81 | struct i2c_bit_adapter* b = data; | ||
82 | return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0; | ||
83 | } | ||
84 | |||
85 | static int matroxfb_gpio_getscl(void* data) { | ||
86 | struct i2c_bit_adapter* b = data; | ||
87 | return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0; | ||
88 | } | ||
89 | |||
90 | static struct i2c_adapter matrox_i2c_adapter_template = | ||
91 | { | ||
92 | .owner = THIS_MODULE, | ||
93 | .id = I2C_HW_B_G400, | ||
94 | }; | ||
95 | |||
96 | static struct i2c_algo_bit_data matrox_i2c_algo_template = | ||
97 | { | ||
98 | NULL, | ||
99 | matroxfb_gpio_setsda, | ||
100 | matroxfb_gpio_setscl, | ||
101 | matroxfb_gpio_getsda, | ||
102 | matroxfb_gpio_getscl, | ||
103 | 10, 10, 100, | ||
104 | }; | ||
105 | |||
106 | static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, | ||
107 | unsigned int data, unsigned int clock, const char* name) { | ||
108 | int err; | ||
109 | |||
110 | b->minfo = minfo; | ||
111 | b->mask.data = data; | ||
112 | b->mask.clock = clock; | ||
113 | b->adapter = matrox_i2c_adapter_template; | ||
114 | snprintf(b->adapter.name, I2C_NAME_SIZE, name, | ||
115 | minfo->fbcon.node); | ||
116 | i2c_set_adapdata(&b->adapter, b); | ||
117 | b->adapter.algo_data = &b->bac; | ||
118 | b->bac = matrox_i2c_algo_template; | ||
119 | b->bac.data = b; | ||
120 | err = i2c_bit_add_bus(&b->adapter); | ||
121 | b->initialized = !err; | ||
122 | return err; | ||
123 | } | ||
124 | |||
125 | static void i2c_bit_bus_del(struct i2c_bit_adapter* b) { | ||
126 | if (b->initialized) { | ||
127 | i2c_bit_del_bus(&b->adapter); | ||
128 | b->initialized = 0; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | static inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) { | ||
133 | i2c_bit_bus_del(&minfo2->maven); | ||
134 | } | ||
135 | |||
136 | static inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) { | ||
137 | i2c_bit_bus_del(&minfo2->ddc1); | ||
138 | } | ||
139 | |||
140 | static inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) { | ||
141 | i2c_bit_bus_del(&minfo2->ddc2); | ||
142 | } | ||
143 | |||
144 | static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { | ||
145 | int err; | ||
146 | unsigned long flags; | ||
147 | struct matroxfb_dh_maven_info* m2info; | ||
148 | |||
149 | m2info = (struct matroxfb_dh_maven_info*)kmalloc(sizeof(*m2info), GFP_KERNEL); | ||
150 | if (!m2info) | ||
151 | return NULL; | ||
152 | |||
153 | matroxfb_DAC_lock_irqsave(flags); | ||
154 | matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0xFF); | ||
155 | matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, 0x00); | ||
156 | matroxfb_DAC_unlock_irqrestore(flags); | ||
157 | |||
158 | memset(m2info, 0, sizeof(*m2info)); | ||
159 | |||
160 | switch (ACCESS_FBINFO(chip)) { | ||
161 | case MGA_2064: | ||
162 | case MGA_2164: | ||
163 | err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, "DDC:fb%u #0"); | ||
164 | break; | ||
165 | default: | ||
166 | err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, "DDC:fb%u #0"); | ||
167 | break; | ||
168 | } | ||
169 | if (err) | ||
170 | goto fail_ddc1; | ||
171 | if (ACCESS_FBINFO(devflags.dualhead)) { | ||
172 | err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1"); | ||
173 | if (err == -ENODEV) { | ||
174 | printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); | ||
175 | } else if (err) | ||
176 | printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); | ||
177 | /* Register maven bus even on G450/G550 */ | ||
178 | err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u"); | ||
179 | if (err) | ||
180 | printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); | ||
181 | } | ||
182 | return m2info; | ||
183 | fail_ddc1:; | ||
184 | kfree(m2info); | ||
185 | printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n"); | ||
186 | return NULL; | ||
187 | } | ||
188 | |||
189 | static void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) { | ||
190 | struct matroxfb_dh_maven_info* m2info = data; | ||
191 | |||
192 | i2c_maven_done(m2info); | ||
193 | i2c_ddc2_done(m2info); | ||
194 | i2c_ddc1_done(m2info); | ||
195 | kfree(m2info); | ||
196 | } | ||
197 | |||
198 | static struct matroxfb_driver i2c_matroxfb = { | ||
199 | .node = LIST_HEAD_INIT(i2c_matroxfb.node), | ||
200 | .name = "i2c-matroxfb", | ||
201 | .probe = i2c_matroxfb_probe, | ||
202 | .remove = i2c_matroxfb_remove, | ||
203 | }; | ||
204 | |||
205 | static int __init i2c_matroxfb_init(void) { | ||
206 | if (matroxfb_register_driver(&i2c_matroxfb)) { | ||
207 | printk(KERN_ERR "i2c-matroxfb: failed to register driver\n"); | ||
208 | return -ENXIO; | ||
209 | } | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static void __exit i2c_matroxfb_exit(void) { | ||
214 | matroxfb_unregister_driver(&i2c_matroxfb); | ||
215 | } | ||
216 | |||
217 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | ||
218 | MODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards"); | ||
219 | |||
220 | module_init(i2c_matroxfb_init); | ||
221 | module_exit(i2c_matroxfb_exit); | ||
222 | /* no __setup required */ | ||
223 | MODULE_LICENSE("GPL"); | ||