xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/rockchip/rk618/rk618_rgb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
4  *
5  * Author: Chen Shunqing <csq@rock-chips.com>
6  */
7 
8 #include <linux/module.h>
9 #include <linux/clk.h>
10 #include <linux/platform_device.h>
11 #include <linux/of.h>
12 #include <video/of_display_timing.h>
13 #include <linux/regmap.h>
14 #include <linux/mfd/rk618.h>
15 
16 #include <drm/drm_drv.h>
17 #include <drm/drm_of.h>
18 #include <drm/drm_atomic.h>
19 #include <drm/drm_crtc_helper.h>
20 #include <drm/drm_atomic_helper.h>
21 #include <drm/drm_panel.h>
22 #include <drm/drm_probe_helper.h>
23 
24 #include <video/videomode.h>
25 
26 #include "../rockchip_drm_drv.h"
27 #include "rk618_dither.h"
28 
29 struct rk618_rgb {
30 	struct drm_bridge base;
31 	struct drm_connector connector;
32 	struct drm_panel *panel;
33 	struct drm_bridge *bridge;
34 	struct device *dev;
35 	struct regmap *regmap;
36 	struct clk *clock;
37 	struct rk618 *parent;
38 	u32 bus_format;
39 	u32 id;
40 	struct rockchip_drm_sub_dev sub_dev;
41 };
42 
bridge_to_rgb(struct drm_bridge * b)43 static inline struct rk618_rgb *bridge_to_rgb(struct drm_bridge *b)
44 {
45 	return container_of(b, struct rk618_rgb, base);
46 }
47 
connector_to_rgb(struct drm_connector * c)48 static inline struct rk618_rgb *connector_to_rgb(struct drm_connector *c)
49 {
50 	return container_of(c, struct rk618_rgb, connector);
51 }
52 
53 static struct drm_encoder *
rk618_rgb_connector_best_encoder(struct drm_connector * connector)54 rk618_rgb_connector_best_encoder(struct drm_connector *connector)
55 {
56 	struct rk618_rgb *rgb = connector_to_rgb(connector);
57 
58 	return rgb->base.encoder;
59 }
60 
rk618_rgb_connector_get_modes(struct drm_connector * connector)61 static int rk618_rgb_connector_get_modes(struct drm_connector *connector)
62 {
63 	struct rk618_rgb *rgb = connector_to_rgb(connector);
64 	struct drm_display_info *info = &connector->display_info;
65 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
66 	int num_modes = 0;
67 
68 	num_modes = drm_panel_get_modes(rgb->panel, connector);
69 
70 	if (info->num_bus_formats)
71 		rgb->bus_format = info->bus_formats[0];
72 	else
73 		rgb->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
74 
75 	drm_display_info_set_bus_formats(&connector->display_info,
76 					 &bus_format, 1);
77 
78 	return num_modes;
79 }
80 
81 static const struct drm_connector_helper_funcs
82 rk618_rgb_connector_helper_funcs = {
83 	.get_modes = rk618_rgb_connector_get_modes,
84 	.best_encoder = rk618_rgb_connector_best_encoder,
85 };
86 
87 static enum drm_connector_status
rk618_rgb_connector_detect(struct drm_connector * connector,bool force)88 rk618_rgb_connector_detect(struct drm_connector *connector, bool force)
89 {
90 	return connector_status_connected;
91 }
92 
rk618_rgb_connector_destroy(struct drm_connector * connector)93 static void rk618_rgb_connector_destroy(struct drm_connector *connector)
94 {
95 	drm_connector_cleanup(connector);
96 }
97 
98 static const struct drm_connector_funcs rk618_rgb_connector_funcs = {
99 	.detect = rk618_rgb_connector_detect,
100 	.fill_modes = drm_helper_probe_single_connector_modes,
101 	.destroy = rk618_rgb_connector_destroy,
102 	.reset = drm_atomic_helper_connector_reset,
103 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
104 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
105 };
106 
rk618_rgb_bridge_enable(struct drm_bridge * bridge)107 static void rk618_rgb_bridge_enable(struct drm_bridge *bridge)
108 {
109 	struct rk618_rgb *rgb = bridge_to_rgb(bridge);
110 	u32 value;
111 
112 	clk_prepare_enable(rgb->clock);
113 
114 	rk618_frc_dclk_invert(rgb->parent);
115 	rk618_frc_dither_init(rgb->parent, rgb->bus_format);
116 
117 	if (rgb->id) {
118 		value = LVDS_CON_CBG_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
119 			LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA0TTL_ENABLE |
120 			LVDS_CON_CHA1TTL_ENABLE | LVDS_CON_PLL_POWER_DOWN;
121 		regmap_write(rgb->regmap, RK618_LVDS_CON, value);
122 
123 		regmap_write(rgb->regmap, RK618_IO_CON0, PORT2_OUTPUT_TTL);
124 	} else {
125 		value = LVDS_CON_CHA1TTL_DISABLE | LVDS_CON_CHA0TTL_DISABLE |
126 			LVDS_CON_CHA1_POWER_DOWN | LVDS_CON_CHA0_POWER_DOWN |
127 			LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN;
128 		regmap_write(rgb->regmap, RK618_LVDS_CON, value);
129 
130 		regmap_write(rgb->regmap, RK618_IO_CON0,
131 			     PORT1_OUTPUT_TTL_ENABLE);
132 	}
133 
134 	if (rgb->panel) {
135 		drm_panel_prepare(rgb->panel);
136 		drm_panel_enable(rgb->panel);
137 	}
138 }
139 
rk618_rgb_bridge_disable(struct drm_bridge * bridge)140 static void rk618_rgb_bridge_disable(struct drm_bridge *bridge)
141 {
142 	struct rk618_rgb *rgb = bridge_to_rgb(bridge);
143 
144 	if (rgb->panel) {
145 		drm_panel_disable(rgb->panel);
146 		drm_panel_unprepare(rgb->panel);
147 	}
148 
149 	if (rgb->id)
150 		regmap_write(rgb->regmap, RK618_LVDS_CON,
151 			     LVDS_CON_CHA0_POWER_DOWN |
152 			     LVDS_CON_CHA1_POWER_DOWN |
153 			     LVDS_CON_CBG_POWER_DOWN |
154 			     LVDS_CON_PLL_POWER_DOWN);
155 	else
156 		regmap_write(rgb->regmap, RK618_IO_CON0,
157 			     PORT1_OUTPUT_TTL_DISABLE);
158 
159 	clk_disable_unprepare(rgb->clock);
160 }
161 
rk618_rgb_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)162 static int rk618_rgb_bridge_attach(struct drm_bridge *bridge,
163 				   enum drm_bridge_attach_flags flags)
164 {
165 	struct rk618_rgb *rgb = bridge_to_rgb(bridge);
166 	struct device *dev = rgb->dev;
167 	struct drm_connector *connector = &rgb->connector;
168 	struct drm_device *drm = bridge->dev;
169 	int ret;
170 
171 	if (rgb->panel) {
172 		ret = drm_connector_init(drm, connector,
173 					 &rk618_rgb_connector_funcs,
174 					 DRM_MODE_CONNECTOR_DPI);
175 		if (ret) {
176 			dev_err(dev, "Failed to initialize connector\n");
177 			return ret;
178 		}
179 
180 		drm_connector_helper_add(connector,
181 					 &rk618_rgb_connector_helper_funcs);
182 		drm_connector_attach_encoder(connector, bridge->encoder);
183 
184 		rgb->sub_dev.connector = &rgb->connector;
185 		rgb->sub_dev.of_node = rgb->dev->of_node;
186 		rockchip_drm_register_sub_dev(&rgb->sub_dev);
187 	} else {
188 		ret = drm_bridge_attach(bridge->encoder, rgb->bridge, bridge, 0);
189 		if (ret) {
190 			dev_err(dev, "failed to attach bridge\n");
191 			return ret;
192 		}
193 	}
194 
195 	return 0;
196 }
197 
rk618_rgb_bridge_detach(struct drm_bridge * bridge)198 static void rk618_rgb_bridge_detach(struct drm_bridge *bridge)
199 {
200 	struct rk618_rgb *rgb = bridge_to_rgb(bridge);
201 
202 	rockchip_drm_unregister_sub_dev(&rgb->sub_dev);
203 }
204 
205 static const struct drm_bridge_funcs rk618_rgb_bridge_funcs = {
206 	.attach = rk618_rgb_bridge_attach,
207 	.detach = rk618_rgb_bridge_detach,
208 	.enable = rk618_rgb_bridge_enable,
209 	.disable = rk618_rgb_bridge_disable,
210 };
211 
rk618_rgb_probe(struct platform_device * pdev)212 static int rk618_rgb_probe(struct platform_device *pdev)
213 {
214 	struct rk618 *rk618 = dev_get_drvdata(pdev->dev.parent);
215 	struct device *dev = &pdev->dev;
216 	struct rk618_rgb *rgb;
217 	int id, ret;
218 
219 	if (!of_device_is_available(dev->of_node))
220 		return -ENODEV;
221 
222 	rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL);
223 	if (!rgb)
224 		return -ENOMEM;
225 
226 	rgb->dev = dev;
227 	rgb->parent = rk618;
228 	rgb->regmap = rk618->regmap;
229 	platform_set_drvdata(pdev, rgb);
230 
231 	rgb->clock = devm_clk_get(dev, "rgb");
232 	if (IS_ERR(rgb->clock)) {
233 		ret = PTR_ERR(rgb->clock);
234 		dev_err(dev, "failed to get rgb clock: %d\n", ret);
235 		return ret;
236 	}
237 
238 	for (id = 0; id < 2; id++) {
239 		struct device_node *remote, *endpoint;
240 
241 		endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, id);
242 		if (!endpoint)
243 			continue;
244 
245 		remote = of_graph_get_remote_port_parent(endpoint);
246 		of_node_put(endpoint);
247 		if (!remote) {
248 			dev_err(dev, "no panel/bridge connected\n");
249 			return -ENODEV;
250 		}
251 
252 		rgb->panel = of_drm_find_panel(remote);
253 		if (IS_ERR(rgb->panel)) {
254 			rgb->panel = NULL;
255 			rgb->bridge = of_drm_find_bridge(remote);
256 		}
257 		of_node_put(remote);
258 		if (!rgb->panel && !rgb->bridge) {
259 			dev_err(dev, "Waiting for panel/bridge driver\n");
260 			return -EPROBE_DEFER;
261 		}
262 
263 		rgb->id = id;
264 	}
265 
266 	rgb->base.funcs = &rk618_rgb_bridge_funcs;
267 	rgb->base.of_node = dev->of_node;
268 	drm_bridge_add(&rgb->base);
269 
270 	return 0;
271 }
272 
rk618_rgb_remove(struct platform_device * pdev)273 static int rk618_rgb_remove(struct platform_device *pdev)
274 {
275 	struct rk618_rgb *rgb = platform_get_drvdata(pdev);
276 
277 	drm_bridge_remove(&rgb->base);
278 
279 	return 0;
280 }
281 
282 static const struct of_device_id rk618_rgb_of_match[] = {
283 	{ .compatible = "rockchip,rk618-rgb", },
284 	{},
285 };
286 MODULE_DEVICE_TABLE(of, rk618_rgb_of_match);
287 
288 static struct platform_driver rk618_rgb_driver = {
289 	.driver = {
290 		.name = "rk618-rgb",
291 		.of_match_table = of_match_ptr(rk618_rgb_of_match),
292 	},
293 	.probe = rk618_rgb_probe,
294 	.remove = rk618_rgb_remove,
295 };
296 module_platform_driver(rk618_rgb_driver);
297 
298 MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
299 MODULE_AUTHOR("Chen Shunqing <csq@rock-chips.com>");
300 MODULE_DESCRIPTION("Rockchip RK618 RGB driver");
301 MODULE_LICENSE("GPL v2");
302