1183000Smav/* 2183000Smav * Copyright �� 2009 Keith Packard 3162924Sjoel * 4162924Sjoel * Permission to use, copy, modify, distribute, and sell this software and its 5162924Sjoel * documentation for any purpose is hereby granted without fee, provided that 6162924Sjoel * the above copyright notice appear in all copies and that both that copyright 7162924Sjoel * notice and this permission notice appear in supporting documentation, and 8162924Sjoel * that the name of the copyright holders not be used in advertising or 9162924Sjoel * publicity pertaining to distribution of the software without specific, 10162924Sjoel * written prior permission. The copyright holders make no representations 11162924Sjoel * about the suitability of this software for any purpose. It is provided "as 12162924Sjoel * is" without express or implied warranty. 13162924Sjoel * 14162924Sjoel * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15162924Sjoel * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16162924Sjoel * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17162924Sjoel * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18162924Sjoel * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19162924Sjoel * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20162924Sjoel * OF THIS SOFTWARE. 21162924Sjoel */ 22162924Sjoel 23162924Sjoel#include <sys/cdefs.h> 24162924Sjoel__FBSDID("$FreeBSD$"); 25162924Sjoel 26162924Sjoel#include <sys/types.h> 27162924Sjoel#include <sys/kobj.h> 28230551Smav#include <sys/bus.h> 29162924Sjoel#include <dev/iicbus/iic.h> 30162924Sjoel#include "iicbus_if.h" 31162924Sjoel#include <dev/iicbus/iiconf.h> 32162924Sjoel#include <dev/drm2/drmP.h> 33162924Sjoel#include <dev/drm2/drm_dp_helper.h> 34162924Sjoel 35162924Sjoelstatic int 36162924Sjoeliic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte, 37162924Sjoel uint8_t *read_byte) 38162924Sjoel{ 39162924Sjoel struct iic_dp_aux_data *aux_data; 40162924Sjoel int ret; 41162924Sjoel 42162924Sjoel aux_data = device_get_softc(idev); 43162924Sjoel ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte); 44162924Sjoel return (ret); 45162924Sjoel} 46162924Sjoel 47162924Sjoel/* 48162924Sjoel * I2C over AUX CH 49183000Smav */ 50183000Smav 51183000Smav/* 52183000Smav * Send the address. If the I2C link is running, this 'restarts' 53183000Smav * the connection with the new address, this is used for doing 54162924Sjoel * a write followed by a read (as needed for DDC) 55162924Sjoel */ 56230130Smavstatic int 57230130Smaviic_dp_aux_address(device_t idev, u16 address, bool reading) 58230130Smav{ 59162924Sjoel struct iic_dp_aux_data *aux_data; 60183000Smav int mode, ret; 61184738Smav 62184751Smav aux_data = device_get_softc(idev); 63199247Smav mode = MODE_I2C_START; 64183000Smav if (reading) 65162924Sjoel mode |= MODE_I2C_READ; 66162924Sjoel else 67162924Sjoel mode |= MODE_I2C_WRITE; 68162924Sjoel aux_data->address = address; 69162924Sjoel aux_data->running = true; 70162924Sjoel ret = iic_dp_aux_transaction(idev, mode, 0, NULL); 71183000Smav return (ret); 72184738Smav} 73183000Smav 74183016Sjoel/* 75162924Sjoel * Stop the I2C transaction. This closes out the link, sending 76183016Sjoel * a bare address packet with the MOT bit turned off 77183016Sjoel */ 78183016Sjoelstatic void 79183016Sjoeliic_dp_aux_stop(device_t idev, bool reading) 80184738Smav{ 81230130Smav struct iic_dp_aux_data *aux_data; 82230130Smav int mode; 83230130Smav 84184738Smav aux_data = device_get_softc(idev); 85184738Smav mode = MODE_I2C_STOP; 86183564Smav if (reading) 87184738Smav mode |= MODE_I2C_READ; 88184738Smav else 89184738Smav mode |= MODE_I2C_WRITE; 90184738Smav if (aux_data->running) { 91184751Smav (void)iic_dp_aux_transaction(idev, mode, 0, NULL); 92166070Sjoel aux_data->running = false; 93166070Sjoel } 94166070Sjoel} 95166070Sjoel 96183000Smav/* 97183000Smav * Write a single byte to the current I2C address, the 98230130Smav * the I2C link must be running or this returns -EIO 99166070Sjoel */ 100230130Smavstatic int 101169279Sjoeliic_dp_aux_put_byte(device_t idev, u8 byte) 102230130Smav{ 103230130Smav struct iic_dp_aux_data *aux_data; 104230130Smav int ret; 105230130Smav 106230130Smav aux_data = device_get_softc(idev); 107230130Smav 108230130Smav if (!aux_data->running) 109230130Smav return (EIO); 110230130Smav 111230130Smav ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL); 112230130Smav return (ret); 113230130Smav} 114230130Smav 115230130Smav/* 116230130Smav * Read a single byte from the current I2C address, the 117169279Sjoel * I2C link must be running or this returns -EIO 118169279Sjoel */ 119169279Sjoelstatic int 120169279Sjoeliic_dp_aux_get_byte(device_t idev, u8 *byte_ret) 121169279Sjoel{ 122166070Sjoel struct iic_dp_aux_data *aux_data; 123166070Sjoel int ret; 124169279Sjoel 125169279Sjoel aux_data = device_get_softc(idev); 126169279Sjoel 127169279Sjoel if (!aux_data->running) 128183000Smav return (EIO); 129166070Sjoel 130166070Sjoel ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret); 131166070Sjoel return (ret); 132166070Sjoel} 133166070Sjoel 134166070Sjoelstatic int 135166094Sjoeliic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num) 136166070Sjoel{ 137166070Sjoel u8 *buf; 138202160Smav int b, m, ret; 139230130Smav u16 len; 140230130Smav bool reading; 141230130Smav 142230130Smav ret = 0; 143230130Smav reading = false; 144230130Smav 145230130Smav for (m = 0; m < num; m++) { 146230130Smav len = msgs[m].len; 147230130Smav buf = msgs[m].buf; 148230130Smav reading = (msgs[m].flags & IIC_M_RD) != 0; 149230130Smav ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading); 150230130Smav if (ret != 0) 151230130Smav break; 152230130Smav if (reading) { 153230130Smav for (b = 0; b < len; b++) { 154230130Smav ret = iic_dp_aux_get_byte(idev, &buf[b]); 155230130Smav if (ret != 0) 156230130Smav break; 157230130Smav } 158230130Smav } else { 159230130Smav for (b = 0; b < len; b++) { 160230130Smav ret = iic_dp_aux_put_byte(idev, buf[b]); 161230130Smav if (ret != 0) 162230130Smav break; 163230130Smav } 164230130Smav } 165230130Smav if (ret != 0) 166230130Smav break; 167230130Smav } 168230130Smav iic_dp_aux_stop(idev, reading); 169230130Smav DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); 170230130Smav return (ret); 171230130Smav} 172184738Smav 173184738Smavstatic void 174183000Smaviic_dp_aux_reset_bus(device_t idev) 175184738Smav{ 176184738Smav 177184738Smav (void)iic_dp_aux_address(idev, 0, false); 178230130Smav (void)iic_dp_aux_stop(idev, false); 179230130Smav} 180184738Smav 181184738Smavstatic int 182184738Smaviic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr) 183184738Smav{ 184184738Smav 185230551Smav iic_dp_aux_reset_bus(idev); 186230551Smav return (0); 187236443Sjoel} 188230551Smav 189230551Smavstatic int 190230551Smaviic_dp_aux_prepare_bus(device_t idev) 191230551Smav{ 192230551Smav 193230551Smav /* adapter->retries = 3; */ 194166070Sjoel iic_dp_aux_reset_bus(idev); 195230551Smav return (0); 196230551Smav} 197230551Smav 198183000Smavstatic int 199183000Smaviic_dp_aux_probe(device_t idev) 200183000Smav{ 201183000Smav 202183000Smav return (BUS_PROBE_DEFAULT); 203183000Smav} 204183000Smav 205184738Smavstatic int 206184738Smaviic_dp_aux_attach(device_t idev) 207184738Smav{ 208184738Smav struct iic_dp_aux_data *aux_data; 209184738Smav 210183000Smav aux_data = device_get_softc(idev); 211183000Smav aux_data->port = device_add_child(idev, "iicbus", -1); 212183000Smav if (aux_data->port == NULL) 213184738Smav return (ENXIO); 214183000Smav device_quiet(aux_data->port); 215230130Smav bus_generic_attach(idev); 216183000Smav return (0); 217184738Smav} 218184738Smav 219184738Smavint 220184738Smaviic_dp_aux_add_bus(device_t dev, const char *name, 221184738Smav int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte), 222184738Smav void *priv, device_t *bus, device_t *adapter) 223184738Smav{ 224183000Smav device_t ibus; 225183000Smav struct iic_dp_aux_data *data; 226184738Smav int idx, error; 227184738Smav static int dp_bus_counter; 228184738Smav 229184738Smav mtx_lock(&Giant); 230184738Smav 231184738Smav idx = atomic_fetchadd_int(&dp_bus_counter, 1); 232184738Smav ibus = device_add_child(dev, "drm_iic_dp_aux", idx); 233184738Smav if (ibus == NULL) { 234184738Smav mtx_unlock(&Giant); 235230130Smav DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx); 236230130Smav return (-ENXIO); 237230130Smav } 238235317Sgjb device_quiet(ibus); 239230130Smav error = device_probe_and_attach(ibus); 240230130Smav if (error != 0) { 241235317Sgjb device_delete_child(dev, ibus); 242230130Smav mtx_unlock(&Giant); 243230130Smav DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n", 244230130Smav idx, error); 245230130Smav return (-error); 246183000Smav } 247183000Smav data = device_get_softc(ibus); 248183000Smav data->running = false; 249183000Smav data->address = 0; 250183000Smav data->aux_ch = ch; 251183000Smav data->priv = priv; 252183000Smav error = iic_dp_aux_prepare_bus(ibus); 253183000Smav if (error == 0) { 254183000Smav *bus = ibus; 255183000Smav *adapter = data->port; 256183000Smav } 257183000Smav mtx_unlock(&Giant); 258183000Smav return (error); 259183000Smav} 260183000Smav 261183000Smavstatic device_method_t drm_iic_dp_aux_methods[] = { 262183000Smav DEVMETHOD(device_probe, iic_dp_aux_probe), 263183000Smav DEVMETHOD(device_attach, iic_dp_aux_attach), 264183000Smav DEVMETHOD(device_detach, bus_generic_detach), 265183000Smav DEVMETHOD(iicbus_reset, iic_dp_aux_reset), 266183000Smav DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer), 267183000Smav DEVMETHOD_END 268183000Smav}; 269184738Smavstatic driver_t drm_iic_dp_aux_driver = { 270183000Smav "drm_iic_dp_aux", 271184738Smav drm_iic_dp_aux_methods, 272183000Smav sizeof(struct iic_dp_aux_data) 273183000Smav}; 274184738Smavstatic devclass_t drm_iic_dp_aux_devclass; 275184738SmavDRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver, 276183000Smav drm_iic_dp_aux_devclass, 0, 0, SI_ORDER_SECOND); 277183000Smav