1103cd8bcSJyri Sarha /* 2103cd8bcSJyri Sarha * Copyright (C) 2015 Texas Instruments 3103cd8bcSJyri Sarha * Author: Jyri Sarha <jsarha@ti.com> 4103cd8bcSJyri Sarha * 5103cd8bcSJyri Sarha * This program is free software; you can redistribute it and/or modify it 6103cd8bcSJyri Sarha * under the terms of the GNU General Public License version 2 as published by 7103cd8bcSJyri Sarha * the Free Software Foundation. 8103cd8bcSJyri Sarha * 9103cd8bcSJyri Sarha */ 10103cd8bcSJyri Sarha 11103cd8bcSJyri Sarha #include <linux/component.h> 12103cd8bcSJyri Sarha #include <linux/of_graph.h> 13103cd8bcSJyri Sarha 14103cd8bcSJyri Sarha #include "tilcdc_drv.h" 15103cd8bcSJyri Sarha #include "tilcdc_external.h" 16103cd8bcSJyri Sarha 17103cd8bcSJyri Sarha static const struct tilcdc_panel_info panel_info_tda998x = { 18103cd8bcSJyri Sarha .ac_bias = 255, 19103cd8bcSJyri Sarha .ac_bias_intrpt = 0, 20103cd8bcSJyri Sarha .dma_burst_sz = 16, 21103cd8bcSJyri Sarha .bpp = 16, 22103cd8bcSJyri Sarha .fdd = 0x80, 23103cd8bcSJyri Sarha .tft_alt_mode = 0, 24103cd8bcSJyri Sarha .invert_pxl_clk = 1, 25103cd8bcSJyri Sarha .sync_edge = 1, 26103cd8bcSJyri Sarha .sync_ctrl = 1, 27103cd8bcSJyri Sarha .raster_order = 0, 28103cd8bcSJyri Sarha }; 29103cd8bcSJyri Sarha 30103cd8bcSJyri Sarha static int tilcdc_external_mode_valid(struct drm_connector *connector, 31103cd8bcSJyri Sarha struct drm_display_mode *mode) 32103cd8bcSJyri Sarha { 33103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = connector->dev->dev_private; 34103cd8bcSJyri Sarha int ret, i; 35103cd8bcSJyri Sarha 36103cd8bcSJyri Sarha ret = tilcdc_crtc_mode_valid(priv->crtc, mode); 37103cd8bcSJyri Sarha if (ret != MODE_OK) 38103cd8bcSJyri Sarha return ret; 39103cd8bcSJyri Sarha 40103cd8bcSJyri Sarha for (i = 0; i < priv->num_connectors && 41103cd8bcSJyri Sarha priv->connectors[i] != connector; i++) 42103cd8bcSJyri Sarha ; 43103cd8bcSJyri Sarha 44103cd8bcSJyri Sarha BUG_ON(priv->connectors[i] != connector); 45103cd8bcSJyri Sarha BUG_ON(!priv->connector_funcs[i]); 46103cd8bcSJyri Sarha 47103cd8bcSJyri Sarha /* If the connector has its own mode_valid call it. */ 48103cd8bcSJyri Sarha if (!IS_ERR(priv->connector_funcs[i]) && 49103cd8bcSJyri Sarha priv->connector_funcs[i]->mode_valid) 50103cd8bcSJyri Sarha return priv->connector_funcs[i]->mode_valid(connector, mode); 51103cd8bcSJyri Sarha 52103cd8bcSJyri Sarha return MODE_OK; 53103cd8bcSJyri Sarha } 54103cd8bcSJyri Sarha 55c5665385SJyri Sarha static int tilcdc_add_external_encoder(struct drm_device *dev, 56103cd8bcSJyri Sarha struct drm_connector *connector) 57103cd8bcSJyri Sarha { 58103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = dev->dev_private; 59103cd8bcSJyri Sarha struct drm_connector_helper_funcs *connector_funcs; 60103cd8bcSJyri Sarha 61103cd8bcSJyri Sarha priv->connectors[priv->num_connectors] = connector; 62103cd8bcSJyri Sarha priv->encoders[priv->num_encoders++] = connector->encoder; 63103cd8bcSJyri Sarha 64103cd8bcSJyri Sarha /* Only tda998x is supported at the moment. */ 65103cd8bcSJyri Sarha tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); 66103cd8bcSJyri Sarha tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); 67103cd8bcSJyri Sarha 68103cd8bcSJyri Sarha connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), 69103cd8bcSJyri Sarha GFP_KERNEL); 70103cd8bcSJyri Sarha if (!connector_funcs) 71103cd8bcSJyri Sarha return -ENOMEM; 72103cd8bcSJyri Sarha 73103cd8bcSJyri Sarha /* connector->helper_private contains always struct 74103cd8bcSJyri Sarha * connector_helper_funcs pointer. For tilcdc crtc to have a 75103cd8bcSJyri Sarha * say if a specific mode is Ok, we need to install our own 76103cd8bcSJyri Sarha * helper functions. In our helper functions we copy 77103cd8bcSJyri Sarha * everything else but use our own mode_valid() (above). 78103cd8bcSJyri Sarha */ 79103cd8bcSJyri Sarha if (connector->helper_private) { 80103cd8bcSJyri Sarha priv->connector_funcs[priv->num_connectors] = 81103cd8bcSJyri Sarha connector->helper_private; 82103cd8bcSJyri Sarha *connector_funcs = *priv->connector_funcs[priv->num_connectors]; 83103cd8bcSJyri Sarha } else { 84103cd8bcSJyri Sarha priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT); 85103cd8bcSJyri Sarha } 86103cd8bcSJyri Sarha connector_funcs->mode_valid = tilcdc_external_mode_valid; 87103cd8bcSJyri Sarha drm_connector_helper_add(connector, connector_funcs); 88103cd8bcSJyri Sarha priv->num_connectors++; 89103cd8bcSJyri Sarha 90103cd8bcSJyri Sarha dev_dbg(dev->dev, "External encoder '%s' connected\n", 91103cd8bcSJyri Sarha connector->encoder->name); 92103cd8bcSJyri Sarha 93103cd8bcSJyri Sarha return 0; 94103cd8bcSJyri Sarha } 95103cd8bcSJyri Sarha 96c5665385SJyri Sarha int tilcdc_add_external_encoders(struct drm_device *dev) 97103cd8bcSJyri Sarha { 98103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = dev->dev_private; 99103cd8bcSJyri Sarha struct drm_connector *connector; 100103cd8bcSJyri Sarha int num_internal_connectors = priv->num_connectors; 101103cd8bcSJyri Sarha 102103cd8bcSJyri Sarha list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 103103cd8bcSJyri Sarha bool found = false; 104103cd8bcSJyri Sarha int i, ret; 105103cd8bcSJyri Sarha 106103cd8bcSJyri Sarha for (i = 0; i < num_internal_connectors; i++) 107103cd8bcSJyri Sarha if (connector == priv->connectors[i]) 108103cd8bcSJyri Sarha found = true; 109103cd8bcSJyri Sarha if (!found) { 110c5665385SJyri Sarha ret = tilcdc_add_external_encoder(dev, connector); 111103cd8bcSJyri Sarha if (ret) 112103cd8bcSJyri Sarha return ret; 113103cd8bcSJyri Sarha } 114103cd8bcSJyri Sarha } 115103cd8bcSJyri Sarha return 0; 116103cd8bcSJyri Sarha } 117103cd8bcSJyri Sarha 118103cd8bcSJyri Sarha void tilcdc_remove_external_encoders(struct drm_device *dev) 119103cd8bcSJyri Sarha { 120103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = dev->dev_private; 121103cd8bcSJyri Sarha int i; 122103cd8bcSJyri Sarha 123103cd8bcSJyri Sarha /* Restore the original helper functions, if any. */ 124103cd8bcSJyri Sarha for (i = 0; i < priv->num_connectors; i++) 125103cd8bcSJyri Sarha if (IS_ERR(priv->connector_funcs[i])) 126103cd8bcSJyri Sarha drm_connector_helper_add(priv->connectors[i], NULL); 127103cd8bcSJyri Sarha else if (priv->connector_funcs[i]) 128103cd8bcSJyri Sarha drm_connector_helper_add(priv->connectors[i], 129103cd8bcSJyri Sarha priv->connector_funcs[i]); 130103cd8bcSJyri Sarha } 131103cd8bcSJyri Sarha 132103cd8bcSJyri Sarha static int dev_match_of(struct device *dev, void *data) 133103cd8bcSJyri Sarha { 134103cd8bcSJyri Sarha return dev->of_node == data; 135103cd8bcSJyri Sarha } 136103cd8bcSJyri Sarha 137103cd8bcSJyri Sarha int tilcdc_get_external_components(struct device *dev, 138103cd8bcSJyri Sarha struct component_match **match) 139103cd8bcSJyri Sarha { 14010a55a18SJyri Sarha struct device_node *node; 141103cd8bcSJyri Sarha struct device_node *ep = NULL; 142103cd8bcSJyri Sarha int count = 0; 143103cd8bcSJyri Sarha 14410a55a18SJyri Sarha /* Avoid error print by of_graph_get_next_endpoint() if there 14510a55a18SJyri Sarha * is no ports present. 14610a55a18SJyri Sarha */ 14710a55a18SJyri Sarha node = of_get_child_by_name(dev->of_node, "ports"); 14810a55a18SJyri Sarha if (!node) 14910a55a18SJyri Sarha node = of_get_child_by_name(dev->of_node, "port"); 15010a55a18SJyri Sarha if (!node) 15110a55a18SJyri Sarha return 0; 15210a55a18SJyri Sarha of_node_put(node); 153103cd8bcSJyri Sarha 15410a55a18SJyri Sarha while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { 155103cd8bcSJyri Sarha node = of_graph_get_remote_port_parent(ep); 156a3479c4fSTeresa Remmet if (!node || !of_device_is_available(node)) { 157103cd8bcSJyri Sarha of_node_put(node); 158103cd8bcSJyri Sarha continue; 159103cd8bcSJyri Sarha } 160103cd8bcSJyri Sarha 161103cd8bcSJyri Sarha dev_dbg(dev, "Subdevice node '%s' found\n", node->name); 162103cd8bcSJyri Sarha if (match) 163103cd8bcSJyri Sarha component_match_add(dev, match, dev_match_of, node); 164103cd8bcSJyri Sarha of_node_put(node); 165103cd8bcSJyri Sarha count++; 166103cd8bcSJyri Sarha } 167103cd8bcSJyri Sarha 168103cd8bcSJyri Sarha if (count > 1) { 169103cd8bcSJyri Sarha dev_err(dev, "Only one external encoder is supported\n"); 170103cd8bcSJyri Sarha return -EINVAL; 171103cd8bcSJyri Sarha } 172103cd8bcSJyri Sarha 173103cd8bcSJyri Sarha return count; 174103cd8bcSJyri Sarha } 175