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 "dc_bios_types.h" 33 34 /* 35 * Header of this unit 36 */ 37 38 #include "i2caux.h" 39 40 /* 41 * Post-requisites: headers required by this unit 42 */ 43 44 #include "engine.h" 45 #include "i2c_engine.h" 46 #include "aux_engine.h" 47 48 /* 49 * This unit 50 */ 51 52 #include "dce80/i2caux_dce80.h" 53 54 #include "dce100/i2caux_dce100.h" 55 56 #include "dce110/i2caux_dce110.h" 57 58 #include "dce112/i2caux_dce112.h" 59 60 #include "dce120/i2caux_dce120.h" 61 62 #if defined(CONFIG_DRM_AMD_DC_DCN1_0) 63 #include "dcn10/i2caux_dcn10.h" 64 #endif 65 66 #include "diagnostics/i2caux_diag.h" 67 68 /* 69 * @brief 70 * Plain API, available publicly 71 */ 72 73 struct i2caux *dal_i2caux_create( 74 struct dc_context *ctx) 75 { 76 if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) { 77 return dal_i2caux_diag_fpga_create(ctx); 78 } 79 80 switch (ctx->dce_version) { 81 case DCE_VERSION_8_0: 82 case DCE_VERSION_8_1: 83 case DCE_VERSION_8_3: 84 return dal_i2caux_dce80_create(ctx); 85 case DCE_VERSION_11_2: 86 case DCE_VERSION_11_22: 87 return dal_i2caux_dce112_create(ctx); 88 case DCE_VERSION_11_0: 89 return dal_i2caux_dce110_create(ctx); 90 case DCE_VERSION_10_0: 91 return dal_i2caux_dce100_create(ctx); 92 case DCE_VERSION_12_0: 93 return dal_i2caux_dce120_create(ctx); 94 #if defined(CONFIG_DRM_AMD_DC_DCN1_0) 95 case DCN_VERSION_1_0: 96 return dal_i2caux_dcn10_create(ctx); 97 #endif 98 99 default: 100 BREAK_TO_DEBUGGER(); 101 return NULL; 102 } 103 } 104 105 bool dal_i2caux_submit_i2c_command( 106 struct i2caux *i2caux, 107 struct ddc *ddc, 108 struct i2c_command *cmd) 109 { 110 struct i2c_engine *engine; 111 uint8_t index_of_payload = 0; 112 bool result; 113 114 if (!ddc) { 115 BREAK_TO_DEBUGGER(); 116 return false; 117 } 118 119 if (!cmd) { 120 BREAK_TO_DEBUGGER(); 121 return false; 122 } 123 124 /* 125 * default will be SW, however there is a feature flag in adapter 126 * service that determines whether SW i2c_engine will be available or 127 * not, if sw i2c is not available we will fallback to hw. This feature 128 * flag is set to not creating sw i2c engine for every dce except dce80 129 * currently 130 */ 131 switch (cmd->engine) { 132 case I2C_COMMAND_ENGINE_DEFAULT: 133 case I2C_COMMAND_ENGINE_SW: 134 /* try to acquire SW engine first, 135 * acquire HW engine if SW engine not available */ 136 engine = i2caux->funcs->acquire_i2c_sw_engine(i2caux, ddc); 137 138 if (!engine) 139 engine = i2caux->funcs->acquire_i2c_hw_engine( 140 i2caux, ddc); 141 break; 142 case I2C_COMMAND_ENGINE_HW: 143 default: 144 /* try to acquire HW engine first, 145 * acquire SW engine if HW engine not available */ 146 engine = i2caux->funcs->acquire_i2c_hw_engine(i2caux, ddc); 147 148 if (!engine) 149 engine = i2caux->funcs->acquire_i2c_sw_engine( 150 i2caux, ddc); 151 } 152 153 if (!engine) 154 return false; 155 156 engine->funcs->set_speed(engine, cmd->speed); 157 158 result = true; 159 160 while (index_of_payload < cmd->number_of_payloads) { 161 bool mot = (index_of_payload != cmd->number_of_payloads - 1); 162 163 struct i2c_payload *payload = cmd->payloads + index_of_payload; 164 165 struct i2caux_transaction_request request = { 0 }; 166 167 request.operation = payload->write ? 168 I2CAUX_TRANSACTION_WRITE : 169 I2CAUX_TRANSACTION_READ; 170 171 request.payload.address_space = 172 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; 173 request.payload.address = (payload->address << 1) | 174 !payload->write; 175 request.payload.length = payload->length; 176 request.payload.data = payload->data; 177 178 if (!engine->base.funcs->submit_request( 179 &engine->base, &request, mot)) { 180 result = false; 181 break; 182 } 183 184 ++index_of_payload; 185 } 186 187 i2caux->funcs->release_engine(i2caux, &engine->base); 188 189 return result; 190 } 191 192 bool dal_i2caux_submit_aux_command( 193 struct i2caux *i2caux, 194 struct ddc *ddc, 195 struct aux_command *cmd) 196 { 197 struct aux_engine *engine; 198 uint8_t index_of_payload = 0; 199 bool result; 200 bool mot; 201 202 if (!ddc) { 203 BREAK_TO_DEBUGGER(); 204 return false; 205 } 206 207 if (!cmd) { 208 BREAK_TO_DEBUGGER(); 209 return false; 210 } 211 212 engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc); 213 214 if (!engine) 215 return false; 216 217 engine->delay = cmd->defer_delay; 218 engine->max_defer_write_retry = cmd->max_defer_write_retry; 219 220 result = true; 221 222 while (index_of_payload < cmd->number_of_payloads) { 223 struct aux_payload *payload = cmd->payloads + index_of_payload; 224 struct i2caux_transaction_request request = { 0 }; 225 226 if (cmd->mot == I2C_MOT_UNDEF) 227 mot = (index_of_payload != cmd->number_of_payloads - 1); 228 else 229 mot = (cmd->mot == I2C_MOT_TRUE); 230 231 request.operation = payload->write ? 232 I2CAUX_TRANSACTION_WRITE : 233 I2CAUX_TRANSACTION_READ; 234 235 if (payload->i2c_over_aux) { 236 request.payload.address_space = 237 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C; 238 239 request.payload.address = (payload->address << 1) | 240 !payload->write; 241 } else { 242 request.payload.address_space = 243 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD; 244 245 request.payload.address = payload->address; 246 } 247 248 request.payload.length = payload->length; 249 request.payload.data = payload->data; 250 251 if (!engine->base.funcs->submit_request( 252 &engine->base, &request, mot)) { 253 result = false; 254 break; 255 } 256 257 ++index_of_payload; 258 } 259 260 i2caux->funcs->release_engine(i2caux, &engine->base); 261 262 return result; 263 } 264 265 static bool get_hw_supported_ddc_line( 266 struct ddc *ddc, 267 enum gpio_ddc_line *line) 268 { 269 enum gpio_ddc_line line_found; 270 271 *line = GPIO_DDC_LINE_UNKNOWN; 272 273 if (!ddc) { 274 BREAK_TO_DEBUGGER(); 275 return false; 276 } 277 278 if (!ddc->hw_info.hw_supported) 279 return false; 280 281 line_found = dal_ddc_get_line(ddc); 282 283 if (line_found >= GPIO_DDC_LINE_COUNT) 284 return false; 285 286 *line = line_found; 287 288 return true; 289 } 290 291 void dal_i2caux_configure_aux( 292 struct i2caux *i2caux, 293 struct ddc *ddc, 294 union aux_config cfg) 295 { 296 struct aux_engine *engine = 297 i2caux->funcs->acquire_aux_engine(i2caux, ddc); 298 299 if (!engine) 300 return; 301 302 engine->funcs->configure(engine, cfg); 303 304 i2caux->funcs->release_engine(i2caux, &engine->base); 305 } 306 307 void dal_i2caux_destroy( 308 struct i2caux **i2caux) 309 { 310 if (!i2caux || !*i2caux) { 311 BREAK_TO_DEBUGGER(); 312 return; 313 } 314 315 (*i2caux)->funcs->destroy(i2caux); 316 317 *i2caux = NULL; 318 } 319 320 /* 321 * @brief 322 * An utility function used by 'struct i2caux' and its descendants 323 */ 324 325 uint32_t dal_i2caux_get_reference_clock( 326 struct dc_bios *bios) 327 { 328 struct dc_firmware_info info = { { 0 } }; 329 330 if (bios->funcs->get_firmware_info(bios, &info) != BP_RESULT_OK) 331 return 0; 332 333 return info.pll_info.crystal_frequency; 334 } 335 336 /* 337 * @brief 338 * i2caux 339 */ 340 341 enum { 342 /* following are expressed in KHz */ 343 DEFAULT_I2C_SW_SPEED = 50, 344 DEFAULT_I2C_HW_SPEED = 50, 345 346 DEFAULT_I2C_SW_SPEED_100KHZ = 100, 347 DEFAULT_I2C_HW_SPEED_100KHZ = 100, 348 349 /* This is the timeout as defined in DP 1.2a, 350 * 2.3.4 "Detailed uPacket TX AUX CH State Description". */ 351 AUX_TIMEOUT_PERIOD = 400, 352 353 /* Ideally, the SW timeout should be just above 550usec 354 * which is programmed in HW. 355 * But the SW timeout of 600usec is not reliable, 356 * because on some systems, delay_in_microseconds() 357 * returns faster than it should. 358 * EPR #379763: by trial-and-error on different systems, 359 * 700usec is the minimum reliable SW timeout for polling 360 * the AUX_SW_STATUS.AUX_SW_DONE bit. 361 * This timeout expires *only* when there is 362 * AUX Error or AUX Timeout conditions - not during normal operation. 363 * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set 364 * at most within ~240usec. That means, 365 * increasing this timeout will not affect normal operation, 366 * and we'll timeout after 367 * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec. 368 * This timeout is especially important for 369 * resume from S3 and CTS. */ 370 SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4 371 }; 372 373 struct i2c_engine *dal_i2caux_acquire_i2c_sw_engine( 374 struct i2caux *i2caux, 375 struct ddc *ddc) 376 { 377 enum gpio_ddc_line line; 378 struct i2c_engine *engine = NULL; 379 380 if (get_hw_supported_ddc_line(ddc, &line)) 381 engine = i2caux->i2c_sw_engines[line]; 382 383 if (!engine) 384 engine = i2caux->i2c_generic_sw_engine; 385 386 if (!engine) 387 return NULL; 388 389 if (!engine->base.funcs->acquire(&engine->base, ddc)) 390 return NULL; 391 392 return engine; 393 } 394 395 struct aux_engine *dal_i2caux_acquire_aux_engine( 396 struct i2caux *i2caux, 397 struct ddc *ddc) 398 { 399 enum gpio_ddc_line line; 400 struct aux_engine *engine; 401 402 if (!get_hw_supported_ddc_line(ddc, &line)) 403 return NULL; 404 405 engine = i2caux->aux_engines[line]; 406 407 if (!engine) 408 return NULL; 409 410 if (!engine->base.funcs->acquire(&engine->base, ddc)) 411 return NULL; 412 413 return engine; 414 } 415 416 void dal_i2caux_release_engine( 417 struct i2caux *i2caux, 418 struct engine *engine) 419 { 420 engine->funcs->release_engine(engine); 421 422 dal_ddc_close(engine->ddc); 423 424 engine->ddc = NULL; 425 } 426 427 void dal_i2caux_construct( 428 struct i2caux *i2caux, 429 struct dc_context *ctx) 430 { 431 uint32_t i = 0; 432 433 i2caux->ctx = ctx; 434 do { 435 i2caux->i2c_sw_engines[i] = NULL; 436 i2caux->i2c_hw_engines[i] = NULL; 437 i2caux->aux_engines[i] = NULL; 438 439 ++i; 440 } while (i < GPIO_DDC_LINE_COUNT); 441 442 i2caux->i2c_generic_sw_engine = NULL; 443 i2caux->i2c_generic_hw_engine = NULL; 444 445 i2caux->aux_timeout_period = 446 SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD; 447 448 if (ctx->dce_version >= DCE_VERSION_11_2) { 449 i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED_100KHZ; 450 i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED_100KHZ; 451 } else { 452 i2caux->default_i2c_hw_speed = DEFAULT_I2C_HW_SPEED; 453 i2caux->default_i2c_sw_speed = DEFAULT_I2C_SW_SPEED; 454 } 455 } 456 457 void dal_i2caux_destruct( 458 struct i2caux *i2caux) 459 { 460 uint32_t i = 0; 461 462 if (i2caux->i2c_generic_hw_engine) 463 i2caux->i2c_generic_hw_engine->funcs->destroy( 464 &i2caux->i2c_generic_hw_engine); 465 466 if (i2caux->i2c_generic_sw_engine) 467 i2caux->i2c_generic_sw_engine->funcs->destroy( 468 &i2caux->i2c_generic_sw_engine); 469 470 do { 471 if (i2caux->aux_engines[i]) 472 i2caux->aux_engines[i]->funcs->destroy( 473 &i2caux->aux_engines[i]); 474 475 if (i2caux->i2c_hw_engines[i]) 476 i2caux->i2c_hw_engines[i]->funcs->destroy( 477 &i2caux->i2c_hw_engines[i]); 478 479 if (i2caux->i2c_sw_engines[i]) 480 i2caux->i2c_sw_engines[i]->funcs->destroy( 481 &i2caux->i2c_sw_engines[i]); 482 483 ++i; 484 } while (i < GPIO_DDC_LINE_COUNT); 485 } 486 487