.. | .. |
---|
5 | 5 | * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org> |
---|
6 | 6 | */ |
---|
7 | 7 | |
---|
8 | | -#include <drm/drmP.h> |
---|
9 | | -#include <drm/drm_bridge.h> |
---|
10 | | -#include <drm/drm_panel.h> |
---|
11 | | - |
---|
12 | 8 | #include <linux/gpio/consumer.h> |
---|
| 9 | +#include <linux/module.h> |
---|
| 10 | +#include <linux/of.h> |
---|
13 | 11 | #include <linux/of_graph.h> |
---|
| 12 | +#include <linux/platform_device.h> |
---|
14 | 13 | #include <linux/regulator/consumer.h> |
---|
15 | 14 | #include <linux/slab.h> |
---|
| 15 | + |
---|
| 16 | +#include <drm/drm_bridge.h> |
---|
| 17 | +#include <drm/drm_panel.h> |
---|
16 | 18 | |
---|
17 | 19 | enum thc63_ports { |
---|
18 | 20 | THC63_LVDS_IN0, |
---|
.. | .. |
---|
31 | 33 | |
---|
32 | 34 | struct drm_bridge bridge; |
---|
33 | 35 | struct drm_bridge *next; |
---|
| 36 | + |
---|
| 37 | + struct drm_bridge_timings timings; |
---|
34 | 38 | }; |
---|
35 | 39 | |
---|
36 | 40 | static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge) |
---|
.. | .. |
---|
38 | 42 | return container_of(bridge, struct thc63_dev, bridge); |
---|
39 | 43 | } |
---|
40 | 44 | |
---|
41 | | -static int thc63_attach(struct drm_bridge *bridge) |
---|
| 45 | +static int thc63_attach(struct drm_bridge *bridge, |
---|
| 46 | + enum drm_bridge_attach_flags flags) |
---|
42 | 47 | { |
---|
43 | 48 | struct thc63_dev *thc63 = to_thc63(bridge); |
---|
44 | 49 | |
---|
45 | | - return drm_bridge_attach(bridge->encoder, thc63->next, bridge); |
---|
| 50 | + return drm_bridge_attach(bridge->encoder, thc63->next, bridge, flags); |
---|
| 51 | +} |
---|
| 52 | + |
---|
| 53 | +static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge, |
---|
| 54 | + const struct drm_display_info *info, |
---|
| 55 | + const struct drm_display_mode *mode) |
---|
| 56 | +{ |
---|
| 57 | + struct thc63_dev *thc63 = to_thc63(bridge); |
---|
| 58 | + unsigned int min_freq; |
---|
| 59 | + unsigned int max_freq; |
---|
| 60 | + |
---|
| 61 | + /* |
---|
| 62 | + * The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but |
---|
| 63 | + * dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out |
---|
| 64 | + * isn't supported by the driver yet, simply derive the limits from the |
---|
| 65 | + * input mode. |
---|
| 66 | + */ |
---|
| 67 | + if (thc63->timings.dual_link) { |
---|
| 68 | + min_freq = 40000; |
---|
| 69 | + max_freq = 150000; |
---|
| 70 | + } else { |
---|
| 71 | + min_freq = 8000; |
---|
| 72 | + max_freq = 135000; |
---|
| 73 | + } |
---|
| 74 | + |
---|
| 75 | + if (mode->clock < min_freq) |
---|
| 76 | + return MODE_CLOCK_LOW; |
---|
| 77 | + |
---|
| 78 | + if (mode->clock > max_freq) |
---|
| 79 | + return MODE_CLOCK_HIGH; |
---|
| 80 | + |
---|
| 81 | + return MODE_OK; |
---|
46 | 82 | } |
---|
47 | 83 | |
---|
48 | 84 | static void thc63_enable(struct drm_bridge *bridge) |
---|
.. | .. |
---|
77 | 113 | |
---|
78 | 114 | static const struct drm_bridge_funcs thc63_bridge_func = { |
---|
79 | 115 | .attach = thc63_attach, |
---|
| 116 | + .mode_valid = thc63_mode_valid, |
---|
80 | 117 | .enable = thc63_enable, |
---|
81 | 118 | .disable = thc63_disable, |
---|
82 | 119 | }; |
---|
83 | 120 | |
---|
84 | 121 | static int thc63_parse_dt(struct thc63_dev *thc63) |
---|
85 | 122 | { |
---|
86 | | - struct device_node *thc63_out; |
---|
| 123 | + struct device_node *endpoint; |
---|
87 | 124 | struct device_node *remote; |
---|
88 | 125 | |
---|
89 | | - thc63_out = of_graph_get_endpoint_by_regs(thc63->dev->of_node, |
---|
90 | | - THC63_RGB_OUT0, -1); |
---|
91 | | - if (!thc63_out) { |
---|
| 126 | + endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node, |
---|
| 127 | + THC63_RGB_OUT0, -1); |
---|
| 128 | + if (!endpoint) { |
---|
92 | 129 | dev_err(thc63->dev, "Missing endpoint in port@%u\n", |
---|
93 | 130 | THC63_RGB_OUT0); |
---|
94 | 131 | return -ENODEV; |
---|
95 | 132 | } |
---|
96 | 133 | |
---|
97 | | - remote = of_graph_get_remote_port_parent(thc63_out); |
---|
98 | | - of_node_put(thc63_out); |
---|
| 134 | + remote = of_graph_get_remote_port_parent(endpoint); |
---|
| 135 | + of_node_put(endpoint); |
---|
99 | 136 | if (!remote) { |
---|
100 | 137 | dev_err(thc63->dev, "Endpoint in port@%u unconnected\n", |
---|
101 | 138 | THC63_RGB_OUT0); |
---|
.. | .. |
---|
113 | 150 | of_node_put(remote); |
---|
114 | 151 | if (!thc63->next) |
---|
115 | 152 | return -EPROBE_DEFER; |
---|
| 153 | + |
---|
| 154 | + endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node, |
---|
| 155 | + THC63_LVDS_IN1, -1); |
---|
| 156 | + if (endpoint) { |
---|
| 157 | + remote = of_graph_get_remote_port_parent(endpoint); |
---|
| 158 | + of_node_put(endpoint); |
---|
| 159 | + |
---|
| 160 | + if (remote) { |
---|
| 161 | + if (of_device_is_available(remote)) |
---|
| 162 | + thc63->timings.dual_link = true; |
---|
| 163 | + of_node_put(remote); |
---|
| 164 | + } |
---|
| 165 | + } |
---|
| 166 | + |
---|
| 167 | + dev_dbg(thc63->dev, "operating in %s-link mode\n", |
---|
| 168 | + thc63->timings.dual_link ? "dual" : "single"); |
---|
116 | 169 | |
---|
117 | 170 | return 0; |
---|
118 | 171 | } |
---|
.. | .. |
---|
170 | 223 | thc63->bridge.driver_private = thc63; |
---|
171 | 224 | thc63->bridge.of_node = pdev->dev.of_node; |
---|
172 | 225 | thc63->bridge.funcs = &thc63_bridge_func; |
---|
| 226 | + thc63->bridge.timings = &thc63->timings; |
---|
173 | 227 | |
---|
174 | 228 | drm_bridge_add(&thc63->bridge); |
---|
175 | 229 | |
---|