linux/drivers/gpu/drm/omapdrm/omap_encoder.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
   4 * Author: Rob Clark <rob@ti.com>
   5 */
   6
   7#include <linux/list.h>
   8
   9#include <drm/drm_crtc.h>
  10#include <drm/drm_modeset_helper_vtables.h>
  11#include <drm/drm_edid.h>
  12#include <drm/drm_panel.h>
  13
  14#include "omap_drv.h"
  15
  16/*
  17 * encoder funcs
  18 */
  19
  20#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
  21
  22/* The encoder and connector both map to same dssdev.. the encoder
  23 * handles the 'active' parts, ie. anything the modifies the state
  24 * of the hw, and the connector handles the 'read-only' parts, like
  25 * detecting connection and reading edid.
  26 */
  27struct omap_encoder {
  28        struct drm_encoder base;
  29        struct omap_dss_device *output;
  30};
  31
  32static void omap_encoder_destroy(struct drm_encoder *encoder)
  33{
  34        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
  35
  36        drm_encoder_cleanup(encoder);
  37        kfree(omap_encoder);
  38}
  39
  40static const struct drm_encoder_funcs omap_encoder_funcs = {
  41        .destroy = omap_encoder_destroy,
  42};
  43
  44static void omap_encoder_update_videomode_flags(struct videomode *vm,
  45                                                u32 bus_flags)
  46{
  47        if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW |
  48                           DISPLAY_FLAGS_DE_HIGH))) {
  49                if (bus_flags & DRM_BUS_FLAG_DE_LOW)
  50                        vm->flags |= DISPLAY_FLAGS_DE_LOW;
  51                else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
  52                        vm->flags |= DISPLAY_FLAGS_DE_HIGH;
  53        }
  54
  55        if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
  56                           DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
  57                if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
  58                        vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
  59                else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
  60                        vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
  61        }
  62
  63        if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
  64                           DISPLAY_FLAGS_SYNC_NEGEDGE))) {
  65                if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
  66                        vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
  67                else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
  68                        vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
  69        }
  70}
  71
  72static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
  73                                       struct drm_encoder *encoder,
  74                                       struct drm_display_mode *adjusted_mode)
  75{
  76        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
  77        struct omap_dss_device *dssdev = omap_encoder->output;
  78        bool hdmi_mode;
  79
  80        hdmi_mode = omap_connector_get_hdmi_mode(connector);
  81
  82        if (dssdev->ops->hdmi.set_hdmi_mode)
  83                dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
  84
  85        if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) {
  86                struct hdmi_avi_infoframe avi;
  87                int r;
  88
  89                r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector,
  90                                                             adjusted_mode);
  91                if (r == 0)
  92                        dssdev->ops->hdmi.set_infoframe(dssdev, &avi);
  93        }
  94}
  95
  96static void omap_encoder_mode_set(struct drm_encoder *encoder,
  97                                  struct drm_display_mode *mode,
  98                                  struct drm_display_mode *adjusted_mode)
  99{
 100        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
 101        struct omap_dss_device *output = omap_encoder->output;
 102        struct omap_dss_device *dssdev;
 103        struct drm_device *dev = encoder->dev;
 104        struct drm_connector *connector;
 105        struct drm_bridge *bridge;
 106        struct videomode vm = { 0 };
 107        u32 bus_flags;
 108
 109        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 110                if (connector->encoder == encoder)
 111                        break;
 112        }
 113
 114        drm_display_mode_to_videomode(adjusted_mode, &vm);
 115
 116        /*
 117         * HACK: This fixes the vm flags.
 118         * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and
 119         * they get lost when converting back and forth between struct
 120         * drm_display_mode and struct videomode. The hack below goes and
 121         * fetches the missing flags.
 122         *
 123         * A better solution is to use DRM's bus-flags through the whole driver.
 124         */
 125        for (dssdev = output; dssdev; dssdev = dssdev->next)
 126                omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
 127
 128        for (bridge = output->bridge; bridge; bridge = bridge->next) {
 129                if (!bridge->timings)
 130                        continue;
 131
 132                bus_flags = bridge->timings->input_bus_flags;
 133                omap_encoder_update_videomode_flags(&vm, bus_flags);
 134        }
 135
 136        bus_flags = connector->display_info.bus_flags;
 137        omap_encoder_update_videomode_flags(&vm, bus_flags);
 138
 139        /* Set timings for all devices in the display pipeline. */
 140        dss_mgr_set_timings(output, &vm);
 141
 142        for (dssdev = output; dssdev; dssdev = dssdev->next) {
 143                if (dssdev->ops->set_timings)
 144                        dssdev->ops->set_timings(dssdev, adjusted_mode);
 145        }
 146
 147        /* Set the HDMI mode and HDMI infoframe if applicable. */
 148        if (output->type == OMAP_DISPLAY_TYPE_HDMI)
 149                omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
 150}
 151
 152static void omap_encoder_disable(struct drm_encoder *encoder)
 153{
 154        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
 155        struct omap_dss_device *dssdev = omap_encoder->output;
 156        struct drm_device *dev = encoder->dev;
 157
 158        dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
 159
 160        /* Disable the panel if present. */
 161        if (dssdev->panel) {
 162                drm_panel_disable(dssdev->panel);
 163                drm_panel_unprepare(dssdev->panel);
 164        }
 165
 166        /*
 167         * Disable the chain of external devices, starting at the one at the
 168         * internal encoder's output.
 169         */
 170        omapdss_device_disable(dssdev->next);
 171
 172        /*
 173         * Disable the internal encoder. This will disable the DSS output. The
 174         * DSI is treated as an exception as DSI pipelines still use the legacy
 175         * flow where the pipeline output controls the encoder.
 176         */
 177        if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
 178                dssdev->ops->disable(dssdev);
 179                dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 180        }
 181
 182        /*
 183         * Perform the post-disable operations on the chain of external devices
 184         * to complete the display pipeline disable.
 185         */
 186        omapdss_device_post_disable(dssdev->next);
 187}
 188
 189static void omap_encoder_enable(struct drm_encoder *encoder)
 190{
 191        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
 192        struct omap_dss_device *dssdev = omap_encoder->output;
 193        struct drm_device *dev = encoder->dev;
 194
 195        dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
 196
 197        /* Prepare the chain of external devices for pipeline enable. */
 198        omapdss_device_pre_enable(dssdev->next);
 199
 200        /*
 201         * Enable the internal encoder. This will enable the DSS output. The
 202         * DSI is treated as an exception as DSI pipelines still use the legacy
 203         * flow where the pipeline output controls the encoder.
 204         */
 205        if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
 206                dssdev->ops->enable(dssdev);
 207                dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 208        }
 209
 210        /*
 211         * Enable the chain of external devices, starting at the one at the
 212         * internal encoder's output.
 213         */
 214        omapdss_device_enable(dssdev->next);
 215
 216        /* Enable the panel if present. */
 217        if (dssdev->panel) {
 218                drm_panel_prepare(dssdev->panel);
 219                drm_panel_enable(dssdev->panel);
 220        }
 221}
 222
 223static int omap_encoder_atomic_check(struct drm_encoder *encoder,
 224                                     struct drm_crtc_state *crtc_state,
 225                                     struct drm_connector_state *conn_state)
 226{
 227        struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
 228        enum drm_mode_status status;
 229
 230        status = omap_connector_mode_fixup(omap_encoder->output,
 231                                           &crtc_state->mode,
 232                                           &crtc_state->adjusted_mode);
 233        if (status != MODE_OK) {
 234                dev_err(encoder->dev->dev, "invalid timings: %d\n", status);
 235                return -EINVAL;
 236        }
 237
 238        return 0;
 239}
 240
 241static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
 242        .mode_set = omap_encoder_mode_set,
 243        .disable = omap_encoder_disable,
 244        .enable = omap_encoder_enable,
 245        .atomic_check = omap_encoder_atomic_check,
 246};
 247
 248/* initialize encoder */
 249struct drm_encoder *omap_encoder_init(struct drm_device *dev,
 250                                      struct omap_dss_device *output)
 251{
 252        struct drm_encoder *encoder = NULL;
 253        struct omap_encoder *omap_encoder;
 254
 255        omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
 256        if (!omap_encoder)
 257                goto fail;
 258
 259        omap_encoder->output = output;
 260
 261        encoder = &omap_encoder->base;
 262
 263        drm_encoder_init(dev, encoder, &omap_encoder_funcs,
 264                         DRM_MODE_ENCODER_TMDS, NULL);
 265        drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
 266
 267        return encoder;
 268
 269fail:
 270        if (encoder)
 271                omap_encoder_destroy(encoder);
 272
 273        return NULL;
 274}
 275