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 107 list_for_each_entry(connector, &ddev->mode_config.connector_list, head) { 108 if (drm_connector_has_possible_encoder(connector, encoder)) 109 return connector; 110 } 111 112 dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", 113 encoder->name, encoder->base.id); 114 115 return NULL; 116 } 117 118 int tilcdc_add_component_encoder(struct drm_device *ddev) 119 { 120 struct tilcdc_drm_private *priv = ddev->dev_private; 121 struct drm_connector *connector; 122 struct drm_encoder *encoder; 123 124 list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) 125 if (encoder->possible_crtcs & (1 << priv->crtc->index)) 126 break; 127 128 if (!encoder) { 129 dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); 130 return -ENODEV; 131 } 132 133 connector = tilcdc_encoder_find_connector(ddev, encoder); 134 135 if (!connector) 136 return -ENODEV; 137 138 /* Only tda998x is supported at the moment. */ 139 tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 140 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 141 142 return tilcdc_add_external_connector(ddev, connector); 143 } 144 145 void tilcdc_remove_external_device(struct drm_device *dev) 146 { 147 struct tilcdc_drm_private *priv = dev->dev_private; 148 149 /* Restore the original helper functions, if any. */ 150 if (IS_ERR(priv->connector_funcs)) 151 drm_connector_helper_add(priv->external_connector, NULL); 152 else if (priv->connector_funcs) 153 drm_connector_helper_add(priv->external_connector, 154 priv->connector_funcs); 155 } 156 157 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = { 158 .destroy = drm_encoder_cleanup, 159 }; 160 161 static 162 int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) 163 { 164 struct tilcdc_drm_private *priv = ddev->dev_private; 165 struct drm_connector *connector; 166 int ret; 167 168 priv->external_encoder->possible_crtcs = BIT(0); 169 170 ret = drm_bridge_attach(priv->external_encoder, bridge, NULL); 171 if (ret) { 172 dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); 173 return ret; 174 } 175 176 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default); 177 178 connector = tilcdc_encoder_find_connector(ddev, priv->external_encoder); 179 if (!connector) 180 return -ENODEV; 181 182 ret = tilcdc_add_external_connector(ddev, connector); 183 184 return ret; 185 } 186 187 int tilcdc_attach_external_device(struct drm_device *ddev) 188 { 189 struct tilcdc_drm_private *priv = ddev->dev_private; 190 struct drm_bridge *bridge; 191 struct drm_panel *panel; 192 int ret; 193 194 ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0, 195 &panel, &bridge); 196 if (ret == -ENODEV) 197 return 0; 198 else if (ret) 199 return ret; 200 201 priv->external_encoder = devm_kzalloc(ddev->dev, 202 sizeof(*priv->external_encoder), 203 GFP_KERNEL); 204 if (!priv->external_encoder) 205 return -ENOMEM; 206 207 ret = drm_encoder_init(ddev, priv->external_encoder, 208 &tilcdc_external_encoder_funcs, 209 DRM_MODE_ENCODER_NONE, NULL); 210 if (ret) { 211 dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 212 return ret; 213 } 214 215 if (panel) { 216 bridge = devm_drm_panel_bridge_add(ddev->dev, panel, 217 DRM_MODE_CONNECTOR_DPI); 218 if (IS_ERR(bridge)) { 219 ret = PTR_ERR(bridge); 220 goto err_encoder_cleanup; 221 } 222 } 223 224 ret = tilcdc_attach_bridge(ddev, bridge); 225 if (ret) 226 goto err_encoder_cleanup; 227 228 return 0; 229 230 err_encoder_cleanup: 231 drm_encoder_cleanup(priv->external_encoder); 232 return ret; 233 } 234 235 static int dev_match_of(struct device *dev, void *data) 236 { 237 return dev->of_node == data; 238 } 239 240 int tilcdc_get_external_components(struct device *dev, 241 struct component_match **match) 242 { 243 struct device_node *node; 244 245 node = of_graph_get_remote_node(dev->of_node, 0, 0); 246 247 if (!of_device_is_compatible(node, "nxp,tda998x")) { 248 of_node_put(node); 249 return 0; 250 } 251 252 if (match) 253 drm_of_component_match_add(dev, match, dev_match_of, node); 254 of_node_put(node); 255 return 1; 256 } 257