1 /*
2  * Copyright (c) 2016 MediaTek Inc.
3  * Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 
15 #include <linux/clk.h>
16 #include <linux/device.h>
17 #include <linux/of.h>
18 #include <linux/of_address.h>
19 #include <linux/of_platform.h>
20 #include <soc/mediatek/smi.h>
21 
22 #include "mtk_mdp_comp.h"
23 
24 
25 static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = {
26 	"mdp_rdma",
27 	"mdp_rsz",
28 	"mdp_wdma",
29 	"mdp_wrot",
30 };
31 
32 struct mtk_mdp_comp_match {
33 	enum mtk_mdp_comp_type type;
34 	int alias_id;
35 };
36 
37 static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = {
38 	{ MTK_MDP_RDMA,	0 },
39 	{ MTK_MDP_RDMA,	1 },
40 	{ MTK_MDP_RSZ,	0 },
41 	{ MTK_MDP_RSZ,	1 },
42 	{ MTK_MDP_RSZ,	2 },
43 	{ MTK_MDP_WDMA,	0 },
44 	{ MTK_MDP_WROT,	0 },
45 	{ MTK_MDP_WROT,	1 },
46 };
47 
mtk_mdp_comp_get_id(struct device * dev,struct device_node * node,enum mtk_mdp_comp_type comp_type)48 int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node,
49 			enum mtk_mdp_comp_type comp_type)
50 {
51 	int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]);
52 	int i;
53 
54 	for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) {
55 		if (comp_type == mtk_mdp_matches[i].type &&
56 		    id == mtk_mdp_matches[i].alias_id)
57 			return i;
58 	}
59 
60 	dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id);
61 
62 	return -EINVAL;
63 }
64 
mtk_mdp_comp_clock_on(struct device * dev,struct mtk_mdp_comp * comp)65 void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)
66 {
67 	int i, err;
68 
69 	if (comp->larb_dev) {
70 		err = mtk_smi_larb_get(comp->larb_dev);
71 		if (err)
72 			dev_err(dev,
73 				"failed to get larb, err %d. type:%d id:%d\n",
74 				err, comp->type, comp->id);
75 	}
76 
77 	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
78 		if (IS_ERR(comp->clk[i]))
79 			continue;
80 		err = clk_prepare_enable(comp->clk[i]);
81 		if (err)
82 			dev_err(dev,
83 			"failed to enable clock, err %d. type:%d id:%d i:%d\n",
84 				err, comp->type, comp->id, i);
85 	}
86 }
87 
mtk_mdp_comp_clock_off(struct device * dev,struct mtk_mdp_comp * comp)88 void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp)
89 {
90 	int i;
91 
92 	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
93 		if (IS_ERR(comp->clk[i]))
94 			continue;
95 		clk_disable_unprepare(comp->clk[i]);
96 	}
97 
98 	if (comp->larb_dev)
99 		mtk_smi_larb_put(comp->larb_dev);
100 }
101 
mtk_mdp_comp_init(struct device * dev,struct device_node * node,struct mtk_mdp_comp * comp,enum mtk_mdp_comp_id comp_id)102 int mtk_mdp_comp_init(struct device *dev, struct device_node *node,
103 		      struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id)
104 {
105 	struct device_node *larb_node;
106 	struct platform_device *larb_pdev;
107 	int i;
108 
109 	if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) {
110 		dev_err(dev, "Invalid comp_id %d\n", comp_id);
111 		return -EINVAL;
112 	}
113 
114 	comp->dev_node = of_node_get(node);
115 	comp->id = comp_id;
116 	comp->type = mtk_mdp_matches[comp_id].type;
117 	comp->regs = of_iomap(node, 0);
118 
119 	for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
120 		comp->clk[i] = of_clk_get(node, i);
121 
122 		/* Only RDMA needs two clocks */
123 		if (comp->type != MTK_MDP_RDMA)
124 			break;
125 	}
126 
127 	/* Only DMA capable components need the LARB property */
128 	comp->larb_dev = NULL;
129 	if (comp->type != MTK_MDP_RDMA &&
130 	    comp->type != MTK_MDP_WDMA &&
131 	    comp->type != MTK_MDP_WROT)
132 		return 0;
133 
134 	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
135 	if (!larb_node) {
136 		dev_err(dev,
137 			"Missing mediadek,larb phandle in %pOF node\n", node);
138 		return -EINVAL;
139 	}
140 
141 	larb_pdev = of_find_device_by_node(larb_node);
142 	if (!larb_pdev) {
143 		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
144 		of_node_put(larb_node);
145 		return -EPROBE_DEFER;
146 	}
147 	of_node_put(larb_node);
148 
149 	comp->larb_dev = &larb_pdev->dev;
150 
151 	return 0;
152 }
153 
mtk_mdp_comp_deinit(struct device * dev,struct mtk_mdp_comp * comp)154 void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp)
155 {
156 	of_node_put(comp->dev_node);
157 }
158