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 priv->external_encoder->bridge = bridge; 171 bridge->encoder = priv->external_encoder; 172 173 ret = drm_bridge_attach(ddev, bridge); 174 if (ret) { 175 dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); 176 return ret; 177 } 178 179 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default); 180 181 connector = tilcdc_encoder_find_connector(ddev, priv->external_encoder); 182 if (!connector) 183 return -ENODEV; 184 185 ret = tilcdc_add_external_connector(ddev, connector); 186 187 return ret; 188 } 189 190 static int tilcdc_node_has_port(struct device_node *dev_node) 191 { 192 struct device_node *node; 193 194 node = of_get_child_by_name(dev_node, "ports"); 195 if (!node) 196 node = of_get_child_by_name(dev_node, "port"); 197 if (!node) 198 return 0; 199 of_node_put(node); 200 201 return 1; 202 } 203 204 static 205 struct device_node *tilcdc_get_remote_node(struct device_node *node) 206 { 207 struct device_node *ep; 208 struct device_node *parent; 209 210 if (!tilcdc_node_has_port(node)) 211 return NULL; 212 213 ep = of_graph_get_next_endpoint(node, NULL); 214 if (!ep) 215 return NULL; 216 217 parent = of_graph_get_remote_port_parent(ep); 218 of_node_put(ep); 219 220 return parent; 221 } 222 223 int tilcdc_attach_external_device(struct drm_device *ddev) 224 { 225 struct tilcdc_drm_private *priv = ddev->dev_private; 226 struct device_node *remote_node; 227 struct drm_bridge *bridge; 228 int ret; 229 230 remote_node = tilcdc_get_remote_node(ddev->dev->of_node); 231 if (!remote_node) 232 return 0; 233 234 bridge = of_drm_find_bridge(remote_node); 235 of_node_put(remote_node); 236 if (!bridge) 237 return -EPROBE_DEFER; 238 239 priv->external_encoder = devm_kzalloc(ddev->dev, 240 sizeof(*priv->external_encoder), 241 GFP_KERNEL); 242 if (!priv->external_encoder) 243 return -ENOMEM; 244 245 ret = drm_encoder_init(ddev, priv->external_encoder, 246 &tilcdc_external_encoder_funcs, 247 DRM_MODE_ENCODER_NONE, NULL); 248 if (ret) { 249 dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 250 return ret; 251 } 252 253 ret = tilcdc_attach_bridge(ddev, bridge); 254 if (ret) 255 drm_encoder_cleanup(priv->external_encoder); 256 257 return ret; 258 } 259 260 static int dev_match_of(struct device *dev, void *data) 261 { 262 return dev->of_node == data; 263 } 264 265 int tilcdc_get_external_components(struct device *dev, 266 struct component_match **match) 267 { 268 struct device_node *node; 269 struct device_node *ep = NULL; 270 int count = 0; 271 int ret = 0; 272 273 if (!tilcdc_node_has_port(dev->of_node)) 274 return 0; 275 276 while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { 277 node = of_graph_get_remote_port_parent(ep); 278 if (!node || !of_device_is_available(node)) { 279 of_node_put(node); 280 continue; 281 } 282 283 dev_dbg(dev, "Subdevice node '%s' found\n", node->name); 284 285 if (of_device_is_compatible(node, "nxp,tda998x")) { 286 if (match) 287 drm_of_component_match_add(dev, match, 288 dev_match_of, node); 289 ret = 1; 290 } 291 292 of_node_put(node); 293 if (count++ > 1) { 294 dev_err(dev, "Only one port is supported\n"); 295 return -EINVAL; 296 } 297 } 298 299 return ret; 300 } 301