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 55103cd8bcSJyri Sarha static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp, 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 *bpp = panel_info_tda998x.bpp; 68103cd8bcSJyri Sarha 69103cd8bcSJyri Sarha connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), 70103cd8bcSJyri Sarha GFP_KERNEL); 71103cd8bcSJyri Sarha if (!connector_funcs) 72103cd8bcSJyri Sarha return -ENOMEM; 73103cd8bcSJyri Sarha 74103cd8bcSJyri Sarha /* connector->helper_private contains always struct 75103cd8bcSJyri Sarha * connector_helper_funcs pointer. For tilcdc crtc to have a 76103cd8bcSJyri Sarha * say if a specific mode is Ok, we need to install our own 77103cd8bcSJyri Sarha * helper functions. In our helper functions we copy 78103cd8bcSJyri Sarha * everything else but use our own mode_valid() (above). 79103cd8bcSJyri Sarha */ 80103cd8bcSJyri Sarha if (connector->helper_private) { 81103cd8bcSJyri Sarha priv->connector_funcs[priv->num_connectors] = 82103cd8bcSJyri Sarha connector->helper_private; 83103cd8bcSJyri Sarha *connector_funcs = *priv->connector_funcs[priv->num_connectors]; 84103cd8bcSJyri Sarha } else { 85103cd8bcSJyri Sarha priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT); 86103cd8bcSJyri Sarha } 87103cd8bcSJyri Sarha connector_funcs->mode_valid = tilcdc_external_mode_valid; 88103cd8bcSJyri Sarha drm_connector_helper_add(connector, connector_funcs); 89103cd8bcSJyri Sarha priv->num_connectors++; 90103cd8bcSJyri Sarha 91103cd8bcSJyri Sarha dev_dbg(dev->dev, "External encoder '%s' connected\n", 92103cd8bcSJyri Sarha connector->encoder->name); 93103cd8bcSJyri Sarha 94103cd8bcSJyri Sarha return 0; 95103cd8bcSJyri Sarha } 96103cd8bcSJyri Sarha 97103cd8bcSJyri Sarha int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp) 98103cd8bcSJyri Sarha { 99103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = dev->dev_private; 100103cd8bcSJyri Sarha struct drm_connector *connector; 101103cd8bcSJyri Sarha int num_internal_connectors = priv->num_connectors; 102103cd8bcSJyri Sarha 103103cd8bcSJyri Sarha list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 104103cd8bcSJyri Sarha bool found = false; 105103cd8bcSJyri Sarha int i, ret; 106103cd8bcSJyri Sarha 107103cd8bcSJyri Sarha for (i = 0; i < num_internal_connectors; i++) 108103cd8bcSJyri Sarha if (connector == priv->connectors[i]) 109103cd8bcSJyri Sarha found = true; 110103cd8bcSJyri Sarha if (!found) { 111103cd8bcSJyri Sarha ret = tilcdc_add_external_encoder(dev, bpp, connector); 112103cd8bcSJyri Sarha if (ret) 113103cd8bcSJyri Sarha return ret; 114103cd8bcSJyri Sarha } 115103cd8bcSJyri Sarha } 116103cd8bcSJyri Sarha return 0; 117103cd8bcSJyri Sarha } 118103cd8bcSJyri Sarha 119103cd8bcSJyri Sarha void tilcdc_remove_external_encoders(struct drm_device *dev) 120103cd8bcSJyri Sarha { 121103cd8bcSJyri Sarha struct tilcdc_drm_private *priv = dev->dev_private; 122103cd8bcSJyri Sarha int i; 123103cd8bcSJyri Sarha 124103cd8bcSJyri Sarha /* Restore the original helper functions, if any. */ 125103cd8bcSJyri Sarha for (i = 0; i < priv->num_connectors; i++) 126103cd8bcSJyri Sarha if (IS_ERR(priv->connector_funcs[i])) 127103cd8bcSJyri Sarha drm_connector_helper_add(priv->connectors[i], NULL); 128103cd8bcSJyri Sarha else if (priv->connector_funcs[i]) 129103cd8bcSJyri Sarha drm_connector_helper_add(priv->connectors[i], 130103cd8bcSJyri Sarha priv->connector_funcs[i]); 131103cd8bcSJyri Sarha } 132103cd8bcSJyri Sarha 133103cd8bcSJyri Sarha static int dev_match_of(struct device *dev, void *data) 134103cd8bcSJyri Sarha { 135103cd8bcSJyri Sarha return dev->of_node == data; 136103cd8bcSJyri Sarha } 137103cd8bcSJyri Sarha 138103cd8bcSJyri Sarha int tilcdc_get_external_components(struct device *dev, 139103cd8bcSJyri Sarha struct component_match **match) 140103cd8bcSJyri Sarha { 14110a55a18SJyri Sarha struct device_node *node; 142103cd8bcSJyri Sarha struct device_node *ep = NULL; 143103cd8bcSJyri Sarha int count = 0; 144103cd8bcSJyri Sarha 14510a55a18SJyri Sarha /* Avoid error print by of_graph_get_next_endpoint() if there 14610a55a18SJyri Sarha * is no ports present. 14710a55a18SJyri Sarha */ 14810a55a18SJyri Sarha node = of_get_child_by_name(dev->of_node, "ports"); 14910a55a18SJyri Sarha if (!node) 15010a55a18SJyri Sarha node = of_get_child_by_name(dev->of_node, "port"); 15110a55a18SJyri Sarha if (!node) 15210a55a18SJyri Sarha return 0; 15310a55a18SJyri Sarha of_node_put(node); 154103cd8bcSJyri Sarha 15510a55a18SJyri Sarha while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { 156103cd8bcSJyri Sarha node = of_graph_get_remote_port_parent(ep); 157103cd8bcSJyri Sarha if (!node && !of_device_is_available(node)) { 158103cd8bcSJyri Sarha of_node_put(node); 159103cd8bcSJyri Sarha continue; 160103cd8bcSJyri Sarha } 161103cd8bcSJyri Sarha 162103cd8bcSJyri Sarha dev_dbg(dev, "Subdevice node '%s' found\n", node->name); 163103cd8bcSJyri Sarha if (match) 164103cd8bcSJyri Sarha component_match_add(dev, match, dev_match_of, node); 165103cd8bcSJyri Sarha of_node_put(node); 166103cd8bcSJyri Sarha count++; 167103cd8bcSJyri Sarha } 168103cd8bcSJyri Sarha 169103cd8bcSJyri Sarha if (count > 1) { 170103cd8bcSJyri Sarha dev_err(dev, "Only one external encoder is supported\n"); 171103cd8bcSJyri Sarha return -EINVAL; 172103cd8bcSJyri Sarha } 173103cd8bcSJyri Sarha 174103cd8bcSJyri Sarha return count; 175103cd8bcSJyri Sarha } 176