Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2015 Free Electrons
0004  * Copyright (C) 2015 NextThing Co
0005  *
0006  * Maxime Ripard <maxime.ripard@free-electrons.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 
0011 #include <drm/drm_atomic_helper.h>
0012 #include <drm/drm_bridge.h>
0013 #include <drm/drm_of.h>
0014 #include <drm/drm_panel.h>
0015 #include <drm/drm_print.h>
0016 #include <drm/drm_probe_helper.h>
0017 #include <drm/drm_simple_kms_helper.h>
0018 
0019 #include "sun4i_crtc.h"
0020 #include "sun4i_tcon.h"
0021 #include "sun4i_rgb.h"
0022 
0023 struct sun4i_rgb {
0024     struct drm_connector    connector;
0025     struct drm_encoder  encoder;
0026 
0027     struct sun4i_tcon   *tcon;
0028     struct drm_panel    *panel;
0029     struct drm_bridge   *bridge;
0030 };
0031 
0032 static inline struct sun4i_rgb *
0033 drm_connector_to_sun4i_rgb(struct drm_connector *connector)
0034 {
0035     return container_of(connector, struct sun4i_rgb,
0036                 connector);
0037 }
0038 
0039 static inline struct sun4i_rgb *
0040 drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
0041 {
0042     return container_of(encoder, struct sun4i_rgb,
0043                 encoder);
0044 }
0045 
0046 static int sun4i_rgb_get_modes(struct drm_connector *connector)
0047 {
0048     struct sun4i_rgb *rgb =
0049         drm_connector_to_sun4i_rgb(connector);
0050 
0051     return drm_panel_get_modes(rgb->panel, connector);
0052 }
0053 
0054 /*
0055  * VESA DMT defines a tolerance of 0.5% on the pixel clock, while the
0056  * CVT spec reuses that tolerance in its examples, so it looks to be a
0057  * good default tolerance for the EDID-based modes. Define it to 5 per
0058  * mille to avoid floating point operations.
0059  */
0060 #define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE  5
0061 
0062 static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
0063                          const struct drm_display_mode *mode)
0064 {
0065     struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(crtc);
0066     struct sun4i_tcon *tcon = rgb->tcon;
0067     u32 hsync = mode->hsync_end - mode->hsync_start;
0068     u32 vsync = mode->vsync_end - mode->vsync_start;
0069     unsigned long long rate = mode->clock * 1000;
0070     unsigned long long lowest, highest;
0071     unsigned long long rounded_rate;
0072 
0073     DRM_DEBUG_DRIVER("Validating modes...\n");
0074 
0075     if (hsync < 1)
0076         return MODE_HSYNC_NARROW;
0077 
0078     if (hsync > 0x3ff)
0079         return MODE_HSYNC_WIDE;
0080 
0081     if ((mode->hdisplay < 1) || (mode->htotal < 1))
0082         return MODE_H_ILLEGAL;
0083 
0084     if ((mode->hdisplay > 0x7ff) || (mode->htotal > 0xfff))
0085         return MODE_BAD_HVALUE;
0086 
0087     DRM_DEBUG_DRIVER("Horizontal parameters OK\n");
0088 
0089     if (vsync < 1)
0090         return MODE_VSYNC_NARROW;
0091 
0092     if (vsync > 0x3ff)
0093         return MODE_VSYNC_WIDE;
0094 
0095     if ((mode->vdisplay < 1) || (mode->vtotal < 1))
0096         return MODE_V_ILLEGAL;
0097 
0098     if ((mode->vdisplay > 0x7ff) || (mode->vtotal > 0xfff))
0099         return MODE_BAD_VVALUE;
0100 
0101     DRM_DEBUG_DRIVER("Vertical parameters OK\n");
0102 
0103     /*
0104      * TODO: We should use the struct display_timing if available
0105      * and / or trying to stretch the timings within that
0106      * tolerancy to take care of panels that we wouldn't be able
0107      * to have a exact match for.
0108      */
0109     if (rgb->panel) {
0110         DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
0111         goto out;
0112     }
0113 
0114     /*
0115      * That shouldn't ever happen unless something is really wrong, but it
0116      * doesn't harm to check.
0117      */
0118     if (!rgb->bridge)
0119         goto out;
0120 
0121     tcon->dclk_min_div = 6;
0122     tcon->dclk_max_div = 127;
0123     rounded_rate = clk_round_rate(tcon->dclk, rate);
0124 
0125     lowest = rate * (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
0126     do_div(lowest, 1000);
0127     if (rounded_rate < lowest)
0128         return MODE_CLOCK_LOW;
0129 
0130     highest = rate * (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
0131     do_div(highest, 1000);
0132     if (rounded_rate > highest)
0133         return MODE_CLOCK_HIGH;
0134 
0135 out:
0136     DRM_DEBUG_DRIVER("Clock rate OK\n");
0137 
0138     return MODE_OK;
0139 }
0140 
0141 static const struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
0142     .get_modes  = sun4i_rgb_get_modes,
0143 };
0144 
0145 static void
0146 sun4i_rgb_connector_destroy(struct drm_connector *connector)
0147 {
0148     drm_connector_cleanup(connector);
0149 }
0150 
0151 static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
0152     .fill_modes     = drm_helper_probe_single_connector_modes,
0153     .destroy        = sun4i_rgb_connector_destroy,
0154     .reset          = drm_atomic_helper_connector_reset,
0155     .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
0156     .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
0157 };
0158 
0159 static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
0160 {
0161     struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
0162 
0163     DRM_DEBUG_DRIVER("Enabling RGB output\n");
0164 
0165     if (rgb->panel) {
0166         drm_panel_prepare(rgb->panel);
0167         drm_panel_enable(rgb->panel);
0168     }
0169 }
0170 
0171 static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
0172 {
0173     struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
0174 
0175     DRM_DEBUG_DRIVER("Disabling RGB output\n");
0176 
0177     if (rgb->panel) {
0178         drm_panel_disable(rgb->panel);
0179         drm_panel_unprepare(rgb->panel);
0180     }
0181 }
0182 
0183 static const struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
0184     .disable    = sun4i_rgb_encoder_disable,
0185     .enable     = sun4i_rgb_encoder_enable,
0186     .mode_valid = sun4i_rgb_mode_valid,
0187 };
0188 
0189 int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
0190 {
0191     struct drm_encoder *encoder;
0192     struct sun4i_rgb *rgb;
0193     int ret;
0194 
0195     rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
0196     if (!rgb)
0197         return -ENOMEM;
0198     rgb->tcon = tcon;
0199     encoder = &rgb->encoder;
0200 
0201     ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
0202                       &rgb->panel, &rgb->bridge);
0203     if (ret) {
0204         dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
0205         return 0;
0206     }
0207 
0208     drm_encoder_helper_add(&rgb->encoder,
0209                    &sun4i_rgb_enc_helper_funcs);
0210     ret = drm_simple_encoder_init(drm, &rgb->encoder,
0211                       DRM_MODE_ENCODER_NONE);
0212     if (ret) {
0213         dev_err(drm->dev, "Couldn't initialise the rgb encoder\n");
0214         goto err_out;
0215     }
0216 
0217     /* The RGB encoder can only work with the TCON channel 0 */
0218     rgb->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
0219 
0220     if (rgb->panel) {
0221         drm_connector_helper_add(&rgb->connector,
0222                      &sun4i_rgb_con_helper_funcs);
0223         ret = drm_connector_init(drm, &rgb->connector,
0224                      &sun4i_rgb_con_funcs,
0225                      DRM_MODE_CONNECTOR_Unknown);
0226         if (ret) {
0227             dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
0228             goto err_cleanup_connector;
0229         }
0230 
0231         drm_connector_attach_encoder(&rgb->connector,
0232                           &rgb->encoder);
0233     }
0234 
0235     if (rgb->bridge) {
0236         ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0);
0237         if (ret)
0238             goto err_cleanup_connector;
0239     }
0240 
0241     return 0;
0242 
0243 err_cleanup_connector:
0244     drm_encoder_cleanup(&rgb->encoder);
0245 err_out:
0246     return ret;
0247 }
0248 EXPORT_SYMBOL(sun4i_rgb_init);