2 * Copyright © 2009 Keith Packard
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.
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
22 * $FreeBSD: head/sys/dev/drm2/drm_dp_iic_helper.c 249249 2013-04-08 08:37:57Z dumbbell $
25 #include <sys/types.h>
28 #include <bus/iicbus/iic.h>
29 #include "iicbus_if.h"
30 #include <bus/iicbus/iiconf.h>
32 #include <drm/drm_dp_helper.h>
35 iic_dp_aux_transaction(device_t idev
, int mode
, uint8_t write_byte
,
38 struct i2c_algo_dp_aux_data
*aux_data
;
41 aux_data
= device_get_softc(idev
);
42 ret
= (*aux_data
->aux_ch
)(idev
, mode
, write_byte
, read_byte
);
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)
56 iic_dp_aux_address(device_t idev
, u16 address
, bool reading
)
58 struct i2c_algo_dp_aux_data
*aux_data
;
61 aux_data
= device_get_softc(idev
);
62 mode
= MODE_I2C_START
;
64 mode
|= MODE_I2C_READ
;
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
);
74 * Stop the I2C transaction. This closes out the link, sending
75 * a bare address packet with the MOT bit turned off
78 iic_dp_aux_stop(device_t idev
, bool reading
)
80 struct i2c_algo_dp_aux_data
*aux_data
;
83 aux_data
= device_get_softc(idev
);
86 mode
|= MODE_I2C_READ
;
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;
96 * Write a single byte to the current I2C address, the
97 * the I2C link must be running or this returns -EIO
100 iic_dp_aux_put_byte(device_t idev
, u8 byte
)
102 struct i2c_algo_dp_aux_data
*aux_data
;
105 aux_data
= device_get_softc(idev
);
107 if (!aux_data
->running
)
110 ret
= iic_dp_aux_transaction(idev
, MODE_I2C_WRITE
, byte
, NULL
);
115 * Read a single byte from the current I2C address, the
116 * I2C link must be running or this returns -EIO
119 iic_dp_aux_get_byte(device_t idev
, u8
*byte_ret
)
121 struct i2c_algo_dp_aux_data
*aux_data
;
124 aux_data
= device_get_softc(idev
);
126 if (!aux_data
->running
)
129 ret
= iic_dp_aux_transaction(idev
, MODE_I2C_READ
, 0, byte_ret
);
134 iic_dp_aux_xfer(device_t idev
, struct iic_msg
*msgs
, uint32_t num
)
144 for (m
= 0; m
< num
; m
++) {
147 reading
= (msgs
[m
].flags
& IIC_M_RD
) != 0;
148 ret
= iic_dp_aux_address(idev
, msgs
[m
].slave
>> 1, reading
);
152 for (b
= 0; b
< len
; b
++) {
153 ret
= iic_dp_aux_get_byte(idev
, &buf
[b
]);
158 for (b
= 0; b
< len
; b
++) {
159 ret
= iic_dp_aux_put_byte(idev
, buf
[b
]);
167 iic_dp_aux_stop(idev
, reading
);
168 DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret
);
173 iic_dp_aux_reset_bus(device_t idev
)
176 (void)iic_dp_aux_address(idev
, 0, false);
177 (void)iic_dp_aux_stop(idev
, false);
181 iic_dp_aux_reset(device_t idev
, u_char speed
, u_char addr
, u_char
*oldaddr
)
184 iic_dp_aux_reset_bus(idev
);
189 iic_dp_aux_prepare_bus(device_t idev
)
192 /* adapter->retries = 3; */
193 iic_dp_aux_reset_bus(idev
);
198 iic_dp_aux_probe(device_t idev
)
201 return (BUS_PROBE_DEFAULT
);
205 iic_dp_aux_attach(device_t idev
)
207 struct i2c_algo_dp_aux_data
*aux_data
;
209 aux_data
= device_get_softc(idev
);
210 aux_data
->port
= device_add_child(idev
, "iicbus", -1);
211 if (aux_data
->port
== NULL
)
213 device_quiet(aux_data
->port
);
214 bus_generic_attach(idev
);
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
)
224 struct i2c_algo_dp_aux_data
*data
;
226 static int dp_bus_counter
;
228 idx
= atomic_fetchadd_int(&dp_bus_counter
, 1);
229 ibus
= device_add_child(dev
, "drm_iic_dp_aux", idx
);
231 DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx
);
235 error
= device_probe_and_attach(ibus
);
237 device_delete_child(dev
, ibus
);
238 DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n",
242 data
= device_get_softc(ibus
);
243 data
->running
= false;
247 error
= iic_dp_aux_prepare_bus(ibus
);
250 *adapter
= data
->port
;
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
),
263 static driver_t drm_iic_dp_aux_driver
= {
265 drm_iic_dp_aux_methods
,
266 sizeof(struct i2c_algo_dp_aux_data
)
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
);