Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 and |
| 6 | * only version 2 as published by the Free Software Foundation. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | * |
| 13 | */ |
| 14 | |
| 15 | #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ |
| 16 | |
| 17 | #include <linux/clk.h> |
| 18 | #include "dp_power.h" |
| 19 | |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 20 | #define DP_CLIENT_NAME_SIZE 20 |
| 21 | |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 22 | struct dp_power_private { |
| 23 | struct dp_parser *parser; |
| 24 | struct platform_device *pdev; |
| 25 | struct clk *pixel_clk_rcg; |
| 26 | struct clk *pixel_parent; |
| 27 | |
| 28 | struct dp_power dp_power; |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 29 | struct sde_power_client *dp_core_client; |
| 30 | struct sde_power_handle *phandle; |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 31 | |
| 32 | bool core_clks_on; |
| 33 | bool link_clks_on; |
| 34 | }; |
| 35 | |
| 36 | static int dp_power_regulator_init(struct dp_power_private *power) |
| 37 | { |
| 38 | int rc = 0, i = 0, j = 0; |
| 39 | struct platform_device *pdev; |
| 40 | struct dp_parser *parser; |
| 41 | |
| 42 | parser = power->parser; |
| 43 | pdev = power->pdev; |
| 44 | |
| 45 | for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) { |
| 46 | rc = msm_dss_config_vreg(&pdev->dev, |
| 47 | parser->mp[i].vreg_config, |
| 48 | parser->mp[i].num_vreg, 1); |
| 49 | if (rc) { |
| 50 | pr_err("failed to init vregs for %s\n", |
| 51 | dp_parser_pm_name(i)); |
| 52 | for (j = i - 1; j >= DP_CORE_PM; j--) { |
| 53 | msm_dss_config_vreg(&pdev->dev, |
| 54 | parser->mp[j].vreg_config, |
| 55 | parser->mp[j].num_vreg, 0); |
| 56 | } |
| 57 | |
| 58 | goto error; |
| 59 | } |
| 60 | } |
| 61 | error: |
| 62 | return rc; |
| 63 | } |
| 64 | |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 65 | static void dp_power_regulator_deinit(struct dp_power_private *power) |
| 66 | { |
| 67 | int rc = 0, i = 0; |
| 68 | struct platform_device *pdev; |
| 69 | struct dp_parser *parser; |
| 70 | |
| 71 | parser = power->parser; |
| 72 | pdev = power->pdev; |
| 73 | |
| 74 | for (i = DP_CORE_PM; (i < DP_MAX_PM); i++) { |
| 75 | rc = msm_dss_config_vreg(&pdev->dev, |
| 76 | parser->mp[i].vreg_config, |
| 77 | parser->mp[i].num_vreg, 0); |
| 78 | if (rc) |
| 79 | pr_err("failed to deinit vregs for %s\n", |
| 80 | dp_parser_pm_name(i)); |
| 81 | } |
| 82 | } |
| 83 | |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 84 | static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable) |
| 85 | { |
| 86 | int rc = 0, i = 0, j = 0; |
| 87 | struct dp_parser *parser; |
| 88 | |
| 89 | parser = power->parser; |
| 90 | |
| 91 | for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { |
| 92 | rc = msm_dss_enable_vreg( |
| 93 | parser->mp[i].vreg_config, |
| 94 | parser->mp[i].num_vreg, enable); |
| 95 | if (rc) { |
| 96 | pr_err("failed to '%s' vregs for %s\n", |
| 97 | enable ? "enable" : "disable", |
| 98 | dp_parser_pm_name(i)); |
| 99 | if (enable) { |
| 100 | for (j = i-1; j >= DP_CORE_PM; j--) { |
| 101 | msm_dss_enable_vreg( |
| 102 | parser->mp[j].vreg_config, |
| 103 | parser->mp[j].num_vreg, 0); |
| 104 | } |
| 105 | } |
| 106 | goto error; |
| 107 | } |
| 108 | } |
| 109 | error: |
| 110 | return rc; |
| 111 | } |
| 112 | |
| 113 | static int dp_power_pinctrl_set(struct dp_power_private *power, bool active) |
| 114 | { |
| 115 | int rc = -EFAULT; |
| 116 | struct pinctrl_state *pin_state; |
| 117 | struct dp_parser *parser; |
| 118 | |
| 119 | parser = power->parser; |
| 120 | |
| 121 | if (IS_ERR_OR_NULL(parser->pinctrl.pin)) |
| 122 | return PTR_ERR(parser->pinctrl.pin); |
| 123 | |
| 124 | pin_state = active ? parser->pinctrl.state_active |
| 125 | : parser->pinctrl.state_suspend; |
| 126 | if (!IS_ERR_OR_NULL(pin_state)) { |
| 127 | rc = pinctrl_select_state(parser->pinctrl.pin, |
| 128 | pin_state); |
| 129 | if (rc) |
| 130 | pr_err("can not set %s pins\n", |
| 131 | active ? "dp_active" |
| 132 | : "dp_sleep"); |
| 133 | } else { |
| 134 | pr_err("invalid '%s' pinstate\n", |
| 135 | active ? "dp_active" |
| 136 | : "dp_sleep"); |
| 137 | } |
| 138 | |
| 139 | return rc; |
| 140 | } |
| 141 | |
| 142 | static int dp_power_clk_init(struct dp_power_private *power, bool enable) |
| 143 | { |
| 144 | int rc = 0; |
| 145 | struct dss_module_power *core, *ctrl; |
| 146 | struct device *dev; |
| 147 | |
| 148 | core = &power->parser->mp[DP_CORE_PM]; |
| 149 | ctrl = &power->parser->mp[DP_CTRL_PM]; |
| 150 | |
| 151 | dev = &power->pdev->dev; |
| 152 | |
| 153 | if (!core || !ctrl) { |
| 154 | pr_err("invalid power_data\n"); |
| 155 | rc = -EINVAL; |
| 156 | goto exit; |
| 157 | } |
| 158 | |
| 159 | if (enable) { |
| 160 | rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk); |
| 161 | if (rc) { |
| 162 | pr_err("failed to get %s clk. err=%d\n", |
| 163 | dp_parser_pm_name(DP_CORE_PM), rc); |
| 164 | goto exit; |
| 165 | } |
| 166 | |
| 167 | rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk); |
| 168 | if (rc) { |
| 169 | pr_err("failed to get %s clk. err=%d\n", |
| 170 | dp_parser_pm_name(DP_CTRL_PM), rc); |
| 171 | goto ctrl_get_error; |
| 172 | } |
| 173 | |
| 174 | power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg"); |
| 175 | if (IS_ERR(power->pixel_clk_rcg)) { |
| 176 | pr_debug("Unable to get DP pixel clk RCG\n"); |
| 177 | power->pixel_clk_rcg = NULL; |
| 178 | } |
| 179 | |
| 180 | power->pixel_parent = devm_clk_get(dev, "pixel_parent"); |
| 181 | if (IS_ERR(power->pixel_parent)) { |
| 182 | pr_debug("Unable to get DP pixel RCG parent\n"); |
| 183 | power->pixel_parent = NULL; |
| 184 | } |
| 185 | } else { |
| 186 | if (power->pixel_parent) |
| 187 | devm_clk_put(dev, power->pixel_parent); |
| 188 | |
| 189 | if (power->pixel_clk_rcg) |
| 190 | devm_clk_put(dev, power->pixel_clk_rcg); |
| 191 | |
| 192 | msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk); |
| 193 | msm_dss_put_clk(core->clk_config, core->num_clk); |
| 194 | } |
| 195 | |
| 196 | return rc; |
| 197 | |
| 198 | ctrl_get_error: |
| 199 | msm_dss_put_clk(core->clk_config, core->num_clk); |
| 200 | exit: |
| 201 | return rc; |
| 202 | } |
| 203 | |
| 204 | static int dp_power_clk_set_rate(struct dp_power_private *power, |
| 205 | enum dp_pm_type module, bool enable) |
| 206 | { |
| 207 | int rc = 0; |
| 208 | struct dss_module_power *mp; |
| 209 | |
| 210 | if (!power) { |
| 211 | pr_err("invalid power data\n"); |
| 212 | rc = -EINVAL; |
| 213 | goto exit; |
| 214 | } |
| 215 | |
| 216 | mp = &power->parser->mp[module]; |
| 217 | |
| 218 | if (enable) { |
| 219 | rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk); |
| 220 | if (rc) { |
| 221 | pr_err("failed to set clks rate.\n"); |
| 222 | goto exit; |
| 223 | } |
| 224 | |
| 225 | rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1); |
| 226 | if (rc) { |
| 227 | pr_err("failed to enable clks\n"); |
| 228 | goto exit; |
| 229 | } |
| 230 | } else { |
| 231 | rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0); |
| 232 | if (rc) { |
| 233 | pr_err("failed to disable clks\n"); |
| 234 | goto exit; |
| 235 | } |
| 236 | } |
| 237 | exit: |
| 238 | return rc; |
| 239 | } |
| 240 | |
| 241 | static int dp_power_clk_enable(struct dp_power *dp_power, |
| 242 | enum dp_pm_type pm_type, bool enable) |
| 243 | { |
| 244 | int rc = 0; |
| 245 | struct dss_module_power *mp; |
| 246 | struct dp_power_private *power; |
| 247 | |
| 248 | if (!dp_power) { |
| 249 | pr_err("invalid power data\n"); |
| 250 | rc = -EINVAL; |
| 251 | goto error; |
| 252 | } |
| 253 | |
| 254 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 255 | |
| 256 | mp = &power->parser->mp[pm_type]; |
| 257 | |
| 258 | if ((pm_type != DP_CORE_PM) && (pm_type != DP_CTRL_PM)) { |
| 259 | pr_err("unsupported power module: %s\n", |
| 260 | dp_parser_pm_name(pm_type)); |
| 261 | return -EINVAL; |
| 262 | } |
| 263 | |
| 264 | if (enable) { |
| 265 | if ((pm_type == DP_CORE_PM) |
| 266 | && (power->core_clks_on)) { |
| 267 | pr_debug("core clks already enabled\n"); |
| 268 | return 0; |
| 269 | } |
| 270 | |
| 271 | if ((pm_type == DP_CTRL_PM) |
| 272 | && (power->link_clks_on)) { |
| 273 | pr_debug("links clks already enabled\n"); |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) { |
| 278 | pr_debug("Need to enable core clks before link clks\n"); |
| 279 | |
| 280 | rc = dp_power_clk_set_rate(power, pm_type, enable); |
| 281 | if (rc) { |
| 282 | pr_err("failed to enable clks: %s. err=%d\n", |
| 283 | dp_parser_pm_name(DP_CORE_PM), rc); |
| 284 | goto error; |
| 285 | } else { |
| 286 | power->core_clks_on = true; |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | rc = dp_power_clk_set_rate(power, pm_type, enable); |
| 292 | if (rc) { |
| 293 | pr_err("failed to '%s' clks for: %s. err=%d\n", |
| 294 | enable ? "enable" : "disable", |
| 295 | dp_parser_pm_name(pm_type), rc); |
| 296 | goto error; |
| 297 | } |
| 298 | |
| 299 | if (pm_type == DP_CORE_PM) |
| 300 | power->core_clks_on = enable; |
| 301 | else |
| 302 | power->link_clks_on = enable; |
| 303 | |
| 304 | pr_debug("%s clocks for %s\n", |
| 305 | enable ? "enable" : "disable", |
| 306 | dp_parser_pm_name(pm_type)); |
| 307 | pr_debug("link_clks:%s core_clks:%s\n", |
| 308 | power->link_clks_on ? "on" : "off", |
| 309 | power->core_clks_on ? "on" : "off"); |
| 310 | error: |
| 311 | return rc; |
| 312 | } |
| 313 | |
| 314 | static int dp_power_request_gpios(struct dp_power_private *power) |
| 315 | { |
| 316 | int rc = 0, i; |
| 317 | struct device *dev; |
| 318 | struct dss_module_power *mp; |
| 319 | static const char * const gpio_names[] = { |
| 320 | "aux_enable", "aux_sel", "usbplug_cc", |
| 321 | }; |
| 322 | |
| 323 | if (!power) { |
| 324 | pr_err("invalid power data\n"); |
| 325 | return -EINVAL; |
| 326 | } |
| 327 | |
| 328 | dev = &power->pdev->dev; |
| 329 | mp = &power->parser->mp[DP_CORE_PM]; |
| 330 | |
| 331 | for (i = 0; i < ARRAY_SIZE(gpio_names); i++) { |
| 332 | unsigned int gpio = mp->gpio_config[i].gpio; |
| 333 | |
| 334 | if (gpio_is_valid(gpio)) { |
| 335 | rc = devm_gpio_request(dev, gpio, gpio_names[i]); |
| 336 | if (rc) { |
| 337 | pr_err("request %s gpio failed, rc=%d\n", |
| 338 | gpio_names[i], rc); |
| 339 | goto error; |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | return 0; |
| 344 | error: |
| 345 | for (i = 0; i < ARRAY_SIZE(gpio_names); i++) { |
| 346 | unsigned int gpio = mp->gpio_config[i].gpio; |
| 347 | |
| 348 | if (gpio_is_valid(gpio)) |
| 349 | gpio_free(gpio); |
| 350 | } |
| 351 | return rc; |
| 352 | } |
| 353 | |
| 354 | static bool dp_power_find_gpio(const char *gpio1, const char *gpio2) |
| 355 | { |
| 356 | return !!strnstr(gpio1, gpio2, strlen(gpio1)); |
| 357 | } |
| 358 | |
| 359 | static void dp_power_set_gpio(struct dp_power_private *power, bool flip) |
| 360 | { |
| 361 | int i; |
| 362 | struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM]; |
| 363 | struct dss_gpio *config = mp->gpio_config; |
| 364 | |
| 365 | for (i = 0; i < mp->num_gpio; i++) { |
| 366 | if (dp_power_find_gpio(config->gpio_name, "aux-sel")) |
| 367 | config->value = flip; |
| 368 | |
| 369 | if (gpio_is_valid(config->gpio)) { |
| 370 | pr_debug("gpio %s, value %d\n", config->gpio_name, |
| 371 | config->value); |
| 372 | |
| 373 | if (dp_power_find_gpio(config->gpio_name, "aux-en") || |
| 374 | dp_power_find_gpio(config->gpio_name, "aux-sel")) |
| 375 | gpio_direction_output(config->gpio, |
| 376 | config->value); |
| 377 | else |
| 378 | gpio_set_value(config->gpio, config->value); |
| 379 | |
| 380 | } |
| 381 | config++; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | static int dp_power_config_gpios(struct dp_power_private *power, bool flip, |
| 386 | bool enable) |
| 387 | { |
| 388 | int rc = 0, i; |
| 389 | struct dss_module_power *mp; |
| 390 | struct dss_gpio *config; |
| 391 | |
| 392 | mp = &power->parser->mp[DP_CORE_PM]; |
| 393 | config = mp->gpio_config; |
| 394 | |
| 395 | if (enable) { |
| 396 | rc = dp_power_request_gpios(power); |
| 397 | if (rc) { |
| 398 | pr_err("gpio request failed\n"); |
| 399 | return rc; |
| 400 | } |
| 401 | |
| 402 | dp_power_set_gpio(power, flip); |
| 403 | } else { |
| 404 | for (i = 0; i < mp->num_gpio; i++) { |
| 405 | gpio_set_value(config[i].gpio, 0); |
| 406 | gpio_free(config[i].gpio); |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | return 0; |
| 411 | } |
| 412 | |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 413 | static int dp_power_client_init(struct dp_power *dp_power, |
| 414 | struct sde_power_handle *phandle) |
| 415 | { |
| 416 | int rc = 0; |
| 417 | struct dp_power_private *power; |
| 418 | char dp_client_name[DP_CLIENT_NAME_SIZE]; |
| 419 | |
| 420 | if (!dp_power) { |
| 421 | pr_err("invalid power data\n"); |
| 422 | return -EINVAL; |
| 423 | } |
| 424 | |
| 425 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 426 | |
| 427 | rc = dp_power_regulator_init(power); |
| 428 | if (rc) { |
| 429 | pr_err("failed to init regulators\n"); |
| 430 | goto error_power; |
| 431 | } |
| 432 | |
| 433 | rc = dp_power_clk_init(power, true); |
| 434 | if (rc) { |
| 435 | pr_err("failed to init clocks\n"); |
| 436 | goto error_clk; |
| 437 | } |
| 438 | |
| 439 | power->phandle = phandle; |
| 440 | snprintf(dp_client_name, DP_CLIENT_NAME_SIZE, "dp_core_client"); |
| 441 | power->dp_core_client = sde_power_client_create(phandle, |
| 442 | dp_client_name); |
| 443 | if (IS_ERR_OR_NULL(power->dp_core_client)) { |
| 444 | pr_err("[%s] client creation failed for DP", dp_client_name); |
| 445 | rc = -EINVAL; |
| 446 | goto error_client; |
| 447 | } |
| 448 | return 0; |
| 449 | |
| 450 | error_client: |
| 451 | dp_power_clk_init(power, false); |
| 452 | error_clk: |
| 453 | dp_power_regulator_deinit(power); |
| 454 | error_power: |
| 455 | return rc; |
| 456 | } |
| 457 | |
| 458 | static void dp_power_client_deinit(struct dp_power *dp_power) |
| 459 | { |
| 460 | struct dp_power_private *power; |
| 461 | |
| 462 | if (!dp_power) { |
| 463 | pr_err("invalid power data\n"); |
| 464 | return; |
| 465 | } |
| 466 | |
| 467 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 468 | |
| 469 | sde_power_client_destroy(power->phandle, power->dp_core_client); |
| 470 | dp_power_clk_init(power, false); |
| 471 | dp_power_regulator_deinit(power); |
| 472 | } |
| 473 | |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 474 | static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power) |
| 475 | { |
| 476 | int rc = 0; |
| 477 | struct dp_power_private *power; |
| 478 | |
| 479 | if (!dp_power) { |
| 480 | pr_err("invalid power data\n"); |
| 481 | rc = -EINVAL; |
| 482 | goto exit; |
| 483 | } |
| 484 | |
| 485 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 486 | |
| 487 | if (power->pixel_clk_rcg && power->pixel_parent) |
| 488 | clk_set_parent(power->pixel_clk_rcg, power->pixel_parent); |
| 489 | exit: |
| 490 | return rc; |
| 491 | } |
| 492 | |
| 493 | static int dp_power_init(struct dp_power *dp_power, bool flip) |
| 494 | { |
| 495 | int rc = 0; |
| 496 | struct dp_power_private *power; |
| 497 | |
| 498 | if (!dp_power) { |
| 499 | pr_err("invalid power data\n"); |
| 500 | rc = -EINVAL; |
| 501 | goto exit; |
| 502 | } |
| 503 | |
| 504 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 505 | |
| 506 | rc = dp_power_regulator_ctrl(power, true); |
| 507 | if (rc) { |
| 508 | pr_err("failed to enable regulators\n"); |
| 509 | goto exit; |
| 510 | } |
| 511 | |
| 512 | rc = dp_power_pinctrl_set(power, true); |
| 513 | if (rc) { |
| 514 | pr_err("failed to set pinctrl state\n"); |
| 515 | goto err_pinctrl; |
| 516 | } |
| 517 | |
| 518 | rc = dp_power_config_gpios(power, flip, true); |
| 519 | if (rc) { |
| 520 | pr_err("failed to enable gpios\n"); |
| 521 | goto err_gpio; |
| 522 | } |
| 523 | |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 524 | rc = sde_power_resource_enable(power->phandle, |
| 525 | power->dp_core_client, true); |
| 526 | if (rc) { |
| 527 | pr_err("Power resource enable failed\n"); |
| 528 | goto err_sde_power; |
| 529 | } |
| 530 | |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 531 | rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true); |
| 532 | if (rc) { |
| 533 | pr_err("failed to enable DP core clocks\n"); |
| 534 | goto err_clk; |
| 535 | } |
| 536 | |
| 537 | return 0; |
| 538 | |
| 539 | err_clk: |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 540 | sde_power_resource_enable(power->phandle, power->dp_core_client, false); |
| 541 | err_sde_power: |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 542 | dp_power_config_gpios(power, flip, false); |
| 543 | err_gpio: |
| 544 | dp_power_pinctrl_set(power, false); |
| 545 | err_pinctrl: |
| 546 | dp_power_regulator_ctrl(power, false); |
| 547 | exit: |
| 548 | return rc; |
| 549 | } |
| 550 | |
| 551 | static int dp_power_deinit(struct dp_power *dp_power) |
| 552 | { |
| 553 | int rc = 0; |
| 554 | struct dp_power_private *power; |
| 555 | |
| 556 | if (!dp_power) { |
| 557 | pr_err("invalid power data\n"); |
| 558 | rc = -EINVAL; |
| 559 | goto exit; |
| 560 | } |
| 561 | |
| 562 | power = container_of(dp_power, struct dp_power_private, dp_power); |
| 563 | |
| 564 | dp_power_clk_enable(dp_power, DP_CORE_PM, false); |
Tatenda Chipeperekwa | db328a8 | 2017-09-29 15:15:56 -0700 | [diff] [blame] | 565 | /* |
| 566 | * If the display power on event was not successful, for example if |
| 567 | * there was a link training failure, then the link clocks could |
| 568 | * possibly still be on. In this scenario, we need to turn off the |
| 569 | * link clocks as soon as the cable is disconnected so that the clock |
| 570 | * state is cleaned up before subsequent connection events. |
| 571 | */ |
| 572 | if (power->link_clks_on) |
| 573 | dp_power_clk_enable(dp_power, DP_CTRL_PM, false); |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 574 | rc = sde_power_resource_enable(power->phandle, |
| 575 | power->dp_core_client, false); |
| 576 | if (rc) { |
| 577 | pr_err("Power resource enable failed, rc=%d\n", rc); |
| 578 | goto exit; |
| 579 | } |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 580 | dp_power_config_gpios(power, false, false); |
| 581 | dp_power_pinctrl_set(power, false); |
| 582 | dp_power_regulator_ctrl(power, false); |
| 583 | exit: |
| 584 | return rc; |
| 585 | } |
| 586 | |
| 587 | struct dp_power *dp_power_get(struct dp_parser *parser) |
| 588 | { |
| 589 | int rc = 0; |
| 590 | struct dp_power_private *power; |
| 591 | struct dp_power *dp_power; |
| 592 | |
| 593 | if (!parser) { |
| 594 | pr_err("invalid input\n"); |
| 595 | rc = -EINVAL; |
| 596 | goto error; |
| 597 | } |
| 598 | |
| 599 | power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL); |
| 600 | if (!power) { |
| 601 | rc = -ENOMEM; |
| 602 | goto error; |
| 603 | } |
| 604 | |
| 605 | power->parser = parser; |
| 606 | power->pdev = parser->pdev; |
| 607 | |
| 608 | dp_power = &power->dp_power; |
| 609 | |
| 610 | dp_power->init = dp_power_init; |
| 611 | dp_power->deinit = dp_power_deinit; |
| 612 | dp_power->clk_enable = dp_power_clk_enable; |
| 613 | dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent; |
Padmanabhan Komanduru | 4152f39 | 2017-05-11 17:53:55 -0700 | [diff] [blame] | 614 | dp_power->power_client_init = dp_power_client_init; |
| 615 | dp_power->power_client_deinit = dp_power_client_deinit; |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 616 | |
| 617 | return dp_power; |
| 618 | error: |
| 619 | return ERR_PTR(rc); |
| 620 | } |
| 621 | |
| 622 | void dp_power_put(struct dp_power *dp_power) |
| 623 | { |
Tatenda Chipeperekwa | 47ddbcd | 2017-08-30 13:43:21 -0700 | [diff] [blame] | 624 | struct dp_power_private *power = NULL; |
| 625 | |
| 626 | if (!dp_power) |
| 627 | return; |
| 628 | |
| 629 | power = container_of(dp_power, struct dp_power_private, dp_power); |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 630 | |
Ajay Singh Parmar | 3e5d49e | 2017-03-20 23:37:57 -0700 | [diff] [blame] | 631 | devm_kfree(&power->pdev->dev, power); |
| 632 | } |