]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/nouveau_gpio.c
Merge tag 'uapi-prep-20121002' of git://git.infradead.org/users/dhowells/linux-headers
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / nouveau_gpio.c
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 <drm/drmP.h>
26 #include "nouveau_drv.h"
27 #include "nouveau_i2c.h"
28 #include "nouveau_gpio.h"
29
30 static u8 *
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 *
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
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
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
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
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
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
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
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
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
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
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(&isr->work);
306                         kfree(isr);
307                 }
308         }
309 }
310
311 int
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
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
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
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
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, 0x00d740 + (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 }