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/types.h> 24 #include <sys/kobj.h> 25 #include <sys/bus.h> 26 #include <dev/iicbus/iic.h> 27 #include "iicbus_if.h" 28 #include <dev/iicbus/iiconf.h> 29 #include <dev/drm2/drmP.h> 30 #include <dev/drm2/drm_dp_helper.h> 31 32 static int 33 iic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte, 34 uint8_t *read_byte) 35 { 36 struct iic_dp_aux_data *aux_data; 37 int ret; 38 39 aux_data = device_get_softc(idev); 40 ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte); 41 if (ret < 0) 42 return (ret); 43 return (0); 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 iic_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 iic_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 iic_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 iic_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 iic_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 iic_dp_aux_data *data; 225 int idx, error; 226 static int dp_bus_counter; 227 228 bus_topo_lock(); 229 230 idx = atomic_fetchadd_int(&dp_bus_counter, 1); 231 ibus = device_add_child(dev, "drm_iic_dp_aux", idx); 232 if (ibus == NULL) { 233 bus_topo_unlock(); 234 DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx); 235 return (-ENXIO); 236 } 237 device_quiet(ibus); 238 error = device_probe_and_attach(ibus); 239 if (error != 0) { 240 device_delete_child(dev, ibus); 241 bus_topo_unlock(); 242 DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n", 243 idx, error); 244 return (-error); 245 } 246 data = device_get_softc(ibus); 247 data->running = false; 248 data->address = 0; 249 data->aux_ch = ch; 250 data->priv = priv; 251 error = iic_dp_aux_prepare_bus(ibus); 252 if (error == 0) { 253 *bus = ibus; 254 *adapter = data->port; 255 } 256 bus_topo_unlock(); 257 return (-error); 258 } 259 260 static device_method_t drm_iic_dp_aux_methods[] = { 261 DEVMETHOD(device_probe, iic_dp_aux_probe), 262 DEVMETHOD(device_attach, iic_dp_aux_attach), 263 DEVMETHOD(device_detach, bus_generic_detach), 264 DEVMETHOD(iicbus_reset, iic_dp_aux_reset), 265 DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer), 266 DEVMETHOD_END 267 }; 268 269 static driver_t drm_iic_dp_aux_driver = { 270 "drm_iic_dp_aux", 271 drm_iic_dp_aux_methods, 272 sizeof(struct iic_dp_aux_data) 273 }; 274 275 DRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver, 0, 0, 276 SI_ORDER_SECOND); 277