linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Traphandler
   4 * Copyright (C) 2014 Free Electrons
   5 * Copyright (C) 2014 Atmel
   6 *
   7 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
   8 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   9 */
  10
  11#include <linux/media-bus-format.h>
  12#include <linux/of_graph.h>
  13
  14#include <drm/drm_bridge.h>
  15#include <drm/drm_encoder.h>
  16#include <drm/drm_of.h>
  17#include <drm/drm_simple_kms_helper.h>
  18
  19#include "atmel_hlcdc_dc.h"
  20
  21struct atmel_hlcdc_rgb_output {
  22        struct drm_encoder encoder;
  23        int bus_fmt;
  24};
  25
  26static struct atmel_hlcdc_rgb_output *
  27atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)
  28{
  29        return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
  30}
  31
  32int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)
  33{
  34        struct atmel_hlcdc_rgb_output *output;
  35
  36        output = atmel_hlcdc_encoder_to_rgb_output(encoder);
  37
  38        return output->bus_fmt;
  39}
  40
  41static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)
  42{
  43        u32 bus_width;
  44        int ret;
  45
  46        ret = of_property_read_u32(ep, "bus-width", &bus_width);
  47        if (ret == -EINVAL)
  48                return 0;
  49        if (ret)
  50                return ret;
  51
  52        switch (bus_width) {
  53        case 12:
  54                return MEDIA_BUS_FMT_RGB444_1X12;
  55        case 16:
  56                return MEDIA_BUS_FMT_RGB565_1X16;
  57        case 18:
  58                return MEDIA_BUS_FMT_RGB666_1X18;
  59        case 24:
  60                return MEDIA_BUS_FMT_RGB888_1X24;
  61        default:
  62                return -EINVAL;
  63        }
  64}
  65
  66static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
  67{
  68        struct atmel_hlcdc_rgb_output *output;
  69        struct device_node *ep;
  70        struct drm_panel *panel;
  71        struct drm_bridge *bridge;
  72        int ret;
  73
  74        ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);
  75        if (!ep)
  76                return -ENODEV;
  77
  78        ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,
  79                                          &panel, &bridge);
  80        if (ret) {
  81                of_node_put(ep);
  82                return ret;
  83        }
  84
  85        output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
  86        if (!output) {
  87                of_node_put(ep);
  88                return -ENOMEM;
  89        }
  90
  91        output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
  92        of_node_put(ep);
  93        if (output->bus_fmt < 0) {
  94                dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);
  95                return -EINVAL;
  96        }
  97
  98        ret = drm_simple_encoder_init(dev, &output->encoder,
  99                                      DRM_MODE_ENCODER_NONE);
 100        if (ret)
 101                return ret;
 102
 103        output->encoder.possible_crtcs = 0x1;
 104
 105        if (panel) {
 106                bridge = drm_panel_bridge_add_typed(panel,
 107                                                    DRM_MODE_CONNECTOR_Unknown);
 108                if (IS_ERR(bridge))
 109                        return PTR_ERR(bridge);
 110        }
 111
 112        if (bridge) {
 113                ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);
 114                if (!ret)
 115                        return 0;
 116
 117                if (panel)
 118                        drm_panel_bridge_remove(bridge);
 119        }
 120
 121        drm_encoder_cleanup(&output->encoder);
 122
 123        return ret;
 124}
 125
 126int atmel_hlcdc_create_outputs(struct drm_device *dev)
 127{
 128        int endpoint, ret = 0;
 129        int attached = 0;
 130
 131        /*
 132         * Always scan the first few endpoints even if we get -ENODEV,
 133         * but keep going after that as long as we keep getting hits.
 134         */
 135        for (endpoint = 0; !ret || endpoint < 4; endpoint++) {
 136                ret = atmel_hlcdc_attach_endpoint(dev, endpoint);
 137                if (ret == -ENODEV)
 138                        continue;
 139                if (ret)
 140                        break;
 141                attached++;
 142        }
 143
 144        /* At least one device was successfully attached.*/
 145        if (ret == -ENODEV && attached)
 146                return 0;
 147
 148        return ret;
 149}
 150