linux/drivers/gpu/drm/drm_dp_i2c_helper.c
<<
>>
Prefs
   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 <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/delay.h>
  26#include <linux/init.h>
  27#include <linux/errno.h>
  28#include <linux/sched.h>
  29#include <linux/i2c.h>
  30#include "drm_dp_helper.h"
  31#include "drmP.h"
  32
  33/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
  34static int
  35i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
  36                            uint8_t write_byte, uint8_t *read_byte)
  37{
  38        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
  39        int ret;
  40        
  41        ret = (*algo_data->aux_ch)(adapter, mode,
  42                                   write_byte, read_byte);
  43        return ret;
  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 */
  55static int
  56i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
  57{
  58        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
  59        int mode = MODE_I2C_START;
  60        int ret;
  61
  62        if (reading)
  63                mode |= MODE_I2C_READ;
  64        else
  65                mode |= MODE_I2C_WRITE;
  66        algo_data->address = address;
  67        algo_data->running = true;
  68        ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
  69        return ret;
  70}
  71
  72/*
  73 * Stop the I2C transaction. This closes out the link, sending
  74 * a bare address packet with the MOT bit turned off
  75 */
  76static void
  77i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
  78{
  79        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
  80        int mode = MODE_I2C_STOP;
  81
  82        if (reading)
  83                mode |= MODE_I2C_READ;
  84        else
  85                mode |= MODE_I2C_WRITE;
  86        if (algo_data->running) {
  87                (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
  88                algo_data->running = false;
  89        }
  90}
  91
  92/*
  93 * Write a single byte to the current I2C address, the
  94 * the I2C link must be running or this returns -EIO
  95 */
  96static int
  97i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
  98{
  99        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
 100        int ret;
 101
 102        if (!algo_data->running)
 103                return -EIO;
 104
 105        ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
 106        return ret;
 107}
 108
 109/*
 110 * Read a single byte from the current I2C address, the
 111 * I2C link must be running or this returns -EIO
 112 */
 113static int
 114i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
 115{
 116        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
 117        int ret;
 118
 119        if (!algo_data->running)
 120                return -EIO;
 121
 122        ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
 123        return ret;
 124}
 125
 126static int
 127i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
 128                     struct i2c_msg *msgs,
 129                     int num)
 130{
 131        int ret = 0;
 132        bool reading = false;
 133        int m;
 134        int b;
 135
 136        for (m = 0; m < num; m++) {
 137                u16 len = msgs[m].len;
 138                u8 *buf = msgs[m].buf;
 139                reading = (msgs[m].flags & I2C_M_RD) != 0;
 140                ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
 141                if (ret < 0)
 142                        break;
 143                if (reading) {
 144                        for (b = 0; b < len; b++) {
 145                                ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
 146                                if (ret < 0)
 147                                        break;
 148                        }
 149                } else {
 150                        for (b = 0; b < len; b++) {
 151                                ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
 152                                if (ret < 0)
 153                                        break;
 154                        }
 155                }
 156                if (ret < 0)
 157                        break;
 158        }
 159        if (ret >= 0)
 160                ret = num;
 161        i2c_algo_dp_aux_stop(adapter, reading);
 162        DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
 163        return ret;
 164}
 165
 166static u32
 167i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
 168{
 169        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
 170               I2C_FUNC_SMBUS_READ_BLOCK_DATA |
 171               I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
 172               I2C_FUNC_10BIT_ADDR;
 173}
 174
 175static const struct i2c_algorithm i2c_dp_aux_algo = {
 176        .master_xfer    = i2c_algo_dp_aux_xfer,
 177        .functionality  = i2c_algo_dp_aux_functionality,
 178};
 179
 180static void
 181i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
 182{
 183        (void) i2c_algo_dp_aux_address(adapter, 0, false);
 184        (void) i2c_algo_dp_aux_stop(adapter, false);
 185                                           
 186}
 187
 188static int
 189i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
 190{
 191        adapter->algo = &i2c_dp_aux_algo;
 192        adapter->retries = 3;
 193        i2c_dp_aux_reset_bus(adapter);
 194        return 0;
 195}
 196
 197int
 198i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
 199{
 200        int error;
 201        
 202        error = i2c_dp_aux_prepare_bus(adapter);
 203        if (error)
 204                return error;
 205        error = i2c_add_adapter(adapter);
 206        return error;
 207}
 208EXPORT_SYMBOL(i2c_dp_aux_add_bus);
 209