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
30static u8 *
31dcb_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
43static u8 *
44dcb_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
57int
58nouveau_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
66int
67nouveau_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
75int
76nouveau_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
145int
146nouveau_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
161int
162nouveau_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
177int
178nouveau_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
196struct 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
207static void
208nouveau_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
226void
227nouveau_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
248int
249nouveau_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
280void
281nouveau_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
311int
312nouveau_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
323void
324nouveau_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
333int
334nouveau_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
346void
347nouveau_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
356void
357nouveau_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