xref: /openbmc/linux/drivers/gpu/drm/arm/display/komeda/komeda_dev.c (revision 06d5d6b7f9948a89543e1160ef852d57892c750d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4  * Author: James.Qian.Wang <james.qian.wang@arm.com>
5  *
6  */
7 #include <linux/io.h>
8 #include <linux/of_device.h>
9 #include <linux/of_graph.h>
10 #include <linux/platform_device.h>
11 
12 #include <drm/drm_print.h>
13 
14 #include "komeda_dev.h"
15 
16 static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
17 {
18 	struct komeda_pipeline *pipe;
19 	struct clk *clk;
20 	u32 pipe_id;
21 	int ret = 0;
22 
23 	ret = of_property_read_u32(np, "reg", &pipe_id);
24 	if (ret != 0 || pipe_id >= mdev->n_pipelines)
25 		return -EINVAL;
26 
27 	pipe = mdev->pipelines[pipe_id];
28 
29 	clk = of_clk_get_by_name(np, "aclk");
30 	if (IS_ERR(clk)) {
31 		DRM_ERROR("get aclk for pipeline %d failed!\n", pipe_id);
32 		return PTR_ERR(clk);
33 	}
34 	pipe->aclk = clk;
35 
36 	clk = of_clk_get_by_name(np, "pxclk");
37 	if (IS_ERR(clk)) {
38 		DRM_ERROR("get pxclk for pipeline %d failed!\n", pipe_id);
39 		return PTR_ERR(clk);
40 	}
41 	pipe->pxlclk = clk;
42 
43 	/* enum ports */
44 	pipe->of_output_dev =
45 		of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 0);
46 	pipe->of_output_port =
47 		of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT);
48 
49 	pipe->of_node = np;
50 
51 	return 0;
52 }
53 
54 static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev)
55 {
56 	struct device_node *child, *np = dev->of_node;
57 	struct clk *clk;
58 	int ret;
59 
60 	clk = devm_clk_get(dev, "mclk");
61 	if (IS_ERR(clk))
62 		return PTR_ERR(clk);
63 
64 	mdev->mclk = clk;
65 
66 	for_each_available_child_of_node(np, child) {
67 		if (of_node_cmp(child->name, "pipeline") == 0) {
68 			ret = komeda_parse_pipe_dt(mdev, child);
69 			if (ret) {
70 				DRM_ERROR("parse pipeline dt error!\n");
71 				of_node_put(child);
72 				break;
73 			}
74 		}
75 	}
76 
77 	return ret;
78 }
79 
80 struct komeda_dev *komeda_dev_create(struct device *dev)
81 {
82 	struct platform_device *pdev = to_platform_device(dev);
83 	const struct komeda_product_data *product;
84 	struct komeda_dev *mdev;
85 	struct resource *io_res;
86 	int err = 0;
87 
88 	product = of_device_get_match_data(dev);
89 	if (!product)
90 		return ERR_PTR(-ENODEV);
91 
92 	io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
93 	if (!io_res) {
94 		DRM_ERROR("No registers defined.\n");
95 		return ERR_PTR(-ENODEV);
96 	}
97 
98 	mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
99 	if (!mdev)
100 		return ERR_PTR(-ENOMEM);
101 
102 	mdev->dev = dev;
103 	mdev->reg_base = devm_ioremap_resource(dev, io_res);
104 	if (IS_ERR(mdev->reg_base)) {
105 		DRM_ERROR("Map register space failed.\n");
106 		err = PTR_ERR(mdev->reg_base);
107 		mdev->reg_base = NULL;
108 		goto err_cleanup;
109 	}
110 
111 	mdev->pclk = devm_clk_get(dev, "pclk");
112 	if (IS_ERR(mdev->pclk)) {
113 		DRM_ERROR("Get APB clk failed.\n");
114 		err = PTR_ERR(mdev->pclk);
115 		mdev->pclk = NULL;
116 		goto err_cleanup;
117 	}
118 
119 	/* Enable APB clock to access the registers */
120 	clk_prepare_enable(mdev->pclk);
121 
122 	mdev->funcs = product->identify(mdev->reg_base, &mdev->chip);
123 	if (!komeda_product_match(mdev, product->product_id)) {
124 		DRM_ERROR("DT configured %x mismatch with real HW %x.\n",
125 			  product->product_id,
126 			  MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id));
127 		err = -ENODEV;
128 		goto err_cleanup;
129 	}
130 
131 	DRM_INFO("Found ARM Mali-D%x version r%dp%d\n",
132 		 MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id),
133 		 MALIDP_CORE_ID_MAJOR(mdev->chip.core_id),
134 		 MALIDP_CORE_ID_MINOR(mdev->chip.core_id));
135 
136 	mdev->funcs->init_format_table(mdev);
137 
138 	err = mdev->funcs->enum_resources(mdev);
139 	if (err) {
140 		DRM_ERROR("enumerate display resource failed.\n");
141 		goto err_cleanup;
142 	}
143 
144 	err = komeda_parse_dt(dev, mdev);
145 	if (err) {
146 		DRM_ERROR("parse device tree failed.\n");
147 		goto err_cleanup;
148 	}
149 
150 	return mdev;
151 
152 err_cleanup:
153 	komeda_dev_destroy(mdev);
154 	return ERR_PTR(err);
155 }
156 
157 void komeda_dev_destroy(struct komeda_dev *mdev)
158 {
159 	struct device *dev = mdev->dev;
160 	struct komeda_dev_funcs *funcs = mdev->funcs;
161 	int i;
162 
163 	for (i = 0; i < mdev->n_pipelines; i++) {
164 		komeda_pipeline_destroy(mdev, mdev->pipelines[i]);
165 		mdev->pipelines[i] = NULL;
166 	}
167 
168 	mdev->n_pipelines = 0;
169 
170 	if (funcs && funcs->cleanup)
171 		funcs->cleanup(mdev);
172 
173 	if (mdev->reg_base) {
174 		devm_iounmap(dev, mdev->reg_base);
175 		mdev->reg_base = NULL;
176 	}
177 
178 	if (mdev->mclk) {
179 		devm_clk_put(dev, mdev->mclk);
180 		mdev->mclk = NULL;
181 	}
182 
183 	if (mdev->pclk) {
184 		clk_disable_unprepare(mdev->pclk);
185 		devm_clk_put(dev, mdev->pclk);
186 		mdev->pclk = NULL;
187 	}
188 
189 	devm_kfree(dev, mdev);
190 }
191