0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026 #include <drm/amdgpu_drm.h>
0027 #include "amdgpu.h"
0028 #include "atom.h"
0029 #include "amdgpu_atombios.h"
0030 #include "atombios_i2c.h"
0031
0032 #define TARGET_HW_I2C_CLOCK 50
0033
0034
0035 #define ATOM_MAX_HW_I2C_WRITE 3
0036 #define ATOM_MAX_HW_I2C_READ 255
0037
0038 static int amdgpu_atombios_i2c_process_i2c_ch(struct amdgpu_i2c_chan *chan,
0039 u8 slave_addr, u8 flags,
0040 u8 *buf, u8 num)
0041 {
0042 struct drm_device *dev = chan->dev;
0043 struct amdgpu_device *adev = drm_to_adev(dev);
0044 PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args;
0045 int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
0046 unsigned char *base;
0047 u16 out = cpu_to_le16(0);
0048 int r = 0;
0049
0050 memset(&args, 0, sizeof(args));
0051
0052 mutex_lock(&chan->mutex);
0053
0054 base = (unsigned char *)adev->mode_info.atom_context->scratch;
0055
0056 if (flags & HW_I2C_WRITE) {
0057 if (num > ATOM_MAX_HW_I2C_WRITE) {
0058 DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num);
0059 r = -EINVAL;
0060 goto done;
0061 }
0062 if (buf == NULL)
0063 args.ucRegIndex = 0;
0064 else
0065 args.ucRegIndex = buf[0];
0066 if (num)
0067 num--;
0068 if (num) {
0069 if (buf) {
0070 memcpy(&out, &buf[1], num);
0071 } else {
0072 DRM_ERROR("hw i2c: missing buf with num > 1\n");
0073 r = -EINVAL;
0074 goto done;
0075 }
0076 }
0077 args.lpI2CDataOut = cpu_to_le16(out);
0078 } else {
0079 args.ucRegIndex = 0;
0080 args.lpI2CDataOut = 0;
0081 }
0082
0083 args.ucFlag = flags;
0084 args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
0085 args.ucTransBytes = num;
0086 args.ucSlaveAddr = slave_addr << 1;
0087 args.ucLineNumber = chan->rec.i2c_id;
0088
0089 amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
0090
0091
0092 if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
0093 DRM_DEBUG_KMS("hw_i2c error\n");
0094 r = -EIO;
0095 goto done;
0096 }
0097
0098 if (!(flags & HW_I2C_WRITE))
0099 amdgpu_atombios_copy_swap(buf, base, num, false);
0100
0101 done:
0102 mutex_unlock(&chan->mutex);
0103
0104 return r;
0105 }
0106
0107 int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap,
0108 struct i2c_msg *msgs, int num)
0109 {
0110 struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
0111 struct i2c_msg *p;
0112 int i, remaining, current_count, buffer_offset, max_bytes, ret;
0113 u8 flags;
0114
0115
0116 p = &msgs[0];
0117 if ((num == 1) && (p->len == 0)) {
0118 ret = amdgpu_atombios_i2c_process_i2c_ch(i2c,
0119 p->addr, HW_I2C_WRITE,
0120 NULL, 0);
0121 if (ret)
0122 return ret;
0123 else
0124 return num;
0125 }
0126
0127 for (i = 0; i < num; i++) {
0128 p = &msgs[i];
0129 remaining = p->len;
0130 buffer_offset = 0;
0131
0132 if (p->flags & I2C_M_RD) {
0133 max_bytes = ATOM_MAX_HW_I2C_READ;
0134 flags = HW_I2C_READ;
0135 } else {
0136 max_bytes = ATOM_MAX_HW_I2C_WRITE;
0137 flags = HW_I2C_WRITE;
0138 }
0139 while (remaining) {
0140 if (remaining > max_bytes)
0141 current_count = max_bytes;
0142 else
0143 current_count = remaining;
0144 ret = amdgpu_atombios_i2c_process_i2c_ch(i2c,
0145 p->addr, flags,
0146 &p->buf[buffer_offset], current_count);
0147 if (ret)
0148 return ret;
0149 remaining -= current_count;
0150 buffer_offset += current_count;
0151 }
0152 }
0153
0154 return num;
0155 }
0156
0157 u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap)
0158 {
0159 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
0160 }
0161
0162 void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device *adev, u8 slave_addr, u8 line_number, u8 offset, u8 data)
0163 {
0164 PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args;
0165 int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
0166
0167 args.ucRegIndex = offset;
0168 args.lpI2CDataOut = data;
0169 args.ucFlag = 1;
0170 args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
0171 args.ucTransBytes = 1;
0172 args.ucSlaveAddr = slave_addr;
0173 args.ucLineNumber = line_number;
0174
0175 amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
0176 }