linux/drivers/gpu/drm/tilcdc/tilcdc_external.c
<<
>>
Prefs
   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
  14#include "tilcdc_drv.h"
  15#include "tilcdc_external.h"
  16
  17static const struct tilcdc_panel_info panel_info_tda998x = {
  18                .ac_bias                = 255,
  19                .ac_bias_intrpt         = 0,
  20                .dma_burst_sz           = 16,
  21                .bpp                    = 16,
  22                .fdd                    = 0x80,
  23                .tft_alt_mode           = 0,
  24                .invert_pxl_clk         = 1,
  25                .sync_edge              = 1,
  26                .sync_ctrl              = 1,
  27                .raster_order           = 0,
  28};
  29
  30static int tilcdc_external_mode_valid(struct drm_connector *connector,
  31                                      struct drm_display_mode *mode)
  32{
  33        struct tilcdc_drm_private *priv = connector->dev->dev_private;
  34        int ret, i;
  35
  36        ret = tilcdc_crtc_mode_valid(priv->crtc, mode);
  37        if (ret != MODE_OK)
  38                return ret;
  39
  40        for (i = 0; i < priv->num_connectors &&
  41                     priv->connectors[i] != connector; i++)
  42                ;
  43
  44        BUG_ON(priv->connectors[i] != connector);
  45        BUG_ON(!priv->connector_funcs[i]);
  46
  47        /* If the connector has its own mode_valid call it. */
  48        if (!IS_ERR(priv->connector_funcs[i]) &&
  49            priv->connector_funcs[i]->mode_valid)
  50                return priv->connector_funcs[i]->mode_valid(connector, mode);
  51
  52        return MODE_OK;
  53}
  54
  55static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp,
  56                                       struct drm_connector *connector)
  57{
  58        struct tilcdc_drm_private *priv = dev->dev_private;
  59        struct drm_connector_helper_funcs *connector_funcs;
  60
  61        priv->connectors[priv->num_connectors] = connector;
  62        priv->encoders[priv->num_encoders++] = connector->encoder;
  63
  64        /* Only tda998x is supported at the moment. */
  65        tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true);
  66        tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x);
  67        *bpp = panel_info_tda998x.bpp;
  68
  69        connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs),
  70                                       GFP_KERNEL);
  71        if (!connector_funcs)
  72                return -ENOMEM;
  73
  74        /* connector->helper_private contains always struct
  75         * connector_helper_funcs pointer. For tilcdc crtc to have a
  76         * say if a specific mode is Ok, we need to install our own
  77         * helper functions. In our helper functions we copy
  78         * everything else but use our own mode_valid() (above).
  79         */
  80        if (connector->helper_private) {
  81                priv->connector_funcs[priv->num_connectors] =
  82                        connector->helper_private;
  83                *connector_funcs = *priv->connector_funcs[priv->num_connectors];
  84        } else {
  85                priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT);
  86        }
  87        connector_funcs->mode_valid = tilcdc_external_mode_valid;
  88        drm_connector_helper_add(connector, connector_funcs);
  89        priv->num_connectors++;
  90
  91        dev_dbg(dev->dev, "External encoder '%s' connected\n",
  92                connector->encoder->name);
  93
  94        return 0;
  95}
  96
  97int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp)
  98{
  99        struct tilcdc_drm_private *priv = dev->dev_private;
 100        struct drm_connector *connector;
 101        int num_internal_connectors = priv->num_connectors;
 102
 103        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 104                bool found = false;
 105                int i, ret;
 106
 107                for (i = 0; i < num_internal_connectors; i++)
 108                        if (connector == priv->connectors[i])
 109                                found = true;
 110                if (!found) {
 111                        ret = tilcdc_add_external_encoder(dev, bpp, connector);
 112                        if (ret)
 113                                return ret;
 114                }
 115        }
 116        return 0;
 117}
 118
 119void tilcdc_remove_external_encoders(struct drm_device *dev)
 120{
 121        struct tilcdc_drm_private *priv = dev->dev_private;
 122        int i;
 123
 124        /* Restore the original helper functions, if any. */
 125        for (i = 0; i < priv->num_connectors; i++)
 126                if (IS_ERR(priv->connector_funcs[i]))
 127                        drm_connector_helper_add(priv->connectors[i], NULL);
 128                else if (priv->connector_funcs[i])
 129                        drm_connector_helper_add(priv->connectors[i],
 130                                                 priv->connector_funcs[i]);
 131}
 132
 133static int dev_match_of(struct device *dev, void *data)
 134{
 135        return dev->of_node == data;
 136}
 137
 138int tilcdc_get_external_components(struct device *dev,
 139                                   struct component_match **match)
 140{
 141        struct device_node *ep = NULL;
 142        int count = 0;
 143
 144        while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
 145                struct device_node *node;
 146
 147                node = of_graph_get_remote_port_parent(ep);
 148                if (!node && !of_device_is_available(node)) {
 149                        of_node_put(node);
 150                        continue;
 151                }
 152
 153                dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
 154                if (match)
 155                        component_match_add(dev, match, dev_match_of, node);
 156                of_node_put(node);
 157                count++;
 158        }
 159
 160        if (count > 1) {
 161                dev_err(dev, "Only one external encoder is supported\n");
 162                return -EINVAL;
 163        }
 164
 165        return count;
 166}
 167