linux/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
<<
>>
Prefs
   1/*
   2 * rcar_du_encoder.c  --  R-Car Display Unit Encoder
   3 *
   4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/export.h>
  15
  16#include <drm/drmP.h>
  17#include <drm/drm_crtc.h>
  18#include <drm/drm_crtc_helper.h>
  19#include <drm/drm_panel.h>
  20
  21#include "rcar_du_drv.h"
  22#include "rcar_du_encoder.h"
  23#include "rcar_du_kms.h"
  24#include "rcar_du_lvdscon.h"
  25#include "rcar_du_lvdsenc.h"
  26
  27/* -----------------------------------------------------------------------------
  28 * Encoder
  29 */
  30
  31static void rcar_du_encoder_disable(struct drm_encoder *encoder)
  32{
  33        struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
  34
  35        if (renc->connector && renc->connector->panel) {
  36                drm_panel_disable(renc->connector->panel);
  37                drm_panel_unprepare(renc->connector->panel);
  38        }
  39
  40        if (renc->lvds)
  41                rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
  42}
  43
  44static void rcar_du_encoder_enable(struct drm_encoder *encoder)
  45{
  46        struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
  47
  48        if (renc->lvds)
  49                rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
  50
  51        if (renc->connector && renc->connector->panel) {
  52                drm_panel_prepare(renc->connector->panel);
  53                drm_panel_enable(renc->connector->panel);
  54        }
  55}
  56
  57static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
  58                                        struct drm_crtc_state *crtc_state,
  59                                        struct drm_connector_state *conn_state)
  60{
  61        struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
  62        struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
  63        const struct drm_display_mode *mode = &crtc_state->mode;
  64        struct drm_connector *connector = conn_state->connector;
  65        struct drm_device *dev = encoder->dev;
  66
  67        /*
  68         * Only panel-related encoder types require validation here, everything
  69         * else is handled by the bridge drivers.
  70         */
  71        if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
  72                const struct drm_display_mode *panel_mode;
  73
  74                if (list_empty(&connector->modes)) {
  75                        dev_dbg(dev->dev, "encoder: empty modes list\n");
  76                        return -EINVAL;
  77                }
  78
  79                panel_mode = list_first_entry(&connector->modes,
  80                                              struct drm_display_mode, head);
  81
  82                /* We're not allowed to modify the resolution. */
  83                if (mode->hdisplay != panel_mode->hdisplay ||
  84                    mode->vdisplay != panel_mode->vdisplay)
  85                        return -EINVAL;
  86
  87                /*
  88                 * The flat panel mode is fixed, just copy it to the adjusted
  89                 * mode.
  90                 */
  91                drm_mode_copy(adjusted_mode, panel_mode);
  92        }
  93
  94        if (renc->lvds)
  95                rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode);
  96
  97        return 0;
  98}
  99
 100static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
 101                                     struct drm_crtc_state *crtc_state,
 102                                     struct drm_connector_state *conn_state)
 103{
 104        struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
 105        struct drm_display_info *info = &conn_state->connector->display_info;
 106        enum rcar_lvds_mode mode;
 107
 108        rcar_du_crtc_route_output(crtc_state->crtc, renc->output);
 109
 110        if (!renc->lvds) {
 111                /*
 112                 * The DU driver creates connectors only for the outputs of the
 113                 * internal LVDS encoders.
 114                 */
 115                renc->connector = NULL;
 116                return;
 117        }
 118
 119        renc->connector = to_rcar_connector(conn_state->connector);
 120
 121        if (!info->num_bus_formats || !info->bus_formats) {
 122                dev_err(encoder->dev->dev, "no LVDS bus format reported\n");
 123                return;
 124        }
 125
 126        switch (info->bus_formats[0]) {
 127        case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
 128        case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
 129                mode = RCAR_LVDS_MODE_JEIDA;
 130                break;
 131        case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
 132                mode = RCAR_LVDS_MODE_VESA;
 133                break;
 134        default:
 135                dev_err(encoder->dev->dev,
 136                        "unsupported LVDS bus format 0x%04x\n",
 137                        info->bus_formats[0]);
 138                return;
 139        }
 140
 141        if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
 142                mode |= RCAR_LVDS_MODE_MIRROR;
 143
 144        rcar_du_lvdsenc_set_mode(renc->lvds, mode);
 145}
 146
 147static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
 148        .atomic_mode_set = rcar_du_encoder_mode_set,
 149        .disable = rcar_du_encoder_disable,
 150        .enable = rcar_du_encoder_enable,
 151        .atomic_check = rcar_du_encoder_atomic_check,
 152};
 153
 154static const struct drm_encoder_funcs encoder_funcs = {
 155        .destroy = drm_encoder_cleanup,
 156};
 157
 158int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 159                         enum rcar_du_output output,
 160                         struct device_node *enc_node,
 161                         struct device_node *con_node)
 162{
 163        struct rcar_du_encoder *renc;
 164        struct drm_encoder *encoder;
 165        struct drm_bridge *bridge = NULL;
 166        int ret;
 167
 168        renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
 169        if (renc == NULL)
 170                return -ENOMEM;
 171
 172        renc->output = output;
 173        encoder = rcar_encoder_to_drm_encoder(renc);
 174
 175        switch (output) {
 176        case RCAR_DU_OUTPUT_LVDS0:
 177                renc->lvds = rcdu->lvds[0];
 178                break;
 179
 180        case RCAR_DU_OUTPUT_LVDS1:
 181                renc->lvds = rcdu->lvds[1];
 182                break;
 183
 184        default:
 185                break;
 186        }
 187
 188        if (enc_node) {
 189                dev_dbg(rcdu->dev, "initializing encoder %s for output %u\n",
 190                        of_node_full_name(enc_node), output);
 191
 192                /* Locate the DRM bridge from the encoder DT node. */
 193                bridge = of_drm_find_bridge(enc_node);
 194                if (!bridge) {
 195                        ret = -EPROBE_DEFER;
 196                        goto done;
 197                }
 198        } else {
 199                dev_dbg(rcdu->dev,
 200                        "initializing internal encoder for output %u\n",
 201                        output);
 202        }
 203
 204        ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
 205                               DRM_MODE_ENCODER_NONE, NULL);
 206        if (ret < 0)
 207                goto done;
 208
 209        drm_encoder_helper_add(encoder, &encoder_helper_funcs);
 210
 211        if (bridge) {
 212                /*
 213                 * Attach the bridge to the encoder. The bridge will create the
 214                 * connector.
 215                 */
 216                ret = drm_bridge_attach(encoder, bridge, NULL);
 217                if (ret) {
 218                        drm_encoder_cleanup(encoder);
 219                        return ret;
 220                }
 221        } else {
 222                /* There's no bridge, create the connector manually. */
 223                switch (output) {
 224                case RCAR_DU_OUTPUT_LVDS0:
 225                case RCAR_DU_OUTPUT_LVDS1:
 226                        ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
 227                        break;
 228
 229                default:
 230                        ret = -EINVAL;
 231                        break;
 232                }
 233        }
 234
 235done:
 236        if (ret < 0) {
 237                if (encoder->name)
 238                        encoder->funcs->destroy(encoder);
 239                devm_kfree(rcdu->dev, renc);
 240        }
 241
 242        return ret;
 243}
 244