1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2015 Texas Instruments 4 * Author: Jyri Sarha <jsarha@ti.com> 5 */ 6 7 #include <linux/component.h> 8 #include <linux/of_graph.h> 9 #include <drm/drm_atomic_helper.h> 10 #include <drm/drm_of.h> 11 12 #include "tilcdc_drv.h" 13 #include "tilcdc_external.h" 14 15 static const struct tilcdc_panel_info panel_info_tda998x = { 16 .ac_bias = 255, 17 .ac_bias_intrpt = 0, 18 .dma_burst_sz = 16, 19 .bpp = 16, 20 .fdd = 0x80, 21 .tft_alt_mode = 0, 22 .invert_pxl_clk = 1, 23 .sync_edge = 1, 24 .sync_ctrl = 1, 25 .raster_order = 0, 26 }; 27 28 static const struct tilcdc_panel_info panel_info_default = { 29 .ac_bias = 255, 30 .ac_bias_intrpt = 0, 31 .dma_burst_sz = 16, 32 .bpp = 16, 33 .fdd = 0x80, 34 .tft_alt_mode = 0, 35 .sync_edge = 0, 36 .sync_ctrl = 1, 37 .raster_order = 0, 38 }; 39 40 static int tilcdc_external_mode_valid(struct drm_connector *connector, 41 struct drm_display_mode *mode) 42 { 43 struct tilcdc_drm_private *priv = connector->dev->dev_private; 44 int ret; 45 46 ret = tilcdc_crtc_mode_valid(priv->crtc, mode); 47 if (ret != MODE_OK) 48 return ret; 49 50 BUG_ON(priv->external_connector != connector); 51 BUG_ON(!priv->connector_funcs); 52 53 /* If the connector has its own mode_valid call it. */ 54 if (!IS_ERR(priv->connector_funcs) && 55 priv->connector_funcs->mode_valid) 56 return priv->connector_funcs->mode_valid(connector, mode); 57 58 return MODE_OK; 59 } 60 61 static int tilcdc_add_external_connector(struct drm_device *dev, 62 struct drm_connector *connector) 63 { 64 struct tilcdc_drm_private *priv = dev->dev_private; 65 struct drm_connector_helper_funcs *connector_funcs; 66 67 /* There should never be more than one connector */ 68 if (WARN_ON(priv->external_connector)) 69 return -EINVAL; 70 71 priv->external_connector = connector; 72 connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), 73 GFP_KERNEL); 74 if (!connector_funcs) 75 return -ENOMEM; 76 77 /* connector->helper_private contains always struct 78 * connector_helper_funcs pointer. For tilcdc crtc to have a 79 * say if a specific mode is Ok, we need to install our own 80 * helper functions. In our helper functions we copy 81 * everything else but use our own mode_valid() (above). 82 */ 83 if (connector->helper_private) { 84 priv->connector_funcs = connector->helper_private; 85 *connector_funcs = *priv->connector_funcs; 86 } else { 87 priv->connector_funcs = ERR_PTR(-ENOENT); 88 } 89 connector_funcs->mode_valid = tilcdc_external_mode_valid; 90 drm_connector_helper_add(connector, connector_funcs); 91 92 dev_dbg(dev->dev, "External connector '%s' connected\n", 93 connector->name); 94 95 return 0; 96 } 97 98 static 99 struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, 100 struct drm_encoder *encoder) 101 { 102 struct drm_connector *connector; 103 104 list_for_each_entry(connector, &ddev->mode_config.connector_list, head) { 105 if (drm_connector_has_possible_encoder(connector, encoder)) 106 return connector; 107 } 108 109 dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", 110 encoder->name, encoder->base.id); 111 112 return NULL; 113 } 114 115 int tilcdc_add_component_encoder(struct drm_device *ddev) 116 { 117 struct tilcdc_drm_private *priv = ddev->dev_private; 118 struct drm_connector *connector; 119 struct drm_encoder *encoder; 120 121 list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) 122 if (encoder->possible_crtcs & (1 << priv->crtc->index)) 123 break; 124 125 if (!encoder) { 126 dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); 127 return -ENODEV; 128 } 129 130 connector = tilcdc_encoder_find_connector(ddev, encoder); 131 132 if (!connector) 133 return -ENODEV; 134 135 /* Only tda998x is supported at the moment. */ 136 tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 137 tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 138 139 return tilcdc_add_external_connector(ddev, connector); 140 } 141 142 void tilcdc_remove_external_device(struct drm_device *dev) 143 { 144 struct tilcdc_drm_private *priv = dev->dev_private; 145 146 /* Restore the original helper functions, if any. */ 147 if (IS_ERR(priv->connector_funcs)) 148 drm_connector_helper_add(priv->external_connector, NULL); 149 else if (priv->connector_funcs) 150 drm_connector_helper_add(priv->external_connector, 151 priv->connector_funcs); 152 } 153 154 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = { 155 .destroy = drm_encoder_cleanup, 156 }; 157 158 static 159 int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) 160 { 161 struct tilcdc_drm_private *priv = ddev->dev_private; 162 struct drm_connector *connector; 163 int ret; 164 165 priv->external_encoder->possible_crtcs = BIT(0); 166 167 ret = drm_bridge_attach(priv->external_encoder, bridge, NULL); 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 int tilcdc_attach_external_device(struct drm_device *ddev) 185 { 186 struct tilcdc_drm_private *priv = ddev->dev_private; 187 struct drm_bridge *bridge; 188 struct drm_panel *panel; 189 int ret; 190 191 ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0, 192 &panel, &bridge); 193 if (ret == -ENODEV) 194 return 0; 195 else if (ret) 196 return ret; 197 198 priv->external_encoder = devm_kzalloc(ddev->dev, 199 sizeof(*priv->external_encoder), 200 GFP_KERNEL); 201 if (!priv->external_encoder) 202 return -ENOMEM; 203 204 ret = drm_encoder_init(ddev, priv->external_encoder, 205 &tilcdc_external_encoder_funcs, 206 DRM_MODE_ENCODER_NONE, NULL); 207 if (ret) { 208 dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 209 return ret; 210 } 211 212 if (panel) { 213 bridge = devm_drm_panel_bridge_add(ddev->dev, panel, 214 DRM_MODE_CONNECTOR_DPI); 215 if (IS_ERR(bridge)) { 216 ret = PTR_ERR(bridge); 217 goto err_encoder_cleanup; 218 } 219 } 220 221 ret = tilcdc_attach_bridge(ddev, bridge); 222 if (ret) 223 goto err_encoder_cleanup; 224 225 return 0; 226 227 err_encoder_cleanup: 228 drm_encoder_cleanup(priv->external_encoder); 229 return ret; 230 } 231 232 static int dev_match_of(struct device *dev, void *data) 233 { 234 return dev->of_node == data; 235 } 236 237 int tilcdc_get_external_components(struct device *dev, 238 struct component_match **match) 239 { 240 struct device_node *node; 241 242 node = of_graph_get_remote_node(dev->of_node, 0, 0); 243 244 if (!of_device_is_compatible(node, "nxp,tda998x")) { 245 of_node_put(node); 246 return 0; 247 } 248 249 if (match) 250 drm_of_component_match_add(dev, match, dev_match_of, node); 251 of_node_put(node); 252 return 1; 253 } 254