text: // SPDX-License-Identifier: GPL-2.0-only
  /* Copyright (c) 2012-2015, 2017-2018, The Linux Foundation.
   * All rights reserved.
   */
  
  #include <linux/clk.h>
  #include <linux/clk/clk-conf.h>
  #include <linux/err.h>
  #include <linux/delay.h>
  #include <linux/of.h>
  
  #include <drm/drm_print.h>
  
  #include "dp_clk_util.h"
  
  void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk)
  {
  	int i;
  
  	for (i = num_clk - 1; i >= 0; i--) {
  		if (clk_arry[i].clk)
  			clk_put(clk_arry[i].clk);
  		clk_arry[i].clk = NULL;
  	}
  }
  
  int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk)
  {
  	int i, rc = 0;
  
  	for (i = 0; i < num_clk; i++) {
  		clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name);
  		rc = PTR_ERR_OR_ZERO(clk_arry[i].clk);
  		if (rc) {
  			DEV_ERR("%pS->%s: '%s' get failed. rc=%d\n",
  				__builtin_return_address(0), __func__,
  				clk_arry[i].clk_name, rc);
  			goto error;
  		}
  	}
  
  	return rc;
  
  error:
  	for (i--; i >= 0; i--) {
  		if (clk_arry[i].clk)
  			clk_put(clk_arry[i].clk);
  		clk_arry[i].clk = NULL;
  	}
  
  	return rc;
  }
  
  int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk)
  {
  	int i, rc = 0;
  
  	for (i = 0; i < num_clk; i++) {
  		if (clk_arry[i].clk) {
  			if (clk_arry[i].type != DSS_CLK_AHB) {
  				DEV_DBG("%pS->%s: '%s' rate %ld\n",
  					__builtin_return_address(0), __func__,
  					clk_arry[i].clk_name,
  					clk_arry[i].rate);
  				rc = clk_set_rate(clk_arry[i].clk,
  					clk_arry[i].rate);
  				if (rc) {
  					DEV_ERR("%pS->%s: %s failed. rc=%d\n",
  						__builtin_return_address(0),
  						__func__,
  						clk_arry[i].clk_name, rc);
  					break;
  				}
  			}
  		} else {
  			DEV_ERR("%pS->%s: '%s' is not available\n",
  				__builtin_return_address(0), __func__,
  				clk_arry[i].clk_name);
  			rc = -EPERM;
  			break;
  		}
  	}
  
  	return rc;
  }
  
  int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable)
  {
  	int i, rc = 0;
  
  	if (enable) {
  		for (i = 0; i < num_clk; i++) {
  			DEV_DBG("%pS->%s: enable '%s'\n",
  				__builtin_return_address(0), __func__,
  				clk_arry[i].clk_name);
  			rc = clk_prepare_enable(clk_arry[i].clk);
  			if (rc)
  				DEV_ERR("%pS->%s: %s en fail. rc=%d\n",
  					__builtin_return_address(0),
  					__func__,
  					clk_arry[i].clk_name, rc);
  
  			if (rc && i) {
  				msm_dss_enable_clk(&clk_arry[i - 1],
  					i - 1, false);
  				break;
  			}
  		}
  	} else {
  		for (i = num_clk - 1; i >= 0; i--) {
  			DEV_DBG("%pS->%s: disable '%s'\n",
  				__builtin_return_address(0), __func__,
  				clk_arry[i].clk_name);
  
  			clk_disable_unprepare(clk_arry[i].clk);
  		}
  	}
  
  	return rc;
  }
  