2 * Copyright (C) 2015 Free Electrons
3 * Copyright (C) 2015 NextThing Co
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
13 #include <linux/clk.h>
16 #include <drm/drm_atomic_helper.h>
17 #include <drm/drm_crtc_helper.h>
18 #include <drm/drm_panel.h>
20 #include "sun4i_drv.h"
21 #include "sun4i_tcon.h"
22 #include "sun4i_rgb.h"
25 struct drm_connector connector
;
26 struct drm_encoder encoder
;
28 struct sun4i_drv
*drv
;
31 static inline struct sun4i_rgb
*
32 drm_connector_to_sun4i_rgb(struct drm_connector
*connector
)
34 return container_of(connector
, struct sun4i_rgb
,
38 static inline struct sun4i_rgb
*
39 drm_encoder_to_sun4i_rgb(struct drm_encoder
*encoder
)
41 return container_of(encoder
, struct sun4i_rgb
,
45 static int sun4i_rgb_get_modes(struct drm_connector
*connector
)
47 struct sun4i_rgb
*rgb
=
48 drm_connector_to_sun4i_rgb(connector
);
49 struct sun4i_drv
*drv
= rgb
->drv
;
50 struct sun4i_tcon
*tcon
= drv
->tcon
;
52 return drm_panel_get_modes(tcon
->panel
);
55 static int sun4i_rgb_mode_valid(struct drm_connector
*connector
,
56 struct drm_display_mode
*mode
)
58 struct sun4i_rgb
*rgb
= drm_connector_to_sun4i_rgb(connector
);
59 struct sun4i_drv
*drv
= rgb
->drv
;
60 struct sun4i_tcon
*tcon
= drv
->tcon
;
61 u32 hsync
= mode
->hsync_end
- mode
->hsync_start
;
62 u32 vsync
= mode
->vsync_end
- mode
->vsync_start
;
63 unsigned long rate
= mode
->clock
* 1000;
66 DRM_DEBUG_DRIVER("Validating modes...\n");
69 return MODE_HSYNC_NARROW
;
72 return MODE_HSYNC_WIDE
;
74 if ((mode
->hdisplay
< 1) || (mode
->htotal
< 1))
75 return MODE_H_ILLEGAL
;
77 if ((mode
->hdisplay
> 0x7ff) || (mode
->htotal
> 0xfff))
78 return MODE_BAD_HVALUE
;
80 DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
83 return MODE_VSYNC_NARROW
;
86 return MODE_VSYNC_WIDE
;
88 if ((mode
->vdisplay
< 1) || (mode
->vtotal
< 1))
89 return MODE_V_ILLEGAL
;
91 if ((mode
->vdisplay
> 0x7ff) || (mode
->vtotal
> 0xfff))
92 return MODE_BAD_VVALUE
;
94 DRM_DEBUG_DRIVER("Vertical parameters OK\n");
96 rounded_rate
= clk_round_rate(tcon
->dclk
, rate
);
97 if (rounded_rate
< rate
)
98 return MODE_CLOCK_LOW
;
100 if (rounded_rate
> rate
)
101 return MODE_CLOCK_HIGH
;
103 DRM_DEBUG_DRIVER("Clock rate OK\n");
108 static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs
= {
109 .get_modes
= sun4i_rgb_get_modes
,
110 .mode_valid
= sun4i_rgb_mode_valid
,
113 static enum drm_connector_status
114 sun4i_rgb_connector_detect(struct drm_connector
*connector
, bool force
)
116 return connector_status_connected
;
120 sun4i_rgb_connector_destroy(struct drm_connector
*connector
)
122 struct sun4i_rgb
*rgb
= drm_connector_to_sun4i_rgb(connector
);
123 struct sun4i_drv
*drv
= rgb
->drv
;
124 struct sun4i_tcon
*tcon
= drv
->tcon
;
126 drm_panel_detach(tcon
->panel
);
127 drm_connector_cleanup(connector
);
130 static struct drm_connector_funcs sun4i_rgb_con_funcs
= {
131 .dpms
= drm_atomic_helper_connector_dpms
,
132 .detect
= sun4i_rgb_connector_detect
,
133 .fill_modes
= drm_helper_probe_single_connector_modes
,
134 .destroy
= sun4i_rgb_connector_destroy
,
135 .reset
= drm_atomic_helper_connector_reset
,
136 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
137 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
140 static int sun4i_rgb_atomic_check(struct drm_encoder
*encoder
,
141 struct drm_crtc_state
*crtc_state
,
142 struct drm_connector_state
*conn_state
)
147 static void sun4i_rgb_encoder_enable(struct drm_encoder
*encoder
)
149 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
150 struct sun4i_drv
*drv
= rgb
->drv
;
151 struct sun4i_tcon
*tcon
= drv
->tcon
;
153 DRM_DEBUG_DRIVER("Enabling RGB output\n");
155 if (!IS_ERR(tcon
->panel
)) {
156 drm_panel_prepare(tcon
->panel
);
157 drm_panel_enable(tcon
->panel
);
160 /* encoder->bridge can be NULL; drm_bridge_enable checks for it */
161 drm_bridge_enable(encoder
->bridge
);
163 sun4i_tcon_channel_enable(tcon
, 0);
166 static void sun4i_rgb_encoder_disable(struct drm_encoder
*encoder
)
168 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
169 struct sun4i_drv
*drv
= rgb
->drv
;
170 struct sun4i_tcon
*tcon
= drv
->tcon
;
172 DRM_DEBUG_DRIVER("Disabling RGB output\n");
174 sun4i_tcon_channel_disable(tcon
, 0);
176 /* encoder->bridge can be NULL; drm_bridge_disable checks for it */
177 drm_bridge_disable(encoder
->bridge
);
179 if (!IS_ERR(tcon
->panel
)) {
180 drm_panel_disable(tcon
->panel
);
181 drm_panel_unprepare(tcon
->panel
);
185 static void sun4i_rgb_encoder_mode_set(struct drm_encoder
*encoder
,
186 struct drm_display_mode
*mode
,
187 struct drm_display_mode
*adjusted_mode
)
189 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
190 struct sun4i_drv
*drv
= rgb
->drv
;
191 struct sun4i_tcon
*tcon
= drv
->tcon
;
193 sun4i_tcon0_mode_set(tcon
, mode
);
195 clk_set_rate(tcon
->dclk
, mode
->crtc_clock
* 1000);
197 /* FIXME: This seems to be board specific */
198 clk_set_phase(tcon
->dclk
, 120);
201 static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs
= {
202 .atomic_check
= sun4i_rgb_atomic_check
,
203 .mode_set
= sun4i_rgb_encoder_mode_set
,
204 .disable
= sun4i_rgb_encoder_disable
,
205 .enable
= sun4i_rgb_encoder_enable
,
208 static void sun4i_rgb_enc_destroy(struct drm_encoder
*encoder
)
210 drm_encoder_cleanup(encoder
);
213 static struct drm_encoder_funcs sun4i_rgb_enc_funcs
= {
214 .destroy
= sun4i_rgb_enc_destroy
,
217 int sun4i_rgb_init(struct drm_device
*drm
)
219 struct sun4i_drv
*drv
= drm
->dev_private
;
220 struct sun4i_tcon
*tcon
= drv
->tcon
;
221 struct drm_encoder
*encoder
;
222 struct sun4i_rgb
*rgb
;
225 rgb
= devm_kzalloc(drm
->dev
, sizeof(*rgb
), GFP_KERNEL
);
229 encoder
= &rgb
->encoder
;
231 tcon
->panel
= sun4i_tcon_find_panel(tcon
->dev
->of_node
);
232 encoder
->bridge
= sun4i_tcon_find_bridge(tcon
->dev
->of_node
);
233 if (IS_ERR(tcon
->panel
) && IS_ERR(encoder
->bridge
)) {
234 dev_info(drm
->dev
, "No panel or bridge found... RGB output disabled\n");
238 drm_encoder_helper_add(&rgb
->encoder
,
239 &sun4i_rgb_enc_helper_funcs
);
240 ret
= drm_encoder_init(drm
,
242 &sun4i_rgb_enc_funcs
,
243 DRM_MODE_ENCODER_NONE
,
246 dev_err(drm
->dev
, "Couldn't initialise the rgb encoder\n");
250 /* The RGB encoder can only work with the TCON channel 0 */
251 rgb
->encoder
.possible_crtcs
= BIT(0);
253 if (!IS_ERR(tcon
->panel
)) {
254 drm_connector_helper_add(&rgb
->connector
,
255 &sun4i_rgb_con_helper_funcs
);
256 ret
= drm_connector_init(drm
, &rgb
->connector
,
257 &sun4i_rgb_con_funcs
,
258 DRM_MODE_CONNECTOR_Unknown
);
260 dev_err(drm
->dev
, "Couldn't initialise the rgb connector\n");
261 goto err_cleanup_connector
;
264 drm_mode_connector_attach_encoder(&rgb
->connector
,
267 ret
= drm_panel_attach(tcon
->panel
, &rgb
->connector
);
269 dev_err(drm
->dev
, "Couldn't attach our panel\n");
270 goto err_cleanup_connector
;
274 if (!IS_ERR(encoder
->bridge
)) {
275 encoder
->bridge
->encoder
= &rgb
->encoder
;
277 ret
= drm_bridge_attach(drm
, encoder
->bridge
);
279 dev_err(drm
->dev
, "Couldn't attach our bridge\n");
280 goto err_cleanup_connector
;
283 encoder
->bridge
= NULL
;
288 err_cleanup_connector
:
289 drm_encoder_cleanup(&rgb
->encoder
);
293 EXPORT_SYMBOL(sun4i_rgb_init
);