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 23 #include <sys/cdefs.h> 24 #include <sys/types.h> 25 #include <sys/kobj.h> 26 #include <sys/bus.h> 27 #include <dev/iicbus/iic.h> 28 #include "iicbus_if.h" 29 #include <dev/iicbus/iiconf.h> 30 #include <dev/drm2/drmP.h> 31 #include <dev/drm2/drm_dp_helper.h> 32 33 static int 34 iic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte, 35 uint8_t *read_byte) 36 { 37 struct iic_dp_aux_data *aux_data; 38 int ret; 39 40 aux_data = device_get_softc(idev); 41 ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte); 42 if (ret < 0) 43 return (ret); 44 return (0); 45 } 46 47 /* 48 * I2C over AUX CH 49 */ 50 51 /* 52 * Send the address. If the I2C link is running, this 'restarts' 53 * the connection with the new address, this is used for doing 54 * a write followed by a read (as needed for DDC) 55 */ 56 static int 57 iic_dp_aux_address(device_t idev, u16 address, bool reading) 58 { 59 struct iic_dp_aux_data *aux_data; 60 int mode, ret; 61 62 aux_data = device_get_softc(idev); 63 mode = MODE_I2C_START; 64 if (reading) 65 mode |= MODE_I2C_READ; 66 else 67 mode |= MODE_I2C_WRITE; 68 aux_data->address = address; 69 aux_data->running = true; 70 ret = iic_dp_aux_transaction(idev, mode, 0, NULL); 71 return (ret); 72 } 73 74 /* 75 * Stop the I2C transaction. This closes out the link, sending 76 * a bare address packet with the MOT bit turned off 77 */ 78 static void 79 iic_dp_aux_stop(device_t idev, bool reading) 80 { 81 struct iic_dp_aux_data *aux_data; 82 int mode; 83 84 aux_data = device_get_softc(idev); 85 mode = MODE_I2C_STOP; 86 if (reading) 87 mode |= MODE_I2C_READ; 88 else 89 mode |= MODE_I2C_WRITE; 90 if (aux_data->running) { 91 (void)iic_dp_aux_transaction(idev, mode, 0, NULL); 92 aux_data->running = false; 93 } 94 } 95 96 /* 97 * Write a single byte to the current I2C address, the 98 * the I2C link must be running or this returns -EIO 99 */ 100 static int 101 iic_dp_aux_put_byte(device_t idev, u8 byte) 102 { 103 struct iic_dp_aux_data *aux_data; 104 int ret; 105 106 aux_data = device_get_softc(idev); 107 108 if (!aux_data->running) 109 return (-EIO); 110 111 ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL); 112 return (ret); 113 } 114 115 /* 116 * Read a single byte from the current I2C address, the 117 * I2C link must be running or this returns -EIO 118 */ 119 static int 120 iic_dp_aux_get_byte(device_t idev, u8 *byte_ret) 121 { 122 struct iic_dp_aux_data *aux_data; 123 int ret; 124 125 aux_data = device_get_softc(idev); 126 127 if (!aux_data->running) 128 return (-EIO); 129 130 ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret); 131 return (ret); 132 } 133 134 static int 135 iic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num) 136 { 137 u8 *buf; 138 int b, m, ret; 139 u16 len; 140 bool reading; 141 142 ret = 0; 143 reading = false; 144 145 for (m = 0; m < num; m++) { 146 len = msgs[m].len; 147 buf = msgs[m].buf; 148 reading = (msgs[m].flags & IIC_M_RD) != 0; 149 ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading); 150 if (ret < 0) 151 break; 152 if (reading) { 153 for (b = 0; b < len; b++) { 154 ret = iic_dp_aux_get_byte(idev, &buf[b]); 155 if (ret != 0) 156 break; 157 } 158 } else { 159 for (b = 0; b < len; b++) { 160 ret = iic_dp_aux_put_byte(idev, buf[b]); 161 if (ret < 0) 162 break; 163 } 164 } 165 if (ret != 0) 166 break; 167 } 168 iic_dp_aux_stop(idev, reading); 169 DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); 170 return (-ret); 171 } 172 173 static void 174 iic_dp_aux_reset_bus(device_t idev) 175 { 176 177 (void)iic_dp_aux_address(idev, 0, false); 178 (void)iic_dp_aux_stop(idev, false); 179 } 180 181 static int 182 iic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr) 183 { 184 185 iic_dp_aux_reset_bus(idev); 186 return (0); 187 } 188 189 static int 190 iic_dp_aux_prepare_bus(device_t idev) 191 { 192 193 /* adapter->retries = 3; */ 194 iic_dp_aux_reset_bus(idev); 195 return (0); 196 } 197 198 static int 199 iic_dp_aux_probe(device_t idev) 200 { 201 202 return (BUS_PROBE_DEFAULT); 203 } 204 205 static int 206 iic_dp_aux_attach(device_t idev) 207 { 208 struct iic_dp_aux_data *aux_data; 209 210 aux_data = device_get_softc(idev); 211 aux_data->port = device_add_child(idev, "iicbus", -1); 212 if (aux_data->port == NULL) 213 return (ENXIO); 214 device_quiet(aux_data->port); 215 bus_generic_attach(idev); 216 return (0); 217 } 218 219 int 220 iic_dp_aux_add_bus(device_t dev, const char *name, 221 int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte), 222 void *priv, device_t *bus, device_t *adapter) 223 { 224 device_t ibus; 225 struct iic_dp_aux_data *data; 226 int idx, error; 227 static int dp_bus_counter; 228 229 bus_topo_lock(); 230 231 idx = atomic_fetchadd_int(&dp_bus_counter, 1); 232 ibus = device_add_child(dev, "drm_iic_dp_aux", idx); 233 if (ibus == NULL) { 234 bus_topo_unlock(); 235 DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx); 236 return (-ENXIO); 237 } 238 device_quiet(ibus); 239 error = device_probe_and_attach(ibus); 240 if (error != 0) { 241 device_delete_child(dev, ibus); 242 bus_topo_unlock(); 243 DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n", 244 idx, error); 245 return (-error); 246 } 247 data = device_get_softc(ibus); 248 data->running = false; 249 data->address = 0; 250 data->aux_ch = ch; 251 data->priv = priv; 252 error = iic_dp_aux_prepare_bus(ibus); 253 if (error == 0) { 254 *bus = ibus; 255 *adapter = data->port; 256 } 257 bus_topo_unlock(); 258 return (-error); 259 } 260 261 static device_method_t drm_iic_dp_aux_methods[] = { 262 DEVMETHOD(device_probe, iic_dp_aux_probe), 263 DEVMETHOD(device_attach, iic_dp_aux_attach), 264 DEVMETHOD(device_detach, bus_generic_detach), 265 DEVMETHOD(iicbus_reset, iic_dp_aux_reset), 266 DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer), 267 DEVMETHOD_END 268 }; 269 270 static driver_t drm_iic_dp_aux_driver = { 271 "drm_iic_dp_aux", 272 drm_iic_dp_aux_methods, 273 sizeof(struct iic_dp_aux_data) 274 }; 275 276 DRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver, 0, 0, 277 SI_ORDER_SECOND); 278