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_of.h>
19 #include <drm/drm_panel.h>
21 #include "sun4i_crtc.h"
22 #include "sun4i_tcon.h"
23 #include "sun4i_rgb.h"
26 struct drm_connector connector
;
27 struct drm_encoder encoder
;
29 struct sun4i_tcon
*tcon
;
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
);
50 struct sun4i_tcon
*tcon
= rgb
->tcon
;
52 return drm_panel_get_modes(tcon
->panel
);
55 static enum drm_mode_status
sun4i_rgb_mode_valid(struct drm_encoder
*crtc
,
56 const struct drm_display_mode
*mode
)
58 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(crtc
);
59 struct sun4i_tcon
*tcon
= rgb
->tcon
;
60 u32 hsync
= mode
->hsync_end
- mode
->hsync_start
;
61 u32 vsync
= mode
->vsync_end
- mode
->vsync_start
;
62 unsigned long rate
= mode
->clock
* 1000;
65 DRM_DEBUG_DRIVER("Validating modes...\n");
68 return MODE_HSYNC_NARROW
;
71 return MODE_HSYNC_WIDE
;
73 if ((mode
->hdisplay
< 1) || (mode
->htotal
< 1))
74 return MODE_H_ILLEGAL
;
76 if ((mode
->hdisplay
> 0x7ff) || (mode
->htotal
> 0xfff))
77 return MODE_BAD_HVALUE
;
79 DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
82 return MODE_VSYNC_NARROW
;
85 return MODE_VSYNC_WIDE
;
87 if ((mode
->vdisplay
< 1) || (mode
->vtotal
< 1))
88 return MODE_V_ILLEGAL
;
90 if ((mode
->vdisplay
> 0x7ff) || (mode
->vtotal
> 0xfff))
91 return MODE_BAD_VVALUE
;
93 DRM_DEBUG_DRIVER("Vertical parameters OK\n");
95 tcon
->dclk_min_div
= 6;
96 tcon
->dclk_max_div
= 127;
97 rounded_rate
= clk_round_rate(tcon
->dclk
, rate
);
98 if (rounded_rate
< rate
)
99 return MODE_CLOCK_LOW
;
101 if (rounded_rate
> rate
)
102 return MODE_CLOCK_HIGH
;
104 DRM_DEBUG_DRIVER("Clock rate OK\n");
109 static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs
= {
110 .get_modes
= sun4i_rgb_get_modes
,
114 sun4i_rgb_connector_destroy(struct drm_connector
*connector
)
116 struct sun4i_rgb
*rgb
= drm_connector_to_sun4i_rgb(connector
);
117 struct sun4i_tcon
*tcon
= rgb
->tcon
;
119 drm_panel_detach(tcon
->panel
);
120 drm_connector_cleanup(connector
);
123 static const struct drm_connector_funcs sun4i_rgb_con_funcs
= {
124 .fill_modes
= drm_helper_probe_single_connector_modes
,
125 .destroy
= sun4i_rgb_connector_destroy
,
126 .reset
= drm_atomic_helper_connector_reset
,
127 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
128 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
131 static void sun4i_rgb_encoder_enable(struct drm_encoder
*encoder
)
133 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
134 struct sun4i_tcon
*tcon
= rgb
->tcon
;
136 DRM_DEBUG_DRIVER("Enabling RGB output\n");
138 if (!IS_ERR(tcon
->panel
)) {
139 drm_panel_prepare(tcon
->panel
);
140 drm_panel_enable(tcon
->panel
);
144 static void sun4i_rgb_encoder_disable(struct drm_encoder
*encoder
)
146 struct sun4i_rgb
*rgb
= drm_encoder_to_sun4i_rgb(encoder
);
147 struct sun4i_tcon
*tcon
= rgb
->tcon
;
149 DRM_DEBUG_DRIVER("Disabling RGB output\n");
151 if (!IS_ERR(tcon
->panel
)) {
152 drm_panel_disable(tcon
->panel
);
153 drm_panel_unprepare(tcon
->panel
);
157 static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs
= {
158 .disable
= sun4i_rgb_encoder_disable
,
159 .enable
= sun4i_rgb_encoder_enable
,
160 .mode_valid
= sun4i_rgb_mode_valid
,
163 static void sun4i_rgb_enc_destroy(struct drm_encoder
*encoder
)
165 drm_encoder_cleanup(encoder
);
168 static struct drm_encoder_funcs sun4i_rgb_enc_funcs
= {
169 .destroy
= sun4i_rgb_enc_destroy
,
172 int sun4i_rgb_init(struct drm_device
*drm
, struct sun4i_tcon
*tcon
)
174 struct drm_encoder
*encoder
;
175 struct drm_bridge
*bridge
;
176 struct sun4i_rgb
*rgb
;
179 rgb
= devm_kzalloc(drm
->dev
, sizeof(*rgb
), GFP_KERNEL
);
183 encoder
= &rgb
->encoder
;
185 ret
= drm_of_find_panel_or_bridge(tcon
->dev
->of_node
, 1, 0,
186 &tcon
->panel
, &bridge
);
188 dev_info(drm
->dev
, "No panel or bridge found... RGB output disabled\n");
192 drm_encoder_helper_add(&rgb
->encoder
,
193 &sun4i_rgb_enc_helper_funcs
);
194 ret
= drm_encoder_init(drm
,
196 &sun4i_rgb_enc_funcs
,
197 DRM_MODE_ENCODER_NONE
,
200 dev_err(drm
->dev
, "Couldn't initialise the rgb encoder\n");
204 /* The RGB encoder can only work with the TCON channel 0 */
205 rgb
->encoder
.possible_crtcs
= drm_crtc_mask(&tcon
->crtc
->crtc
);
208 drm_connector_helper_add(&rgb
->connector
,
209 &sun4i_rgb_con_helper_funcs
);
210 ret
= drm_connector_init(drm
, &rgb
->connector
,
211 &sun4i_rgb_con_funcs
,
212 DRM_MODE_CONNECTOR_Unknown
);
214 dev_err(drm
->dev
, "Couldn't initialise the rgb connector\n");
215 goto err_cleanup_connector
;
218 drm_connector_attach_encoder(&rgb
->connector
,
221 ret
= drm_panel_attach(tcon
->panel
, &rgb
->connector
);
223 dev_err(drm
->dev
, "Couldn't attach our panel\n");
224 goto err_cleanup_connector
;
229 ret
= drm_bridge_attach(encoder
, bridge
, NULL
);
231 dev_err(drm
->dev
, "Couldn't attach our bridge\n");
232 goto err_cleanup_connector
;
238 err_cleanup_connector
:
239 drm_encoder_cleanup(&rgb
->encoder
);
243 EXPORT_SYMBOL(sun4i_rgb_init
);