linux/drivers/gpu/drm/sun4i/sun4i_lvds.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2017 Free Electrons
   4 * Maxime Ripard <maxime.ripard@free-electrons.com>
   5 */
   6
   7#include <linux/clk.h>
   8
   9#include <drm/drmP.h>
  10#include <drm/drm_atomic_helper.h>
  11#include <drm/drm_crtc_helper.h>
  12#include <drm/drm_of.h>
  13#include <drm/drm_panel.h>
  14
  15#include "sun4i_crtc.h"
  16#include "sun4i_tcon.h"
  17#include "sun4i_lvds.h"
  18
  19struct sun4i_lvds {
  20        struct drm_connector    connector;
  21        struct drm_encoder      encoder;
  22
  23        struct sun4i_tcon       *tcon;
  24};
  25
  26static inline struct sun4i_lvds *
  27drm_connector_to_sun4i_lvds(struct drm_connector *connector)
  28{
  29        return container_of(connector, struct sun4i_lvds,
  30                            connector);
  31}
  32
  33static inline struct sun4i_lvds *
  34drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
  35{
  36        return container_of(encoder, struct sun4i_lvds,
  37                            encoder);
  38}
  39
  40static int sun4i_lvds_get_modes(struct drm_connector *connector)
  41{
  42        struct sun4i_lvds *lvds =
  43                drm_connector_to_sun4i_lvds(connector);
  44        struct sun4i_tcon *tcon = lvds->tcon;
  45
  46        return drm_panel_get_modes(tcon->panel);
  47}
  48
  49static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
  50        .get_modes      = sun4i_lvds_get_modes,
  51};
  52
  53static void
  54sun4i_lvds_connector_destroy(struct drm_connector *connector)
  55{
  56        struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
  57        struct sun4i_tcon *tcon = lvds->tcon;
  58
  59        drm_panel_detach(tcon->panel);
  60        drm_connector_cleanup(connector);
  61}
  62
  63static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
  64        .fill_modes             = drm_helper_probe_single_connector_modes,
  65        .destroy                = sun4i_lvds_connector_destroy,
  66        .reset                  = drm_atomic_helper_connector_reset,
  67        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
  68        .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
  69};
  70
  71static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
  72{
  73        struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
  74        struct sun4i_tcon *tcon = lvds->tcon;
  75
  76        DRM_DEBUG_DRIVER("Enabling LVDS output\n");
  77
  78        if (!IS_ERR(tcon->panel)) {
  79                drm_panel_prepare(tcon->panel);
  80                drm_panel_enable(tcon->panel);
  81        }
  82}
  83
  84static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
  85{
  86        struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
  87        struct sun4i_tcon *tcon = lvds->tcon;
  88
  89        DRM_DEBUG_DRIVER("Disabling LVDS output\n");
  90
  91        if (!IS_ERR(tcon->panel)) {
  92                drm_panel_disable(tcon->panel);
  93                drm_panel_unprepare(tcon->panel);
  94        }
  95}
  96
  97static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
  98        .disable        = sun4i_lvds_encoder_disable,
  99        .enable         = sun4i_lvds_encoder_enable,
 100};
 101
 102static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
 103        .destroy        = drm_encoder_cleanup,
 104};
 105
 106int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
 107{
 108        struct drm_encoder *encoder;
 109        struct drm_bridge *bridge;
 110        struct sun4i_lvds *lvds;
 111        int ret;
 112
 113        lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
 114        if (!lvds)
 115                return -ENOMEM;
 116        lvds->tcon = tcon;
 117        encoder = &lvds->encoder;
 118
 119        ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
 120                                          &tcon->panel, &bridge);
 121        if (ret) {
 122                dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
 123                return 0;
 124        }
 125
 126        drm_encoder_helper_add(&lvds->encoder,
 127                               &sun4i_lvds_enc_helper_funcs);
 128        ret = drm_encoder_init(drm,
 129                               &lvds->encoder,
 130                               &sun4i_lvds_enc_funcs,
 131                               DRM_MODE_ENCODER_LVDS,
 132                               NULL);
 133        if (ret) {
 134                dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
 135                goto err_out;
 136        }
 137
 138        /* The LVDS encoder can only work with the TCON channel 0 */
 139        lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc));
 140
 141        if (tcon->panel) {
 142                drm_connector_helper_add(&lvds->connector,
 143                                         &sun4i_lvds_con_helper_funcs);
 144                ret = drm_connector_init(drm, &lvds->connector,
 145                                         &sun4i_lvds_con_funcs,
 146                                         DRM_MODE_CONNECTOR_LVDS);
 147                if (ret) {
 148                        dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
 149                        goto err_cleanup_connector;
 150                }
 151
 152                drm_mode_connector_attach_encoder(&lvds->connector,
 153                                                  &lvds->encoder);
 154
 155                ret = drm_panel_attach(tcon->panel, &lvds->connector);
 156                if (ret) {
 157                        dev_err(drm->dev, "Couldn't attach our panel\n");
 158                        goto err_cleanup_connector;
 159                }
 160        }
 161
 162        if (bridge) {
 163                ret = drm_bridge_attach(encoder, bridge, NULL);
 164                if (ret) {
 165                        dev_err(drm->dev, "Couldn't attach our bridge\n");
 166                        goto err_cleanup_connector;
 167                }
 168        }
 169
 170        return 0;
 171
 172err_cleanup_connector:
 173        drm_encoder_cleanup(&lvds->encoder);
 174err_out:
 175        return ret;
 176}
 177EXPORT_SYMBOL(sun4i_lvds_init);
 178