.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Avionic Design GmbH |
---|
3 | 4 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundation. |
---|
8 | 5 | */ |
---|
9 | 6 | |
---|
10 | 7 | #include <linux/clk.h> |
---|
11 | 8 | |
---|
12 | 9 | #include <drm/drm_atomic_helper.h> |
---|
13 | | -#include <drm/drm_panel.h> |
---|
| 10 | +#include <drm/drm_bridge_connector.h> |
---|
| 11 | +#include <drm/drm_simple_kms_helper.h> |
---|
14 | 12 | |
---|
15 | 13 | #include "drm.h" |
---|
16 | 14 | #include "dc.h" |
---|
.. | .. |
---|
87 | 85 | tegra_dc_writel(dc, table[i].value, table[i].offset); |
---|
88 | 86 | } |
---|
89 | 87 | |
---|
90 | | -static const struct drm_connector_funcs tegra_rgb_connector_funcs = { |
---|
91 | | - .reset = drm_atomic_helper_connector_reset, |
---|
92 | | - .detect = tegra_output_connector_detect, |
---|
93 | | - .fill_modes = drm_helper_probe_single_connector_modes, |
---|
94 | | - .destroy = tegra_output_connector_destroy, |
---|
95 | | - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
---|
96 | | - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
---|
97 | | -}; |
---|
98 | | - |
---|
99 | | -static enum drm_mode_status |
---|
100 | | -tegra_rgb_connector_mode_valid(struct drm_connector *connector, |
---|
101 | | - struct drm_display_mode *mode) |
---|
102 | | -{ |
---|
103 | | - /* |
---|
104 | | - * FIXME: For now, always assume that the mode is okay. There are |
---|
105 | | - * unresolved issues with clk_round_rate(), which doesn't always |
---|
106 | | - * reliably report whether a frequency can be set or not. |
---|
107 | | - */ |
---|
108 | | - return MODE_OK; |
---|
109 | | -} |
---|
110 | | - |
---|
111 | | -static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = { |
---|
112 | | - .get_modes = tegra_output_connector_get_modes, |
---|
113 | | - .mode_valid = tegra_rgb_connector_mode_valid, |
---|
114 | | -}; |
---|
115 | | - |
---|
116 | | -static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = { |
---|
117 | | - .destroy = tegra_output_encoder_destroy, |
---|
118 | | -}; |
---|
119 | | - |
---|
120 | 88 | static void tegra_rgb_encoder_disable(struct drm_encoder *encoder) |
---|
121 | 89 | { |
---|
122 | 90 | struct tegra_output *output = encoder_to_output(encoder); |
---|
123 | 91 | struct tegra_rgb *rgb = to_rgb(output); |
---|
124 | 92 | |
---|
125 | | - if (output->panel) |
---|
126 | | - drm_panel_disable(output->panel); |
---|
127 | | - |
---|
128 | 93 | tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); |
---|
129 | 94 | tegra_dc_commit(rgb->dc); |
---|
130 | | - |
---|
131 | | - if (output->panel) |
---|
132 | | - drm_panel_unprepare(output->panel); |
---|
133 | 95 | } |
---|
134 | 96 | |
---|
135 | 97 | static void tegra_rgb_encoder_enable(struct drm_encoder *encoder) |
---|
.. | .. |
---|
137 | 99 | struct tegra_output *output = encoder_to_output(encoder); |
---|
138 | 100 | struct tegra_rgb *rgb = to_rgb(output); |
---|
139 | 101 | u32 value; |
---|
140 | | - |
---|
141 | | - if (output->panel) |
---|
142 | | - drm_panel_prepare(output->panel); |
---|
143 | 102 | |
---|
144 | 103 | tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable)); |
---|
145 | 104 | |
---|
.. | .. |
---|
162 | 121 | tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); |
---|
163 | 122 | |
---|
164 | 123 | tegra_dc_commit(rgb->dc); |
---|
165 | | - |
---|
166 | | - if (output->panel) |
---|
167 | | - drm_panel_enable(output->panel); |
---|
168 | 124 | } |
---|
169 | 125 | |
---|
170 | 126 | static int |
---|
.. | .. |
---|
273 | 229 | int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) |
---|
274 | 230 | { |
---|
275 | 231 | struct tegra_output *output = dc->rgb; |
---|
| 232 | + struct drm_connector *connector; |
---|
276 | 233 | int err; |
---|
277 | 234 | |
---|
278 | 235 | if (!dc->rgb) |
---|
279 | 236 | return -ENODEV; |
---|
280 | 237 | |
---|
281 | | - drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs, |
---|
282 | | - DRM_MODE_CONNECTOR_LVDS); |
---|
283 | | - drm_connector_helper_add(&output->connector, |
---|
284 | | - &tegra_rgb_connector_helper_funcs); |
---|
285 | | - output->connector.dpms = DRM_MODE_DPMS_OFF; |
---|
286 | | - |
---|
287 | | - drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs, |
---|
288 | | - DRM_MODE_ENCODER_LVDS, NULL); |
---|
| 238 | + drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS); |
---|
289 | 239 | drm_encoder_helper_add(&output->encoder, |
---|
290 | 240 | &tegra_rgb_encoder_helper_funcs); |
---|
291 | 241 | |
---|
292 | | - drm_connector_attach_encoder(&output->connector, |
---|
293 | | - &output->encoder); |
---|
294 | | - drm_connector_register(&output->connector); |
---|
| 242 | + /* |
---|
| 243 | + * Wrap directly-connected panel into DRM bridge in order to let |
---|
| 244 | + * DRM core to handle panel for us. |
---|
| 245 | + */ |
---|
| 246 | + if (output->panel) { |
---|
| 247 | + output->bridge = devm_drm_panel_bridge_add(output->dev, |
---|
| 248 | + output->panel); |
---|
| 249 | + if (IS_ERR(output->bridge)) { |
---|
| 250 | + dev_err(output->dev, |
---|
| 251 | + "failed to wrap panel into bridge: %pe\n", |
---|
| 252 | + output->bridge); |
---|
| 253 | + return PTR_ERR(output->bridge); |
---|
| 254 | + } |
---|
| 255 | + |
---|
| 256 | + output->panel = NULL; |
---|
| 257 | + } |
---|
| 258 | + |
---|
| 259 | + /* |
---|
| 260 | + * Tegra devices that have LVDS panel utilize LVDS encoder bridge |
---|
| 261 | + * for converting up to 28 LCD LVTTL lanes into 5/4 LVDS lanes that |
---|
| 262 | + * go to display panel's receiver. |
---|
| 263 | + * |
---|
| 264 | + * Encoder usually have a power-down control which needs to be enabled |
---|
| 265 | + * in order to transmit data to the panel. Historically devices that |
---|
| 266 | + * use an older device-tree version didn't model the bridge, assuming |
---|
| 267 | + * that encoder is turned ON by default, while today's DRM allows us |
---|
| 268 | + * to model LVDS encoder properly. |
---|
| 269 | + * |
---|
| 270 | + * Newer device-trees utilize LVDS encoder bridge, which provides |
---|
| 271 | + * us with a connector and handles the display panel. |
---|
| 272 | + * |
---|
| 273 | + * For older device-trees we wrapped panel into the panel-bridge. |
---|
| 274 | + */ |
---|
| 275 | + if (output->bridge) { |
---|
| 276 | + err = drm_bridge_attach(&output->encoder, output->bridge, |
---|
| 277 | + NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
---|
| 278 | + if (err) { |
---|
| 279 | + dev_err(output->dev, "failed to attach bridge: %d\n", |
---|
| 280 | + err); |
---|
| 281 | + return err; |
---|
| 282 | + } |
---|
| 283 | + |
---|
| 284 | + connector = drm_bridge_connector_init(drm, &output->encoder); |
---|
| 285 | + if (IS_ERR(connector)) { |
---|
| 286 | + dev_err(output->dev, |
---|
| 287 | + "failed to initialize bridge connector: %pe\n", |
---|
| 288 | + connector); |
---|
| 289 | + return PTR_ERR(connector); |
---|
| 290 | + } |
---|
| 291 | + |
---|
| 292 | + drm_connector_attach_encoder(connector, &output->encoder); |
---|
| 293 | + } |
---|
295 | 294 | |
---|
296 | 295 | err = tegra_output_init(drm, output); |
---|
297 | 296 | if (err < 0) { |
---|