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 | |
31 | static 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(map: 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(panel: interface->drm_panel); |
43 | drm_panel_enable(panel: interface->drm_panel); |
44 | } |
45 | } |
46 | |
47 | static 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(panel: interface->drm_panel); |
54 | drm_panel_unprepare(panel: interface->drm_panel); |
55 | } |
56 | } |
57 | |
58 | static const struct drm_encoder_helper_funcs logicvc_encoder_helper_funcs = { |
59 | .enable = logicvc_encoder_enable, |
60 | .disable = logicvc_encoder_disable, |
61 | }; |
62 | |
63 | static const struct drm_encoder_funcs logicvc_encoder_funcs = { |
64 | .destroy = drm_encoder_cleanup, |
65 | }; |
66 | |
67 | static 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(panel: interface->drm_panel, connector: drm_connector); |
74 | |
75 | WARN_ONCE(1, "Retrieving modes from a native connector is not implemented." ); |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static const struct drm_connector_helper_funcs logicvc_connector_helper_funcs = { |
81 | .get_modes = logicvc_connector_get_modes, |
82 | }; |
83 | |
84 | static 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 | |
92 | static 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 | |
108 | static 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 | |
124 | static 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 | |
134 | void logicvc_interface_attach_crtc(struct logicvc_drm *logicvc) |
135 | { |
136 | uint32_t possible_crtcs = drm_crtc_mask(crtc: &logicvc->crtc->drm_crtc); |
137 | |
138 | logicvc->interface->drm_encoder.possible_crtcs = possible_crtcs; |
139 | } |
140 | |
141 | int 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, size: sizeof(*interface), GFP_KERNEL); |
153 | if (!interface) { |
154 | ret = -ENOMEM; |
155 | goto error_early; |
156 | } |
157 | |
158 | ret = drm_of_find_panel_or_bridge(np: of_node, port: 0, endpoint: 0, panel: &interface->drm_panel, |
159 | bridge: &interface->drm_bridge); |
160 | if (ret == -EPROBE_DEFER) |
161 | goto error_early; |
162 | |
163 | ret = drm_encoder_init(dev: drm_dev, encoder: &interface->drm_encoder, |
164 | funcs: &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(encoder: &interface->drm_encoder, |
171 | funcs: &logicvc_encoder_helper_funcs); |
172 | |
173 | if (native_connector || interface->drm_panel) { |
174 | ret = drm_connector_init(dev: drm_dev, connector: &interface->drm_connector, |
175 | funcs: &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(connector: &interface->drm_connector, |
183 | funcs: &logicvc_connector_helper_funcs); |
184 | |
185 | ret = drm_connector_attach_encoder(connector: &interface->drm_connector, |
186 | encoder: &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(encoder: &interface->drm_encoder, |
196 | bridge: interface->drm_bridge, NULL, flags: 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 | |
208 | error_encoder: |
209 | drm_encoder_cleanup(encoder: &interface->drm_encoder); |
210 | |
211 | error_early: |
212 | return ret; |
213 | } |
214 | |