1 | // SPDX-License-Identifier: GPL-2.0-only |
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> |
9 | */ |
10 | |
11 | #include <linux/media-bus-format.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_graph.h> |
14 | |
15 | #include <drm/drm_bridge.h> |
16 | #include <drm/drm_encoder.h> |
17 | #include <drm/drm_of.h> |
18 | #include <drm/drm_simple_kms_helper.h> |
19 | |
20 | #include "atmel_hlcdc_dc.h" |
21 | |
22 | struct atmel_hlcdc_rgb_output { |
23 | struct drm_encoder encoder; |
24 | int bus_fmt; |
25 | }; |
26 | |
27 | static struct atmel_hlcdc_rgb_output * |
28 | atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) |
29 | { |
30 | return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); |
31 | } |
32 | |
33 | int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) |
34 | { |
35 | struct atmel_hlcdc_rgb_output *output; |
36 | |
37 | output = atmel_hlcdc_encoder_to_rgb_output(encoder); |
38 | |
39 | return output->bus_fmt; |
40 | } |
41 | |
42 | static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) |
43 | { |
44 | u32 bus_width; |
45 | int ret; |
46 | |
47 | ret = of_property_read_u32(np: ep, propname: "bus-width" , out_value: &bus_width); |
48 | if (ret == -EINVAL) |
49 | return 0; |
50 | if (ret) |
51 | return ret; |
52 | |
53 | switch (bus_width) { |
54 | case 12: |
55 | return MEDIA_BUS_FMT_RGB444_1X12; |
56 | case 16: |
57 | return MEDIA_BUS_FMT_RGB565_1X16; |
58 | case 18: |
59 | return MEDIA_BUS_FMT_RGB666_1X18; |
60 | case 24: |
61 | return MEDIA_BUS_FMT_RGB888_1X24; |
62 | default: |
63 | return -EINVAL; |
64 | } |
65 | } |
66 | |
67 | static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) |
68 | { |
69 | struct atmel_hlcdc_rgb_output *output; |
70 | struct device_node *ep; |
71 | struct drm_panel *panel; |
72 | struct drm_bridge *bridge; |
73 | int ret; |
74 | |
75 | ep = of_graph_get_endpoint_by_regs(parent: dev->dev->of_node, port_reg: 0, reg: endpoint); |
76 | if (!ep) |
77 | return -ENODEV; |
78 | |
79 | ret = drm_of_find_panel_or_bridge(np: dev->dev->of_node, port: 0, endpoint, |
80 | panel: &panel, bridge: &bridge); |
81 | if (ret) { |
82 | of_node_put(node: ep); |
83 | return ret; |
84 | } |
85 | |
86 | output = devm_kzalloc(dev: dev->dev, size: sizeof(*output), GFP_KERNEL); |
87 | if (!output) { |
88 | of_node_put(node: ep); |
89 | return -ENOMEM; |
90 | } |
91 | |
92 | output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); |
93 | of_node_put(node: ep); |
94 | if (output->bus_fmt < 0) { |
95 | dev_err(dev->dev, "endpoint %d: invalid bus width\n" , endpoint); |
96 | return -EINVAL; |
97 | } |
98 | |
99 | ret = drm_simple_encoder_init(dev, encoder: &output->encoder, |
100 | DRM_MODE_ENCODER_NONE); |
101 | if (ret) |
102 | return ret; |
103 | |
104 | output->encoder.possible_crtcs = 0x1; |
105 | |
106 | if (panel) { |
107 | bridge = drm_panel_bridge_add_typed(panel, |
108 | DRM_MODE_CONNECTOR_Unknown); |
109 | if (IS_ERR(ptr: bridge)) |
110 | return PTR_ERR(ptr: bridge); |
111 | } |
112 | |
113 | if (bridge) { |
114 | ret = drm_bridge_attach(encoder: &output->encoder, bridge, NULL, flags: 0); |
115 | if (!ret) |
116 | return 0; |
117 | |
118 | if (panel) |
119 | drm_panel_bridge_remove(bridge); |
120 | } |
121 | |
122 | drm_encoder_cleanup(encoder: &output->encoder); |
123 | |
124 | return ret; |
125 | } |
126 | |
127 | int atmel_hlcdc_create_outputs(struct drm_device *dev) |
128 | { |
129 | int endpoint, ret = 0; |
130 | int attached = 0; |
131 | |
132 | /* |
133 | * Always scan the first few endpoints even if we get -ENODEV, |
134 | * but keep going after that as long as we keep getting hits. |
135 | */ |
136 | for (endpoint = 0; !ret || endpoint < 4; endpoint++) { |
137 | ret = atmel_hlcdc_attach_endpoint(dev, endpoint); |
138 | if (ret == -ENODEV) |
139 | continue; |
140 | if (ret) |
141 | break; |
142 | attached++; |
143 | } |
144 | |
145 | /* At least one device was successfully attached.*/ |
146 | if (ret == -ENODEV && attached) |
147 | return 0; |
148 | |
149 | return ret; |
150 | } |
151 | |