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
ls7a1000_dpi_connector_get_modes(struct drm_connector * conn)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 *
ls7a1000_dpi_connector_get_best_encoder(struct drm_connector * connector,struct drm_atomic_state * state)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
ls7a1000_dpi_connector_detect(struct drm_connector * connector,bool force)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
ls7a1000_pipe0_encoder_reset(struct drm_encoder * encoder)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
ls7a1000_pipe1_encoder_reset(struct drm_encoder * encoder)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
ls7a1000_output_init(struct drm_device * ddev,struct lsdc_display_pipe * dispipe,struct i2c_adapter * ddc,unsigned int index)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