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