xref: /linux/drivers/gpu/drm/loongson/lsdc_output_7a1000.c (revision 55d0969c451159cff86949b38c39171cab962069)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2023 Loongson Technology Corporation Limited
4  */
5 
6 #include <drm/drm_atomic_helper.h>
7 #include <drm/drm_edid.h>
8 #include <drm/drm_probe_helper.h>
9 
10 #include "lsdc_drv.h"
11 #include "lsdc_output.h"
12 
13 /*
14  * The display controller in the LS7A1000 exports two DVO interfaces, thus
15  * external encoder is required, except connected to the DPI panel directly.
16  *
17  *       ___________________                                     _________
18  *      |            -------|                                   |         |
19  *      |  CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display |
20  *      |  _   _     -------|        ^             ^            |_________|
21  *      | | | | |  +------+ |        |             |
22  *      | |_| |_|  | i2c6 | <--------+-------------+
23  *      |          +------+ |
24  *      |                   |
25  *      |  DC in LS7A1000   |
26  *      |                   |
27  *      |  _   _   +------+ |
28  *      | | | | |  | i2c7 | <--------+-------------+
29  *      | |_| |_|  +------+ |        |             |             _________
30  *      |            -------|        |             |            |         |
31  *      |  CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> |  Panel  |
32  *      |            -------|                                   |_________|
33  *      |___________________|
34  *
35  * Currently, we assume the external encoders connected to the DVO are
36  * transparent. Loongson's DVO interface can directly drive RGB888 panels.
37  *
38  *  TODO: Add support for non-transparent encoders
39  */
40 
41 static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
42 {
43 	int num;
44 
45 	if (conn->ddc) {
46 		const struct drm_edid *drm_edid;
47 
48 		drm_edid = drm_edid_read(conn);
49 		drm_edid_connector_update(conn, drm_edid);
50 		num = drm_edid_connector_add_modes(conn);
51 		drm_edid_free(drm_edid);
52 
53 		return num;
54 	}
55 
56 	num = drm_add_modes_noedid(conn, 1920, 1200);
57 
58 	drm_set_preferred_mode(conn, 1024, 768);
59 
60 	return num;
61 }
62 
63 static struct drm_encoder *
64 ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
65 					struct drm_atomic_state *state)
66 {
67 	struct lsdc_output *output = connector_to_lsdc_output(connector);
68 
69 	return &output->encoder;
70 }
71 
72 static const struct drm_connector_helper_funcs
73 ls7a1000_dpi_connector_helpers = {
74 	.atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
75 	.get_modes = ls7a1000_dpi_connector_get_modes,
76 };
77 
78 static enum drm_connector_status
79 ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
80 {
81 	struct i2c_adapter *ddc = connector->ddc;
82 
83 	if (ddc) {
84 		if (drm_probe_ddc(ddc))
85 			return connector_status_connected;
86 
87 		return connector_status_disconnected;
88 	}
89 
90 	return connector_status_unknown;
91 }
92 
93 static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
94 	.detect = ls7a1000_dpi_connector_detect,
95 	.fill_modes = drm_helper_probe_single_connector_modes,
96 	.destroy = drm_connector_cleanup,
97 	.reset = drm_atomic_helper_connector_reset,
98 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
99 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
100 };
101 
102 static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
103 {
104 	struct drm_device *ddev = encoder->dev;
105 	struct lsdc_device *ldev = to_lsdc(ddev);
106 
107 	/*
108 	 * We need this for S3 support, screen will not lightup if don't set
109 	 * this register correctly.
110 	 */
111 	lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
112 		    PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
113 }
114 
115 static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
116 {
117 	struct drm_device *ddev = encoder->dev;
118 	struct lsdc_device *ldev = to_lsdc(ddev);
119 
120 	/*
121 	 * We need this for S3 support, screen will not lightup if don't set
122 	 * this register correctly.
123 	 */
124 
125 	/* DVO */
126 	lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
127 		    BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
128 }
129 
130 static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
131 	{
132 		.reset = ls7a1000_pipe0_encoder_reset,
133 		.destroy = drm_encoder_cleanup,
134 	},
135 	{
136 		.reset = ls7a1000_pipe1_encoder_reset,
137 		.destroy = drm_encoder_cleanup,
138 	},
139 };
140 
141 int ls7a1000_output_init(struct drm_device *ddev,
142 			 struct lsdc_display_pipe *dispipe,
143 			 struct i2c_adapter *ddc,
144 			 unsigned int index)
145 {
146 	struct lsdc_output *output = &dispipe->output;
147 	struct drm_encoder *encoder = &output->encoder;
148 	struct drm_connector *connector = &output->connector;
149 	int ret;
150 
151 	ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
152 			       DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
153 	if (ret)
154 		return ret;
155 
156 	encoder->possible_crtcs = BIT(index);
157 
158 	ret = drm_connector_init_with_ddc(ddev, connector,
159 					  &ls7a1000_dpi_connector_funcs,
160 					  DRM_MODE_CONNECTOR_DPI, ddc);
161 	if (ret)
162 		return ret;
163 
164 	drm_info(ddev, "display pipe-%u has a DVO\n", index);
165 
166 	drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
167 
168 	drm_connector_attach_encoder(connector, encoder);
169 
170 	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
171 			    DRM_CONNECTOR_POLL_DISCONNECT;
172 
173 	connector->interlace_allowed = 0;
174 	connector->doublescan_allowed = 0;
175 
176 	return 0;
177 }
178