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
41static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
42{
43	unsigned int num = 0;
44	struct edid *edid;
45
46	if (conn->ddc) {
47		edid = drm_get_edid(conn, conn->ddc);
48		if (edid) {
49			drm_connector_update_edid_property(conn, edid);
50			num = drm_add_edid_modes(conn, edid);
51			kfree(edid);
52		}
53
54		return num;
55	}
56
57	num = drm_add_modes_noedid(conn, 1920, 1200);
58
59	drm_set_preferred_mode(conn, 1024, 768);
60
61	return num;
62}
63
64static struct drm_encoder *
65ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
66					struct drm_atomic_state *state)
67{
68	struct lsdc_output *output = connector_to_lsdc_output(connector);
69
70	return &output->encoder;
71}
72
73static const struct drm_connector_helper_funcs
74ls7a1000_dpi_connector_helpers = {
75	.atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
76	.get_modes = ls7a1000_dpi_connector_get_modes,
77};
78
79static enum drm_connector_status
80ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
81{
82	struct i2c_adapter *ddc = connector->ddc;
83
84	if (ddc) {
85		if (drm_probe_ddc(ddc))
86			return connector_status_connected;
87
88		return connector_status_disconnected;
89	}
90
91	return connector_status_unknown;
92}
93
94static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
95	.detect = ls7a1000_dpi_connector_detect,
96	.fill_modes = drm_helper_probe_single_connector_modes,
97	.destroy = drm_connector_cleanup,
98	.reset = drm_atomic_helper_connector_reset,
99	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
100	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
101};
102
103static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
104{
105	struct drm_device *ddev = encoder->dev;
106	struct lsdc_device *ldev = to_lsdc(ddev);
107
108	/*
109	 * We need this for S3 support, screen will not lightup if don't set
110	 * this register correctly.
111	 */
112	lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
113		    PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
114}
115
116static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
117{
118	struct drm_device *ddev = encoder->dev;
119	struct lsdc_device *ldev = to_lsdc(ddev);
120
121	/*
122	 * We need this for S3 support, screen will not lightup if don't set
123	 * this register correctly.
124	 */
125
126	/* DVO */
127	lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
128		    BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
129}
130
131static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
132	{
133		.reset = ls7a1000_pipe0_encoder_reset,
134		.destroy = drm_encoder_cleanup,
135	},
136	{
137		.reset = ls7a1000_pipe1_encoder_reset,
138		.destroy = drm_encoder_cleanup,
139	},
140};
141
142int ls7a1000_output_init(struct drm_device *ddev,
143			 struct lsdc_display_pipe *dispipe,
144			 struct i2c_adapter *ddc,
145			 unsigned int index)
146{
147	struct lsdc_output *output = &dispipe->output;
148	struct drm_encoder *encoder = &output->encoder;
149	struct drm_connector *connector = &output->connector;
150	int ret;
151
152	ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
153			       DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
154	if (ret)
155		return ret;
156
157	encoder->possible_crtcs = BIT(index);
158
159	ret = drm_connector_init_with_ddc(ddev, connector,
160					  &ls7a1000_dpi_connector_funcs,
161					  DRM_MODE_CONNECTOR_DPI, ddc);
162	if (ret)
163		return ret;
164
165	drm_info(ddev, "display pipe-%u has a DVO\n", index);
166
167	drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
168
169	drm_connector_attach_encoder(connector, encoder);
170
171	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
172			    DRM_CONNECTOR_POLL_DISCONNECT;
173
174	connector->interlace_allowed = 0;
175	connector->doublescan_allowed = 0;
176
177	return 0;
178}
179