drm_dp_iic_helper.c revision 268564
1235783Skib/* 2235783Skib * Copyright �� 2009 Keith Packard 3235783Skib * 4235783Skib * Permission to use, copy, modify, distribute, and sell this software and its 5235783Skib * documentation for any purpose is hereby granted without fee, provided that 6235783Skib * the above copyright notice appear in all copies and that both that copyright 7235783Skib * notice and this permission notice appear in supporting documentation, and 8235783Skib * that the name of the copyright holders not be used in advertising or 9235783Skib * publicity pertaining to distribution of the software without specific, 10235783Skib * written prior permission. The copyright holders make no representations 11235783Skib * about the suitability of this software for any purpose. It is provided "as 12235783Skib * is" without express or implied warranty. 13235783Skib * 14235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20235783Skib * OF THIS SOFTWARE. 21235783Skib */ 22235783Skib 23235783Skib#include <sys/cdefs.h> 24235783Skib__FBSDID("$FreeBSD: head/sys/dev/drm2/drm_dp_iic_helper.c 268564 2014-07-12 06:23:42Z rpaulo $"); 25235783Skib 26235783Skib#include <sys/types.h> 27235783Skib#include <sys/kobj.h> 28235783Skib#include <sys/bus.h> 29268564Srpaulo#include <dev/iicbus/iic.h> 30235783Skib#include "iicbus_if.h" 31235783Skib#include <dev/iicbus/iiconf.h> 32235783Skib#include <dev/drm2/drmP.h> 33235783Skib#include <dev/drm2/drm_dp_helper.h> 34235783Skib 35235783Skibstatic int 36235783Skibiic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte, 37235783Skib uint8_t *read_byte) 38235783Skib{ 39235783Skib struct iic_dp_aux_data *aux_data; 40235783Skib int ret; 41235783Skib 42235783Skib aux_data = device_get_softc(idev); 43235783Skib ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte); 44235783Skib return (ret); 45235783Skib} 46235783Skib 47235783Skib/* 48235783Skib * I2C over AUX CH 49235783Skib */ 50235783Skib 51235783Skib/* 52235783Skib * Send the address. If the I2C link is running, this 'restarts' 53235783Skib * the connection with the new address, this is used for doing 54235783Skib * a write followed by a read (as needed for DDC) 55235783Skib */ 56235783Skibstatic int 57235783Skibiic_dp_aux_address(device_t idev, u16 address, bool reading) 58235783Skib{ 59235783Skib struct iic_dp_aux_data *aux_data; 60235783Skib int mode, ret; 61235783Skib 62235783Skib aux_data = device_get_softc(idev); 63235783Skib mode = MODE_I2C_START; 64235783Skib if (reading) 65235783Skib mode |= MODE_I2C_READ; 66235783Skib else 67235783Skib mode |= MODE_I2C_WRITE; 68235783Skib aux_data->address = address; 69235783Skib aux_data->running = true; 70235783Skib ret = iic_dp_aux_transaction(idev, mode, 0, NULL); 71235783Skib return (ret); 72235783Skib} 73235783Skib 74235783Skib/* 75235783Skib * Stop the I2C transaction. This closes out the link, sending 76235783Skib * a bare address packet with the MOT bit turned off 77235783Skib */ 78235783Skibstatic void 79235783Skibiic_dp_aux_stop(device_t idev, bool reading) 80235783Skib{ 81235783Skib struct iic_dp_aux_data *aux_data; 82235783Skib int mode; 83235783Skib 84235783Skib aux_data = device_get_softc(idev); 85235783Skib mode = MODE_I2C_STOP; 86235783Skib if (reading) 87235783Skib mode |= MODE_I2C_READ; 88235783Skib else 89235783Skib mode |= MODE_I2C_WRITE; 90235783Skib if (aux_data->running) { 91235783Skib (void)iic_dp_aux_transaction(idev, mode, 0, NULL); 92235783Skib aux_data->running = false; 93235783Skib } 94235783Skib} 95235783Skib 96235783Skib/* 97235783Skib * Write a single byte to the current I2C address, the 98235783Skib * the I2C link must be running or this returns -EIO 99235783Skib */ 100235783Skibstatic int 101235783Skibiic_dp_aux_put_byte(device_t idev, u8 byte) 102235783Skib{ 103235783Skib struct iic_dp_aux_data *aux_data; 104235783Skib int ret; 105235783Skib 106235783Skib aux_data = device_get_softc(idev); 107235783Skib 108235783Skib if (!aux_data->running) 109235783Skib return (EIO); 110235783Skib 111235783Skib ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL); 112235783Skib return (ret); 113235783Skib} 114235783Skib 115235783Skib/* 116235783Skib * Read a single byte from the current I2C address, the 117235783Skib * I2C link must be running or this returns -EIO 118235783Skib */ 119235783Skibstatic int 120235783Skibiic_dp_aux_get_byte(device_t idev, u8 *byte_ret) 121235783Skib{ 122235783Skib struct iic_dp_aux_data *aux_data; 123235783Skib int ret; 124235783Skib 125235783Skib aux_data = device_get_softc(idev); 126235783Skib 127235783Skib if (!aux_data->running) 128235783Skib return (EIO); 129235783Skib 130235783Skib ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret); 131235783Skib return (ret); 132235783Skib} 133235783Skib 134235783Skibstatic int 135235783Skibiic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num) 136235783Skib{ 137235783Skib u8 *buf; 138235783Skib int b, m, ret; 139235783Skib u16 len; 140235783Skib bool reading; 141235783Skib 142235783Skib ret = 0; 143235783Skib reading = false; 144235783Skib 145235783Skib for (m = 0; m < num; m++) { 146235783Skib len = msgs[m].len; 147235783Skib buf = msgs[m].buf; 148235783Skib reading = (msgs[m].flags & IIC_M_RD) != 0; 149249249Sdumbbell ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading); 150235783Skib if (ret != 0) 151235783Skib break; 152235783Skib if (reading) { 153235783Skib for (b = 0; b < len; b++) { 154235783Skib ret = iic_dp_aux_get_byte(idev, &buf[b]); 155235783Skib if (ret != 0) 156235783Skib break; 157235783Skib } 158235783Skib } else { 159235783Skib for (b = 0; b < len; b++) { 160235783Skib ret = iic_dp_aux_put_byte(idev, buf[b]); 161235783Skib if (ret != 0) 162235783Skib break; 163235783Skib } 164235783Skib } 165235783Skib if (ret != 0) 166235783Skib break; 167235783Skib } 168235783Skib iic_dp_aux_stop(idev, reading); 169235783Skib DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); 170235783Skib return (ret); 171235783Skib} 172235783Skib 173235783Skibstatic void 174235783Skibiic_dp_aux_reset_bus(device_t idev) 175235783Skib{ 176235783Skib 177235783Skib (void)iic_dp_aux_address(idev, 0, false); 178235783Skib (void)iic_dp_aux_stop(idev, false); 179235783Skib} 180235783Skib 181235783Skibstatic int 182235783Skibiic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr) 183235783Skib{ 184235783Skib 185235783Skib iic_dp_aux_reset_bus(idev); 186235783Skib return (0); 187235783Skib} 188235783Skib 189235783Skibstatic int 190235783Skibiic_dp_aux_prepare_bus(device_t idev) 191235783Skib{ 192235783Skib 193235783Skib /* adapter->retries = 3; */ 194235783Skib iic_dp_aux_reset_bus(idev); 195235783Skib return (0); 196235783Skib} 197235783Skib 198235783Skibstatic int 199235783Skibiic_dp_aux_probe(device_t idev) 200235783Skib{ 201235783Skib 202235783Skib return (BUS_PROBE_DEFAULT); 203235783Skib} 204235783Skib 205235783Skibstatic int 206235783Skibiic_dp_aux_attach(device_t idev) 207235783Skib{ 208235783Skib struct iic_dp_aux_data *aux_data; 209235783Skib 210235783Skib aux_data = device_get_softc(idev); 211235783Skib aux_data->port = device_add_child(idev, "iicbus", -1); 212235783Skib if (aux_data->port == NULL) 213235783Skib return (ENXIO); 214235783Skib device_quiet(aux_data->port); 215235783Skib bus_generic_attach(idev); 216235783Skib return (0); 217235783Skib} 218235783Skib 219235783Skibint 220235783Skibiic_dp_aux_add_bus(device_t dev, const char *name, 221235783Skib int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte), 222235783Skib void *priv, device_t *bus, device_t *adapter) 223235783Skib{ 224235783Skib device_t ibus; 225235783Skib struct iic_dp_aux_data *data; 226235783Skib int idx, error; 227235783Skib static int dp_bus_counter; 228235783Skib 229235783Skib mtx_lock(&Giant); 230235783Skib 231235783Skib idx = atomic_fetchadd_int(&dp_bus_counter, 1); 232235783Skib ibus = device_add_child(dev, "drm_iic_dp_aux", idx); 233235783Skib if (ibus == NULL) { 234235783Skib mtx_unlock(&Giant); 235235783Skib DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx); 236235783Skib return (-ENXIO); 237235783Skib } 238235783Skib device_quiet(ibus); 239235783Skib error = device_probe_and_attach(ibus); 240235783Skib if (error != 0) { 241235783Skib device_delete_child(dev, ibus); 242235783Skib mtx_unlock(&Giant); 243235783Skib DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n", 244235783Skib idx, error); 245235783Skib return (-error); 246235783Skib } 247235783Skib data = device_get_softc(ibus); 248235783Skib data->running = false; 249235783Skib data->address = 0; 250235783Skib data->aux_ch = ch; 251235783Skib data->priv = priv; 252235783Skib error = iic_dp_aux_prepare_bus(ibus); 253235783Skib if (error == 0) { 254235783Skib *bus = ibus; 255235783Skib *adapter = data->port; 256235783Skib } 257235783Skib mtx_unlock(&Giant); 258235783Skib return (error); 259235783Skib} 260235783Skib 261235783Skibstatic device_method_t drm_iic_dp_aux_methods[] = { 262235783Skib DEVMETHOD(device_probe, iic_dp_aux_probe), 263235783Skib DEVMETHOD(device_attach, iic_dp_aux_attach), 264254819Sdumbbell DEVMETHOD(device_detach, bus_generic_detach), 265235783Skib DEVMETHOD(iicbus_reset, iic_dp_aux_reset), 266235783Skib DEVMETHOD(iicbus_transfer, iic_dp_aux_xfer), 267235783Skib DEVMETHOD_END 268235783Skib}; 269235783Skibstatic driver_t drm_iic_dp_aux_driver = { 270235783Skib "drm_iic_dp_aux", 271235783Skib drm_iic_dp_aux_methods, 272235783Skib sizeof(struct iic_dp_aux_data) 273235783Skib}; 274235783Skibstatic devclass_t drm_iic_dp_aux_devclass; 275235783SkibDRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver, 276235783Skib drm_iic_dp_aux_devclass, 0, 0, SI_ORDER_SECOND); 277