]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1a396789 BB |
2 | /* |
3 | * Copyright (C) 2014 Traphandler | |
4 | * Copyright (C) 2014 Free Electrons | |
5 | * Copyright (C) 2014 Atmel | |
6 | * | |
7 | * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> | |
8 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
1a396789 BB |
9 | */ |
10 | ||
11 | #include <linux/of_graph.h> | |
12 | ||
13 | #include <drm/drmP.h> | |
ebc94461 | 14 | #include <drm/drm_of.h> |
96160a80 | 15 | #include <drm/drm_bridge.h> |
1a396789 BB |
16 | |
17 | #include "atmel_hlcdc_dc.h" | |
18 | ||
b6e075c3 PR |
19 | struct atmel_hlcdc_rgb_output { |
20 | struct drm_encoder encoder; | |
21 | int bus_fmt; | |
22 | }; | |
23 | ||
1a396789 | 24 | static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { |
510fc3c0 | 25 | .destroy = drm_encoder_cleanup, |
1a396789 BB |
26 | }; |
27 | ||
b6e075c3 PR |
28 | static struct atmel_hlcdc_rgb_output * |
29 | atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) | |
30 | { | |
31 | return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); | |
32 | } | |
33 | ||
34 | int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) | |
35 | { | |
36 | struct atmel_hlcdc_rgb_output *output; | |
37 | ||
38 | output = atmel_hlcdc_encoder_to_rgb_output(encoder); | |
39 | ||
40 | return output->bus_fmt; | |
41 | } | |
42 | ||
43 | static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) | |
44 | { | |
45 | u32 bus_width; | |
46 | int ret; | |
47 | ||
48 | ret = of_property_read_u32(ep, "bus-width", &bus_width); | |
49 | if (ret == -EINVAL) | |
50 | return 0; | |
51 | if (ret) | |
52 | return ret; | |
53 | ||
54 | switch (bus_width) { | |
55 | case 12: | |
56 | return MEDIA_BUS_FMT_RGB444_1X12; | |
57 | case 16: | |
58 | return MEDIA_BUS_FMT_RGB565_1X16; | |
59 | case 18: | |
60 | return MEDIA_BUS_FMT_RGB666_1X18; | |
61 | case 24: | |
62 | return MEDIA_BUS_FMT_RGB888_1X24; | |
63 | default: | |
64 | return -EINVAL; | |
65 | } | |
66 | } | |
67 | ||
6bee9b78 | 68 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) |
17a8e03e | 69 | { |
b6e075c3 PR |
70 | struct atmel_hlcdc_rgb_output *output; |
71 | struct device_node *ep; | |
17a8e03e BB |
72 | struct drm_panel *panel; |
73 | struct drm_bridge *bridge; | |
74 | int ret; | |
1a396789 | 75 | |
b6e075c3 PR |
76 | ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); |
77 | if (!ep) | |
78 | return -ENODEV; | |
79 | ||
6bee9b78 BB |
80 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, |
81 | &panel, &bridge); | |
b6e075c3 PR |
82 | if (ret) { |
83 | of_node_put(ep); | |
6bee9b78 | 84 | return ret; |
b6e075c3 | 85 | } |
6bee9b78 | 86 | |
b6e075c3 PR |
87 | output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); |
88 | if (!output) { | |
89 | of_node_put(ep); | |
90 | return -ENOMEM; | |
91 | } | |
92 | ||
93 | output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); | |
94 | of_node_put(ep); | |
95 | if (output->bus_fmt < 0) { | |
96 | dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); | |
17a8e03e | 97 | return -EINVAL; |
b6e075c3 | 98 | } |
1a396789 | 99 | |
b6e075c3 | 100 | ret = drm_encoder_init(dev, &output->encoder, |
1a396789 | 101 | &atmel_hlcdc_panel_encoder_funcs, |
abfc936e | 102 | DRM_MODE_ENCODER_NONE, NULL); |
1a396789 BB |
103 | if (ret) |
104 | return ret; | |
105 | ||
b6e075c3 | 106 | output->encoder.possible_crtcs = 0x1; |
17a8e03e | 107 | |
17a8e03e | 108 | if (panel) { |
96160a80 EA |
109 | bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); |
110 | if (IS_ERR(bridge)) | |
111 | return PTR_ERR(bridge); | |
17a8e03e BB |
112 | } |
113 | ||
17a8e03e | 114 | if (bridge) { |
b6e075c3 | 115 | ret = drm_bridge_attach(&output->encoder, bridge, NULL); |
17a8e03e BB |
116 | if (!ret) |
117 | return 0; | |
96160a80 EA |
118 | |
119 | if (panel) | |
120 | drm_panel_bridge_remove(bridge); | |
17a8e03e | 121 | } |
1a396789 | 122 | |
b6e075c3 | 123 | drm_encoder_cleanup(&output->encoder); |
1a396789 BB |
124 | |
125 | return ret; | |
126 | } | |
127 | ||
128 | int atmel_hlcdc_create_outputs(struct drm_device *dev) | |
129 | { | |
6bee9b78 | 130 | int endpoint, ret = 0; |
012877b7 | 131 | int attached = 0; |
6bee9b78 | 132 | |
012877b7 PR |
133 | /* |
134 | * Always scan the first few endpoints even if we get -ENODEV, | |
135 | * but keep going after that as long as we keep getting hits. | |
136 | */ | |
137 | for (endpoint = 0; !ret || endpoint < 4; endpoint++) { | |
6bee9b78 | 138 | ret = atmel_hlcdc_attach_endpoint(dev, endpoint); |
012877b7 PR |
139 | if (ret == -ENODEV) |
140 | continue; | |
141 | if (ret) | |
142 | break; | |
143 | attached++; | |
144 | } | |
6bee9b78 BB |
145 | |
146 | /* At least one device was successfully attached.*/ | |
012877b7 | 147 | if (ret == -ENODEV && attached) |
6bee9b78 | 148 | return 0; |
1a396789 | 149 | |
ebc94461 | 150 | return ret; |
1a396789 | 151 | } |