Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tilcdc: Add drm bridge support for attaching drm bridge drivers

Adds drm bride support for attaching drm bridge drivers to tilcdc. The
decision whether a video port leads to an external encoder or bridge
is made simply based on remote device's compatible string. The code
has been tested with BeagleBone-Black with and without BeagleBone
DVI-D Cape Rev A3 using ti-tfp410 driver.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
Tested-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>

+211 -78
+8 -3
drivers/gpu/drm/tilcdc/tilcdc_drv.c
··· 209 209 210 210 drm_irq_uninstall(dev); 211 211 drm_mode_config_cleanup(dev); 212 - tilcdc_remove_external_encoders(dev); 212 + tilcdc_remove_external_device(dev); 213 213 214 214 #ifdef CONFIG_CPU_FREQ 215 215 if (priv->freq_transition.notifier_call) ··· 381 381 if (ret < 0) 382 382 goto init_failed; 383 383 384 - ret = tilcdc_add_external_encoders(ddev); 384 + ret = tilcdc_add_component_encoder(ddev); 385 385 if (ret < 0) 386 + goto init_failed; 387 + } else { 388 + ret = tilcdc_attach_external_device(ddev); 389 + if (ret) 386 390 goto init_failed; 387 391 } 388 392 389 - if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { 393 + if (!priv->external_connector && 394 + ((priv->num_encoders == 0) || (priv->num_connectors == 0))) { 390 395 dev_err(dev, "no encoders/connectors found\n"); 391 396 ret = -ENXIO; 392 397 goto init_failed;
+4 -1
drivers/gpu/drm/tilcdc/tilcdc_drv.h
··· 88 88 89 89 unsigned int num_connectors; 90 90 struct drm_connector *connectors[8]; 91 - const struct drm_connector_helper_funcs *connector_funcs[8]; 91 + 92 + struct drm_encoder *external_encoder; 93 + struct drm_connector *external_connector; 94 + const struct drm_connector_helper_funcs *connector_funcs; 92 95 93 96 bool is_registered; 94 97 bool is_componentized;
+196 -72
drivers/gpu/drm/tilcdc/tilcdc_external.c
··· 28 28 .raster_order = 0, 29 29 }; 30 30 31 + static const struct tilcdc_panel_info panel_info_default = { 32 + .ac_bias = 255, 33 + .ac_bias_intrpt = 0, 34 + .dma_burst_sz = 16, 35 + .bpp = 16, 36 + .fdd = 0x80, 37 + .tft_alt_mode = 0, 38 + .sync_edge = 0, 39 + .sync_ctrl = 1, 40 + .raster_order = 0, 41 + }; 42 + 31 43 static int tilcdc_external_mode_valid(struct drm_connector *connector, 32 44 struct drm_display_mode *mode) 33 45 { 34 46 struct tilcdc_drm_private *priv = connector->dev->dev_private; 35 - int ret, i; 47 + int ret; 36 48 37 49 ret = tilcdc_crtc_mode_valid(priv->crtc, mode); 38 50 if (ret != MODE_OK) 39 51 return ret; 40 52 41 - for (i = 0; i < priv->num_connectors && 42 - priv->connectors[i] != connector; i++) 43 - ; 44 - 45 - BUG_ON(priv->connectors[i] != connector); 46 - BUG_ON(!priv->connector_funcs[i]); 53 + BUG_ON(priv->external_connector != connector); 54 + BUG_ON(!priv->connector_funcs); 47 55 48 56 /* If the connector has its own mode_valid call it. */ 49 - if (!IS_ERR(priv->connector_funcs[i]) && 50 - priv->connector_funcs[i]->mode_valid) 51 - return priv->connector_funcs[i]->mode_valid(connector, mode); 57 + if (!IS_ERR(priv->connector_funcs) && 58 + priv->connector_funcs->mode_valid) 59 + return priv->connector_funcs->mode_valid(connector, mode); 52 60 53 61 return MODE_OK; 54 62 } 55 63 56 - static int tilcdc_add_external_encoder(struct drm_device *dev, 57 - struct drm_connector *connector) 64 + static int tilcdc_add_external_connector(struct drm_device *dev, 65 + struct drm_connector *connector) 58 66 { 59 67 struct tilcdc_drm_private *priv = dev->dev_private; 60 68 struct drm_connector_helper_funcs *connector_funcs; 61 69 62 - priv->connectors[priv->num_connectors] = connector; 63 - priv->encoders[priv->num_encoders++] = connector->encoder; 70 + /* There should never be more than one connector */ 71 + if (WARN_ON(priv->external_connector)) 72 + return -EINVAL; 64 73 65 - /* Only tda998x is supported at the moment. */ 66 - tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 67 - tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 68 - 74 + priv->external_connector = connector; 69 75 connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), 70 76 GFP_KERNEL); 71 77 if (!connector_funcs) ··· 84 78 * everything else but use our own mode_valid() (above). 85 79 */ 86 80 if (connector->helper_private) { 87 - priv->connector_funcs[priv->num_connectors] = 88 - connector->helper_private; 89 - *connector_funcs = *priv->connector_funcs[priv->num_connectors]; 81 + priv->connector_funcs = connector->helper_private; 82 + *connector_funcs = *priv->connector_funcs; 90 83 } else { 91 - priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT); 84 + priv->connector_funcs = ERR_PTR(-ENOENT); 92 85 } 93 86 connector_funcs->mode_valid = tilcdc_external_mode_valid; 94 87 drm_connector_helper_add(connector, connector_funcs); 95 - priv->num_connectors++; 96 88 97 - dev_dbg(dev->dev, "External encoder '%s' connected\n", 98 - connector->encoder->name); 89 + dev_dbg(dev->dev, "External connector '%s' connected\n", 90 + connector->name); 99 91 100 92 return 0; 101 93 } 102 94 103 - int tilcdc_add_external_encoders(struct drm_device *dev) 95 + static 96 + struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, 97 + struct drm_encoder *encoder) 104 98 { 105 - struct tilcdc_drm_private *priv = dev->dev_private; 106 99 struct drm_connector *connector; 107 - int num_internal_connectors = priv->num_connectors; 108 - 109 - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 110 - bool found = false; 111 - int i, ret; 112 - 113 - for (i = 0; i < num_internal_connectors; i++) 114 - if (connector == priv->connectors[i]) 115 - found = true; 116 - if (!found) { 117 - ret = tilcdc_add_external_encoder(dev, connector); 118 - if (ret) 119 - return ret; 120 - } 121 - } 122 - return 0; 123 - } 124 - 125 - void tilcdc_remove_external_encoders(struct drm_device *dev) 126 - { 127 - struct tilcdc_drm_private *priv = dev->dev_private; 128 100 int i; 129 101 102 + list_for_each_entry(connector, &ddev->mode_config.connector_list, head) 103 + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) 104 + if (connector->encoder_ids[i] == encoder->base.id) 105 + return connector; 106 + 107 + dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", 108 + encoder->name, encoder->base.id); 109 + 110 + return NULL; 111 + } 112 + 113 + int tilcdc_add_component_encoder(struct drm_device *ddev) 114 + { 115 + struct tilcdc_drm_private *priv = ddev->dev_private; 116 + struct drm_connector *connector; 117 + struct drm_encoder *encoder; 118 + 119 + list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) 120 + if (encoder->possible_crtcs & (1 << priv->crtc->index)) 121 + break; 122 + 123 + if (!encoder) { 124 + dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); 125 + return -ENODEV; 126 + } 127 + 128 + connector = tilcdc_encoder_find_connector(ddev, encoder); 129 + 130 + if (!connector) 131 + return -ENODEV; 132 + 133 + /* Only tda998x is supported at the moment. */ 134 + tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 135 + tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 136 + 137 + return tilcdc_add_external_connector(ddev, connector); 138 + } 139 + 140 + void tilcdc_remove_external_device(struct drm_device *dev) 141 + { 142 + struct tilcdc_drm_private *priv = dev->dev_private; 143 + 130 144 /* Restore the original helper functions, if any. */ 131 - for (i = 0; i < priv->num_connectors; i++) 132 - if (IS_ERR(priv->connector_funcs[i])) 133 - drm_connector_helper_add(priv->connectors[i], NULL); 134 - else if (priv->connector_funcs[i]) 135 - drm_connector_helper_add(priv->connectors[i], 136 - priv->connector_funcs[i]); 145 + if (IS_ERR(priv->connector_funcs)) 146 + drm_connector_helper_add(priv->external_connector, NULL); 147 + else if (priv->connector_funcs) 148 + drm_connector_helper_add(priv->external_connector, 149 + priv->connector_funcs); 150 + } 151 + 152 + static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = { 153 + .destroy = drm_encoder_cleanup, 154 + }; 155 + 156 + static 157 + int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) 158 + { 159 + struct tilcdc_drm_private *priv = ddev->dev_private; 160 + struct drm_connector *connector; 161 + int ret; 162 + 163 + priv->external_encoder->possible_crtcs = BIT(0); 164 + priv->external_encoder->bridge = bridge; 165 + bridge->encoder = priv->external_encoder; 166 + 167 + ret = drm_bridge_attach(ddev, bridge); 168 + if (ret) { 169 + dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); 170 + return ret; 171 + } 172 + 173 + tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default); 174 + 175 + connector = tilcdc_encoder_find_connector(ddev, priv->external_encoder); 176 + if (!connector) 177 + return -ENODEV; 178 + 179 + ret = tilcdc_add_external_connector(ddev, connector); 180 + 181 + return ret; 182 + } 183 + 184 + static int tilcdc_node_has_port(struct device_node *dev_node) 185 + { 186 + struct device_node *node; 187 + 188 + node = of_get_child_by_name(dev_node, "ports"); 189 + if (!node) 190 + node = of_get_child_by_name(dev_node, "port"); 191 + if (!node) 192 + return 0; 193 + of_node_put(node); 194 + 195 + return 1; 196 + } 197 + 198 + static 199 + struct device_node *tilcdc_get_remote_node(struct device_node *node) 200 + { 201 + struct device_node *ep; 202 + struct device_node *parent; 203 + 204 + if (!tilcdc_node_has_port(node)) 205 + return NULL; 206 + 207 + ep = of_graph_get_next_endpoint(node, NULL); 208 + if (!ep) 209 + return NULL; 210 + 211 + parent = of_graph_get_remote_port_parent(ep); 212 + of_node_put(ep); 213 + 214 + return parent; 215 + } 216 + 217 + int tilcdc_attach_external_device(struct drm_device *ddev) 218 + { 219 + struct tilcdc_drm_private *priv = ddev->dev_private; 220 + struct device_node *remote_node; 221 + struct drm_bridge *bridge; 222 + int ret; 223 + 224 + remote_node = tilcdc_get_remote_node(ddev->dev->of_node); 225 + if (!remote_node) 226 + return 0; 227 + 228 + bridge = of_drm_find_bridge(remote_node); 229 + of_node_put(remote_node); 230 + if (!bridge) 231 + return -EPROBE_DEFER; 232 + 233 + priv->external_encoder = devm_kzalloc(ddev->dev, 234 + sizeof(*priv->external_encoder), 235 + GFP_KERNEL); 236 + if (!priv->external_encoder) 237 + return -ENOMEM; 238 + 239 + ret = drm_encoder_init(ddev, priv->external_encoder, 240 + &tilcdc_external_encoder_funcs, 241 + DRM_MODE_ENCODER_NONE, NULL); 242 + if (ret) { 243 + dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 244 + return ret; 245 + } 246 + 247 + ret = tilcdc_attach_bridge(ddev, bridge); 248 + if (ret) 249 + drm_encoder_cleanup(priv->external_encoder); 250 + 251 + return ret; 137 252 } 138 253 139 254 static int dev_match_of(struct device *dev, void *data) ··· 268 141 struct device_node *node; 269 142 struct device_node *ep = NULL; 270 143 int count = 0; 144 + int ret = 0; 271 145 272 - /* Avoid error print by of_graph_get_next_endpoint() if there 273 - * is no ports present. 274 - */ 275 - node = of_get_child_by_name(dev->of_node, "ports"); 276 - if (!node) 277 - node = of_get_child_by_name(dev->of_node, "port"); 278 - if (!node) 146 + if (!tilcdc_node_has_port(dev->of_node)) 279 147 return 0; 280 - of_node_put(node); 281 148 282 149 while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { 283 150 node = of_graph_get_remote_port_parent(ep); ··· 281 160 } 282 161 283 162 dev_dbg(dev, "Subdevice node '%s' found\n", node->name); 284 - if (match) 285 - drm_of_component_match_add(dev, match, dev_match_of, 286 - node); 163 + 164 + if (of_device_is_compatible(node, "nxp,tda998x")) { 165 + if (match) 166 + drm_of_component_match_add(dev, match, 167 + dev_match_of, node); 168 + ret = 1; 169 + } 170 + 287 171 of_node_put(node); 288 - count++; 172 + if (count++ > 1) { 173 + dev_err(dev, "Only one port is supported\n"); 174 + return -EINVAL; 175 + } 289 176 } 290 177 291 - if (count > 1) { 292 - dev_err(dev, "Only one external encoder is supported\n"); 293 - return -EINVAL; 294 - } 295 - 296 - return count; 178 + return ret; 297 179 }
+3 -2
drivers/gpu/drm/tilcdc/tilcdc_external.h
··· 18 18 #ifndef __TILCDC_EXTERNAL_H__ 19 19 #define __TILCDC_EXTERNAL_H__ 20 20 21 - int tilcdc_add_external_encoders(struct drm_device *dev); 22 - void tilcdc_remove_external_encoders(struct drm_device *dev); 21 + int tilcdc_add_component_encoder(struct drm_device *dev); 22 + void tilcdc_remove_external_device(struct drm_device *dev); 23 23 int tilcdc_get_external_components(struct device *dev, 24 24 struct component_match **match); 25 + int tilcdc_attach_external_device(struct drm_device *ddev); 25 26 #endif /* __TILCDC_SLAVE_H__ */