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