1/* 2 * drivers/gpu/drm/omapdrm/omap_connector.c 3 * 4 * Copyright (C) 2011 Texas Instruments 5 * Author: Rob Clark <rob@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "omap_drv.h" 21 22#include "drm_crtc.h" 23#include "drm_crtc_helper.h" 24 25/* 26 * connector funcs 27 */ 28 29#define to_omap_connector(x) container_of(x, struct omap_connector, base) 30 31struct omap_connector { 32 struct drm_connector base; 33 struct omap_dss_device *dssdev; 34 struct drm_encoder *encoder; 35 bool hdmi_mode; 36}; 37 38bool omap_connector_get_hdmi_mode(struct drm_connector *connector) 39{ 40 struct omap_connector *omap_connector = to_omap_connector(connector); 41 42 return omap_connector->hdmi_mode; 43} 44 45void copy_timings_omap_to_drm(struct drm_display_mode *mode, 46 struct omap_video_timings *timings) 47{ 48 mode->clock = timings->pixelclock / 1000; 49 50 mode->hdisplay = timings->x_res; 51 mode->hsync_start = mode->hdisplay + timings->hfp; 52 mode->hsync_end = mode->hsync_start + timings->hsw; 53 mode->htotal = mode->hsync_end + timings->hbp; 54 55 mode->vdisplay = timings->y_res; 56 mode->vsync_start = mode->vdisplay + timings->vfp; 57 mode->vsync_end = mode->vsync_start + timings->vsw; 58 mode->vtotal = mode->vsync_end + timings->vbp; 59 60 mode->flags = 0; 61 62 if (timings->interlace) 63 mode->flags |= DRM_MODE_FLAG_INTERLACE; 64 65 if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) 66 mode->flags |= DRM_MODE_FLAG_PHSYNC; 67 else 68 mode->flags |= DRM_MODE_FLAG_NHSYNC; 69 70 if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) 71 mode->flags |= DRM_MODE_FLAG_PVSYNC; 72 else 73 mode->flags |= DRM_MODE_FLAG_NVSYNC; 74} 75 76void copy_timings_drm_to_omap(struct omap_video_timings *timings, 77 struct drm_display_mode *mode) 78{ 79 timings->pixelclock = mode->clock * 1000; 80 81 timings->x_res = mode->hdisplay; 82 timings->hfp = mode->hsync_start - mode->hdisplay; 83 timings->hsw = mode->hsync_end - mode->hsync_start; 84 timings->hbp = mode->htotal - mode->hsync_end; 85 86 timings->y_res = mode->vdisplay; 87 timings->vfp = mode->vsync_start - mode->vdisplay; 88 timings->vsw = mode->vsync_end - mode->vsync_start; 89 timings->vbp = mode->vtotal - mode->vsync_end; 90 91 timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 92 93 if (mode->flags & DRM_MODE_FLAG_PHSYNC) 94 timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 95 else 96 timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW; 97 98 if (mode->flags & DRM_MODE_FLAG_PVSYNC) 99 timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 100 else 101 timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW; 102 103 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 104 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; 105 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; 106} 107 108static enum drm_connector_status omap_connector_detect( 109 struct drm_connector *connector, bool force) 110{ 111 struct omap_connector *omap_connector = to_omap_connector(connector); 112 struct omap_dss_device *dssdev = omap_connector->dssdev; 113 struct omap_dss_driver *dssdrv = dssdev->driver; 114 enum drm_connector_status ret; 115 116 if (dssdrv->detect) { 117 if (dssdrv->detect(dssdev)) 118 ret = connector_status_connected; 119 else 120 ret = connector_status_disconnected; 121 } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI || 122 dssdev->type == OMAP_DISPLAY_TYPE_DBI || 123 dssdev->type == OMAP_DISPLAY_TYPE_SDI || 124 dssdev->type == OMAP_DISPLAY_TYPE_DSI) { 125 ret = connector_status_connected; 126 } else { 127 ret = connector_status_unknown; 128 } 129 130 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); 131 132 return ret; 133} 134 135static void omap_connector_destroy(struct drm_connector *connector) 136{ 137 struct omap_connector *omap_connector = to_omap_connector(connector); 138 struct omap_dss_device *dssdev = omap_connector->dssdev; 139 140 DBG("%s", omap_connector->dssdev->name); 141 drm_connector_unregister(connector); 142 drm_connector_cleanup(connector); 143 kfree(omap_connector); 144 145 omap_dss_put_device(dssdev); 146} 147 148#define MAX_EDID 512 149 150static int omap_connector_get_modes(struct drm_connector *connector) 151{ 152 struct omap_connector *omap_connector = to_omap_connector(connector); 153 struct omap_dss_device *dssdev = omap_connector->dssdev; 154 struct omap_dss_driver *dssdrv = dssdev->driver; 155 struct drm_device *dev = connector->dev; 156 int n = 0; 157 158 DBG("%s", omap_connector->dssdev->name); 159 160 /* if display exposes EDID, then we parse that in the normal way to 161 * build table of supported modes.. otherwise (ie. fixed resolution 162 * LCD panels) we just return a single mode corresponding to the 163 * currently configured timings: 164 */ 165 if (dssdrv->read_edid) { 166 void *edid = kzalloc(MAX_EDID, GFP_KERNEL); 167 168 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && 169 drm_edid_is_valid(edid)) { 170 drm_mode_connector_update_edid_property( 171 connector, edid); 172 n = drm_add_edid_modes(connector, edid); 173 174 omap_connector->hdmi_mode = 175 drm_detect_hdmi_monitor(edid); 176 } else { 177 drm_mode_connector_update_edid_property( 178 connector, NULL); 179 } 180 181 kfree(edid); 182 } else { 183 struct drm_display_mode *mode = drm_mode_create(dev); 184 struct omap_video_timings timings = {0}; 185 186 dssdrv->get_timings(dssdev, &timings); 187 188 copy_timings_omap_to_drm(mode, &timings); 189 190 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 191 drm_mode_set_name(mode); 192 drm_mode_probed_add(connector, mode); 193 194 n = 1; 195 } 196 197 return n; 198} 199 200static int omap_connector_mode_valid(struct drm_connector *connector, 201 struct drm_display_mode *mode) 202{ 203 struct omap_connector *omap_connector = to_omap_connector(connector); 204 struct omap_dss_device *dssdev = omap_connector->dssdev; 205 struct omap_dss_driver *dssdrv = dssdev->driver; 206 struct omap_video_timings timings = {0}; 207 struct drm_device *dev = connector->dev; 208 struct drm_display_mode *new_mode; 209 int r, ret = MODE_BAD; 210 211 copy_timings_drm_to_omap(&timings, mode); 212 mode->vrefresh = drm_mode_vrefresh(mode); 213 214 /* 215 * if the panel driver doesn't have a check_timings, it's most likely 216 * a fixed resolution panel, check if the timings match with the 217 * panel's timings 218 */ 219 if (dssdrv->check_timings) { 220 r = dssdrv->check_timings(dssdev, &timings); 221 } else { 222 struct omap_video_timings t = {0}; 223 224 dssdrv->get_timings(dssdev, &t); 225 226 if (memcmp(&timings, &t, sizeof(struct omap_video_timings))) 227 r = -EINVAL; 228 else 229 r = 0; 230 } 231 232 if (!r) { 233 /* check if vrefresh is still valid */ 234 new_mode = drm_mode_duplicate(dev, mode); 235 new_mode->clock = timings.pixelclock / 1000; 236 new_mode->vrefresh = 0; 237 if (mode->vrefresh == drm_mode_vrefresh(new_mode)) 238 ret = MODE_OK; 239 drm_mode_destroy(dev, new_mode); 240 } 241 242 DBG("connector: mode %s: " 243 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", 244 (ret == MODE_OK) ? "valid" : "invalid", 245 mode->base.id, mode->name, mode->vrefresh, mode->clock, 246 mode->hdisplay, mode->hsync_start, 247 mode->hsync_end, mode->htotal, 248 mode->vdisplay, mode->vsync_start, 249 mode->vsync_end, mode->vtotal, mode->type, mode->flags); 250 251 return ret; 252} 253 254struct drm_encoder *omap_connector_attached_encoder( 255 struct drm_connector *connector) 256{ 257 struct omap_connector *omap_connector = to_omap_connector(connector); 258 return omap_connector->encoder; 259} 260 261static const struct drm_connector_funcs omap_connector_funcs = { 262 .dpms = drm_helper_connector_dpms, 263 .detect = omap_connector_detect, 264 .fill_modes = drm_helper_probe_single_connector_modes, 265 .destroy = omap_connector_destroy, 266}; 267 268static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { 269 .get_modes = omap_connector_get_modes, 270 .mode_valid = omap_connector_mode_valid, 271 .best_encoder = omap_connector_attached_encoder, 272}; 273 274/* initialize connector */ 275struct drm_connector *omap_connector_init(struct drm_device *dev, 276 int connector_type, struct omap_dss_device *dssdev, 277 struct drm_encoder *encoder) 278{ 279 struct drm_connector *connector = NULL; 280 struct omap_connector *omap_connector; 281 282 DBG("%s", dssdev->name); 283 284 omap_dss_get_device(dssdev); 285 286 omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); 287 if (!omap_connector) 288 goto fail; 289 290 omap_connector->dssdev = dssdev; 291 omap_connector->encoder = encoder; 292 293 connector = &omap_connector->base; 294 295 drm_connector_init(dev, connector, &omap_connector_funcs, 296 connector_type); 297 drm_connector_helper_add(connector, &omap_connector_helper_funcs); 298 299#if 0 /* enable when dss2 supports hotplug */ 300 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) 301 connector->polled = 0; 302 else 303#endif 304 connector->polled = DRM_CONNECTOR_POLL_CONNECT | 305 DRM_CONNECTOR_POLL_DISCONNECT; 306 307 connector->interlace_allowed = 1; 308 connector->doublescan_allowed = 0; 309 310 drm_connector_register(connector); 311 312 return connector; 313 314fail: 315 if (connector) 316 omap_connector_destroy(connector); 317 318 return NULL; 319} 320