0001
0002
0003
0004
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
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);