1/* 2 * Copyright (C) 2014 Red Hat 3 * Author: Rob Clark <robdclark@gmail.com> 4 * Author: Vinay Simha <vinaysimha@inforcecomputing.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published by 8 * the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19#include <linux/gpio.h> 20 21#include "mdp4_kms.h" 22 23struct mdp4_lvds_connector { 24 struct drm_connector base; 25 struct drm_encoder *encoder; 26 struct drm_panel *panel; 27}; 28#define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base) 29 30static enum drm_connector_status mdp4_lvds_connector_detect( 31 struct drm_connector *connector, bool force) 32{ 33 struct mdp4_lvds_connector *mdp4_lvds_connector = 34 to_mdp4_lvds_connector(connector); 35 36 return mdp4_lvds_connector->panel ? 37 connector_status_connected : 38 connector_status_disconnected; 39} 40 41static void mdp4_lvds_connector_destroy(struct drm_connector *connector) 42{ 43 struct mdp4_lvds_connector *mdp4_lvds_connector = 44 to_mdp4_lvds_connector(connector); 45 struct drm_panel *panel = mdp4_lvds_connector->panel; 46 47 if (panel) 48 drm_panel_detach(panel); 49 50 drm_connector_unregister(connector); 51 drm_connector_cleanup(connector); 52 53 kfree(mdp4_lvds_connector); 54} 55 56static int mdp4_lvds_connector_get_modes(struct drm_connector *connector) 57{ 58 struct mdp4_lvds_connector *mdp4_lvds_connector = 59 to_mdp4_lvds_connector(connector); 60 struct drm_panel *panel = mdp4_lvds_connector->panel; 61 int ret = 0; 62 63 if (panel) 64 ret = panel->funcs->get_modes(panel); 65 66 return ret; 67} 68 69static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector, 70 struct drm_display_mode *mode) 71{ 72 struct mdp4_lvds_connector *mdp4_lvds_connector = 73 to_mdp4_lvds_connector(connector); 74 struct drm_encoder *encoder = mdp4_lvds_connector->encoder; 75 long actual, requested; 76 77 requested = 1000 * mode->clock; 78 actual = mdp4_lcdc_round_pixclk(encoder, requested); 79 80 DBG("requested=%ld, actual=%ld", requested, actual); 81 82 if (actual != requested) 83 return MODE_CLOCK_RANGE; 84 85 return MODE_OK; 86} 87 88static struct drm_encoder * 89mdp4_lvds_connector_best_encoder(struct drm_connector *connector) 90{ 91 struct mdp4_lvds_connector *mdp4_lvds_connector = 92 to_mdp4_lvds_connector(connector); 93 return mdp4_lvds_connector->encoder; 94} 95 96static const struct drm_connector_funcs mdp4_lvds_connector_funcs = { 97 .dpms = drm_atomic_helper_connector_dpms, 98 .detect = mdp4_lvds_connector_detect, 99 .fill_modes = drm_helper_probe_single_connector_modes, 100 .destroy = mdp4_lvds_connector_destroy, 101 .reset = drm_atomic_helper_connector_reset, 102 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 103 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 104}; 105 106static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = { 107 .get_modes = mdp4_lvds_connector_get_modes, 108 .mode_valid = mdp4_lvds_connector_mode_valid, 109 .best_encoder = mdp4_lvds_connector_best_encoder, 110}; 111 112/* initialize connector */ 113struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev, 114 struct drm_panel *panel, struct drm_encoder *encoder) 115{ 116 struct drm_connector *connector = NULL; 117 struct mdp4_lvds_connector *mdp4_lvds_connector; 118 int ret; 119 120 mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL); 121 if (!mdp4_lvds_connector) { 122 ret = -ENOMEM; 123 goto fail; 124 } 125 126 mdp4_lvds_connector->encoder = encoder; 127 mdp4_lvds_connector->panel = panel; 128 129 connector = &mdp4_lvds_connector->base; 130 131 drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs, 132 DRM_MODE_CONNECTOR_LVDS); 133 drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs); 134 135 connector->polled = 0; 136 137 connector->interlace_allowed = 0; 138 connector->doublescan_allowed = 0; 139 140 drm_connector_register(connector); 141 142 drm_mode_connector_attach_encoder(connector, encoder); 143 144 if (panel) 145 drm_panel_attach(panel, connector); 146 147 return connector; 148 149fail: 150 if (connector) 151 mdp4_lvds_connector_destroy(connector); 152 153 return ERR_PTR(ret); 154} 155