143ecec16SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
243ecec16SMauro Carvalho Chehab /*
343ecec16SMauro Carvalho Chehab  * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c
443ecec16SMauro Carvalho Chehab  *
543ecec16SMauro Carvalho Chehab  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
643ecec16SMauro Carvalho Chehab  *		http://www.samsung.com/
743ecec16SMauro Carvalho Chehab  */
843ecec16SMauro Carvalho Chehab 
943ecec16SMauro Carvalho Chehab #include <linux/clk.h>
1043ecec16SMauro Carvalho Chehab #include <linux/err.h>
1143ecec16SMauro Carvalho Chehab #include <linux/platform_device.h>
1243ecec16SMauro Carvalho Chehab #include <linux/pm_runtime.h>
1343ecec16SMauro Carvalho Chehab #include "s5p_mfc_common.h"
1443ecec16SMauro Carvalho Chehab #include "s5p_mfc_debug.h"
1543ecec16SMauro Carvalho Chehab #include "s5p_mfc_pm.h"
1643ecec16SMauro Carvalho Chehab 
1743ecec16SMauro Carvalho Chehab static struct s5p_mfc_pm *pm;
1843ecec16SMauro Carvalho Chehab static struct s5p_mfc_dev *p_dev;
1943ecec16SMauro Carvalho Chehab static atomic_t clk_ref;
2043ecec16SMauro Carvalho Chehab 
s5p_mfc_init_pm(struct s5p_mfc_dev * dev)2143ecec16SMauro Carvalho Chehab int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
2243ecec16SMauro Carvalho Chehab {
2343ecec16SMauro Carvalho Chehab 	int i;
2443ecec16SMauro Carvalho Chehab 
2543ecec16SMauro Carvalho Chehab 	pm = &dev->pm;
2643ecec16SMauro Carvalho Chehab 	p_dev = dev;
2743ecec16SMauro Carvalho Chehab 
2843ecec16SMauro Carvalho Chehab 	pm->num_clocks = dev->variant->num_clocks;
2943ecec16SMauro Carvalho Chehab 	pm->clk_names = dev->variant->clk_names;
3043ecec16SMauro Carvalho Chehab 	pm->device = &dev->plat_dev->dev;
3143ecec16SMauro Carvalho Chehab 	pm->clock_gate = NULL;
3243ecec16SMauro Carvalho Chehab 
3343ecec16SMauro Carvalho Chehab 	/* clock control */
3443ecec16SMauro Carvalho Chehab 	for (i = 0; i < pm->num_clocks; i++) {
3543ecec16SMauro Carvalho Chehab 		pm->clocks[i] = devm_clk_get(pm->device, pm->clk_names[i]);
3643ecec16SMauro Carvalho Chehab 		if (IS_ERR(pm->clocks[i])) {
3743ecec16SMauro Carvalho Chehab 			/* additional clocks are optional */
3843ecec16SMauro Carvalho Chehab 			if (i && PTR_ERR(pm->clocks[i]) == -ENOENT) {
3943ecec16SMauro Carvalho Chehab 				pm->clocks[i] = NULL;
4043ecec16SMauro Carvalho Chehab 				continue;
4143ecec16SMauro Carvalho Chehab 			}
4243ecec16SMauro Carvalho Chehab 			mfc_err("Failed to get clock: %s\n",
4343ecec16SMauro Carvalho Chehab 				pm->clk_names[i]);
4443ecec16SMauro Carvalho Chehab 			return PTR_ERR(pm->clocks[i]);
4543ecec16SMauro Carvalho Chehab 		}
4643ecec16SMauro Carvalho Chehab 	}
4743ecec16SMauro Carvalho Chehab 
4843ecec16SMauro Carvalho Chehab 	if (dev->variant->use_clock_gating)
4943ecec16SMauro Carvalho Chehab 		pm->clock_gate = pm->clocks[0];
5043ecec16SMauro Carvalho Chehab 
5143ecec16SMauro Carvalho Chehab 	pm_runtime_enable(pm->device);
5243ecec16SMauro Carvalho Chehab 	atomic_set(&clk_ref, 0);
5343ecec16SMauro Carvalho Chehab 	return 0;
5443ecec16SMauro Carvalho Chehab }
5543ecec16SMauro Carvalho Chehab 
s5p_mfc_final_pm(struct s5p_mfc_dev * dev)5643ecec16SMauro Carvalho Chehab void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
5743ecec16SMauro Carvalho Chehab {
5843ecec16SMauro Carvalho Chehab 	pm_runtime_disable(pm->device);
5943ecec16SMauro Carvalho Chehab }
6043ecec16SMauro Carvalho Chehab 
s5p_mfc_clock_on(void)6143ecec16SMauro Carvalho Chehab int s5p_mfc_clock_on(void)
6243ecec16SMauro Carvalho Chehab {
6343ecec16SMauro Carvalho Chehab 	atomic_inc(&clk_ref);
6443ecec16SMauro Carvalho Chehab 	mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
6543ecec16SMauro Carvalho Chehab 
6643ecec16SMauro Carvalho Chehab 	return clk_enable(pm->clock_gate);
6743ecec16SMauro Carvalho Chehab }
6843ecec16SMauro Carvalho Chehab 
s5p_mfc_clock_off(void)6943ecec16SMauro Carvalho Chehab void s5p_mfc_clock_off(void)
7043ecec16SMauro Carvalho Chehab {
7143ecec16SMauro Carvalho Chehab 	atomic_dec(&clk_ref);
7243ecec16SMauro Carvalho Chehab 	mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
7343ecec16SMauro Carvalho Chehab 
7443ecec16SMauro Carvalho Chehab 	clk_disable(pm->clock_gate);
7543ecec16SMauro Carvalho Chehab }
7643ecec16SMauro Carvalho Chehab 
s5p_mfc_power_on(void)7743ecec16SMauro Carvalho Chehab int s5p_mfc_power_on(void)
7843ecec16SMauro Carvalho Chehab {
7943ecec16SMauro Carvalho Chehab 	int i, ret = 0;
8043ecec16SMauro Carvalho Chehab 
8143ecec16SMauro Carvalho Chehab 	ret = pm_runtime_resume_and_get(pm->device);
8243ecec16SMauro Carvalho Chehab 	if (ret < 0)
8343ecec16SMauro Carvalho Chehab 		return ret;
8443ecec16SMauro Carvalho Chehab 
8543ecec16SMauro Carvalho Chehab 	/* clock control */
8643ecec16SMauro Carvalho Chehab 	for (i = 0; i < pm->num_clocks; i++) {
8743ecec16SMauro Carvalho Chehab 		ret = clk_prepare_enable(pm->clocks[i]);
8843ecec16SMauro Carvalho Chehab 		if (ret < 0) {
8943ecec16SMauro Carvalho Chehab 			mfc_err("clock prepare failed for clock: %s\n",
9043ecec16SMauro Carvalho Chehab 				pm->clk_names[i]);
9143ecec16SMauro Carvalho Chehab 			goto err;
9243ecec16SMauro Carvalho Chehab 		}
9343ecec16SMauro Carvalho Chehab 	}
9443ecec16SMauro Carvalho Chehab 
9543ecec16SMauro Carvalho Chehab 	/* prepare for software clock gating */
9643ecec16SMauro Carvalho Chehab 	clk_disable(pm->clock_gate);
9743ecec16SMauro Carvalho Chehab 
9843ecec16SMauro Carvalho Chehab 	return 0;
9943ecec16SMauro Carvalho Chehab err:
100*8cc7b3d3SChengguang Xu 	while (--i >= 0)
10143ecec16SMauro Carvalho Chehab 		clk_disable_unprepare(pm->clocks[i]);
10243ecec16SMauro Carvalho Chehab 	pm_runtime_put(pm->device);
10343ecec16SMauro Carvalho Chehab 	return ret;
10443ecec16SMauro Carvalho Chehab }
10543ecec16SMauro Carvalho Chehab 
s5p_mfc_power_off(void)10643ecec16SMauro Carvalho Chehab int s5p_mfc_power_off(void)
10743ecec16SMauro Carvalho Chehab {
10843ecec16SMauro Carvalho Chehab 	int i;
10943ecec16SMauro Carvalho Chehab 
11043ecec16SMauro Carvalho Chehab 	/* finish software clock gating */
11143ecec16SMauro Carvalho Chehab 	clk_enable(pm->clock_gate);
11243ecec16SMauro Carvalho Chehab 
11343ecec16SMauro Carvalho Chehab 	for (i = 0; i < pm->num_clocks; i++)
11443ecec16SMauro Carvalho Chehab 		clk_disable_unprepare(pm->clocks[i]);
11543ecec16SMauro Carvalho Chehab 
11643ecec16SMauro Carvalho Chehab 	return pm_runtime_put_sync(pm->device);
11743ecec16SMauro Carvalho Chehab }
11843ecec16SMauro Carvalho Chehab 
119