1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2019-2022 Bootlin
4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5 */
6
7#include <linux/types.h>
8
9#include <drm/drm_atomic_helper.h>
10#include <drm/drm_bridge.h>
11#include <drm/drm_connector.h>
12#include <drm/drm_drv.h>
13#include <drm/drm_encoder.h>
14#include <drm/drm_gem_dma_helper.h>
15#include <drm/drm_modeset_helper_vtables.h>
16#include <drm/drm_of.h>
17#include <drm/drm_panel.h>
18#include <drm/drm_print.h>
19#include <drm/drm_probe_helper.h>
20
21#include "logicvc_crtc.h"
22#include "logicvc_drm.h"
23#include "logicvc_interface.h"
24#include "logicvc_regs.h"
25
26#define logicvc_interface_from_drm_encoder(c) \
27	container_of(c, struct logicvc_interface, drm_encoder)
28#define logicvc_interface_from_drm_connector(c) \
29	container_of(c, struct logicvc_interface, drm_connector)
30
31static void logicvc_encoder_enable(struct drm_encoder *drm_encoder)
32{
33	struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev);
34	struct logicvc_interface *interface =
35		logicvc_interface_from_drm_encoder(drm_encoder);
36
37	regmap_update_bits(logicvc->regmap, LOGICVC_POWER_CTRL_REG,
38			   LOGICVC_POWER_CTRL_VIDEO_ENABLE,
39			   LOGICVC_POWER_CTRL_VIDEO_ENABLE);
40
41	if (interface->drm_panel) {
42		drm_panel_prepare(interface->drm_panel);
43		drm_panel_enable(interface->drm_panel);
44	}
45}
46
47static void logicvc_encoder_disable(struct drm_encoder *drm_encoder)
48{
49	struct logicvc_interface *interface =
50		logicvc_interface_from_drm_encoder(drm_encoder);
51
52	if (interface->drm_panel) {
53		drm_panel_disable(interface->drm_panel);
54		drm_panel_unprepare(interface->drm_panel);
55	}
56}
57
58static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = {
59	.enable			= logicvc_encoder_enable,
60	.disable		= logicvc_encoder_disable,
61};
62
63static const struct drm_encoder_funcs logicvc_encoder_funcs = {
64	.destroy		= drm_encoder_cleanup,
65};
66
67static int logicvc_connector_get_modes(struct drm_connector *drm_connector)
68{
69	struct logicvc_interface *interface =
70		logicvc_interface_from_drm_connector(drm_connector);
71
72	if (interface->drm_panel)
73		return drm_panel_get_modes(interface->drm_panel, drm_connector);
74
75	WARN_ONCE(1, "Retrieving modes from a native connector is not implemented.");
76
77	return 0;
78}
79
80static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = {
81	.get_modes		= logicvc_connector_get_modes,
82};
83
84static const struct drm_connector_funcs logicvc_connector_funcs = {
85	.reset			= drm_atomic_helper_connector_reset,
86	.fill_modes		= drm_helper_probe_single_connector_modes,
87	.destroy		= drm_connector_cleanup,
88	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
89	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
90};
91
92static int logicvc_interface_encoder_type(struct logicvc_drm *logicvc)
93{
94	switch (logicvc->config.display_interface) {
95	case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
96	case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
97	case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
98		return DRM_MODE_ENCODER_LVDS;
99	case LOGICVC_DISPLAY_INTERFACE_DVI:
100		return DRM_MODE_ENCODER_TMDS;
101	case LOGICVC_DISPLAY_INTERFACE_RGB:
102		return DRM_MODE_ENCODER_DPI;
103	default:
104		return DRM_MODE_ENCODER_NONE;
105	}
106}
107
108static int logicvc_interface_connector_type(struct logicvc_drm *logicvc)
109{
110	switch (logicvc->config.display_interface) {
111	case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS:
112	case LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS_CAMERA:
113	case LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS:
114		return DRM_MODE_CONNECTOR_LVDS;
115	case LOGICVC_DISPLAY_INTERFACE_DVI:
116		return DRM_MODE_CONNECTOR_DVID;
117	case LOGICVC_DISPLAY_INTERFACE_RGB:
118		return DRM_MODE_CONNECTOR_DPI;
119	default:
120		return DRM_MODE_CONNECTOR_Unknown;
121	}
122}
123
124static bool logicvc_interface_native_connector(struct logicvc_drm *logicvc)
125{
126	switch (logicvc->config.display_interface) {
127	case LOGICVC_DISPLAY_INTERFACE_DVI:
128		return true;
129	default:
130		return false;
131	}
132}
133
134void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc)
135{
136	uint32_t possible_crtcs = drm_crtc_mask(&logicvc->crtc->drm_crtc);
137
138	logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs;
139}
140
141int logicvc_interface_init(struct logicvc_drm *logicvc)
142{
143	struct logicvc_interface *interface;
144	struct drm_device *drm_dev = &logicvc->drm_dev;
145	struct device *dev = drm_dev->dev;
146	struct device_node *of_node = dev->of_node;
147	int encoder_type = logicvc_interface_encoder_type(logicvc);
148	int connector_type = logicvc_interface_connector_type(logicvc);
149	bool native_connector = logicvc_interface_native_connector(logicvc);
150	int ret;
151
152	interface = devm_kzalloc(dev, sizeof(*interface), GFP_KERNEL);
153	if (!interface) {
154		ret = -ENOMEM;
155		goto error_early;
156	}
157
158	ret = drm_of_find_panel_or_bridge(of_node, 0, 0, &interface->drm_panel,
159					  &interface->drm_bridge);
160	if (ret == -EPROBE_DEFER)
161		goto error_early;
162
163	ret = drm_encoder_init(drm_dev, &interface->drm_encoder,
164			       &logicvc_encoder_funcs, encoder_type, NULL);
165	if (ret) {
166		drm_err(drm_dev, "Failed to initialize encoder\n");
167		goto error_early;
168	}
169
170	drm_encoder_helper_add(&interface->drm_encoder,
171			       &logicvc_encoder_helper_funcs);
172
173	if (native_connector || interface->drm_panel) {
174		ret = drm_connector_init(drm_dev, &interface->drm_connector,
175					 &logicvc_connector_funcs,
176					 connector_type);
177		if (ret) {
178			drm_err(drm_dev, "Failed to initialize connector\n");
179			goto error_encoder;
180		}
181
182		drm_connector_helper_add(&interface->drm_connector,
183					 &logicvc_connector_helper_funcs);
184
185		ret = drm_connector_attach_encoder(&interface->drm_connector,
186						   &interface->drm_encoder);
187		if (ret) {
188			drm_err(drm_dev,
189				"Failed to attach connector to encoder\n");
190			goto error_encoder;
191		}
192	}
193
194	if (interface->drm_bridge) {
195		ret = drm_bridge_attach(&interface->drm_encoder,
196					interface->drm_bridge, NULL, 0);
197		if (ret) {
198			drm_err(drm_dev,
199				"Failed to attach bridge to encoder\n");
200			goto error_encoder;
201		}
202	}
203
204	logicvc->interface = interface;
205
206	return 0;
207
208error_encoder:
209	drm_encoder_cleanup(&interface->drm_encoder);
210
211error_early:
212	return ret;
213}
214