GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / gpu / drm / amd / display / dc / i2caux / i2c_generic_hw_engine.c
1 /*
2  * Copyright 2012-15 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25
26 #include "dm_services.h"
27
28 /*
29  * Pre-requisites: headers required by header of this unit
30  */
31 #include "include/i2caux_interface.h"
32 #include "engine.h"
33 #include "i2c_engine.h"
34 #include "i2c_hw_engine.h"
35
36 /*
37  * Header of this unit
38  */
39
40 #include "i2c_generic_hw_engine.h"
41
42 /*
43  * Post-requisites: headers required by this unit
44  */
45
46 /*
47  * This unit
48  */
49
50 /*
51  * @brief
52  * Cast 'struct i2c_hw_engine *'
53  * to 'struct i2c_generic_hw_engine *'
54  */
55 #define FROM_I2C_HW_ENGINE(ptr) \
56         container_of((ptr), struct i2c_generic_hw_engine, base)
57
58 /*
59  * @brief
60  * Cast 'struct i2c_engine *'
61  * to 'struct i2c_generic_hw_engine *'
62  */
63 #define FROM_I2C_ENGINE(ptr) \
64         FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base))
65
66 /*
67  * @brief
68  * Cast 'struct engine *'
69  * to 'struct i2c_generic_hw_engine *'
70  */
71 #define FROM_ENGINE(ptr) \
72         FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
73
74 enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type(
75         const struct engine *engine)
76 {
77         return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW;
78 }
79
80 /*
81  * @brief
82  * Single transaction handling.
83  * Since transaction may be bigger than HW buffer size,
84  * it divides transaction to sub-transactions
85  * and uses batch transaction feature of the engine.
86  */
87 bool dal_i2c_generic_hw_engine_submit_request(
88         struct engine *engine,
89         struct i2caux_transaction_request *i2caux_request,
90         bool middle_of_transaction)
91 {
92         struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine);
93
94         struct i2c_hw_engine *base = &hw_engine->base;
95
96         uint32_t max_payload_size =
97                 base->funcs->get_hw_buffer_available_size(base);
98
99         bool initial_stop_bit = !middle_of_transaction;
100
101         struct i2c_generic_transaction_attributes attributes;
102
103         enum i2c_channel_operation_result operation_result =
104                 I2C_CHANNEL_OPERATION_FAILED;
105
106         bool result = false;
107
108         /* setup transaction initial properties */
109
110         uint8_t address = i2caux_request->payload.address;
111         uint8_t *current_payload = i2caux_request->payload.data;
112         uint32_t remaining_payload_size = i2caux_request->payload.length;
113
114         bool first_iteration = true;
115
116         if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
117                 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ;
118         else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
119                 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
120         else {
121                 i2caux_request->status =
122                         I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
123                 return false;
124         }
125
126         /* Do batch transaction.
127          * Divide read/write data into payloads which fit HW buffer size.
128          * 1. Single transaction:
129          *    start_bit = 1, stop_bit depends on session state, ack_on_read = 0;
130          * 2. Start of batch transaction:
131          *    start_bit = 1, stop_bit = 0, ack_on_read = 1;
132          * 3. Middle of batch transaction:
133          *    start_bit = 0, stop_bit = 0, ack_on_read = 1;
134          * 4. End of batch transaction:
135          *    start_bit = 0, stop_bit depends on session state, ack_on_read = 0.
136          * Session stop bit is set if 'middle_of_transaction' = 0. */
137
138         while (remaining_payload_size) {
139                 uint32_t current_transaction_size;
140                 uint32_t current_payload_size;
141
142                 bool last_iteration;
143                 bool stop_bit;
144
145                 /* Calculate current transaction size and payload size.
146                  * Transaction size = total number of bytes in transaction,
147                  * including slave's address;
148                  * Payload size = number of data bytes in transaction. */
149
150                 if (first_iteration) {
151                         /* In the first sub-transaction we send slave's address
152                          * thus we need to reserve one byte for it */
153                         current_transaction_size =
154                         (remaining_payload_size > max_payload_size - 1) ?
155                                 max_payload_size :
156                                 remaining_payload_size + 1;
157
158                         current_payload_size = current_transaction_size - 1;
159                 } else {
160                         /* Second and further sub-transactions will have
161                          * entire buffer reserved for data */
162                         current_transaction_size =
163                                 (remaining_payload_size > max_payload_size) ?
164                                 max_payload_size :
165                                 remaining_payload_size;
166
167                         current_payload_size = current_transaction_size;
168                 }
169
170                 last_iteration =
171                         (remaining_payload_size == current_payload_size);
172
173                 stop_bit = last_iteration ? initial_stop_bit : false;
174
175                 /* write slave device address */
176
177                 if (first_iteration)
178                         hw_engine->funcs->write_address(hw_engine, address);
179
180                 /* write current portion of data, if requested */
181
182                 if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
183                         hw_engine->funcs->write_data(
184                                 hw_engine,
185                                 current_payload,
186                                 current_payload_size);
187
188                 /* execute transaction */
189
190                 attributes.start_bit = first_iteration;
191                 attributes.stop_bit = stop_bit;
192                 attributes.last_read = last_iteration;
193                 attributes.transaction_size = current_transaction_size;
194
195                 hw_engine->funcs->execute_transaction(hw_engine, &attributes);
196
197                 /* wait until transaction is processed; if it fails - quit */
198
199                 operation_result = base->funcs->wait_on_operation_result(
200                         base,
201                         base->funcs->get_transaction_timeout(
202                                 base, current_transaction_size),
203                         I2C_CHANNEL_OPERATION_ENGINE_BUSY);
204
205                 if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED)
206                         break;
207
208                 /* read current portion of data, if requested */
209
210                 /* the read offset should be 1 for first sub-transaction,
211                  * and 0 for any next one */
212
213                 if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
214                         hw_engine->funcs->read_data(hw_engine, current_payload,
215                                 current_payload_size, first_iteration ? 1 : 0);
216
217                 /* update loop variables */
218
219                 first_iteration = false;
220                 current_payload += current_payload_size;
221                 remaining_payload_size -= current_payload_size;
222         }
223
224         /* update transaction status */
225
226         switch (operation_result) {
227         case I2C_CHANNEL_OPERATION_SUCCEEDED:
228                 i2caux_request->status =
229                         I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
230                 result = true;
231         break;
232         case I2C_CHANNEL_OPERATION_NO_RESPONSE:
233                 i2caux_request->status =
234                         I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
235         break;
236         case I2C_CHANNEL_OPERATION_TIMEOUT:
237                 i2caux_request->status =
238                         I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
239         break;
240         case I2C_CHANNEL_OPERATION_FAILED:
241                 i2caux_request->status =
242                         I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
243         break;
244         default:
245                 i2caux_request->status =
246                         I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
247         }
248
249         return result;
250 }
251
252 /*
253  * @brief
254  * Returns number of microseconds to wait until timeout to be considered
255  */
256 uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout(
257         const struct i2c_hw_engine *engine,
258         uint32_t length)
259 {
260         const struct i2c_engine *base = &engine->base;
261
262         uint32_t speed = base->funcs->get_speed(base);
263
264         if (!speed)
265                 return 0;
266
267         /* total timeout = period_timeout * (start + data bits count + stop) */
268
269         return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) *
270                 (1 + (length << 3) + 1);
271 }
272
273 void dal_i2c_generic_hw_engine_construct(
274         struct i2c_generic_hw_engine *engine,
275         struct dc_context *ctx)
276 {
277         dal_i2c_hw_engine_construct(&engine->base, ctx);
278 }
279
280 void dal_i2c_generic_hw_engine_destruct(
281         struct i2c_generic_hw_engine *engine)
282 {
283         dal_i2c_hw_engine_destruct(&engine->base);
284 }