]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
Merge remote-tracking branches 'regulator/topic/db8500', 'regulator/topic/gpio',...
[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/gpio.h>
26 #include <subdev/bios.h>
27 #include <subdev/bios/gpio.h>
28
29 static int
30 nouveau_gpio_drive(struct nouveau_gpio *gpio,
31                    int idx, int line, int dir, int out)
32 {
33         return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV;
34 }
35
36 static int
37 nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
38 {
39         return gpio->sense ? gpio->sense(gpio, line) : -ENODEV;
40 }
41
42 static int
43 nouveau_gpio_find(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line,
44                   struct dcb_gpio_func *func)
45 {
46         struct nouveau_bios *bios = nouveau_bios(gpio);
47         u8  ver, len;
48         u16 data;
49
50         if (line == 0xff && tag == 0xff)
51                 return -EINVAL;
52
53         data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
54         if (data)
55                 return 0;
56
57         /* Apple iMac G4 NV18 */
58         if (nv_device_match(nv_object(gpio), 0x0189, 0x10de, 0x0010)) {
59                 if (tag == DCB_GPIO_TVDAC0) {
60                         *func = (struct dcb_gpio_func) {
61                                 .func = DCB_GPIO_TVDAC0,
62                                 .line = 4,
63                                 .log[0] = 0,
64                                 .log[1] = 1,
65                         };
66                         return 0;
67                 }
68         }
69
70         return -ENOENT;
71 }
72
73 static int
74 nouveau_gpio_set(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, int state)
75 {
76         struct dcb_gpio_func func;
77         int ret;
78
79         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
80         if (ret == 0) {
81                 int dir = !!(func.log[state] & 0x02);
82                 int out = !!(func.log[state] & 0x01);
83                 ret = nouveau_gpio_drive(gpio, idx, func.line, dir, out);
84         }
85
86         return ret;
87 }
88
89 static int
90 nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
91 {
92         struct dcb_gpio_func func;
93         int ret;
94
95         ret = nouveau_gpio_find(gpio, idx, tag, line, &func);
96         if (ret == 0) {
97                 ret = nouveau_gpio_sense(gpio, idx, func.line);
98                 if (ret >= 0)
99                         ret = (ret == (func.log[1] & 1));
100         }
101
102         return ret;
103 }
104
105 void
106 _nouveau_gpio_dtor(struct nouveau_object *object)
107 {
108         struct nouveau_gpio *gpio = (void *)object;
109         nouveau_event_destroy(&gpio->events);
110         nouveau_subdev_destroy(&gpio->base);
111 }
112
113 int
114 nouveau_gpio_create_(struct nouveau_object *parent,
115                      struct nouveau_object *engine,
116                      struct nouveau_oclass *oclass, int lines,
117                      int length, void **pobject)
118 {
119         struct nouveau_gpio *gpio;
120         int ret;
121
122         ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
123                                      length, pobject);
124         gpio = *pobject;
125         if (ret)
126                 return ret;
127
128         ret = nouveau_event_create(lines, &gpio->events);
129         if (ret)
130                 return ret;
131
132         gpio->find = nouveau_gpio_find;
133         gpio->set  = nouveau_gpio_set;
134         gpio->get  = nouveau_gpio_get;
135         return 0;
136 }
137
138 static struct dmi_system_id gpio_reset_ids[] = {
139         {
140                 .ident = "Apple Macbook 10,1",
141                 .matches = {
142                         DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
143                         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
144                 }
145         },
146         { }
147 };
148
149 int
150 nouveau_gpio_init(struct nouveau_gpio *gpio)
151 {
152         int ret = nouveau_subdev_init(&gpio->base);
153         if (ret == 0 && gpio->reset) {
154                 if (dmi_check_system(gpio_reset_ids))
155                         gpio->reset(gpio, DCB_GPIO_UNUSED);
156         }
157         return ret;
158 }