1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2015 Free Electrons
4 * Copyright (C) 2015 NextThing Co
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_bridge.h>
13 #include <drm/drm_of.h>
14 #include <drm/drm_panel.h>
15 #include <drm/drm_print.h>
16 #include <drm/drm_probe_helper.h>
17 #include <drm/drm_simple_kms_helper.h>
19 #include "sun4i_crtc.h"
20 #include "sun4i_tcon.h"
21 #include "sun4i_rgb.h"
24 struct drm_connector connector
;
25 struct drm_encoder encoder
;
27 struct sun4i_tcon
*tcon
;
28 struct drm_panel
*panel
;
29 struct drm_bridge
*bridge
;
32 static inline struct sun4i_rgb
*
33 drm_connector_to_sun4i_rgb(struct drm_connector
*connector
)
35 return container_of(connector
, struct sun4i_rgb
,
39 static inline struct sun4i_rgb
*
40 drm_encoder_to_sun4i_rgb(struct drm_encoder
*encoder
)
42 return container_of(encoder
, struct sun4i_rgb
,
46 static int sun4i_rgb_get_modes(struct drm_connector
*connector
)
48 struct sun4i_rgb
*rgb
=
49 drm_connector_to_sun4i_rgb(connector
);
51 return drm_panel_get_modes(rgb
->panel
, connector
);
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.
60 #define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE 5
62 static enum drm_mode_status
sun4i_rgb_mode_valid(struct drm_encoder
*crtc
,
63 const struct drm_display_mode
*mode
)
65 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(crtc
);
66 struct sun4i_tcon
*tcon
= rgb
->tcon
;
67 u32 hsync
= mode
->hsync_end
- mode
->hsync_start
;
68 u32 vsync
= mode
->vsync_end
- mode
->vsync_start
;
69 unsigned long long rate
= mode
->clock
* 1000;
70 unsigned long long lowest
, highest
;
71 unsigned long long rounded_rate
;
73 DRM_DEBUG_DRIVER("Validating modes...\n");
76 return MODE_HSYNC_NARROW
;
79 return MODE_HSYNC_WIDE
;
81 if ((mode
->hdisplay
< 1) || (mode
->htotal
< 1))
82 return MODE_H_ILLEGAL
;
84 if ((mode
->hdisplay
> 0x7ff) || (mode
->htotal
> 0xfff))
85 return MODE_BAD_HVALUE
;
87 DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
90 return MODE_VSYNC_NARROW
;
93 return MODE_VSYNC_WIDE
;
95 if ((mode
->vdisplay
< 1) || (mode
->vtotal
< 1))
96 return MODE_V_ILLEGAL
;
98 if ((mode
->vdisplay
> 0x7ff) || (mode
->vtotal
> 0xfff))
99 return MODE_BAD_VVALUE
;
101 DRM_DEBUG_DRIVER("Vertical parameters OK\n");
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.
110 DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
115 * That shouldn't ever happen unless something is really wrong, but it
116 * doesn't harm to check.
121 tcon
->dclk_min_div
= 6;
122 tcon
->dclk_max_div
= 127;
123 rounded_rate
= clk_round_rate(tcon
->dclk
, rate
);
125 lowest
= rate
* (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE
);
126 do_div(lowest
, 1000);
127 if (rounded_rate
< lowest
)
128 return MODE_CLOCK_LOW
;
130 highest
= rate
* (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE
);
131 do_div(highest
, 1000);
132 if (rounded_rate
> highest
)
133 return MODE_CLOCK_HIGH
;
136 DRM_DEBUG_DRIVER("Clock rate OK\n");
141 static const struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs
= {
142 .get_modes
= sun4i_rgb_get_modes
,
146 sun4i_rgb_connector_destroy(struct drm_connector
*connector
)
148 drm_connector_cleanup(connector
);
151 static const struct drm_connector_funcs sun4i_rgb_con_funcs
= {
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
,
159 static void sun4i_rgb_encoder_enable(struct drm_encoder
*encoder
)
161 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
163 DRM_DEBUG_DRIVER("Enabling RGB output\n");
166 drm_panel_prepare(rgb
->panel
);
167 drm_panel_enable(rgb
->panel
);
171 static void sun4i_rgb_encoder_disable(struct drm_encoder
*encoder
)
173 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
175 DRM_DEBUG_DRIVER("Disabling RGB output\n");
178 drm_panel_disable(rgb
->panel
);
179 drm_panel_unprepare(rgb
->panel
);
183 static const struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs
= {
184 .disable
= sun4i_rgb_encoder_disable
,
185 .enable
= sun4i_rgb_encoder_enable
,
186 .mode_valid
= sun4i_rgb_mode_valid
,
189 int sun4i_rgb_init(struct drm_device
*drm
, struct sun4i_tcon
*tcon
)
191 struct drm_encoder
*encoder
;
192 struct sun4i_rgb
*rgb
;
195 rgb
= devm_kzalloc(drm
->dev
, sizeof(*rgb
), GFP_KERNEL
);
199 encoder
= &rgb
->encoder
;
201 ret
= drm_of_find_panel_or_bridge(tcon
->dev
->of_node
, 1, 0,
202 &rgb
->panel
, &rgb
->bridge
);
204 dev_info(drm
->dev
, "No panel or bridge found... RGB output disabled\n");
208 drm_encoder_helper_add(&rgb
->encoder
,
209 &sun4i_rgb_enc_helper_funcs
);
210 ret
= drm_simple_encoder_init(drm
, &rgb
->encoder
,
211 DRM_MODE_ENCODER_NONE
);
213 dev_err(drm
->dev
, "Couldn't initialise the rgb encoder\n");
217 /* The RGB encoder can only work with the TCON channel 0 */
218 rgb
->encoder
.possible_crtcs
= drm_crtc_mask(&tcon
->crtc
->crtc
);
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
);
227 dev_err(drm
->dev
, "Couldn't initialise the rgb connector\n");
228 goto err_cleanup_connector
;
231 drm_connector_attach_encoder(&rgb
->connector
,
236 ret
= drm_bridge_attach(encoder
, rgb
->bridge
, NULL
, 0);
238 dev_err(drm
->dev
, "Couldn't attach our bridge\n");
239 goto err_cleanup_connector
;
245 err_cleanup_connector
:
246 drm_encoder_cleanup(&rgb
->encoder
);
250 EXPORT_SYMBOL(sun4i_rgb_init
);