Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  4 * Copyright (C) 2017 Broadcom
  5 */
  6
  7#include <drm/drm_atomic_helper.h>
  8#include <drm/drm_bridge.h>
  9#include <drm/drm_connector.h>
 10#include <drm/drm_encoder.h>
 11#include <drm/drm_modeset_helper_vtables.h>
 12#include <drm/drm_panel.h>
 13#include <drm/drm_print.h>
 14#include <drm/drm_probe_helper.h>
 15
 16struct panel_bridge {
 17	struct drm_bridge bridge;
 18	struct drm_connector connector;
 19	struct drm_panel *panel;
 20	u32 connector_type;
 21};
 22
 23static inline struct panel_bridge *
 24drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
 25{
 26	return container_of(bridge, struct panel_bridge, bridge);
 27}
 28
 29static inline struct panel_bridge *
 30drm_connector_to_panel_bridge(struct drm_connector *connector)
 31{
 32	return container_of(connector, struct panel_bridge, connector);
 33}
 34
 35static int panel_bridge_connector_get_modes(struct drm_connector *connector)
 36{
 37	struct panel_bridge *panel_bridge =
 38		drm_connector_to_panel_bridge(connector);
 39
 40	return drm_panel_get_modes(panel_bridge->panel, connector);
 41}
 42
 43static const struct drm_connector_helper_funcs
 44panel_bridge_connector_helper_funcs = {
 45	.get_modes = panel_bridge_connector_get_modes,
 46};
 47
 48static const struct drm_connector_funcs panel_bridge_connector_funcs = {
 49	.reset = drm_atomic_helper_connector_reset,
 50	.fill_modes = drm_helper_probe_single_connector_modes,
 51	.destroy = drm_connector_cleanup,
 52	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 53	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 54};
 55
 56static int panel_bridge_attach(struct drm_bridge *bridge,
 57			       enum drm_bridge_attach_flags flags)
 58{
 59	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 60	struct drm_connector *connector = &panel_bridge->connector;
 61	int ret;
 62
 63	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
 64		return 0;
 65
 66	if (!bridge->encoder) {
 67		DRM_ERROR("Missing encoder\n");
 68		return -ENODEV;
 69	}
 70
 71	drm_connector_helper_add(connector,
 72				 &panel_bridge_connector_helper_funcs);
 73
 74	ret = drm_connector_init(bridge->dev, connector,
 75				 &panel_bridge_connector_funcs,
 76				 panel_bridge->connector_type);
 77	if (ret) {
 78		DRM_ERROR("Failed to initialize connector\n");
 79		return ret;
 80	}
 81
 82	drm_connector_attach_encoder(&panel_bridge->connector,
 83					  bridge->encoder);
 84
 85	return 0;
 86}
 87
 88static void panel_bridge_detach(struct drm_bridge *bridge)
 89{
 90	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 91	struct drm_connector *connector = &panel_bridge->connector;
 92
 93	/*
 94	 * Cleanup the connector if we know it was initialized.
 95	 *
 96	 * FIXME: This wouldn't be needed if the panel_bridge structure was
 97	 * allocated with drmm_kzalloc(). This might be tricky since the
 98	 * drm_device pointer can only be retrieved when the bridge is attached.
 99	 */
100	if (connector->dev)
101		drm_connector_cleanup(connector);
102}
103
104static void panel_bridge_pre_enable(struct drm_bridge *bridge)
105{
106	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
107
108	drm_panel_prepare(panel_bridge->panel);
109}
110
111static void panel_bridge_enable(struct drm_bridge *bridge)
112{
113	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
114
115	drm_panel_enable(panel_bridge->panel);
116}
117
118static void panel_bridge_disable(struct drm_bridge *bridge)
119{
120	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
121
122	drm_panel_disable(panel_bridge->panel);
123}
124
125static void panel_bridge_post_disable(struct drm_bridge *bridge)
126{
127	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
128
129	drm_panel_unprepare(panel_bridge->panel);
130}
131
132static int panel_bridge_get_modes(struct drm_bridge *bridge,
133				  struct drm_connector *connector)
134{
135	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
136
137	return drm_panel_get_modes(panel_bridge->panel, connector);
138}
139
140static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
141	.attach = panel_bridge_attach,
142	.detach = panel_bridge_detach,
143	.pre_enable = panel_bridge_pre_enable,
144	.enable = panel_bridge_enable,
145	.disable = panel_bridge_disable,
146	.post_disable = panel_bridge_post_disable,
147	.get_modes = panel_bridge_get_modes,
148	.atomic_reset = drm_atomic_helper_bridge_reset,
149	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
150	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
151	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
152};
153
154/**
155 * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
156 * just calls the appropriate functions from &drm_panel.
157 *
158 * @panel: The drm_panel being wrapped.  Must be non-NULL.
159 *
160 * For drivers converting from directly using drm_panel: The expected
161 * usage pattern is that during either encoder module probe or DSI
162 * host attach, a drm_panel will be looked up through
163 * drm_of_find_panel_or_bridge().  drm_panel_bridge_add() is used to
164 * wrap that panel in the new bridge, and the result can then be
165 * passed to drm_bridge_attach().  The drm_panel_prepare() and related
166 * functions can be dropped from the encoder driver (they're now
167 * called by the KMS helpers before calling into the encoder), along
168 * with connector creation.  When done with the bridge (after
169 * drm_mode_config_cleanup() if the bridge has already been attached), then
170 * drm_panel_bridge_remove() to free it.
171 *
172 * The connector type is set to @panel->connector_type, which must be set to a
173 * known type. Calling this function with a panel whose connector type is
174 * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL).
175 *
176 * See devm_drm_panel_bridge_add() for an automatically managed version of this
177 * function.
178 */
179struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
180{
181	if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
182		return ERR_PTR(-EINVAL);
183
184	return drm_panel_bridge_add_typed(panel, panel->connector_type);
185}
186EXPORT_SYMBOL(drm_panel_bridge_add);
187
188/**
189 * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with
190 * an explicit connector type.
191 * @panel: The drm_panel being wrapped.  Must be non-NULL.
192 * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
193 *
194 * This is just like drm_panel_bridge_add(), but forces the connector type to
195 * @connector_type instead of infering it from the panel.
196 *
197 * This function is deprecated and should not be used in new drivers. Use
198 * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they
199 * don't report a connector type.
200 */
201struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
202					      u32 connector_type)
203{
204	struct panel_bridge *panel_bridge;
205
206	if (!panel)
207		return ERR_PTR(-EINVAL);
208
209	panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
210				    GFP_KERNEL);
211	if (!panel_bridge)
212		return ERR_PTR(-ENOMEM);
213
214	panel_bridge->connector_type = connector_type;
215	panel_bridge->panel = panel;
216
217	panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
218#ifdef CONFIG_OF
219	panel_bridge->bridge.of_node = panel->dev->of_node;
220#endif
221	panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
222	panel_bridge->bridge.type = connector_type;
223
224	drm_bridge_add(&panel_bridge->bridge);
225
226	return &panel_bridge->bridge;
227}
228EXPORT_SYMBOL(drm_panel_bridge_add_typed);
229
230/**
231 * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
232 * created by drm_panel_bridge_add().
233 *
234 * @bridge: The drm_bridge being freed.
235 */
236void drm_panel_bridge_remove(struct drm_bridge *bridge)
237{
238	struct panel_bridge *panel_bridge;
239
240	if (!bridge)
241		return;
242
243	if (bridge->funcs != &panel_bridge_bridge_funcs)
244		return;
245
246	panel_bridge = drm_bridge_to_panel_bridge(bridge);
247
248	drm_bridge_remove(bridge);
249	devm_kfree(panel_bridge->panel->dev, bridge);
250}
251EXPORT_SYMBOL(drm_panel_bridge_remove);
252
253static void devm_drm_panel_bridge_release(struct device *dev, void *res)
254{
255	struct drm_bridge **bridge = res;
256
257	drm_panel_bridge_remove(*bridge);
258}
259
260/**
261 * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
262 * that just calls the appropriate functions from &drm_panel.
263 * @dev: device to tie the bridge lifetime to
264 * @panel: The drm_panel being wrapped.  Must be non-NULL.
265 *
266 * This is the managed version of drm_panel_bridge_add() which automatically
267 * calls drm_panel_bridge_remove() when @dev is unbound.
268 */
269struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
270					     struct drm_panel *panel)
271{
272	if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
273		return ERR_PTR(-EINVAL);
274
275	return devm_drm_panel_bridge_add_typed(dev, panel,
276					       panel->connector_type);
277}
278EXPORT_SYMBOL(devm_drm_panel_bridge_add);
279
280/**
281 * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
282 * &drm_connector with an explicit connector type.
283 * @dev: device to tie the bridge lifetime to
284 * @panel: The drm_panel being wrapped.  Must be non-NULL.
285 * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
286 *
287 * This is just like devm_drm_panel_bridge_add(), but forces the connector type
288 * to @connector_type instead of infering it from the panel.
289 *
290 * This function is deprecated and should not be used in new drivers. Use
291 * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if
292 * they don't report a connector type.
293 */
294struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
295						   struct drm_panel *panel,
296						   u32 connector_type)
297{
298	struct drm_bridge **ptr, *bridge;
299
300	ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
301			   GFP_KERNEL);
302	if (!ptr)
303		return ERR_PTR(-ENOMEM);
304
305	bridge = drm_panel_bridge_add_typed(panel, connector_type);
306	if (!IS_ERR(bridge)) {
307		*ptr = bridge;
308		devres_add(dev, ptr);
309	} else {
310		devres_free(ptr);
311	}
312
313	return bridge;
314}
315EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
316
317/**
318 * drm_panel_bridge_connector - return the connector for the panel bridge
319 * @bridge: The drm_bridge.
320 *
321 * drm_panel_bridge creates the connector.
322 * This function gives external access to the connector.
323 *
324 * Returns: Pointer to drm_connector
325 */
326struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
327{
328	struct panel_bridge *panel_bridge;
329
330	panel_bridge = drm_bridge_to_panel_bridge(bridge);
331
332	return &panel_bridge->connector;
333}
334EXPORT_SYMBOL(drm_panel_bridge_connector);