xref: /linux/drivers/gpu/drm/sun4i/sun4i_lvds.c (revision a0c1214e)
1*a0c1214eSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+
2*a0c1214eSMaxime Ripard /*
3*a0c1214eSMaxime Ripard  * Copyright (C) 2017 Free Electrons
4*a0c1214eSMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
5*a0c1214eSMaxime Ripard  */
6*a0c1214eSMaxime Ripard 
7*a0c1214eSMaxime Ripard #include <linux/clk.h>
8*a0c1214eSMaxime Ripard 
9*a0c1214eSMaxime Ripard #include <drm/drmP.h>
10*a0c1214eSMaxime Ripard #include <drm/drm_atomic_helper.h>
11*a0c1214eSMaxime Ripard #include <drm/drm_crtc_helper.h>
12*a0c1214eSMaxime Ripard #include <drm/drm_of.h>
13*a0c1214eSMaxime Ripard #include <drm/drm_panel.h>
14*a0c1214eSMaxime Ripard 
15*a0c1214eSMaxime Ripard #include "sun4i_crtc.h"
16*a0c1214eSMaxime Ripard #include "sun4i_tcon.h"
17*a0c1214eSMaxime Ripard #include "sun4i_lvds.h"
18*a0c1214eSMaxime Ripard 
19*a0c1214eSMaxime Ripard struct sun4i_lvds {
20*a0c1214eSMaxime Ripard 	struct drm_connector	connector;
21*a0c1214eSMaxime Ripard 	struct drm_encoder	encoder;
22*a0c1214eSMaxime Ripard 
23*a0c1214eSMaxime Ripard 	struct sun4i_tcon	*tcon;
24*a0c1214eSMaxime Ripard };
25*a0c1214eSMaxime Ripard 
26*a0c1214eSMaxime Ripard static inline struct sun4i_lvds *
27*a0c1214eSMaxime Ripard drm_connector_to_sun4i_lvds(struct drm_connector *connector)
28*a0c1214eSMaxime Ripard {
29*a0c1214eSMaxime Ripard 	return container_of(connector, struct sun4i_lvds,
30*a0c1214eSMaxime Ripard 			    connector);
31*a0c1214eSMaxime Ripard }
32*a0c1214eSMaxime Ripard 
33*a0c1214eSMaxime Ripard static inline struct sun4i_lvds *
34*a0c1214eSMaxime Ripard drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
35*a0c1214eSMaxime Ripard {
36*a0c1214eSMaxime Ripard 	return container_of(encoder, struct sun4i_lvds,
37*a0c1214eSMaxime Ripard 			    encoder);
38*a0c1214eSMaxime Ripard }
39*a0c1214eSMaxime Ripard 
40*a0c1214eSMaxime Ripard static int sun4i_lvds_get_modes(struct drm_connector *connector)
41*a0c1214eSMaxime Ripard {
42*a0c1214eSMaxime Ripard 	struct sun4i_lvds *lvds =
43*a0c1214eSMaxime Ripard 		drm_connector_to_sun4i_lvds(connector);
44*a0c1214eSMaxime Ripard 	struct sun4i_tcon *tcon = lvds->tcon;
45*a0c1214eSMaxime Ripard 
46*a0c1214eSMaxime Ripard 	return drm_panel_get_modes(tcon->panel);
47*a0c1214eSMaxime Ripard }
48*a0c1214eSMaxime Ripard 
49*a0c1214eSMaxime Ripard static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
50*a0c1214eSMaxime Ripard 	.get_modes	= sun4i_lvds_get_modes,
51*a0c1214eSMaxime Ripard };
52*a0c1214eSMaxime Ripard 
53*a0c1214eSMaxime Ripard static void
54*a0c1214eSMaxime Ripard sun4i_lvds_connector_destroy(struct drm_connector *connector)
55*a0c1214eSMaxime Ripard {
56*a0c1214eSMaxime Ripard 	struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
57*a0c1214eSMaxime Ripard 	struct sun4i_tcon *tcon = lvds->tcon;
58*a0c1214eSMaxime Ripard 
59*a0c1214eSMaxime Ripard 	drm_panel_detach(tcon->panel);
60*a0c1214eSMaxime Ripard 	drm_connector_cleanup(connector);
61*a0c1214eSMaxime Ripard }
62*a0c1214eSMaxime Ripard 
63*a0c1214eSMaxime Ripard static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
64*a0c1214eSMaxime Ripard 	.fill_modes		= drm_helper_probe_single_connector_modes,
65*a0c1214eSMaxime Ripard 	.destroy		= sun4i_lvds_connector_destroy,
66*a0c1214eSMaxime Ripard 	.reset			= drm_atomic_helper_connector_reset,
67*a0c1214eSMaxime Ripard 	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
68*a0c1214eSMaxime Ripard 	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
69*a0c1214eSMaxime Ripard };
70*a0c1214eSMaxime Ripard 
71*a0c1214eSMaxime Ripard static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
72*a0c1214eSMaxime Ripard {
73*a0c1214eSMaxime Ripard 	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
74*a0c1214eSMaxime Ripard 	struct sun4i_tcon *tcon = lvds->tcon;
75*a0c1214eSMaxime Ripard 
76*a0c1214eSMaxime Ripard 	DRM_DEBUG_DRIVER("Enabling LVDS output\n");
77*a0c1214eSMaxime Ripard 
78*a0c1214eSMaxime Ripard 	if (!IS_ERR(tcon->panel)) {
79*a0c1214eSMaxime Ripard 		drm_panel_prepare(tcon->panel);
80*a0c1214eSMaxime Ripard 		drm_panel_enable(tcon->panel);
81*a0c1214eSMaxime Ripard 	}
82*a0c1214eSMaxime Ripard }
83*a0c1214eSMaxime Ripard 
84*a0c1214eSMaxime Ripard static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
85*a0c1214eSMaxime Ripard {
86*a0c1214eSMaxime Ripard 	struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
87*a0c1214eSMaxime Ripard 	struct sun4i_tcon *tcon = lvds->tcon;
88*a0c1214eSMaxime Ripard 
89*a0c1214eSMaxime Ripard 	DRM_DEBUG_DRIVER("Disabling LVDS output\n");
90*a0c1214eSMaxime Ripard 
91*a0c1214eSMaxime Ripard 	if (!IS_ERR(tcon->panel)) {
92*a0c1214eSMaxime Ripard 		drm_panel_disable(tcon->panel);
93*a0c1214eSMaxime Ripard 		drm_panel_unprepare(tcon->panel);
94*a0c1214eSMaxime Ripard 	}
95*a0c1214eSMaxime Ripard }
96*a0c1214eSMaxime Ripard 
97*a0c1214eSMaxime Ripard static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
98*a0c1214eSMaxime Ripard 	.disable	= sun4i_lvds_encoder_disable,
99*a0c1214eSMaxime Ripard 	.enable		= sun4i_lvds_encoder_enable,
100*a0c1214eSMaxime Ripard };
101*a0c1214eSMaxime Ripard 
102*a0c1214eSMaxime Ripard static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
103*a0c1214eSMaxime Ripard 	.destroy	= drm_encoder_cleanup,
104*a0c1214eSMaxime Ripard };
105*a0c1214eSMaxime Ripard 
106*a0c1214eSMaxime Ripard int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
107*a0c1214eSMaxime Ripard {
108*a0c1214eSMaxime Ripard 	struct drm_encoder *encoder;
109*a0c1214eSMaxime Ripard 	struct drm_bridge *bridge;
110*a0c1214eSMaxime Ripard 	struct sun4i_lvds *lvds;
111*a0c1214eSMaxime Ripard 	int ret;
112*a0c1214eSMaxime Ripard 
113*a0c1214eSMaxime Ripard 	lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
114*a0c1214eSMaxime Ripard 	if (!lvds)
115*a0c1214eSMaxime Ripard 		return -ENOMEM;
116*a0c1214eSMaxime Ripard 	lvds->tcon = tcon;
117*a0c1214eSMaxime Ripard 	encoder = &lvds->encoder;
118*a0c1214eSMaxime Ripard 
119*a0c1214eSMaxime Ripard 	ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
120*a0c1214eSMaxime Ripard 					  &tcon->panel, &bridge);
121*a0c1214eSMaxime Ripard 	if (ret) {
122*a0c1214eSMaxime Ripard 		dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
123*a0c1214eSMaxime Ripard 		return 0;
124*a0c1214eSMaxime Ripard 	}
125*a0c1214eSMaxime Ripard 
126*a0c1214eSMaxime Ripard 	drm_encoder_helper_add(&lvds->encoder,
127*a0c1214eSMaxime Ripard 			       &sun4i_lvds_enc_helper_funcs);
128*a0c1214eSMaxime Ripard 	ret = drm_encoder_init(drm,
129*a0c1214eSMaxime Ripard 			       &lvds->encoder,
130*a0c1214eSMaxime Ripard 			       &sun4i_lvds_enc_funcs,
131*a0c1214eSMaxime Ripard 			       DRM_MODE_ENCODER_LVDS,
132*a0c1214eSMaxime Ripard 			       NULL);
133*a0c1214eSMaxime Ripard 	if (ret) {
134*a0c1214eSMaxime Ripard 		dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
135*a0c1214eSMaxime Ripard 		goto err_out;
136*a0c1214eSMaxime Ripard 	}
137*a0c1214eSMaxime Ripard 
138*a0c1214eSMaxime Ripard 	/* The LVDS encoder can only work with the TCON channel 0 */
139*a0c1214eSMaxime Ripard 	lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc));
140*a0c1214eSMaxime Ripard 
141*a0c1214eSMaxime Ripard 	if (tcon->panel) {
142*a0c1214eSMaxime Ripard 		drm_connector_helper_add(&lvds->connector,
143*a0c1214eSMaxime Ripard 					 &sun4i_lvds_con_helper_funcs);
144*a0c1214eSMaxime Ripard 		ret = drm_connector_init(drm, &lvds->connector,
145*a0c1214eSMaxime Ripard 					 &sun4i_lvds_con_funcs,
146*a0c1214eSMaxime Ripard 					 DRM_MODE_CONNECTOR_LVDS);
147*a0c1214eSMaxime Ripard 		if (ret) {
148*a0c1214eSMaxime Ripard 			dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
149*a0c1214eSMaxime Ripard 			goto err_cleanup_connector;
150*a0c1214eSMaxime Ripard 		}
151*a0c1214eSMaxime Ripard 
152*a0c1214eSMaxime Ripard 		drm_mode_connector_attach_encoder(&lvds->connector,
153*a0c1214eSMaxime Ripard 						  &lvds->encoder);
154*a0c1214eSMaxime Ripard 
155*a0c1214eSMaxime Ripard 		ret = drm_panel_attach(tcon->panel, &lvds->connector);
156*a0c1214eSMaxime Ripard 		if (ret) {
157*a0c1214eSMaxime Ripard 			dev_err(drm->dev, "Couldn't attach our panel\n");
158*a0c1214eSMaxime Ripard 			goto err_cleanup_connector;
159*a0c1214eSMaxime Ripard 		}
160*a0c1214eSMaxime Ripard 	}
161*a0c1214eSMaxime Ripard 
162*a0c1214eSMaxime Ripard 	if (bridge) {
163*a0c1214eSMaxime Ripard 		ret = drm_bridge_attach(encoder, bridge, NULL);
164*a0c1214eSMaxime Ripard 		if (ret) {
165*a0c1214eSMaxime Ripard 			dev_err(drm->dev, "Couldn't attach our bridge\n");
166*a0c1214eSMaxime Ripard 			goto err_cleanup_connector;
167*a0c1214eSMaxime Ripard 		}
168*a0c1214eSMaxime Ripard 	}
169*a0c1214eSMaxime Ripard 
170*a0c1214eSMaxime Ripard 	return 0;
171*a0c1214eSMaxime Ripard 
172*a0c1214eSMaxime Ripard err_cleanup_connector:
173*a0c1214eSMaxime Ripard 	drm_encoder_cleanup(&lvds->encoder);
174*a0c1214eSMaxime Ripard err_out:
175*a0c1214eSMaxime Ripard 	return ret;
176*a0c1214eSMaxime Ripard }
177*a0c1214eSMaxime Ripard EXPORT_SYMBOL(sun4i_lvds_init);
178