1 /* 2 * Copyright (C) 2015 Texas Instruments 3 * Author: Jyri Sarha <jsarha@ti.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 */ 10 11 #include <linux/component.h> 12 #include <linux/of_graph.h> 13 #include <drm/drm_of.h> 14 15 #include "tilcdc_drv.h" 16 #include "tilcdc_external.h" 17 18 static const struct tilcdc_panel_info panel_info_tda998x = { 19 .ac_bias = 255, 20 .ac_bias_intrpt = 0, 21 .dma_burst_sz = 16, 22 .bpp = 16, 23 .fdd = 0x80, 24 .tft_alt_mode = 0, 25 .invert_pxl_clk = 1, 26 .sync_edge = 1, 27 .sync_ctrl = 1, 28 .raster_order = 0, 29 }; 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 43 static int tilcdc_external_mode_valid(struct drm_connector *connector, 44 struct drm_display_mode *mode) 45 { 46 struct tilcdc_drm_private *priv = connector->dev->dev_private; 47 int ret; 48 49 ret = tilcdc_crtc_mode_valid(priv->crtc, mode); 50 if (ret != MODE_OK) 51 return ret; 52 53 BUG_ON(priv->external_connector != connector); 54 BUG_ON(!priv->connector_funcs); 55 56 /* If the connector has its own mode_valid call it. */ 57 if (!IS_ERR(priv->connector_funcs) && 58 priv->connector_funcs->mode_valid) 59 return priv->connector_funcs->mode_valid(connector, mode); 60 61 return MODE_OK; 62 } 63 64 static int tilcdc_add_external_connector(struct drm_device *dev, 65 struct drm_connector *connector) 66 { 67 struct tilcdc_drm_private *priv = dev->dev_private; 68 struct drm_connector_helper_funcs *connector_funcs; 69 70 /* There should never be more than one connector */ 71 if (WARN_ON(priv->external_connector)) 72 return -EINVAL; 73 74 priv->external_connector = connector; 75 connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), 76 GFP_KERNEL); 77 if (!connector_funcs) 78 return -ENOMEM; 79 80 /* connector->helper_private contains always struct 81 * connector_helper_funcs pointer. For tilcdc crtc to have a 82 * say if a specific mode is Ok, we need to install our own 83 * helper functions. In our helper functions we copy 84 * everything else but use our own mode_valid() (above). 85 */ 86 if (connector->helper_private) { 87 priv->connector_funcs = connector->helper_private; 88 *connector_funcs = *priv->connector_funcs; 89 } else { 90 priv->connector_funcs = ERR_PTR(-ENOENT); 91 } 92 connector_funcs->mode_valid = tilcdc_external_mode_valid; 93 drm_connector_helper_add(connector, connector_funcs); 94 95 dev_dbg(dev->dev, "External connector '%s' connected\n", 96 connector->name); 97 98 return 0; 99 } 100 101 static 102 struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, 103 struct drm_encoder *encoder) 104 { 105 struct drm_connector *connector; 106 int i; 107 108 list_for_each_entry(connector, &ddev->mode_config.connector_list, head) 109 for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) 110 if (connector->encoder_ids[i] == encoder->base.id) 111 return connector; 112 113 dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", 114 encoder->name, encoder->base.id); 115 116 return NULL; 117 } 118 119 int tilcdc_add_component_encoder(struct drm_device *ddev) 120 { 121 struct tilcdc_drm_private *priv = ddev->dev_private; 122 struct drm_connector *connector; 123 struct drm_encoder *encoder; 124 125 list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) 126 if (encoder->possible_crtcs & (1 << priv->crtc->index)) 127 break; 128 129 if (!encoder) { 130 dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); 131 return -ENODEV; 132 } 133 134 connector = tilcdc_encoder_find_connector(ddev, encoder); 135 136 if (!connector) 137 return -ENODEV; 138 139 /* Only tda998x is supported at the moment. */ 140 tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 141 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 142 143 return tilcdc_add_external_connector(ddev, connector); 144 } 145 146 void tilcdc_remove_external_device(struct drm_device *dev) 147 { 148 struct tilcdc_drm_private *priv = dev->dev_private; 149 150 /* Restore the original helper functions, if any. */ 151 if (IS_ERR(priv->connector_funcs)) 152 drm_connector_helper_add(priv->external_connector, NULL); 153 else if (priv->connector_funcs) 154 drm_connector_helper_add(priv->external_connector, 155 priv->connector_funcs); 156 } 157 158 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = { 159 .destroy = drm_encoder_cleanup, 160 }; 161 162 static 163 int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) 164 { 165 struct tilcdc_drm_private *priv = ddev->dev_private; 166 struct drm_connector *connector; 167 int ret; 168 169 priv->external_encoder->possible_crtcs = BIT(0); 170 171 ret = drm_bridge_attach(priv->external_encoder, bridge, NULL); 172 if (ret) { 173 dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); 174 return ret; 175 } 176 177 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default); 178 179 connector = tilcdc_encoder_find_connector(ddev, priv->external_encoder); 180 if (!connector) 181 return -ENODEV; 182 183 ret = tilcdc_add_external_connector(ddev, connector); 184 185 return ret; 186 } 187 188 static int tilcdc_node_has_port(struct device_node *dev_node) 189 { 190 struct device_node *node; 191 192 node = of_get_child_by_name(dev_node, "ports"); 193 if (!node) 194 node = of_get_child_by_name(dev_node, "port"); 195 if (!node) 196 return 0; 197 of_node_put(node); 198 199 return 1; 200 } 201 202 static 203 struct device_node *tilcdc_get_remote_node(struct device_node *node) 204 { 205 struct device_node *ep; 206 struct device_node *parent; 207 208 if (!tilcdc_node_has_port(node)) 209 return NULL; 210 211 ep = of_graph_get_next_endpoint(node, NULL); 212 if (!ep) 213 return NULL; 214 215 parent = of_graph_get_remote_port_parent(ep); 216 of_node_put(ep); 217 218 return parent; 219 } 220 221 int tilcdc_attach_external_device(struct drm_device *ddev) 222 { 223 struct tilcdc_drm_private *priv = ddev->dev_private; 224 struct device_node *remote_node; 225 struct drm_bridge *bridge; 226 int ret; 227 228 remote_node = tilcdc_get_remote_node(ddev->dev->of_node); 229 if (!remote_node) 230 return 0; 231 232 bridge = of_drm_find_bridge(remote_node); 233 of_node_put(remote_node); 234 if (!bridge) 235 return -EPROBE_DEFER; 236 237 priv->external_encoder = devm_kzalloc(ddev->dev, 238 sizeof(*priv->external_encoder), 239 GFP_KERNEL); 240 if (!priv->external_encoder) 241 return -ENOMEM; 242 243 ret = drm_encoder_init(ddev, priv->external_encoder, 244 &tilcdc_external_encoder_funcs, 245 DRM_MODE_ENCODER_NONE, NULL); 246 if (ret) { 247 dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 248 return ret; 249 } 250 251 ret = tilcdc_attach_bridge(ddev, bridge); 252 if (ret) 253 drm_encoder_cleanup(priv->external_encoder); 254 255 return ret; 256 } 257 258 static int dev_match_of(struct device *dev, void *data) 259 { 260 return dev->of_node == data; 261 } 262 263 int tilcdc_get_external_components(struct device *dev, 264 struct component_match **match) 265 { 266 struct device_node *node; 267 struct device_node *ep = NULL; 268 int count = 0; 269 int ret = 0; 270 271 if (!tilcdc_node_has_port(dev->of_node)) 272 return 0; 273 274 while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { 275 node = of_graph_get_remote_port_parent(ep); 276 if (!node || !of_device_is_available(node)) { 277 of_node_put(node); 278 continue; 279 } 280 281 dev_dbg(dev, "Subdevice node '%s' found\n", node->name); 282 283 if (of_device_is_compatible(node, "nxp,tda998x")) { 284 if (match) 285 drm_of_component_match_add(dev, match, 286 dev_match_of, node); 287 ret = 1; 288 } 289 290 of_node_put(node); 291 if (count++ > 1) { 292 dev_err(dev, "Only one port is supported\n"); 293 return -EINVAL; 294 } 295 } 296 297 return ret; 298 } 299