diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/cdv_intel_dp.c')
-rw-r--r-- | drivers/gpu/drm/gma500/cdv_intel_dp.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9f158eab517a..0fafb8e2483a 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c | |||
@@ -37,6 +37,201 @@ | |||
37 | #include "gma_display.h" | 37 | #include "gma_display.h" |
38 | #include <drm/drm_dp_helper.h> | 38 | #include <drm/drm_dp_helper.h> |
39 | 39 | ||
40 | /** | ||
41 | * struct i2c_algo_dp_aux_data - driver interface structure for i2c over dp | ||
42 | * aux algorithm | ||
43 | * @running: set by the algo indicating whether an i2c is ongoing or whether | ||
44 | * the i2c bus is quiescent | ||
45 | * @address: i2c target address for the currently ongoing transfer | ||
46 | * @aux_ch: driver callback to transfer a single byte of the i2c payload | ||
47 | */ | ||
48 | struct i2c_algo_dp_aux_data { | ||
49 | bool running; | ||
50 | u16 address; | ||
51 | int (*aux_ch) (struct i2c_adapter *adapter, | ||
52 | int mode, uint8_t write_byte, | ||
53 | uint8_t *read_byte); | ||
54 | }; | ||
55 | |||
56 | /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ | ||
57 | static int | ||
58 | i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, | ||
59 | uint8_t write_byte, uint8_t *read_byte) | ||
60 | { | ||
61 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
62 | int ret; | ||
63 | |||
64 | ret = (*algo_data->aux_ch)(adapter, mode, | ||
65 | write_byte, read_byte); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * I2C over AUX CH | ||
71 | */ | ||
72 | |||
73 | /* | ||
74 | * Send the address. If the I2C link is running, this 'restarts' | ||
75 | * the connection with the new address, this is used for doing | ||
76 | * a write followed by a read (as needed for DDC) | ||
77 | */ | ||
78 | static int | ||
79 | i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) | ||
80 | { | ||
81 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
82 | int mode = MODE_I2C_START; | ||
83 | int ret; | ||
84 | |||
85 | if (reading) | ||
86 | mode |= MODE_I2C_READ; | ||
87 | else | ||
88 | mode |= MODE_I2C_WRITE; | ||
89 | algo_data->address = address; | ||
90 | algo_data->running = true; | ||
91 | ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Stop the I2C transaction. This closes out the link, sending | ||
97 | * a bare address packet with the MOT bit turned off | ||
98 | */ | ||
99 | static void | ||
100 | i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) | ||
101 | { | ||
102 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
103 | int mode = MODE_I2C_STOP; | ||
104 | |||
105 | if (reading) | ||
106 | mode |= MODE_I2C_READ; | ||
107 | else | ||
108 | mode |= MODE_I2C_WRITE; | ||
109 | if (algo_data->running) { | ||
110 | (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); | ||
111 | algo_data->running = false; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Write a single byte to the current I2C address, the | ||
117 | * the I2C link must be running or this returns -EIO | ||
118 | */ | ||
119 | static int | ||
120 | i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) | ||
121 | { | ||
122 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
123 | int ret; | ||
124 | |||
125 | if (!algo_data->running) | ||
126 | return -EIO; | ||
127 | |||
128 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); | ||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Read a single byte from the current I2C address, the | ||
134 | * I2C link must be running or this returns -EIO | ||
135 | */ | ||
136 | static int | ||
137 | i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) | ||
138 | { | ||
139 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
140 | int ret; | ||
141 | |||
142 | if (!algo_data->running) | ||
143 | return -EIO; | ||
144 | |||
145 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); | ||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | static int | ||
150 | i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, | ||
151 | struct i2c_msg *msgs, | ||
152 | int num) | ||
153 | { | ||
154 | int ret = 0; | ||
155 | bool reading = false; | ||
156 | int m; | ||
157 | int b; | ||
158 | |||
159 | for (m = 0; m < num; m++) { | ||
160 | u16 len = msgs[m].len; | ||
161 | u8 *buf = msgs[m].buf; | ||
162 | reading = (msgs[m].flags & I2C_M_RD) != 0; | ||
163 | ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); | ||
164 | if (ret < 0) | ||
165 | break; | ||
166 | if (reading) { | ||
167 | for (b = 0; b < len; b++) { | ||
168 | ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); | ||
169 | if (ret < 0) | ||
170 | break; | ||
171 | } | ||
172 | } else { | ||
173 | for (b = 0; b < len; b++) { | ||
174 | ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); | ||
175 | if (ret < 0) | ||
176 | break; | ||
177 | } | ||
178 | } | ||
179 | if (ret < 0) | ||
180 | break; | ||
181 | } | ||
182 | if (ret >= 0) | ||
183 | ret = num; | ||
184 | i2c_algo_dp_aux_stop(adapter, reading); | ||
185 | DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static u32 | ||
190 | i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) | ||
191 | { | ||
192 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | | ||
193 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | | ||
194 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
195 | I2C_FUNC_10BIT_ADDR; | ||
196 | } | ||
197 | |||
198 | static const struct i2c_algorithm i2c_dp_aux_algo = { | ||
199 | .master_xfer = i2c_algo_dp_aux_xfer, | ||
200 | .functionality = i2c_algo_dp_aux_functionality, | ||
201 | }; | ||
202 | |||
203 | static void | ||
204 | i2c_dp_aux_reset_bus(struct i2c_adapter *adapter) | ||
205 | { | ||
206 | (void) i2c_algo_dp_aux_address(adapter, 0, false); | ||
207 | (void) i2c_algo_dp_aux_stop(adapter, false); | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) | ||
212 | { | ||
213 | adapter->algo = &i2c_dp_aux_algo; | ||
214 | adapter->retries = 3; | ||
215 | i2c_dp_aux_reset_bus(adapter); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * FIXME: This is the old dp aux helper, gma500 is the last driver that needs to | ||
221 | * be ported over to the new helper code in drm_dp_helper.c like i915 or radeon. | ||
222 | */ | ||
223 | static int __deprecated | ||
224 | i2c_dp_aux_add_bus(struct i2c_adapter *adapter) | ||
225 | { | ||
226 | int error; | ||
227 | |||
228 | error = i2c_dp_aux_prepare_bus(adapter); | ||
229 | if (error) | ||
230 | return error; | ||
231 | error = i2c_add_adapter(adapter); | ||
232 | return error; | ||
233 | } | ||
234 | |||
40 | #define _wait_for(COND, MS, W) ({ \ | 235 | #define _wait_for(COND, MS, W) ({ \ |
41 | unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ | 236 | unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ |
42 | int ret__ = 0; \ | 237 | int ret__ = 0; \ |