1 /*
2  * Copyright 2011 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 
25 #include "drmP.h"
26 #include "nouveau_drv.h"
27 #include "nouveau_i2c.h"
28 #include "nouveau_gpio.h"
29 
30 static u8 *
dcb_gpio_table(struct drm_device * dev)31 dcb_gpio_table(struct drm_device *dev)
32 {
33 	u8 *dcb = dcb_table(dev);
34 	if (dcb) {
35 		if (dcb[0] >= 0x30 && dcb[1] >= 0x0c)
36 			return ROMPTR(dev, dcb[0x0a]);
37 		if (dcb[0] >= 0x22 && dcb[-1] >= 0x13)
38 			return ROMPTR(dev, dcb[-15]);
39 	}
40 	return NULL;
41 }
42 
43 static u8 *
dcb_gpio_entry(struct drm_device * dev,int idx,int ent,u8 * version)44 dcb_gpio_entry(struct drm_device *dev, int idx, int ent, u8 *version)
45 {
46 	u8 *table = dcb_gpio_table(dev);
47 	if (table) {
48 		*version = table[0];
49 		if (*version < 0x30 && ent < table[2])
50 			return table + 3 + (ent * table[1]);
51 		else if (ent < table[2])
52 			return table + table[1] + (ent * table[3]);
53 	}
54 	return NULL;
55 }
56 
57 int
nouveau_gpio_drive(struct drm_device * dev,int idx,int line,int dir,int out)58 nouveau_gpio_drive(struct drm_device *dev, int idx, int line, int dir, int out)
59 {
60 	struct drm_nouveau_private *dev_priv = dev->dev_private;
61 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
62 
63 	return pgpio->drive ? pgpio->drive(dev, line, dir, out) : -ENODEV;
64 }
65 
66 int
nouveau_gpio_sense(struct drm_device * dev,int idx,int line)67 nouveau_gpio_sense(struct drm_device *dev, int idx, int line)
68 {
69 	struct drm_nouveau_private *dev_priv = dev->dev_private;
70 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
71 
72 	return pgpio->sense ? pgpio->sense(dev, line) : -ENODEV;
73 }
74 
75 int
nouveau_gpio_find(struct drm_device * dev,int idx,u8 func,u8 line,struct gpio_func * gpio)76 nouveau_gpio_find(struct drm_device *dev, int idx, u8 func, u8 line,
77 		  struct gpio_func *gpio)
78 {
79 	u8 *table, *entry, version;
80 	int i = -1;
81 
82 	if (line == 0xff && func == 0xff)
83 		return -EINVAL;
84 
85 	while ((entry = dcb_gpio_entry(dev, idx, ++i, &version))) {
86 		if (version < 0x40) {
87 			u16 data = ROM16(entry[0]);
88 			*gpio = (struct gpio_func) {
89 				.line = (data & 0x001f) >> 0,
90 				.func = (data & 0x07e0) >> 5,
91 				.log[0] = (data & 0x1800) >> 11,
92 				.log[1] = (data & 0x6000) >> 13,
93 			};
94 		} else
95 		if (version < 0x41) {
96 			*gpio = (struct gpio_func) {
97 				.line = entry[0] & 0x1f,
98 				.func = entry[1],
99 				.log[0] = (entry[3] & 0x18) >> 3,
100 				.log[1] = (entry[3] & 0x60) >> 5,
101 			};
102 		} else {
103 			*gpio = (struct gpio_func) {
104 				.line = entry[0] & 0x3f,
105 				.func = entry[1],
106 				.log[0] = (entry[4] & 0x30) >> 4,
107 				.log[1] = (entry[4] & 0xc0) >> 6,
108 			};
109 		}
110 
111 		if ((line == 0xff || line == gpio->line) &&
112 		    (func == 0xff || func == gpio->func))
113 			return 0;
114 	}
115 
116 	/* DCB 2.2, fixed TVDAC GPIO data */
117 	if ((table = dcb_table(dev)) && table[0] >= 0x22) {
118 		if (func == DCB_GPIO_TVDAC0) {
119 			*gpio = (struct gpio_func) {
120 				.func = DCB_GPIO_TVDAC0,
121 				.line = table[-4] >> 4,
122 				.log[0] = !!(table[-5] & 2),
123 				.log[1] =  !(table[-5] & 2),
124 			};
125 			return 0;
126 		}
127 	}
128 
129 	/* Apple iMac G4 NV18 */
130 	if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
131 		if (func == DCB_GPIO_TVDAC0) {
132 			*gpio = (struct gpio_func) {
133 				.func = DCB_GPIO_TVDAC0,
134 				.line = 4,
135 				.log[0] = 0,
136 				.log[1] = 1,
137 			};
138 			return 0;
139 		}
140 	}
141 
142 	return -EINVAL;
143 }
144 
145 int
nouveau_gpio_set(struct drm_device * dev,int idx,u8 tag,u8 line,int state)146 nouveau_gpio_set(struct drm_device *dev, int idx, u8 tag, u8 line, int state)
147 {
148 	struct gpio_func gpio;
149 	int ret;
150 
151 	ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
152 	if (ret == 0) {
153 		int dir = !!(gpio.log[state] & 0x02);
154 		int out = !!(gpio.log[state] & 0x01);
155 		ret = nouveau_gpio_drive(dev, idx, gpio.line, dir, out);
156 	}
157 
158 	return ret;
159 }
160 
161 int
nouveau_gpio_get(struct drm_device * dev,int idx,u8 tag,u8 line)162 nouveau_gpio_get(struct drm_device *dev, int idx, u8 tag, u8 line)
163 {
164 	struct gpio_func gpio;
165 	int ret;
166 
167 	ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
168 	if (ret == 0) {
169 		ret = nouveau_gpio_sense(dev, idx, gpio.line);
170 		if (ret >= 0)
171 			ret = (ret == (gpio.log[1] & 1));
172 	}
173 
174 	return ret;
175 }
176 
177 int
nouveau_gpio_irq(struct drm_device * dev,int idx,u8 tag,u8 line,bool on)178 nouveau_gpio_irq(struct drm_device *dev, int idx, u8 tag, u8 line, bool on)
179 {
180 	struct drm_nouveau_private *dev_priv = dev->dev_private;
181 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
182 	struct gpio_func gpio;
183 	int ret;
184 
185 	ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
186 	if (ret == 0) {
187 		if (idx == 0 && pgpio->irq_enable)
188 			pgpio->irq_enable(dev, gpio.line, on);
189 		else
190 			ret = -ENODEV;
191 	}
192 
193 	return ret;
194 }
195 
196 struct gpio_isr {
197 	struct drm_device *dev;
198 	struct list_head head;
199 	struct work_struct work;
200 	int idx;
201 	struct gpio_func func;
202 	void (*handler)(void *, int);
203 	void *data;
204 	bool inhibit;
205 };
206 
207 static void
nouveau_gpio_isr_bh(struct work_struct * work)208 nouveau_gpio_isr_bh(struct work_struct *work)
209 {
210 	struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
211 	struct drm_device *dev = isr->dev;
212 	struct drm_nouveau_private *dev_priv = dev->dev_private;
213 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
214 	unsigned long flags;
215 	int state;
216 
217 	state = nouveau_gpio_get(dev, isr->idx, isr->func.func, isr->func.line);
218 	if (state >= 0)
219 		isr->handler(isr->data, state);
220 
221 	spin_lock_irqsave(&pgpio->lock, flags);
222 	isr->inhibit = false;
223 	spin_unlock_irqrestore(&pgpio->lock, flags);
224 }
225 
226 void
nouveau_gpio_isr(struct drm_device * dev,int idx,u32 line_mask)227 nouveau_gpio_isr(struct drm_device *dev, int idx, u32 line_mask)
228 {
229 	struct drm_nouveau_private *dev_priv = dev->dev_private;
230 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
231 	struct gpio_isr *isr;
232 
233 	if (idx != 0)
234 		return;
235 
236 	spin_lock(&pgpio->lock);
237 	list_for_each_entry(isr, &pgpio->isr, head) {
238 		if (line_mask & (1 << isr->func.line)) {
239 			if (isr->inhibit)
240 				continue;
241 			isr->inhibit = true;
242 			schedule_work(&isr->work);
243 		}
244 	}
245 	spin_unlock(&pgpio->lock);
246 }
247 
248 int
nouveau_gpio_isr_add(struct drm_device * dev,int idx,u8 tag,u8 line,void (* handler)(void *,int),void * data)249 nouveau_gpio_isr_add(struct drm_device *dev, int idx, u8 tag, u8 line,
250 		     void (*handler)(void *, int), void *data)
251 {
252 	struct drm_nouveau_private *dev_priv = dev->dev_private;
253 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
254 	struct gpio_isr *isr;
255 	unsigned long flags;
256 	int ret;
257 
258 	isr = kzalloc(sizeof(*isr), GFP_KERNEL);
259 	if (!isr)
260 		return -ENOMEM;
261 
262 	ret = nouveau_gpio_find(dev, idx, tag, line, &isr->func);
263 	if (ret) {
264 		kfree(isr);
265 		return ret;
266 	}
267 
268 	INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
269 	isr->dev = dev;
270 	isr->handler = handler;
271 	isr->data = data;
272 	isr->idx = idx;
273 
274 	spin_lock_irqsave(&pgpio->lock, flags);
275 	list_add(&isr->head, &pgpio->isr);
276 	spin_unlock_irqrestore(&pgpio->lock, flags);
277 	return 0;
278 }
279 
280 void
nouveau_gpio_isr_del(struct drm_device * dev,int idx,u8 tag,u8 line,void (* handler)(void *,int),void * data)281 nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line,
282 		     void (*handler)(void *, int), void *data)
283 {
284 	struct drm_nouveau_private *dev_priv = dev->dev_private;
285 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
286 	struct gpio_isr *isr, *tmp;
287 	struct gpio_func func;
288 	unsigned long flags;
289 	LIST_HEAD(tofree);
290 	int ret;
291 
292 	ret = nouveau_gpio_find(dev, idx, tag, line, &func);
293 	if (ret == 0) {
294 		spin_lock_irqsave(&pgpio->lock, flags);
295 		list_for_each_entry_safe(isr, tmp, &pgpio->isr, head) {
296 			if (memcmp(&isr->func, &func, sizeof(func)) ||
297 			    isr->idx != idx ||
298 			    isr->handler != handler || isr->data != data)
299 				continue;
300 			list_move(&isr->head, &tofree);
301 		}
302 		spin_unlock_irqrestore(&pgpio->lock, flags);
303 
304 		list_for_each_entry_safe(isr, tmp, &tofree, head) {
305 			flush_work_sync(&isr->work);
306 			kfree(isr);
307 		}
308 	}
309 }
310 
311 int
nouveau_gpio_create(struct drm_device * dev)312 nouveau_gpio_create(struct drm_device *dev)
313 {
314 	struct drm_nouveau_private *dev_priv = dev->dev_private;
315 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
316 
317 	INIT_LIST_HEAD(&pgpio->isr);
318 	spin_lock_init(&pgpio->lock);
319 
320 	return nouveau_gpio_init(dev);
321 }
322 
323 void
nouveau_gpio_destroy(struct drm_device * dev)324 nouveau_gpio_destroy(struct drm_device *dev)
325 {
326 	struct drm_nouveau_private *dev_priv = dev->dev_private;
327 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
328 
329 	nouveau_gpio_fini(dev);
330 	BUG_ON(!list_empty(&pgpio->isr));
331 }
332 
333 int
nouveau_gpio_init(struct drm_device * dev)334 nouveau_gpio_init(struct drm_device *dev)
335 {
336 	struct drm_nouveau_private *dev_priv = dev->dev_private;
337 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
338 	int ret = 0;
339 
340 	if (pgpio->init)
341 		ret = pgpio->init(dev);
342 
343 	return ret;
344 }
345 
346 void
nouveau_gpio_fini(struct drm_device * dev)347 nouveau_gpio_fini(struct drm_device *dev)
348 {
349 	struct drm_nouveau_private *dev_priv = dev->dev_private;
350 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
351 
352 	if (pgpio->fini)
353 		pgpio->fini(dev);
354 }
355 
356 void
nouveau_gpio_reset(struct drm_device * dev)357 nouveau_gpio_reset(struct drm_device *dev)
358 {
359 	struct drm_nouveau_private *dev_priv = dev->dev_private;
360 	u8 *entry, version;
361 	int ent = -1;
362 
363 	while ((entry = dcb_gpio_entry(dev, 0, ++ent, &version))) {
364 		u8 func = 0xff, line, defs, unk0, unk1;
365 		if (version >= 0x41) {
366 			defs = !!(entry[0] & 0x80);
367 			line = entry[0] & 0x3f;
368 			func = entry[1];
369 			unk0 = entry[2];
370 			unk1 = entry[3] & 0x1f;
371 		} else
372 		if (version >= 0x40) {
373 			line = entry[0] & 0x1f;
374 			func = entry[1];
375 			defs = !!(entry[3] & 0x01);
376 			unk0 = !!(entry[3] & 0x02);
377 			unk1 = !!(entry[3] & 0x04);
378 		} else {
379 			break;
380 		}
381 
382 		if (func == 0xff)
383 			continue;
384 
385 		nouveau_gpio_func_set(dev, func, defs);
386 
387 		if (dev_priv->card_type >= NV_D0) {
388 			nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0);
389 			if (unk1--)
390 				nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line);
391 		} else
392 		if (dev_priv->card_type >= NV_50) {
393 			static const u32 regs[] = { 0xe100, 0xe28c };
394 			u32 val = (unk1 << 16) | unk0;
395 			u32 reg = regs[line >> 4]; line &= 0x0f;
396 
397 			nv_mask(dev, reg, 0x00010001 << line, val << line);
398 		}
399 	}
400 }
401