Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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