GNU Linux-libre 4.19.281-gnu1
[releases.git] / drivers / gpu / drm / amd / display / dc / i2caux / i2c_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
35 /*
36  * Header of this unit
37  */
38
39 #include "i2c_hw_engine.h"
40
41 /*
42  * Post-requisites: headers required by this unit
43  */
44
45 /*
46  * This unit
47  */
48
49 /*
50  * @brief
51  * Cast 'struct i2c_engine *'
52  * to 'struct i2c_hw_engine *'
53  */
54 #define FROM_I2C_ENGINE(ptr) \
55         container_of((ptr), struct i2c_hw_engine, base)
56
57 /*
58  * @brief
59  * Cast 'struct engine *'
60  * to 'struct i2c_hw_engine *'
61  */
62 #define FROM_ENGINE(ptr) \
63         FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
64
65 enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type(
66         const struct engine *engine)
67 {
68         return I2CAUX_ENGINE_TYPE_I2C_DDC_HW;
69 }
70
71 bool dal_i2c_hw_engine_submit_request(
72         struct engine *engine,
73         struct i2caux_transaction_request *i2caux_request,
74         bool middle_of_transaction)
75 {
76         struct i2c_hw_engine *hw_engine = FROM_ENGINE(engine);
77
78         struct i2c_request_transaction_data request;
79
80         uint32_t transaction_timeout;
81
82         enum i2c_channel_operation_result operation_result;
83
84         bool result = false;
85
86         /* We need following:
87          * transaction length will not exceed
88          * the number of free bytes in HW buffer (minus one for address)*/
89
90         if (i2caux_request->payload.length >=
91                 hw_engine->funcs->get_hw_buffer_available_size(hw_engine)) {
92                 i2caux_request->status =
93                         I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW;
94                 return false;
95         }
96
97         if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
98                 request.action = middle_of_transaction ?
99                         I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
100                         I2CAUX_TRANSACTION_ACTION_I2C_READ;
101         else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
102                 request.action = middle_of_transaction ?
103                         I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
104                         I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
105         else {
106                 i2caux_request->status =
107                         I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
108                 /* [anaumov] in DAL2, there was no "return false" */
109                 return false;
110         }
111
112         request.address = (uint8_t)i2caux_request->payload.address;
113         request.length = i2caux_request->payload.length;
114         request.data = i2caux_request->payload.data;
115
116         /* obtain timeout value before submitting request */
117
118         transaction_timeout = hw_engine->funcs->get_transaction_timeout(
119                 hw_engine, i2caux_request->payload.length + 1);
120
121         hw_engine->base.funcs->submit_channel_request(
122                 &hw_engine->base, &request);
123
124         if ((request.status == I2C_CHANNEL_OPERATION_FAILED) ||
125                 (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) {
126                 i2caux_request->status =
127                         I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY;
128                 return false;
129         }
130
131         /* wait until transaction proceed */
132
133         operation_result = hw_engine->funcs->wait_on_operation_result(
134                 hw_engine,
135                 transaction_timeout,
136                 I2C_CHANNEL_OPERATION_ENGINE_BUSY);
137
138         /* update transaction status */
139
140         switch (operation_result) {
141         case I2C_CHANNEL_OPERATION_SUCCEEDED:
142                 i2caux_request->status =
143                         I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
144                 result = true;
145         break;
146         case I2C_CHANNEL_OPERATION_NO_RESPONSE:
147                 i2caux_request->status =
148                         I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
149         break;
150         case I2C_CHANNEL_OPERATION_TIMEOUT:
151                 i2caux_request->status =
152                         I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
153         break;
154         case I2C_CHANNEL_OPERATION_FAILED:
155                 i2caux_request->status =
156                         I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
157         break;
158         default:
159                 i2caux_request->status =
160                         I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
161         }
162
163         if (result && (i2caux_request->operation == I2CAUX_TRANSACTION_READ)) {
164                 struct i2c_reply_transaction_data reply;
165
166                 reply.data = i2caux_request->payload.data;
167                 reply.length = i2caux_request->payload.length;
168
169                 hw_engine->base.funcs->
170                         process_channel_reply(&hw_engine->base, &reply);
171         }
172
173         return result;
174 }
175
176 bool dal_i2c_hw_engine_acquire_engine(
177         struct i2c_engine *engine,
178         struct ddc *ddc)
179 {
180         enum gpio_result result;
181         uint32_t current_speed;
182
183         result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
184                 GPIO_DDC_CONFIG_TYPE_MODE_I2C);
185
186         if (result != GPIO_RESULT_OK)
187                 return false;
188
189         engine->base.ddc = ddc;
190
191         current_speed = engine->funcs->get_speed(engine);
192
193         if (current_speed)
194                 FROM_I2C_ENGINE(engine)->original_speed = current_speed;
195
196         return true;
197 }
198 /*
199  * @brief
200  * Queries in a loop for current engine status
201  * until retrieved status matches 'expected_result', or timeout occurs.
202  * Timeout given in microseconds
203  * and the status query frequency is also one per microsecond.
204  */
205 enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result(
206         struct i2c_hw_engine *engine,
207         uint32_t timeout,
208         enum i2c_channel_operation_result expected_result)
209 {
210         enum i2c_channel_operation_result result;
211         uint32_t i = 0;
212
213         if (!timeout)
214                 return I2C_CHANNEL_OPERATION_SUCCEEDED;
215
216         do {
217                 result = engine->base.funcs->get_channel_status(
218                         &engine->base, NULL);
219
220                 if (result != expected_result)
221                         break;
222
223                 udelay(1);
224
225                 ++i;
226         } while (i < timeout);
227
228         return result;
229 }
230
231 void dal_i2c_hw_engine_construct(
232         struct i2c_hw_engine *engine,
233         struct dc_context *ctx)
234 {
235         dal_i2c_engine_construct(&engine->base, ctx);
236         engine->original_speed = I2CAUX_DEFAULT_I2C_HW_SPEED;
237         engine->default_speed = I2CAUX_DEFAULT_I2C_HW_SPEED;
238 }
239
240 void dal_i2c_hw_engine_destruct(
241         struct i2c_hw_engine *engine)
242 {
243         dal_i2c_engine_destruct(&engine->base);
244 }