diff options
Diffstat (limited to 'drivers/gpu/drm/pl111/pl111_drv.c')
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drv.c | 138 |
1 files changed, 88 insertions, 50 deletions
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index acb738c69873..1231905150d0 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c | |||
@@ -58,6 +58,8 @@ | |||
58 | #include <linux/dma-buf.h> | 58 | #include <linux/dma-buf.h> |
59 | #include <linux/module.h> | 59 | #include <linux/module.h> |
60 | #include <linux/slab.h> | 60 | #include <linux/slab.h> |
61 | #include <linux/of.h> | ||
62 | #include <linux/of_graph.h> | ||
61 | 63 | ||
62 | #include <drm/drmP.h> | 64 | #include <drm/drmP.h> |
63 | #include <drm/drm_atomic_helper.h> | 65 | #include <drm/drm_atomic_helper.h> |
@@ -85,9 +87,13 @@ static int pl111_modeset_init(struct drm_device *dev) | |||
85 | { | 87 | { |
86 | struct drm_mode_config *mode_config; | 88 | struct drm_mode_config *mode_config; |
87 | struct pl111_drm_dev_private *priv = dev->dev_private; | 89 | struct pl111_drm_dev_private *priv = dev->dev_private; |
88 | struct drm_panel *panel; | 90 | struct device_node *np = dev->dev->of_node; |
89 | struct drm_bridge *bridge; | 91 | struct device_node *remote; |
92 | struct drm_panel *panel = NULL; | ||
93 | struct drm_bridge *bridge = NULL; | ||
94 | bool defer = false; | ||
90 | int ret = 0; | 95 | int ret = 0; |
96 | int i; | ||
91 | 97 | ||
92 | drm_mode_config_init(dev); | 98 | drm_mode_config_init(dev); |
93 | mode_config = &dev->mode_config; | 99 | mode_config = &dev->mode_config; |
@@ -97,10 +103,54 @@ static int pl111_modeset_init(struct drm_device *dev) | |||
97 | mode_config->min_height = 1; | 103 | mode_config->min_height = 1; |
98 | mode_config->max_height = 768; | 104 | mode_config->max_height = 768; |
99 | 105 | ||
100 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, | 106 | i = 0; |
101 | 0, 0, &panel, &bridge); | 107 | for_each_endpoint_of_node(np, remote) { |
102 | if (ret && ret != -ENODEV) | 108 | struct drm_panel *tmp_panel; |
103 | return ret; | 109 | struct drm_bridge *tmp_bridge; |
110 | |||
111 | dev_dbg(dev->dev, "checking endpoint %d\n", i); | ||
112 | |||
113 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, | ||
114 | 0, i, | ||
115 | &tmp_panel, | ||
116 | &tmp_bridge); | ||
117 | if (ret) { | ||
118 | if (ret == -EPROBE_DEFER) { | ||
119 | /* | ||
120 | * Something deferred, but that is often just | ||
121 | * another way of saying -ENODEV, but let's | ||
122 | * cast a vote for later deferral. | ||
123 | */ | ||
124 | defer = true; | ||
125 | } else if (ret != -ENODEV) { | ||
126 | /* Continue, maybe something else is working */ | ||
127 | dev_err(dev->dev, | ||
128 | "endpoint %d returns %d\n", i, ret); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | if (tmp_panel) { | ||
133 | dev_info(dev->dev, | ||
134 | "found panel on endpoint %d\n", i); | ||
135 | panel = tmp_panel; | ||
136 | } | ||
137 | if (tmp_bridge) { | ||
138 | dev_info(dev->dev, | ||
139 | "found bridge on endpoint %d\n", i); | ||
140 | bridge = tmp_bridge; | ||
141 | } | ||
142 | |||
143 | i++; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * If we can't find neither panel nor bridge on any of the | ||
148 | * endpoints, and any of them retured -EPROBE_DEFER, then | ||
149 | * let's defer this driver too. | ||
150 | */ | ||
151 | if ((!panel && !bridge) && defer) | ||
152 | return -EPROBE_DEFER; | ||
153 | |||
104 | if (panel) { | 154 | if (panel) { |
105 | bridge = drm_panel_bridge_add(panel, | 155 | bridge = drm_panel_bridge_add(panel, |
106 | DRM_MODE_CONNECTOR_Unknown); | 156 | DRM_MODE_CONNECTOR_Unknown); |
@@ -108,11 +158,17 @@ static int pl111_modeset_init(struct drm_device *dev) | |||
108 | ret = PTR_ERR(bridge); | 158 | ret = PTR_ERR(bridge); |
109 | goto out_config; | 159 | goto out_config; |
110 | } | 160 | } |
111 | /* | 161 | } else if (bridge) { |
112 | * TODO: when we are using a different bridge than a panel | 162 | dev_info(dev->dev, "Using non-panel bridge\n"); |
113 | * (such as a dumb VGA connector) we need to devise a different | 163 | } else { |
114 | * method to get the connector out of the bridge. | 164 | dev_err(dev->dev, "No bridge, exiting\n"); |
115 | */ | 165 | return -ENODEV; |
166 | } | ||
167 | |||
168 | priv->bridge = bridge; | ||
169 | if (panel) { | ||
170 | priv->panel = panel; | ||
171 | priv->connector = panel->connector; | ||
116 | } | 172 | } |
117 | 173 | ||
118 | ret = pl111_display_init(dev); | 174 | ret = pl111_display_init(dev); |
@@ -126,14 +182,12 @@ static int pl111_modeset_init(struct drm_device *dev) | |||
126 | if (ret) | 182 | if (ret) |
127 | return ret; | 183 | return ret; |
128 | 184 | ||
129 | priv->bridge = bridge; | 185 | if (!priv->variant->broken_vblank) { |
130 | priv->panel = panel; | 186 | ret = drm_vblank_init(dev, 1); |
131 | priv->connector = panel->connector; | 187 | if (ret != 0) { |
132 | 188 | dev_err(dev->dev, "Failed to init vblank\n"); | |
133 | ret = drm_vblank_init(dev, 1); | 189 | goto out_bridge; |
134 | if (ret != 0) { | 190 | } |
135 | dev_err(dev->dev, "Failed to init vblank\n"); | ||
136 | goto out_bridge; | ||
137 | } | 191 | } |
138 | 192 | ||
139 | drm_mode_config_reset(dev); | 193 | drm_mode_config_reset(dev); |
@@ -170,10 +224,6 @@ static struct drm_driver pl111_drm_driver = { | |||
170 | .dumb_create = drm_gem_cma_dumb_create, | 224 | .dumb_create = drm_gem_cma_dumb_create, |
171 | .gem_free_object_unlocked = drm_gem_cma_free_object, | 225 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
172 | .gem_vm_ops = &drm_gem_cma_vm_ops, | 226 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
173 | |||
174 | .enable_vblank = pl111_enable_vblank, | ||
175 | .disable_vblank = pl111_disable_vblank, | ||
176 | |||
177 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | 227 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
178 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | 228 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
179 | .gem_prime_import = drm_gem_prime_import, | 229 | .gem_prime_import = drm_gem_prime_import, |
@@ -191,7 +241,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev, | |||
191 | { | 241 | { |
192 | struct device *dev = &amba_dev->dev; | 242 | struct device *dev = &amba_dev->dev; |
193 | struct pl111_drm_dev_private *priv; | 243 | struct pl111_drm_dev_private *priv; |
194 | struct pl111_variant_data *variant = id->data; | 244 | const struct pl111_variant_data *variant = id->data; |
195 | struct drm_device *drm; | 245 | struct drm_device *drm; |
196 | int ret; | 246 | int ret; |
197 | 247 | ||
@@ -199,6 +249,11 @@ static int pl111_amba_probe(struct amba_device *amba_dev, | |||
199 | if (!priv) | 249 | if (!priv) |
200 | return -ENOMEM; | 250 | return -ENOMEM; |
201 | 251 | ||
252 | if (!variant->broken_vblank) { | ||
253 | pl111_drm_driver.enable_vblank = pl111_enable_vblank; | ||
254 | pl111_drm_driver.disable_vblank = pl111_disable_vblank; | ||
255 | } | ||
256 | |||
202 | drm = drm_dev_alloc(&pl111_drm_driver, dev); | 257 | drm = drm_dev_alloc(&pl111_drm_driver, dev); |
203 | if (IS_ERR(drm)) | 258 | if (IS_ERR(drm)) |
204 | return PTR_ERR(drm); | 259 | return PTR_ERR(drm); |
@@ -207,27 +262,10 @@ static int pl111_amba_probe(struct amba_device *amba_dev, | |||
207 | drm->dev_private = priv; | 262 | drm->dev_private = priv; |
208 | priv->variant = variant; | 263 | priv->variant = variant; |
209 | 264 | ||
210 | /* | 265 | /* The two variants swap this register */ |
211 | * The PL110 and PL111 variants have two registers | ||
212 | * swapped: interrupt enable and control. For this reason | ||
213 | * we use offsets that we can change per variant. | ||
214 | */ | ||
215 | if (variant->is_pl110) { | 266 | if (variant->is_pl110) { |
216 | /* | 267 | priv->ienb = CLCD_PL110_IENB; |
217 | * The ARM Versatile boards are even more special: | 268 | priv->ctrl = CLCD_PL110_CNTL; |
218 | * their PrimeCell ID say they are PL110 but the | ||
219 | * control and interrupt enable registers are anyway | ||
220 | * swapped to the PL111 order so they are not following | ||
221 | * the PL110 datasheet. | ||
222 | */ | ||
223 | if (of_machine_is_compatible("arm,versatile-ab") || | ||
224 | of_machine_is_compatible("arm,versatile-pb")) { | ||
225 | priv->ienb = CLCD_PL111_IENB; | ||
226 | priv->ctrl = CLCD_PL111_CNTL; | ||
227 | } else { | ||
228 | priv->ienb = CLCD_PL110_IENB; | ||
229 | priv->ctrl = CLCD_PL110_CNTL; | ||
230 | } | ||
231 | } else { | 269 | } else { |
232 | priv->ienb = CLCD_PL111_IENB; | 270 | priv->ienb = CLCD_PL111_IENB; |
233 | priv->ctrl = CLCD_PL111_CNTL; | 271 | priv->ctrl = CLCD_PL111_CNTL; |
@@ -239,6 +277,11 @@ static int pl111_amba_probe(struct amba_device *amba_dev, | |||
239 | return PTR_ERR(priv->regs); | 277 | return PTR_ERR(priv->regs); |
240 | } | 278 | } |
241 | 279 | ||
280 | /* This may override some variant settings */ | ||
281 | ret = pl111_versatile_init(dev, priv); | ||
282 | if (ret) | ||
283 | goto dev_unref; | ||
284 | |||
242 | /* turn off interrupts before requesting the irq */ | 285 | /* turn off interrupts before requesting the irq */ |
243 | writel(0, priv->regs + priv->ienb); | 286 | writel(0, priv->regs + priv->ienb); |
244 | 287 | ||
@@ -249,10 +292,6 @@ static int pl111_amba_probe(struct amba_device *amba_dev, | |||
249 | return ret; | 292 | return ret; |
250 | } | 293 | } |
251 | 294 | ||
252 | ret = pl111_versatile_init(dev, priv); | ||
253 | if (ret) | ||
254 | goto dev_unref; | ||
255 | |||
256 | ret = pl111_modeset_init(drm); | 295 | ret = pl111_modeset_init(drm); |
257 | if (ret != 0) | 296 | if (ret != 0) |
258 | goto dev_unref; | 297 | goto dev_unref; |
@@ -284,8 +323,7 @@ static int pl111_amba_remove(struct amba_device *amba_dev) | |||
284 | } | 323 | } |
285 | 324 | ||
286 | /* | 325 | /* |
287 | * This variant exist in early versions like the ARM Integrator | 326 | * This early variant lacks the 565 and 444 pixel formats. |
288 | * and this version lacks the 565 and 444 pixel formats. | ||
289 | */ | 327 | */ |
290 | static const u32 pl110_pixel_formats[] = { | 328 | static const u32 pl110_pixel_formats[] = { |
291 | DRM_FORMAT_ABGR8888, | 329 | DRM_FORMAT_ABGR8888, |