]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
Merge branch 'topic/omap3isp' of git://git.kernel.org/pub/scm/linux/kernel/git/mcheha...
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / core / subdev / gpio / base.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 <subdev/bios.h>
26 #include <subdev/bios/gpio.h>
27
28 #include "priv.h"
29
30 static int
31 nouveau_gpio_drive(struct nouveau_gpio *gpio,
32                    int idx, int line, int dir, int out)
33 {
34         const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
35         return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV;
36 }
37
38 static int
39 nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
40 {
41         const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
42         return impl->sense ? impl->sense(gpio, line) : -ENODEV;
43 }
44
45 static int
46 nouveau_gpio_find(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
47                   struct dcb_gpio_func *func)
48 {
49         struct nouveau_bios *bios = nouveau_bios(gpio);
50         u8  ver, len;
51         u16 data;
52
53         if (line == 0xff && tag == 0xff)
54                 return -EINVAL;
55
56         data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
57         if (data)
58                 return 0;
59
60         /* Apple iMac G4 NV18 */
61         if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
62                 if (tag == DCB_GPIO_TVDAC0) {
63                         *func = (struct dcb_gpio_func) {
64                                 .func = DCB_GPIO_TVDAC0,
65                                 .line = 4,
66                                 .log[0] = 0,
67                                 .log[1] = 1,
68                         };
69                         return 0;
70                 }
71         }
72
73         return -ENOENT;
74 }
75
76 static int
77 nouveau_gpio_set(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, int state)
78 {
79         struct dcb_gpio_func func;
80         int ret;
81
82         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
83         if (ret == 0) {
84                 int dir = !!(func.log[state] & 0x02);
85                 int out = !!(func.log[state] & 0x01);
86                 ret = nouveau_gpio_drive(gpio, idx, func.line, dir, out);
87         }
88
89         return ret;
90 }
91
92 static int
93 nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
94 {
95         struct dcb_gpio_func func;
96         int ret;
97
98         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
99         if (ret == 0) {
100                 ret = nouveau_gpio_sense(gpio, idx, func.line);
101                 if (ret >= 0)
102                         ret = (ret == (func.log[1] & 1));
103         }
104
105         return ret;
106 }
107
108 static void
109 nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index)
110 {
111         struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
112         const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
113         impl->intr_mask(gpio, type, 1 << index, 0);
114 }
115
116 static void
117 nouveau_gpio_intr_enable(struct nouveau_event *event, int type, int index)
118 {
119         struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
120         const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
121         impl->intr_mask(gpio, type, 1 << index, 1 << index);
122 }
123
124 static void
125 nouveau_gpio_intr(struct nouveau_subdev *subdev)
126 {
127         struct nouveau_gpio *gpio = nouveau_gpio(subdev);
128         const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
129         u32 hi, lo, e, i;
130
131         impl->intr_stat(gpio, &hi, &lo);
132
133         for (i = 0; e = 0, (hi | lo) && i < impl->lines; i++) {
134                 if (hi & (1 << i))
135                         e |= NVKM_GPIO_HI;
136                 if (lo & (1 << i))
137                         e |= NVKM_GPIO_LO;
138                 nouveau_event_trigger(gpio->events, e, i);
139         }
140 }
141
142 int
143 _nouveau_gpio_fini(struct nouveau_object *object, bool suspend)
144 {
145         const struct nouveau_gpio_impl *impl = (void *)object->oclass;
146         struct nouveau_gpio *gpio = nouveau_gpio(object);
147         u32 mask = (1 << impl->lines) - 1;
148
149         impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
150         impl->intr_stat(gpio, &mask, &mask);
151
152         return nouveau_subdev_fini(&gpio->base, suspend);
153 }
154
155 static struct dmi_system_id gpio_reset_ids[] = {
156         {
157                 .ident = "Apple Macbook 10,1",
158                 .matches = {
159                         DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
160                         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
161                 }
162         },
163         { }
164 };
165
166 int
167 _nouveau_gpio_init(struct nouveau_object *object)
168 {
169         struct nouveau_gpio *gpio = nouveau_gpio(object);
170         int ret;
171
172         ret = nouveau_subdev_init(&gpio->base);
173         if (ret)
174                 return ret;
175
176         if (gpio->reset && dmi_check_system(gpio_reset_ids))
177                 gpio->reset(gpio, DCB_GPIO_UNUSED);
178
179         return ret;
180 }
181
182 void
183 _nouveau_gpio_dtor(struct nouveau_object *object)
184 {
185         struct nouveau_gpio *gpio = (void *)object;
186         nouveau_event_destroy(&gpio->events);
187         nouveau_subdev_destroy(&gpio->base);
188 }
189
190 int
191 nouveau_gpio_create_(struct nouveau_object *parent,
192                      struct nouveau_object *engine,
193                      struct nouveau_oclass *oclass,
194                      int length, void **pobject)
195 {
196         const struct nouveau_gpio_impl *impl = (void *)oclass;
197         struct nouveau_gpio *gpio;
198         int ret;
199
200         ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
201                                      length, pobject);
202         gpio = *pobject;
203         if (ret)
204                 return ret;
205
206         gpio->find = nouveau_gpio_find;
207         gpio->set  = nouveau_gpio_set;
208         gpio->get  = nouveau_gpio_get;
209         gpio->reset = impl->reset;
210
211         ret = nouveau_event_create(2, impl->lines, &gpio->events);
212         if (ret)
213                 return ret;
214
215         gpio->events->priv = gpio;
216         gpio->events->enable = nouveau_gpio_intr_enable;
217         gpio->events->disable = nouveau_gpio_intr_disable;
218         nv_subdev(gpio)->intr = nouveau_gpio_intr;
219         return 0;
220 }
221
222 int
223 _nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
224                    struct nouveau_oclass *oclass, void *data, u32 size,
225                    struct nouveau_object **pobject)
226 {
227         struct nouveau_gpio *gpio;
228         int ret;
229
230         ret = nouveau_gpio_create(parent, engine, oclass, &gpio);
231         *pobject = nv_object(gpio);
232         if (ret)
233                 return ret;
234
235         return 0;
236 }