Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2017 Free Electrons
0004  * Maxime Ripard <maxime.ripard@free-electrons.com>
0005  */
0006 
0007 #include <linux/clk.h>
0008 
0009 #include <drm/drm_atomic_helper.h>
0010 #include <drm/drm_bridge.h>
0011 #include <drm/drm_of.h>
0012 #include <drm/drm_panel.h>
0013 #include <drm/drm_print.h>
0014 #include <drm/drm_probe_helper.h>
0015 #include <drm/drm_simple_kms_helper.h>
0016 
0017 #include "sun4i_crtc.h"
0018 #include "sun4i_tcon.h"
0019 #include "sun4i_lvds.h"
0020 
0021 struct sun4i_lvds {
0022     struct drm_connector    connector;
0023     struct drm_encoder  encoder;
0024 
0025     struct drm_panel    *panel;
0026 };
0027 
0028 static inline struct sun4i_lvds *
0029 drm_connector_to_sun4i_lvds(struct drm_connector *connector)
0030 {
0031     return container_of(connector, struct sun4i_lvds,
0032                 connector);
0033 }
0034 
0035 static inline struct sun4i_lvds *
0036 drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
0037 {
0038     return container_of(encoder, struct sun4i_lvds,
0039                 encoder);
0040 }
0041 
0042 static int sun4i_lvds_get_modes(struct drm_connector *connector)
0043 {
0044     struct sun4i_lvds *lvds =
0045         drm_connector_to_sun4i_lvds(connector);
0046 
0047     return drm_panel_get_modes(lvds->panel, connector);
0048 }
0049 
0050 static const struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
0051     .get_modes  = sun4i_lvds_get_modes,
0052 };
0053 
0054 static void
0055 sun4i_lvds_connector_destroy(struct drm_connector *connector)
0056 {
0057     drm_connector_cleanup(connector);
0058 }
0059 
0060 static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
0061     .fill_modes     = drm_helper_probe_single_connector_modes,
0062     .destroy        = sun4i_lvds_connector_destroy,
0063     .reset          = drm_atomic_helper_connector_reset,
0064     .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
0065     .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
0066 };
0067 
0068 static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
0069 {
0070     struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
0071 
0072     DRM_DEBUG_DRIVER("Enabling LVDS output\n");
0073 
0074     if (lvds->panel) {
0075         drm_panel_prepare(lvds->panel);
0076         drm_panel_enable(lvds->panel);
0077     }
0078 }
0079 
0080 static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
0081 {
0082     struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
0083 
0084     DRM_DEBUG_DRIVER("Disabling LVDS output\n");
0085 
0086     if (lvds->panel) {
0087         drm_panel_disable(lvds->panel);
0088         drm_panel_unprepare(lvds->panel);
0089     }
0090 }
0091 
0092 static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
0093     .disable    = sun4i_lvds_encoder_disable,
0094     .enable     = sun4i_lvds_encoder_enable,
0095 };
0096 
0097 int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
0098 {
0099     struct drm_encoder *encoder;
0100     struct drm_bridge *bridge;
0101     struct sun4i_lvds *lvds;
0102     int ret;
0103 
0104     lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
0105     if (!lvds)
0106         return -ENOMEM;
0107     encoder = &lvds->encoder;
0108 
0109     ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
0110                       &lvds->panel, &bridge);
0111     if (ret) {
0112         dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
0113         return 0;
0114     }
0115 
0116     drm_encoder_helper_add(&lvds->encoder,
0117                    &sun4i_lvds_enc_helper_funcs);
0118     ret = drm_simple_encoder_init(drm, &lvds->encoder,
0119                       DRM_MODE_ENCODER_LVDS);
0120     if (ret) {
0121         dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
0122         goto err_out;
0123     }
0124 
0125     /* The LVDS encoder can only work with the TCON channel 0 */
0126     lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
0127 
0128     if (lvds->panel) {
0129         drm_connector_helper_add(&lvds->connector,
0130                      &sun4i_lvds_con_helper_funcs);
0131         ret = drm_connector_init(drm, &lvds->connector,
0132                      &sun4i_lvds_con_funcs,
0133                      DRM_MODE_CONNECTOR_LVDS);
0134         if (ret) {
0135             dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
0136             goto err_cleanup_connector;
0137         }
0138 
0139         drm_connector_attach_encoder(&lvds->connector,
0140                           &lvds->encoder);
0141     }
0142 
0143     if (bridge) {
0144         ret = drm_bridge_attach(encoder, bridge, NULL, 0);
0145         if (ret)
0146             goto err_cleanup_connector;
0147     }
0148 
0149     return 0;
0150 
0151 err_cleanup_connector:
0152     drm_encoder_cleanup(&lvds->encoder);
0153 err_out:
0154     return ret;
0155 }
0156 EXPORT_SYMBOL(sun4i_lvds_init);