diff options
| -rw-r--r-- | drivers/gpu/drm/vc4/vc4_dpi.c | 164 |
1 files changed, 30 insertions, 134 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 39d68080873c..2e0fe46aeb2e 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c | |||
| @@ -23,8 +23,10 @@ | |||
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | #include <drm/drm_atomic_helper.h> | 25 | #include <drm/drm_atomic_helper.h> |
| 26 | #include <drm/drm_bridge.h> | ||
| 26 | #include <drm/drm_crtc_helper.h> | 27 | #include <drm/drm_crtc_helper.h> |
| 27 | #include <drm/drm_edid.h> | 28 | #include <drm/drm_edid.h> |
| 29 | #include <drm/drm_of.h> | ||
| 28 | #include <drm/drm_panel.h> | 30 | #include <drm/drm_panel.h> |
| 29 | #include <linux/clk.h> | 31 | #include <linux/clk.h> |
| 30 | #include <linux/component.h> | 32 | #include <linux/component.h> |
| @@ -95,7 +97,8 @@ struct vc4_dpi { | |||
| 95 | 97 | ||
| 96 | struct drm_encoder *encoder; | 98 | struct drm_encoder *encoder; |
| 97 | struct drm_connector *connector; | 99 | struct drm_connector *connector; |
| 98 | struct drm_panel *panel; | 100 | struct drm_bridge *bridge; |
| 101 | bool is_panel_bridge; | ||
| 99 | 102 | ||
| 100 | void __iomem *regs; | 103 | void __iomem *regs; |
| 101 | 104 | ||
| @@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder) | |||
| 118 | return container_of(encoder, struct vc4_dpi_encoder, base.base); | 121 | return container_of(encoder, struct vc4_dpi_encoder, base.base); |
| 119 | } | 122 | } |
| 120 | 123 | ||
| 121 | /* VC4 DPI connector KMS struct */ | ||
| 122 | struct vc4_dpi_connector { | ||
| 123 | struct drm_connector base; | ||
| 124 | struct vc4_dpi *dpi; | ||
| 125 | |||
| 126 | /* Since the connector is attached to just the one encoder, | ||
| 127 | * this is the reference to it so we can do the best_encoder() | ||
| 128 | * hook. | ||
| 129 | */ | ||
| 130 | struct drm_encoder *encoder; | ||
| 131 | }; | ||
| 132 | |||
| 133 | static inline struct vc4_dpi_connector * | ||
| 134 | to_vc4_dpi_connector(struct drm_connector *connector) | ||
| 135 | { | ||
| 136 | return container_of(connector, struct vc4_dpi_connector, base); | ||
| 137 | } | ||
| 138 | |||
| 139 | #define DPI_REG(reg) { reg, #reg } | 124 | #define DPI_REG(reg) { reg, #reg } |
| 140 | static const struct { | 125 | static const struct { |
| 141 | u32 reg; | 126 | u32 reg; |
| @@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused) | |||
| 167 | } | 152 | } |
| 168 | #endif | 153 | #endif |
| 169 | 154 | ||
| 170 | static enum drm_connector_status | ||
| 171 | vc4_dpi_connector_detect(struct drm_connector *connector, bool force) | ||
| 172 | { | ||
| 173 | struct vc4_dpi_connector *vc4_connector = | ||
| 174 | to_vc4_dpi_connector(connector); | ||
| 175 | struct vc4_dpi *dpi = vc4_connector->dpi; | ||
| 176 | |||
| 177 | if (dpi->panel) | ||
| 178 | return connector_status_connected; | ||
| 179 | else | ||
| 180 | return connector_status_disconnected; | ||
| 181 | } | ||
| 182 | |||
| 183 | static void vc4_dpi_connector_destroy(struct drm_connector *connector) | ||
| 184 | { | ||
| 185 | drm_connector_unregister(connector); | ||
| 186 | drm_connector_cleanup(connector); | ||
| 187 | } | ||
| 188 | |||
| 189 | static int vc4_dpi_connector_get_modes(struct drm_connector *connector) | ||
| 190 | { | ||
| 191 | struct vc4_dpi_connector *vc4_connector = | ||
| 192 | to_vc4_dpi_connector(connector); | ||
| 193 | struct vc4_dpi *dpi = vc4_connector->dpi; | ||
| 194 | |||
| 195 | if (dpi->panel) | ||
| 196 | return drm_panel_get_modes(dpi->panel); | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static const struct drm_connector_funcs vc4_dpi_connector_funcs = { | ||
| 202 | .dpms = drm_atomic_helper_connector_dpms, | ||
| 203 | .detect = vc4_dpi_connector_detect, | ||
| 204 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 205 | .destroy = vc4_dpi_connector_destroy, | ||
| 206 | .reset = drm_atomic_helper_connector_reset, | ||
| 207 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
| 208 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
| 209 | }; | ||
| 210 | |||
| 211 | static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = { | ||
| 212 | .get_modes = vc4_dpi_connector_get_modes, | ||
| 213 | }; | ||
| 214 | |||
| 215 | static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev, | ||
| 216 | struct vc4_dpi *dpi) | ||
| 217 | { | ||
| 218 | struct drm_connector *connector = NULL; | ||
| 219 | struct vc4_dpi_connector *dpi_connector; | ||
| 220 | |||
| 221 | dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector), | ||
| 222 | GFP_KERNEL); | ||
| 223 | if (!dpi_connector) | ||
| 224 | return ERR_PTR(-ENOMEM); | ||
| 225 | |||
| 226 | connector = &dpi_connector->base; | ||
| 227 | |||
| 228 | dpi_connector->encoder = dpi->encoder; | ||
| 229 | dpi_connector->dpi = dpi; | ||
| 230 | |||
| 231 | drm_connector_init(dev, connector, &vc4_dpi_connector_funcs, | ||
| 232 | DRM_MODE_CONNECTOR_DPI); | ||
| 233 | drm_connector_helper_add(connector, &vc4_dpi_connector_helper_funcs); | ||
| 234 | |||
| 235 | connector->polled = 0; | ||
| 236 | connector->interlace_allowed = 0; | ||
| 237 | connector->doublescan_allowed = 0; | ||
| 238 | |||
| 239 | drm_mode_connector_attach_encoder(connector, dpi->encoder); | ||
| 240 | |||
| 241 | return connector; | ||
| 242 | } | ||
| 243 | |||
| 244 | static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { | 155 | static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { |
| 245 | .destroy = drm_encoder_cleanup, | 156 | .destroy = drm_encoder_cleanup, |
| 246 | }; | 157 | }; |
| @@ -250,11 +161,7 @@ static void vc4_dpi_encoder_disable(struct drm_encoder *encoder) | |||
| 250 | struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); | 161 | struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); |
| 251 | struct vc4_dpi *dpi = vc4_encoder->dpi; | 162 | struct vc4_dpi *dpi = vc4_encoder->dpi; |
| 252 | 163 | ||
| 253 | drm_panel_disable(dpi->panel); | ||
| 254 | |||
| 255 | clk_disable_unprepare(dpi->pixel_clock); | 164 | clk_disable_unprepare(dpi->pixel_clock); |
| 256 | |||
| 257 | drm_panel_unprepare(dpi->panel); | ||
| 258 | } | 165 | } |
| 259 | 166 | ||
| 260 | static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) | 167 | static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) |
| @@ -265,12 +172,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) | |||
| 265 | u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; | 172 | u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; |
| 266 | int ret; | 173 | int ret; |
| 267 | 174 | ||
| 268 | ret = drm_panel_prepare(dpi->panel); | ||
| 269 | if (ret) { | ||
| 270 | DRM_ERROR("Panel failed to prepare\n"); | ||
| 271 | return; | ||
| 272 | } | ||
| 273 | |||
| 274 | if (dpi->connector->display_info.num_bus_formats) { | 175 | if (dpi->connector->display_info.num_bus_formats) { |
| 275 | u32 bus_format = dpi->connector->display_info.bus_formats[0]; | 176 | u32 bus_format = dpi->connector->display_info.bus_formats[0]; |
| 276 | 177 | ||
| @@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) | |||
| 321 | ret = clk_prepare_enable(dpi->pixel_clock); | 222 | ret = clk_prepare_enable(dpi->pixel_clock); |
| 322 | if (ret) | 223 | if (ret) |
| 323 | DRM_ERROR("Failed to set clock rate: %d\n", ret); | 224 | DRM_ERROR("Failed to set clock rate: %d\n", ret); |
| 324 | |||
| 325 | ret = drm_panel_enable(dpi->panel); | ||
| 326 | if (ret) { | ||
| 327 | DRM_ERROR("Panel failed to enable\n"); | ||
| 328 | drm_panel_unprepare(dpi->panel); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | } | 225 | } |
| 332 | 226 | ||
| 333 | static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder, | 227 | static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder, |
| @@ -351,24 +245,34 @@ static const struct of_device_id vc4_dpi_dt_match[] = { | |||
| 351 | {} | 245 | {} |
| 352 | }; | 246 | }; |
| 353 | 247 | ||
| 354 | /* Walks the OF graph to find the panel node and then asks DRM to look | 248 | /* Sets up the next link in the display chain, whether it's a panel or |
| 355 | * up the panel. | 249 | * a bridge. |
| 356 | */ | 250 | */ |
| 357 | static struct drm_panel *vc4_dpi_get_panel(struct device *dev) | 251 | static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) |
| 358 | { | 252 | { |
| 359 | struct device_node *panel_node; | 253 | struct device *dev = &dpi->pdev->dev; |
| 360 | struct device_node *np = dev->of_node; | ||
| 361 | struct drm_panel *panel; | 254 | struct drm_panel *panel; |
| 255 | int ret; | ||
| 362 | 256 | ||
| 363 | /* don't proceed if we have an endpoint but no panel_node tied to it */ | 257 | ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, |
| 364 | panel_node = of_graph_get_remote_node(np, 0, 0); | 258 | &panel, &dpi->bridge); |
| 365 | if (!panel_node) | 259 | if (ret) { |
| 366 | return NULL; | 260 | /* If nothing was connected in the DT, that's not an |
| 261 | * error. | ||
| 262 | */ | ||
| 263 | if (ret == -ENODEV) | ||
| 264 | return 0; | ||
| 265 | else | ||
| 266 | return ret; | ||
| 267 | } | ||
| 367 | 268 | ||
| 368 | panel = of_drm_find_panel(panel_node); | 269 | if (panel) { |
| 369 | of_node_put(panel_node); | 270 | dpi->bridge = drm_panel_bridge_add(panel, |
| 271 | DRM_MODE_CONNECTOR_DPI); | ||
| 272 | dpi->is_panel_bridge = true; | ||
| 273 | } | ||
| 370 | 274 | ||
| 371 | return panel; | 275 | return drm_bridge_attach(dpi->encoder, dpi->bridge, NULL); |
| 372 | } | 276 | } |
| 373 | 277 | ||
| 374 | static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) | 278 | static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) |
| @@ -422,20 +326,13 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) | |||
| 422 | if (ret) | 326 | if (ret) |
| 423 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); | 327 | DRM_ERROR("Failed to turn on core clock: %d\n", ret); |
| 424 | 328 | ||
| 425 | dpi->panel = vc4_dpi_get_panel(dev); | ||
| 426 | |||
| 427 | drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs, | 329 | drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs, |
| 428 | DRM_MODE_ENCODER_DPI, NULL); | 330 | DRM_MODE_ENCODER_DPI, NULL); |
| 429 | drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); | 331 | drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); |
| 430 | 332 | ||
| 431 | dpi->connector = vc4_dpi_connector_init(drm, dpi); | 333 | ret = vc4_dpi_init_bridge(dpi); |
| 432 | if (IS_ERR(dpi->connector)) { | 334 | if (ret) |
| 433 | ret = PTR_ERR(dpi->connector); | ||
| 434 | goto err_destroy_encoder; | 335 | goto err_destroy_encoder; |
| 435 | } | ||
| 436 | |||
| 437 | if (dpi->panel) | ||
| 438 | drm_panel_attach(dpi->panel, dpi->connector); | ||
| 439 | 336 | ||
| 440 | dev_set_drvdata(dev, dpi); | 337 | dev_set_drvdata(dev, dpi); |
| 441 | 338 | ||
| @@ -456,10 +353,9 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, | |||
| 456 | struct vc4_dev *vc4 = to_vc4_dev(drm); | 353 | struct vc4_dev *vc4 = to_vc4_dev(drm); |
| 457 | struct vc4_dpi *dpi = dev_get_drvdata(dev); | 354 | struct vc4_dpi *dpi = dev_get_drvdata(dev); |
| 458 | 355 | ||
| 459 | if (dpi->panel) | 356 | if (dpi->is_panel_bridge) |
| 460 | drm_panel_detach(dpi->panel); | 357 | drm_panel_bridge_remove(dpi->bridge); |
| 461 | 358 | ||
| 462 | vc4_dpi_connector_destroy(dpi->connector); | ||
| 463 | drm_encoder_cleanup(dpi->encoder); | 359 | drm_encoder_cleanup(dpi->encoder); |
| 464 | 360 | ||
| 465 | clk_disable_unprepare(dpi->core_clock); | 361 | clk_disable_unprepare(dpi->core_clock); |
