Merge tag 'v5.12-rc7' into ecryptfs/next
[linux-block.git] / drivers / gpu / drm / sun4i / sun4i_rgb.c
CommitLineData
2874c5fd 1// SPDX-License-Identifier: GPL-2.0-or-later
29e57fab
MR
2/*
3 * Copyright (C) 2015 Free Electrons
4 * Copyright (C) 2015 NextThing Co
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
29e57fab
MR
7 */
8
9#include <linux/clk.h>
10
29e57fab 11#include <drm/drm_atomic_helper.h>
ee68c743 12#include <drm/drm_bridge.h>
ebc94461 13#include <drm/drm_of.h>
29e57fab 14#include <drm/drm_panel.h>
9c25a297 15#include <drm/drm_print.h>
fcd70cd3 16#include <drm/drm_probe_helper.h>
f9f3a38d 17#include <drm/drm_simple_kms_helper.h>
29e57fab 18
a5154a4d 19#include "sun4i_crtc.h"
29e57fab 20#include "sun4i_tcon.h"
0c3ff44c 21#include "sun4i_rgb.h"
29e57fab
MR
22
23struct sun4i_rgb {
24 struct drm_connector connector;
25 struct drm_encoder encoder;
26
b9c8506c 27 struct sun4i_tcon *tcon;
1ce6f91c 28 struct drm_panel *panel;
19d0ffe0 29 struct drm_bridge *bridge;
29e57fab
MR
30};
31
32static inline struct sun4i_rgb *
33drm_connector_to_sun4i_rgb(struct drm_connector *connector)
34{
35 return container_of(connector, struct sun4i_rgb,
36 connector);
37}
38
39static inline struct sun4i_rgb *
40drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
41{
42 return container_of(encoder, struct sun4i_rgb,
43 encoder);
44}
45
46static int sun4i_rgb_get_modes(struct drm_connector *connector)
47{
48 struct sun4i_rgb *rgb =
49 drm_connector_to_sun4i_rgb(connector);
29e57fab 50
06c4a9c2 51 return drm_panel_get_modes(rgb->panel, connector);
29e57fab
MR
52}
53
e2771deb
MR
54/*
55 * VESA DMT defines a tolerance of 0.5% on the pixel clock, while the
56 * CVT spec reuses that tolerance in its examples, so it looks to be a
57 * good default tolerance for the EDID-based modes. Define it to 5 per
58 * mille to avoid floating point operations.
59 */
60#define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE 5
61
cde8b754
GB
62static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
63 const struct drm_display_mode *mode)
29e57fab 64{
cde8b754 65 struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(crtc);
b9c8506c 66 struct sun4i_tcon *tcon = rgb->tcon;
29e57fab
MR
67 u32 hsync = mode->hsync_end - mode->hsync_start;
68 u32 vsync = mode->vsync_end - mode->vsync_start;
9f7dfd0c 69 unsigned long long rate = mode->clock * 1000;
e2771deb 70 unsigned long long lowest, highest;
9f7dfd0c 71 unsigned long long rounded_rate;
29e57fab
MR
72
73 DRM_DEBUG_DRIVER("Validating modes...\n");
74
75 if (hsync < 1)
76 return MODE_HSYNC_NARROW;
77
78 if (hsync > 0x3ff)
79 return MODE_HSYNC_WIDE;
80
81 if ((mode->hdisplay < 1) || (mode->htotal < 1))
82 return MODE_H_ILLEGAL;
83
84 if ((mode->hdisplay > 0x7ff) || (mode->htotal > 0xfff))
85 return MODE_BAD_HVALUE;
86
87 DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
88
89 if (vsync < 1)
90 return MODE_VSYNC_NARROW;
91
92 if (vsync > 0x3ff)
93 return MODE_VSYNC_WIDE;
94
95 if ((mode->vdisplay < 1) || (mode->vtotal < 1))
96 return MODE_V_ILLEGAL;
97
98 if ((mode->vdisplay > 0x7ff) || (mode->vtotal > 0xfff))
99 return MODE_BAD_VVALUE;
100
101 DRM_DEBUG_DRIVER("Vertical parameters OK\n");
102
e2771deb
MR
103 /*
104 * TODO: We should use the struct display_timing if available
105 * and / or trying to stretch the timings within that
106 * tolerancy to take care of panels that we wouldn't be able
107 * to have a exact match for.
108 */
109 if (rgb->panel) {
110 DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
111 goto out;
112 }
113
114 /*
115 * That shouldn't ever happen unless something is really wrong, but it
116 * doesn't harm to check.
117 */
118 if (!rgb->bridge)
119 goto out;
120
5af894bd
MR
121 tcon->dclk_min_div = 6;
122 tcon->dclk_max_div = 127;
bb43d40d 123 rounded_rate = clk_round_rate(tcon->dclk, rate);
e2771deb
MR
124
125 lowest = rate * (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
126 do_div(lowest, 1000);
127 if (rounded_rate < lowest)
bb43d40d
MR
128 return MODE_CLOCK_LOW;
129
e2771deb
MR
130 highest = rate * (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
131 do_div(highest, 1000);
132 if (rounded_rate > highest)
bb43d40d
MR
133 return MODE_CLOCK_HIGH;
134
e2771deb 135out:
bb43d40d
MR
136 DRM_DEBUG_DRIVER("Clock rate OK\n");
137
29e57fab
MR
138 return MODE_OK;
139}
140
f13478c9 141static const struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
29e57fab 142 .get_modes = sun4i_rgb_get_modes,
29e57fab
MR
143};
144
29e57fab
MR
145static void
146sun4i_rgb_connector_destroy(struct drm_connector *connector)
147{
29e57fab
MR
148 drm_connector_cleanup(connector);
149}
150
32b4d575 151static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
29e57fab
MR
152 .fill_modes = drm_helper_probe_single_connector_modes,
153 .destroy = sun4i_rgb_connector_destroy,
154 .reset = drm_atomic_helper_connector_reset,
155 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
156 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
157};
158
29e57fab
MR
159static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
160{
161 struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
29e57fab
MR
162
163 DRM_DEBUG_DRIVER("Enabling RGB output\n");
164
1ce6f91c
MR
165 if (rgb->panel) {
166 drm_panel_prepare(rgb->panel);
167 drm_panel_enable(rgb->panel);
45e88f99 168 }
29e57fab
MR
169}
170
171static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
172{
173 struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
29e57fab
MR
174
175 DRM_DEBUG_DRIVER("Disabling RGB output\n");
176
1ce6f91c
MR
177 if (rgb->panel) {
178 drm_panel_disable(rgb->panel);
179 drm_panel_unprepare(rgb->panel);
45e88f99 180 }
29e57fab
MR
181}
182
f13478c9 183static const struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
29e57fab
MR
184 .disable = sun4i_rgb_encoder_disable,
185 .enable = sun4i_rgb_encoder_enable,
cde8b754 186 .mode_valid = sun4i_rgb_mode_valid,
29e57fab
MR
187};
188
b9c8506c 189int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
29e57fab 190{
894f5a9f 191 struct drm_encoder *encoder;
29e57fab
MR
192 struct sun4i_rgb *rgb;
193 int ret;
194
29e57fab
MR
195 rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
196 if (!rgb)
197 return -ENOMEM;
b9c8506c 198 rgb->tcon = tcon;
894f5a9f 199 encoder = &rgb->encoder;
29e57fab 200
ebc94461 201 ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
19d0ffe0 202 &rgb->panel, &rgb->bridge);
ebc94461 203 if (ret) {
894f5a9f 204 dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
a8444c7e
MR
205 return 0;
206 }
207
29e57fab
MR
208 drm_encoder_helper_add(&rgb->encoder,
209 &sun4i_rgb_enc_helper_funcs);
f9f3a38d
TZ
210 ret = drm_simple_encoder_init(drm, &rgb->encoder,
211 DRM_MODE_ENCODER_NONE);
29e57fab
MR
212 if (ret) {
213 dev_err(drm->dev, "Couldn't initialise the rgb encoder\n");
214 goto err_out;
215 }
216
217 /* The RGB encoder can only work with the TCON channel 0 */
dbf8f9e4 218 rgb->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
29e57fab 219
1ce6f91c 220 if (rgb->panel) {
894f5a9f
MR
221 drm_connector_helper_add(&rgb->connector,
222 &sun4i_rgb_con_helper_funcs);
223 ret = drm_connector_init(drm, &rgb->connector,
224 &sun4i_rgb_con_funcs,
225 DRM_MODE_CONNECTOR_Unknown);
226 if (ret) {
227 dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
228 goto err_cleanup_connector;
229 }
230
cde4c44d 231 drm_connector_attach_encoder(&rgb->connector,
894f5a9f 232 &rgb->encoder);
29e57fab
MR
233 }
234
19d0ffe0 235 if (rgb->bridge) {
a25b988f 236 ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0);
894f5a9f
MR
237 if (ret) {
238 dev_err(drm->dev, "Couldn't attach our bridge\n");
239 goto err_cleanup_connector;
240 }
241 }
29e57fab
MR
242
243 return 0;
244
245err_cleanup_connector:
246 drm_encoder_cleanup(&rgb->encoder);
247err_out:
248 return ret;
249}
250EXPORT_SYMBOL(sun4i_rgb_init);