0001
0002
0003
0004
0005
0006
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
0056
0057
0058
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
0105
0106
0107
0108
0109 if (rgb->panel) {
0110 DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
0111 goto out;
0112 }
0113
0114
0115
0116
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
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);