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