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