aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/atmel-hlcdc
diff options
context:
space:
mode:
authorPeter Rosin <peda@axentia.se>2018-08-25 04:56:20 -0400
committerBoris Brezillon <boris.brezillon@bootlin.com>2018-08-27 15:22:52 -0400
commitb6e075c3cb6e57d487f6ca1be688e88eb8fc6a79 (patch)
treee194800baca57d56aac8d574d84dd1f18a1acf81 /drivers/gpu/drm/atmel-hlcdc
parent012877b76c2f004f03c9b119b96f73494b8c5196 (diff)
drm/atmel-hlcdc: support bus-width (12/16/18/24) in endpoint nodes
This beats the heuristic that the connector is involved in what format should be output for cases where this fails. E.g. if there is a bridge that changes format between the encoder and the connector, or if some of the RGB pins between the lcd controller and the encoder are not routed on the PCB. This is critical for the devices that have the "conflicting output formats" issue (SAM9N12, SAM9X5, SAMA5D3), since the most significant RGB bits move around depending on the selected output mode. For devices that do not have the "conflicting output formats" issue (SAMA5D2, SAMA5D4), this is completely irrelevant. Acked-by: Boris Brezillon <boris.brezillon@bootlin.com> Reviewed-by: Jacopo Mondi <jacopo+renesas@jmondi.org> Signed-off-by: Peter Rosin <peda@axentia.se> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> Link: https://patchwork.freedesktop.org/patch/msgid/20180825085620.10566-5-peda@axentia.se
Diffstat (limited to 'drivers/gpu/drm/atmel-hlcdc')
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c70
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h1
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c77
3 files changed, 120 insertions, 28 deletions
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 229138130ce1..9e34bce089d0 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -242,6 +242,55 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
242#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) 242#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3)
243#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) 243#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0)
244 244
245static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
246{
247 struct drm_connector *connector = state->connector;
248 struct drm_display_info *info = &connector->display_info;
249 struct drm_encoder *encoder;
250 unsigned int supported_fmts = 0;
251 int j;
252
253 encoder = state->best_encoder;
254 if (!encoder)
255 encoder = connector->encoder;
256
257 switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
258 case 0:
259 break;
260 case MEDIA_BUS_FMT_RGB444_1X12:
261 return ATMEL_HLCDC_RGB444_OUTPUT;
262 case MEDIA_BUS_FMT_RGB565_1X16:
263 return ATMEL_HLCDC_RGB565_OUTPUT;
264 case MEDIA_BUS_FMT_RGB666_1X18:
265 return ATMEL_HLCDC_RGB666_OUTPUT;
266 case MEDIA_BUS_FMT_RGB888_1X24:
267 return ATMEL_HLCDC_RGB888_OUTPUT;
268 default:
269 return -EINVAL;
270 }
271
272 for (j = 0; j < info->num_bus_formats; j++) {
273 switch (info->bus_formats[j]) {
274 case MEDIA_BUS_FMT_RGB444_1X12:
275 supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
276 break;
277 case MEDIA_BUS_FMT_RGB565_1X16:
278 supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
279 break;
280 case MEDIA_BUS_FMT_RGB666_1X18:
281 supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
282 break;
283 case MEDIA_BUS_FMT_RGB888_1X24:
284 supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
285 break;
286 default:
287 break;
288 }
289 }
290
291 return supported_fmts;
292}
293
245static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) 294static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
246{ 295{
247 unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; 296 unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK;
@@ -254,31 +303,12 @@ static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
254 crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); 303 crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
255 304
256 for_each_new_connector_in_state(state->state, connector, cstate, i) { 305 for_each_new_connector_in_state(state->state, connector, cstate, i) {
257 struct drm_display_info *info = &connector->display_info;
258 unsigned int supported_fmts = 0; 306 unsigned int supported_fmts = 0;
259 int j;
260 307
261 if (!cstate->crtc) 308 if (!cstate->crtc)
262 continue; 309 continue;
263 310
264 for (j = 0; j < info->num_bus_formats; j++) { 311 supported_fmts = atmel_hlcdc_connector_output_mode(cstate);
265 switch (info->bus_formats[j]) {
266 case MEDIA_BUS_FMT_RGB444_1X12:
267 supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
268 break;
269 case MEDIA_BUS_FMT_RGB565_1X16:
270 supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
271 break;
272 case MEDIA_BUS_FMT_RGB666_1X18:
273 supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
274 break;
275 case MEDIA_BUS_FMT_RGB888_1X24:
276 supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
277 break;
278 default:
279 break;
280 }
281 }
282 312
283 if (crtc->dc->desc->conflicting_output_formats) 313 if (crtc->dc->desc->conflicting_output_formats)
284 output_fmts &= supported_fmts; 314 output_fmts &= supported_fmts;
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
index 60c937f42114..4cc1e03f0aee 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -441,5 +441,6 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c);
441int atmel_hlcdc_crtc_create(struct drm_device *dev); 441int atmel_hlcdc_crtc_create(struct drm_device *dev);
442 442
443int atmel_hlcdc_create_outputs(struct drm_device *dev); 443int atmel_hlcdc_create_outputs(struct drm_device *dev);
444int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder);
444 445
445#endif /* DRM_ATMEL_HLCDC_H */ 446#endif /* DRM_ATMEL_HLCDC_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index c05c2b744981..f73d8a92274e 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -27,33 +27,94 @@
27 27
28#include "atmel_hlcdc_dc.h" 28#include "atmel_hlcdc_dc.h"
29 29
30struct atmel_hlcdc_rgb_output {
31 struct drm_encoder encoder;
32 int bus_fmt;
33};
34
30static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { 35static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = {
31 .destroy = drm_encoder_cleanup, 36 .destroy = drm_encoder_cleanup,
32}; 37};
33 38
39static struct atmel_hlcdc_rgb_output *
40atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)
41{
42 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
43}
44
45int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)
46{
47 struct atmel_hlcdc_rgb_output *output;
48
49 output = atmel_hlcdc_encoder_to_rgb_output(encoder);
50
51 return output->bus_fmt;
52}
53
54static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)
55{
56 u32 bus_width;
57 int ret;
58
59 ret = of_property_read_u32(ep, "bus-width", &bus_width);
60 if (ret == -EINVAL)
61 return 0;
62 if (ret)
63 return ret;
64
65 switch (bus_width) {
66 case 12:
67 return MEDIA_BUS_FMT_RGB444_1X12;
68 case 16:
69 return MEDIA_BUS_FMT_RGB565_1X16;
70 case 18:
71 return MEDIA_BUS_FMT_RGB666_1X18;
72 case 24:
73 return MEDIA_BUS_FMT_RGB888_1X24;
74 default:
75 return -EINVAL;
76 }
77}
78
34static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) 79static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
35{ 80{
36 struct drm_encoder *encoder; 81 struct atmel_hlcdc_rgb_output *output;
82 struct device_node *ep;
37 struct drm_panel *panel; 83 struct drm_panel *panel;
38 struct drm_bridge *bridge; 84 struct drm_bridge *bridge;
39 int ret; 85 int ret;
40 86
87 ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);
88 if (!ep)
89 return -ENODEV;
90
41 ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, 91 ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,
42 &panel, &bridge); 92 &panel, &bridge);
43 if (ret) 93 if (ret) {
94 of_node_put(ep);
44 return ret; 95 return ret;
96 }
45 97
46 encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); 98 output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
47 if (!encoder) 99 if (!output) {
100 of_node_put(ep);
101 return -ENOMEM;
102 }
103
104 output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
105 of_node_put(ep);
106 if (output->bus_fmt < 0) {
107 dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);
48 return -EINVAL; 108 return -EINVAL;
109 }
49 110
50 ret = drm_encoder_init(dev, encoder, 111 ret = drm_encoder_init(dev, &output->encoder,
51 &atmel_hlcdc_panel_encoder_funcs, 112 &atmel_hlcdc_panel_encoder_funcs,
52 DRM_MODE_ENCODER_NONE, NULL); 113 DRM_MODE_ENCODER_NONE, NULL);
53 if (ret) 114 if (ret)
54 return ret; 115 return ret;
55 116
56 encoder->possible_crtcs = 0x1; 117 output->encoder.possible_crtcs = 0x1;
57 118
58 if (panel) { 119 if (panel) {
59 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); 120 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown);
@@ -62,7 +123,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
62 } 123 }
63 124
64 if (bridge) { 125 if (bridge) {
65 ret = drm_bridge_attach(encoder, bridge, NULL); 126 ret = drm_bridge_attach(&output->encoder, bridge, NULL);
66 if (!ret) 127 if (!ret)
67 return 0; 128 return 0;
68 129
@@ -70,7 +131,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
70 drm_panel_bridge_remove(bridge); 131 drm_panel_bridge_remove(bridge);
71 } 132 }
72 133
73 drm_encoder_cleanup(encoder); 134 drm_encoder_cleanup(&output->encoder);
74 135
75 return ret; 136 return ret;
76} 137}