Path: blob/master/drivers/gpu/drm/drm_dp_i2c_helper.c
15111 views
/*1* Copyright © 2009 Keith Packard2*3* Permission to use, copy, modify, distribute, and sell this software and its4* documentation for any purpose is hereby granted without fee, provided that5* the above copyright notice appear in all copies and that both that copyright6* notice and this permission notice appear in supporting documentation, and7* that the name of the copyright holders not be used in advertising or8* publicity pertaining to distribution of the software without specific,9* written prior permission. The copyright holders make no representations10* about the suitability of this software for any purpose. It is provided "as11* is" without express or implied warranty.12*13* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,14* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO15* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR16* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,17* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER18* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE19* OF THIS SOFTWARE.20*/2122#include <linux/kernel.h>23#include <linux/module.h>24#include <linux/delay.h>25#include <linux/init.h>26#include <linux/errno.h>27#include <linux/sched.h>28#include <linux/i2c.h>29#include "drm_dp_helper.h"30#include "drmP.h"3132/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */33static int34i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,35uint8_t write_byte, uint8_t *read_byte)36{37struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;38int ret;3940ret = (*algo_data->aux_ch)(adapter, mode,41write_byte, read_byte);42return ret;43}4445/*46* I2C over AUX CH47*/4849/*50* Send the address. If the I2C link is running, this 'restarts'51* the connection with the new address, this is used for doing52* a write followed by a read (as needed for DDC)53*/54static int55i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)56{57struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;58int mode = MODE_I2C_START;59int ret;6061if (reading)62mode |= MODE_I2C_READ;63else64mode |= MODE_I2C_WRITE;65algo_data->address = address;66algo_data->running = true;67ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);68return ret;69}7071/*72* Stop the I2C transaction. This closes out the link, sending73* a bare address packet with the MOT bit turned off74*/75static void76i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)77{78struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;79int mode = MODE_I2C_STOP;8081if (reading)82mode |= MODE_I2C_READ;83else84mode |= MODE_I2C_WRITE;85if (algo_data->running) {86(void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);87algo_data->running = false;88}89}9091/*92* Write a single byte to the current I2C address, the93* the I2C link must be running or this returns -EIO94*/95static int96i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)97{98struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;99int ret;100101if (!algo_data->running)102return -EIO;103104ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);105return ret;106}107108/*109* Read a single byte from the current I2C address, the110* I2C link must be running or this returns -EIO111*/112static int113i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)114{115struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;116int ret;117118if (!algo_data->running)119return -EIO;120121ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);122return ret;123}124125static int126i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,127struct i2c_msg *msgs,128int num)129{130int ret = 0;131bool reading = false;132int m;133int b;134135for (m = 0; m < num; m++) {136u16 len = msgs[m].len;137u8 *buf = msgs[m].buf;138reading = (msgs[m].flags & I2C_M_RD) != 0;139ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);140if (ret < 0)141break;142if (reading) {143for (b = 0; b < len; b++) {144ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);145if (ret < 0)146break;147}148} else {149for (b = 0; b < len; b++) {150ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);151if (ret < 0)152break;153}154}155if (ret < 0)156break;157}158if (ret >= 0)159ret = num;160i2c_algo_dp_aux_stop(adapter, reading);161DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);162return ret;163}164165static u32166i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)167{168return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |169I2C_FUNC_SMBUS_READ_BLOCK_DATA |170I2C_FUNC_SMBUS_BLOCK_PROC_CALL |171I2C_FUNC_10BIT_ADDR;172}173174static const struct i2c_algorithm i2c_dp_aux_algo = {175.master_xfer = i2c_algo_dp_aux_xfer,176.functionality = i2c_algo_dp_aux_functionality,177};178179static void180i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)181{182(void) i2c_algo_dp_aux_address(adapter, 0, false);183(void) i2c_algo_dp_aux_stop(adapter, false);184185}186187static int188i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)189{190adapter->algo = &i2c_dp_aux_algo;191adapter->retries = 3;192i2c_dp_aux_reset_bus(adapter);193return 0;194}195196int197i2c_dp_aux_add_bus(struct i2c_adapter *adapter)198{199int error;200201error = i2c_dp_aux_prepare_bus(adapter);202if (error)203return error;204error = i2c_add_adapter(adapter);205return error;206}207EXPORT_SYMBOL(i2c_dp_aux_add_bus);208209210