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 int tilcdc_attach_external_device(struct drm_device *ddev) 189 { 190 struct tilcdc_drm_private *priv = ddev->dev_private; 191 struct device_node *remote_node; 192 struct drm_bridge *bridge; 193 int ret; 194 195 remote_node = of_graph_get_remote_node(ddev->dev->of_node, 0, 0); 196 if (!remote_node) 197 return 0; 198 199 bridge = of_drm_find_bridge(remote_node); 200 of_node_put(remote_node); 201 if (!bridge) 202 return -EPROBE_DEFER; 203 204 priv->external_encoder = devm_kzalloc(ddev->dev, 205 sizeof(*priv->external_encoder), 206 GFP_KERNEL); 207 if (!priv->external_encoder) 208 return -ENOMEM; 209 210 ret = drm_encoder_init(ddev, priv->external_encoder, 211 &tilcdc_external_encoder_funcs, 212 DRM_MODE_ENCODER_NONE, NULL); 213 if (ret) { 214 dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); 215 return ret; 216 } 217 218 ret = tilcdc_attach_bridge(ddev, bridge); 219 if (ret) 220 drm_encoder_cleanup(priv->external_encoder); 221 222 return ret; 223 } 224 225 static int dev_match_of(struct device *dev, void *data) 226 { 227 return dev->of_node == data; 228 } 229 230 int tilcdc_get_external_components(struct device *dev, 231 struct component_match **match) 232 { 233 struct device_node *node; 234 235 node = of_graph_get_remote_node(dev->of_node, 0, 0); 236 237 if (!of_device_is_compatible(node, "nxp,tda998x")) { 238 of_node_put(node); 239 return 0; 240 } 241 242 if (match) 243 drm_of_component_match_add(dev, match, dev_match_of, node); 244 of_node_put(node); 245 return 1; 246 } 247