2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/of_graph.h>
25 #include <drm/drm_panel.h>
27 #include "atmel_hlcdc_dc.h"
30 * Atmel HLCDC RGB output mode
32 enum atmel_hlcdc_connector_rgb_mode
{
33 ATMEL_HLCDC_CONNECTOR_RGB444
,
34 ATMEL_HLCDC_CONNECTOR_RGB565
,
35 ATMEL_HLCDC_CONNECTOR_RGB666
,
36 ATMEL_HLCDC_CONNECTOR_RGB888
,
40 * Atmel HLCDC RGB connector structure
42 * This structure stores RGB slave device information.
44 * @connector: DRM connector
45 * @encoder: DRM encoder
46 * @dc: pointer to the atmel_hlcdc_dc structure
47 * @dpms: current DPMS mode
49 struct atmel_hlcdc_rgb_output
{
50 struct drm_connector connector
;
51 struct drm_encoder encoder
;
52 struct atmel_hlcdc_dc
*dc
;
56 static inline struct atmel_hlcdc_rgb_output
*
57 drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector
*connector
)
59 return container_of(connector
, struct atmel_hlcdc_rgb_output
,
63 static inline struct atmel_hlcdc_rgb_output
*
64 drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder
*encoder
)
66 return container_of(encoder
, struct atmel_hlcdc_rgb_output
, encoder
);
70 * Atmel HLCDC Panel device structure
72 * This structure is specialization of the slave device structure to
73 * interface with drm panels.
75 * @base: base slave device fields
76 * @panel: drm panel attached to this slave device
78 struct atmel_hlcdc_panel
{
79 struct atmel_hlcdc_rgb_output base
;
80 struct drm_panel
*panel
;
83 static inline struct atmel_hlcdc_panel
*
84 atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output
*output
)
86 return container_of(output
, struct atmel_hlcdc_panel
, base
);
89 static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder
*encoder
)
91 struct atmel_hlcdc_rgb_output
*rgb
=
92 drm_encoder_to_atmel_hlcdc_rgb_output(encoder
);
93 struct atmel_hlcdc_panel
*panel
= atmel_hlcdc_rgb_output_to_panel(rgb
);
95 drm_panel_enable(panel
->panel
);
98 static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder
*encoder
)
100 struct atmel_hlcdc_rgb_output
*rgb
=
101 drm_encoder_to_atmel_hlcdc_rgb_output(encoder
);
102 struct atmel_hlcdc_panel
*panel
= atmel_hlcdc_rgb_output_to_panel(rgb
);
104 drm_panel_disable(panel
->panel
);
108 atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder
*encoder
,
109 const struct drm_display_mode
*mode
,
110 struct drm_display_mode
*adjusted
)
116 atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder
*encoder
,
117 struct drm_display_mode
*mode
,
118 struct drm_display_mode
*adjusted
)
120 struct atmel_hlcdc_rgb_output
*rgb
=
121 drm_encoder_to_atmel_hlcdc_rgb_output(encoder
);
122 struct drm_display_info
*info
= &rgb
->connector
.display_info
;
127 if (info
->num_bus_formats
) {
128 switch (info
->bus_formats
[0]) {
129 case MEDIA_BUS_FMT_RGB565_1X16
:
130 cfg
|= ATMEL_HLCDC_CONNECTOR_RGB565
<< 8;
132 case MEDIA_BUS_FMT_RGB666_1X18
:
133 cfg
|= ATMEL_HLCDC_CONNECTOR_RGB666
<< 8;
135 case MEDIA_BUS_FMT_RGB888_1X24
:
136 cfg
|= ATMEL_HLCDC_CONNECTOR_RGB888
<< 8;
138 case MEDIA_BUS_FMT_RGB444_1X12
:
144 regmap_update_bits(rgb
->dc
->hlcdc
->regmap
, ATMEL_HLCDC_CFG(5),
145 ATMEL_HLCDC_MODE_MASK
,
149 static struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs
= {
150 .mode_fixup
= atmel_hlcdc_panel_encoder_mode_fixup
,
151 .mode_set
= atmel_hlcdc_rgb_encoder_mode_set
,
152 .disable
= atmel_hlcdc_panel_encoder_disable
,
153 .enable
= atmel_hlcdc_panel_encoder_enable
,
156 static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder
*encoder
)
158 drm_encoder_cleanup(encoder
);
159 memset(encoder
, 0, sizeof(*encoder
));
162 static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs
= {
163 .destroy
= atmel_hlcdc_rgb_encoder_destroy
,
166 static int atmel_hlcdc_panel_get_modes(struct drm_connector
*connector
)
168 struct atmel_hlcdc_rgb_output
*rgb
=
169 drm_connector_to_atmel_hlcdc_rgb_output(connector
);
170 struct atmel_hlcdc_panel
*panel
= atmel_hlcdc_rgb_output_to_panel(rgb
);
172 return panel
->panel
->funcs
->get_modes(panel
->panel
);
175 static int atmel_hlcdc_rgb_mode_valid(struct drm_connector
*connector
,
176 struct drm_display_mode
*mode
)
178 struct atmel_hlcdc_rgb_output
*rgb
=
179 drm_connector_to_atmel_hlcdc_rgb_output(connector
);
181 return atmel_hlcdc_dc_mode_valid(rgb
->dc
, mode
);
186 static struct drm_encoder
*
187 atmel_hlcdc_rgb_best_encoder(struct drm_connector
*connector
)
189 struct atmel_hlcdc_rgb_output
*rgb
=
190 drm_connector_to_atmel_hlcdc_rgb_output(connector
);
192 return &rgb
->encoder
;
195 static struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs
= {
196 .get_modes
= atmel_hlcdc_panel_get_modes
,
197 .mode_valid
= atmel_hlcdc_rgb_mode_valid
,
198 .best_encoder
= atmel_hlcdc_rgb_best_encoder
,
201 static enum drm_connector_status
202 atmel_hlcdc_panel_connector_detect(struct drm_connector
*connector
, bool force
)
204 return connector_status_connected
;
208 atmel_hlcdc_panel_connector_destroy(struct drm_connector
*connector
)
210 struct atmel_hlcdc_rgb_output
*rgb
=
211 drm_connector_to_atmel_hlcdc_rgb_output(connector
);
212 struct atmel_hlcdc_panel
*panel
= atmel_hlcdc_rgb_output_to_panel(rgb
);
214 drm_panel_detach(panel
->panel
);
215 drm_connector_cleanup(connector
);
218 static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs
= {
219 .dpms
= drm_atomic_helper_connector_dpms
,
220 .detect
= atmel_hlcdc_panel_connector_detect
,
221 .fill_modes
= drm_helper_probe_single_connector_modes
,
222 .destroy
= atmel_hlcdc_panel_connector_destroy
,
223 .reset
= drm_atomic_helper_connector_reset
,
224 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
225 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
228 static int atmel_hlcdc_create_panel_output(struct drm_device
*dev
,
229 struct of_endpoint
*ep
)
231 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
232 struct device_node
*np
;
233 struct drm_panel
*p
= NULL
;
234 struct atmel_hlcdc_panel
*panel
;
237 np
= of_graph_get_remote_port_parent(ep
->local_node
);
241 p
= of_drm_find_panel(np
);
245 return -EPROBE_DEFER
;
247 panel
= devm_kzalloc(dev
->dev
, sizeof(*panel
), GFP_KERNEL
);
251 panel
->base
.dpms
= DRM_MODE_DPMS_OFF
;
255 drm_encoder_helper_add(&panel
->base
.encoder
,
256 &atmel_hlcdc_panel_encoder_helper_funcs
);
257 ret
= drm_encoder_init(dev
, &panel
->base
.encoder
,
258 &atmel_hlcdc_panel_encoder_funcs
,
259 DRM_MODE_ENCODER_LVDS
);
263 panel
->base
.connector
.dpms
= DRM_MODE_DPMS_OFF
;
264 panel
->base
.connector
.polled
= DRM_CONNECTOR_POLL_CONNECT
;
265 drm_connector_helper_add(&panel
->base
.connector
,
266 &atmel_hlcdc_panel_connector_helper_funcs
);
267 ret
= drm_connector_init(dev
, &panel
->base
.connector
,
268 &atmel_hlcdc_panel_connector_funcs
,
269 DRM_MODE_CONNECTOR_LVDS
);
271 goto err_encoder_cleanup
;
273 drm_mode_connector_attach_encoder(&panel
->base
.connector
,
274 &panel
->base
.encoder
);
275 panel
->base
.encoder
.possible_crtcs
= 0x1;
277 drm_panel_attach(p
, &panel
->base
.connector
);
283 drm_encoder_cleanup(&panel
->base
.encoder
);
288 int atmel_hlcdc_create_outputs(struct drm_device
*dev
)
290 struct device_node
*port_np
, *np
;
291 struct of_endpoint ep
;
294 port_np
= of_get_child_by_name(dev
->dev
->of_node
, "port");
298 np
= of_get_child_by_name(port_np
, "endpoint");
299 of_node_put(port_np
);
304 ret
= of_graph_parse_endpoint(np
, &ep
);
305 of_node_put(port_np
);
310 /* We currently only support panel output */
311 return atmel_hlcdc_create_panel_output(dev
, &ep
);