drm/linux: Add dmi_match()
[dragonfly.git] / sys / dev / drm / drm_dp_iic_helper.c
1 /*
2  * Copyright © 2009 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  *
22  * $FreeBSD: head/sys/dev/drm2/drm_dp_iic_helper.c 249249 2013-04-08 08:37:57Z dumbbell $
23  */
24
25 #include <sys/types.h>
26 #include <sys/kobj.h>
27 #include <sys/bus.h>
28 #include <bus/iicbus/iic.h>
29 #include "iicbus_if.h"
30 #include <bus/iicbus/iiconf.h>
31 #include <drm/drmP.h>
32 #include <drm/drm_dp_helper.h>
33
34 static int
35 iic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte,
36     uint8_t *read_byte)
37 {
38         struct i2c_algo_dp_aux_data *aux_data;
39         int ret;
40
41         aux_data = device_get_softc(idev);
42         ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte);
43         return (ret);
44 }
45
46 /*
47  * I2C over AUX CH
48  */
49
50 /*
51  * Send the address. If the I2C link is running, this 'restarts'
52  * the connection with the new address, this is used for doing
53  * a write followed by a read (as needed for DDC)
54  */
55 static int
56 iic_dp_aux_address(device_t idev, u16 address, bool reading)
57 {
58         struct i2c_algo_dp_aux_data *aux_data;
59         int mode, ret;
60
61         aux_data = device_get_softc(idev);
62         mode = MODE_I2C_START;
63         if (reading)
64                 mode |= MODE_I2C_READ;
65         else
66                 mode |= MODE_I2C_WRITE;
67         aux_data->address = address;
68         aux_data->running = true;
69         ret = iic_dp_aux_transaction(idev, mode, 0, NULL);
70         return (ret);
71 }
72
73 /*
74  * Stop the I2C transaction. This closes out the link, sending
75  * a bare address packet with the MOT bit turned off
76  */
77 static void
78 iic_dp_aux_stop(device_t idev, bool reading)
79 {
80         struct i2c_algo_dp_aux_data *aux_data;
81         int mode;
82
83         aux_data = device_get_softc(idev);
84         mode = MODE_I2C_STOP;
85         if (reading)
86                 mode |= MODE_I2C_READ;
87         else
88                 mode |= MODE_I2C_WRITE;
89         if (aux_data->running) {
90                 (void)iic_dp_aux_transaction(idev, mode, 0, NULL);
91                 aux_data->running = false;
92         }
93 }
94
95 /*
96  * Write a single byte to the current I2C address, the
97  * the I2C link must be running or this returns -EIO
98  */
99 static int
100 iic_dp_aux_put_byte(device_t idev, u8 byte)
101 {
102         struct i2c_algo_dp_aux_data *aux_data;
103         int ret;
104
105         aux_data = device_get_softc(idev);
106
107         if (!aux_data->running)
108                 return (EIO);
109
110         ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL);
111         return (ret);
112 }
113
114 /*
115  * Read a single byte from the current I2C address, the
116  * I2C link must be running or this returns -EIO
117  */
118 static int
119 iic_dp_aux_get_byte(device_t idev, u8 *byte_ret)
120 {
121         struct i2c_algo_dp_aux_data *aux_data;
122         int ret;
123
124         aux_data = device_get_softc(idev);
125
126         if (!aux_data->running)
127                 return (EIO);
128
129         ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret);
130         return (ret);
131 }
132
133 static int
134 iic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
135 {
136         u8 *buf;
137         int b, m, ret;
138         u16 len;
139         bool reading;
140
141         ret = 0;
142         reading = false;
143
144         for (m = 0; m < num; m++) {
145                 len = msgs[m].len;
146                 buf = msgs[m].buf;
147                 reading = (msgs[m].flags & IIC_M_RD) != 0;
148                 ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading);
149                 if (ret != 0)
150                         break;
151                 if (reading) {
152                         for (b = 0; b < len; b++) {
153                                 ret = iic_dp_aux_get_byte(idev, &buf[b]);
154                                 if (ret != 0)
155                                         break;
156                         }
157                 } else {
158                         for (b = 0; b < len; b++) {
159                                 ret = iic_dp_aux_put_byte(idev, buf[b]);
160                                 if (ret != 0)
161                                         break;
162                         }
163                 }
164                 if (ret != 0)
165                         break;
166         }
167         iic_dp_aux_stop(idev, reading);
168         DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
169         return (ret);
170 }
171
172 static void
173 iic_dp_aux_reset_bus(device_t idev)
174 {
175
176         (void)iic_dp_aux_address(idev, 0, false);
177         (void)iic_dp_aux_stop(idev, false);
178 }
179
180 static int
181 iic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
182 {
183
184         iic_dp_aux_reset_bus(idev);
185         return (0);                                
186 }
187
188 static int
189 iic_dp_aux_prepare_bus(device_t idev)
190 {
191
192         /* adapter->retries = 3; */
193         iic_dp_aux_reset_bus(idev);
194         return (0);
195 }
196
197 static int
198 iic_dp_aux_probe(device_t idev)
199 {
200
201         return (BUS_PROBE_DEFAULT);
202 }
203
204 static int
205 iic_dp_aux_attach(device_t idev)
206 {
207         struct i2c_algo_dp_aux_data *aux_data;
208
209         aux_data = device_get_softc(idev);
210         aux_data->port = device_add_child(idev, "iicbus", -1);
211         if (aux_data->port == NULL)
212                 return (ENXIO);
213         device_quiet(aux_data->port);
214         bus_generic_attach(idev);
215         return (0);
216 }
217
218 int
219 iic_dp_aux_add_bus(device_t dev, const char *name,
220     int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte),
221     void *priv, device_t *bus, device_t *adapter)
222 {
223         device_t ibus;
224         struct i2c_algo_dp_aux_data *data;
225         int idx, error;
226         static int dp_bus_counter;
227
228         idx = atomic_fetchadd_int(&dp_bus_counter, 1);
229         ibus = device_add_child(dev, "drm_iic_dp_aux", idx);
230         if (ibus == NULL) {
231                 DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx);
232                 return (-ENXIO);
233         }
234         device_quiet(ibus);
235         error = device_probe_and_attach(ibus);
236         if (error != 0) {
237                 device_delete_child(dev, ibus);
238                 DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n",
239                     idx, error);
240                 return (-error);
241         }
242         data = device_get_softc(ibus);
243         data->running = false;
244         data->address = 0;
245         data->aux_ch = ch;
246         data->priv = priv;
247         error = iic_dp_aux_prepare_bus(ibus);
248         if (error == 0) {
249                 *bus = ibus;
250                 *adapter = data->port;
251         }
252         return (error);
253 }
254
255 static device_method_t drm_iic_dp_aux_methods[] = {
256         DEVMETHOD(device_probe,         iic_dp_aux_probe),
257         DEVMETHOD(device_attach,        iic_dp_aux_attach),
258         DEVMETHOD(device_detach,        bus_generic_detach),
259         DEVMETHOD(iicbus_reset,         iic_dp_aux_reset),
260         DEVMETHOD(iicbus_transfer,      iic_dp_aux_xfer),
261         DEVMETHOD_END
262 };
263 static driver_t drm_iic_dp_aux_driver = {
264         "drm_iic_dp_aux",
265         drm_iic_dp_aux_methods,
266         sizeof(struct i2c_algo_dp_aux_data)
267 };
268 static devclass_t drm_iic_dp_aux_devclass;
269 DRIVER_MODULE_ORDERED(drm_iic_dp_aux, drm, drm_iic_dp_aux_driver,
270     drm_iic_dp_aux_devclass, NULL, NULL, SI_ORDER_SECOND);