linux/drivers/gpu/drm/bridge/panel.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
   3 * Copyright (C) 2017 Broadcom
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License as
   7 * published by the Free Software Foundation; either version 2 of
   8 * the License, or (at your option) any later version.
   9 */
  10
  11#include <drm/drmP.h>
  12#include <drm/drm_panel.h>
  13#include <drm/drm_atomic_helper.h>
  14#include <drm/drm_connector.h>
  15#include <drm/drm_crtc_helper.h>
  16#include <drm/drm_encoder.h>
  17#include <drm/drm_modeset_helper_vtables.h>
  18#include <drm/drm_panel.h>
  19
  20struct panel_bridge {
  21        struct drm_bridge bridge;
  22        struct drm_connector connector;
  23        struct drm_panel *panel;
  24        u32 connector_type;
  25};
  26
  27static inline struct panel_bridge *
  28drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
  29{
  30        return container_of(bridge, struct panel_bridge, bridge);
  31}
  32
  33static inline struct panel_bridge *
  34drm_connector_to_panel_bridge(struct drm_connector *connector)
  35{
  36        return container_of(connector, struct panel_bridge, connector);
  37}
  38
  39static int panel_bridge_connector_get_modes(struct drm_connector *connector)
  40{
  41        struct panel_bridge *panel_bridge =
  42                drm_connector_to_panel_bridge(connector);
  43
  44        return drm_panel_get_modes(panel_bridge->panel);
  45}
  46
  47static const struct drm_connector_helper_funcs
  48panel_bridge_connector_helper_funcs = {
  49        .get_modes = panel_bridge_connector_get_modes,
  50};
  51
  52static const struct drm_connector_funcs panel_bridge_connector_funcs = {
  53        .reset = drm_atomic_helper_connector_reset,
  54        .fill_modes = drm_helper_probe_single_connector_modes,
  55        .destroy = drm_connector_cleanup,
  56        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
  57        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
  58};
  59
  60static int panel_bridge_attach(struct drm_bridge *bridge)
  61{
  62        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
  63        struct drm_connector *connector = &panel_bridge->connector;
  64        int ret;
  65
  66        if (!bridge->encoder) {
  67                DRM_ERROR("Missing encoder\n");
  68                return -ENODEV;
  69        }
  70
  71        drm_connector_helper_add(connector,
  72                                 &panel_bridge_connector_helper_funcs);
  73
  74        ret = drm_connector_init(bridge->dev, connector,
  75                                 &panel_bridge_connector_funcs,
  76                                 panel_bridge->connector_type);
  77        if (ret) {
  78                DRM_ERROR("Failed to initialize connector\n");
  79                return ret;
  80        }
  81
  82        drm_connector_attach_encoder(&panel_bridge->connector,
  83                                          bridge->encoder);
  84
  85        ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
  86        if (ret < 0)
  87                return ret;
  88
  89        return 0;
  90}
  91
  92static void panel_bridge_detach(struct drm_bridge *bridge)
  93{
  94        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
  95
  96        drm_panel_detach(panel_bridge->panel);
  97}
  98
  99static void panel_bridge_pre_enable(struct drm_bridge *bridge)
 100{
 101        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 102
 103        drm_panel_prepare(panel_bridge->panel);
 104}
 105
 106static void panel_bridge_enable(struct drm_bridge *bridge)
 107{
 108        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 109
 110        drm_panel_enable(panel_bridge->panel);
 111}
 112
 113static void panel_bridge_disable(struct drm_bridge *bridge)
 114{
 115        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 116
 117        drm_panel_disable(panel_bridge->panel);
 118}
 119
 120static void panel_bridge_post_disable(struct drm_bridge *bridge)
 121{
 122        struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 123
 124        drm_panel_unprepare(panel_bridge->panel);
 125}
 126
 127static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
 128        .attach = panel_bridge_attach,
 129        .detach = panel_bridge_detach,
 130        .pre_enable = panel_bridge_pre_enable,
 131        .enable = panel_bridge_enable,
 132        .disable = panel_bridge_disable,
 133        .post_disable = panel_bridge_post_disable,
 134};
 135
 136/**
 137 * drm_panel_bridge_add - Creates a drm_bridge and drm_connector that
 138 * just calls the appropriate functions from drm_panel.
 139 *
 140 * @panel: The drm_panel being wrapped.  Must be non-NULL.
 141 * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
 142 * created.
 143 *
 144 * For drivers converting from directly using drm_panel: The expected
 145 * usage pattern is that during either encoder module probe or DSI
 146 * host attach, a drm_panel will be looked up through
 147 * drm_of_find_panel_or_bridge().  drm_panel_bridge_add() is used to
 148 * wrap that panel in the new bridge, and the result can then be
 149 * passed to drm_bridge_attach().  The drm_panel_prepare() and related
 150 * functions can be dropped from the encoder driver (they're now
 151 * called by the KMS helpers before calling into the encoder), along
 152 * with connector creation.  When done with the bridge,
 153 * drm_bridge_detach() should be called as normal, then
 154 * drm_panel_bridge_remove() to free it.
 155 */
 156struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
 157                                        u32 connector_type)
 158{
 159        struct panel_bridge *panel_bridge;
 160
 161        if (!panel)
 162                return ERR_PTR(-EINVAL);
 163
 164        panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
 165                                    GFP_KERNEL);
 166        if (!panel_bridge)
 167                return ERR_PTR(-ENOMEM);
 168
 169        panel_bridge->connector_type = connector_type;
 170        panel_bridge->panel = panel;
 171
 172        panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
 173#ifdef CONFIG_OF
 174        panel_bridge->bridge.of_node = panel->dev->of_node;
 175#endif
 176
 177        drm_bridge_add(&panel_bridge->bridge);
 178
 179        return &panel_bridge->bridge;
 180}
 181EXPORT_SYMBOL(drm_panel_bridge_add);
 182
 183/**
 184 * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
 185 * created by drm_panel_bridge_add().
 186 *
 187 * @bridge: The drm_bridge being freed.
 188 */
 189void drm_panel_bridge_remove(struct drm_bridge *bridge)
 190{
 191        struct panel_bridge *panel_bridge;
 192
 193        if (!bridge)
 194                return;
 195
 196        if (bridge->funcs != &panel_bridge_bridge_funcs)
 197                return;
 198
 199        panel_bridge = drm_bridge_to_panel_bridge(bridge);
 200
 201        drm_bridge_remove(bridge);
 202        devm_kfree(panel_bridge->panel->dev, bridge);
 203}
 204EXPORT_SYMBOL(drm_panel_bridge_remove);
 205
 206static void devm_drm_panel_bridge_release(struct device *dev, void *res)
 207{
 208        struct drm_bridge **bridge = res;
 209
 210        drm_panel_bridge_remove(*bridge);
 211}
 212
 213struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
 214                                             struct drm_panel *panel,
 215                                             u32 connector_type)
 216{
 217        struct drm_bridge **ptr, *bridge;
 218
 219        ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
 220                           GFP_KERNEL);
 221        if (!ptr)
 222                return ERR_PTR(-ENOMEM);
 223
 224        bridge = drm_panel_bridge_add(panel, connector_type);
 225        if (!IS_ERR(bridge)) {
 226                *ptr = bridge;
 227                devres_add(dev, ptr);
 228        } else {
 229                devres_free(ptr);
 230        }
 231
 232        return bridge;
 233}
 234EXPORT_SYMBOL(devm_drm_panel_bridge_add);
 235