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