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 v3.13-rc8 319 lines 9.2 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 "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}; 36 37void copy_timings_omap_to_drm(struct drm_display_mode *mode, 38 struct omap_video_timings *timings) 39{ 40 mode->clock = timings->pixel_clock; 41 42 mode->hdisplay = timings->x_res; 43 mode->hsync_start = mode->hdisplay + timings->hfp; 44 mode->hsync_end = mode->hsync_start + timings->hsw; 45 mode->htotal = mode->hsync_end + timings->hbp; 46 47 mode->vdisplay = timings->y_res; 48 mode->vsync_start = mode->vdisplay + timings->vfp; 49 mode->vsync_end = mode->vsync_start + timings->vsw; 50 mode->vtotal = mode->vsync_end + timings->vbp; 51 52 mode->flags = 0; 53 54 if (timings->interlace) 55 mode->flags |= DRM_MODE_FLAG_INTERLACE; 56 57 if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) 58 mode->flags |= DRM_MODE_FLAG_PHSYNC; 59 else 60 mode->flags |= DRM_MODE_FLAG_NHSYNC; 61 62 if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) 63 mode->flags |= DRM_MODE_FLAG_PVSYNC; 64 else 65 mode->flags |= DRM_MODE_FLAG_NVSYNC; 66} 67 68void copy_timings_drm_to_omap(struct omap_video_timings *timings, 69 struct drm_display_mode *mode) 70{ 71 timings->pixel_clock = mode->clock; 72 73 timings->x_res = mode->hdisplay; 74 timings->hfp = mode->hsync_start - mode->hdisplay; 75 timings->hsw = mode->hsync_end - mode->hsync_start; 76 timings->hbp = mode->htotal - mode->hsync_end; 77 78 timings->y_res = mode->vdisplay; 79 timings->vfp = mode->vsync_start - mode->vdisplay; 80 timings->vsw = mode->vsync_end - mode->vsync_start; 81 timings->vbp = mode->vtotal - mode->vsync_end; 82 83 timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 84 85 if (mode->flags & DRM_MODE_FLAG_PHSYNC) 86 timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 87 else 88 timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW; 89 90 if (mode->flags & DRM_MODE_FLAG_PVSYNC) 91 timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 92 else 93 timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW; 94 95 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 96 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; 97 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; 98} 99 100static enum drm_connector_status omap_connector_detect( 101 struct drm_connector *connector, bool force) 102{ 103 struct omap_connector *omap_connector = to_omap_connector(connector); 104 struct omap_dss_device *dssdev = omap_connector->dssdev; 105 struct omap_dss_driver *dssdrv = dssdev->driver; 106 enum drm_connector_status ret; 107 108 if (dssdrv->detect) { 109 if (dssdrv->detect(dssdev)) 110 ret = connector_status_connected; 111 else 112 ret = connector_status_disconnected; 113 } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI || 114 dssdev->type == OMAP_DISPLAY_TYPE_DBI || 115 dssdev->type == OMAP_DISPLAY_TYPE_SDI || 116 dssdev->type == OMAP_DISPLAY_TYPE_DSI) { 117 ret = connector_status_connected; 118 } else { 119 ret = connector_status_unknown; 120 } 121 122 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); 123 124 return ret; 125} 126 127static void omap_connector_destroy(struct drm_connector *connector) 128{ 129 struct omap_connector *omap_connector = to_omap_connector(connector); 130 struct omap_dss_device *dssdev = omap_connector->dssdev; 131 132 DBG("%s", omap_connector->dssdev->name); 133 drm_sysfs_connector_remove(connector); 134 drm_connector_cleanup(connector); 135 kfree(omap_connector); 136 137 omap_dss_put_device(dssdev); 138} 139 140#define MAX_EDID 512 141 142static int omap_connector_get_modes(struct drm_connector *connector) 143{ 144 struct omap_connector *omap_connector = to_omap_connector(connector); 145 struct omap_dss_device *dssdev = omap_connector->dssdev; 146 struct omap_dss_driver *dssdrv = dssdev->driver; 147 struct drm_device *dev = connector->dev; 148 int n = 0; 149 150 DBG("%s", omap_connector->dssdev->name); 151 152 /* if display exposes EDID, then we parse that in the normal way to 153 * build table of supported modes.. otherwise (ie. fixed resolution 154 * LCD panels) we just return a single mode corresponding to the 155 * currently configured timings: 156 */ 157 if (dssdrv->read_edid) { 158 void *edid = kzalloc(MAX_EDID, GFP_KERNEL); 159 160 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && 161 drm_edid_is_valid(edid)) { 162 drm_mode_connector_update_edid_property( 163 connector, edid); 164 n = drm_add_edid_modes(connector, edid); 165 } else { 166 drm_mode_connector_update_edid_property( 167 connector, NULL); 168 } 169 kfree(edid); 170 } else { 171 struct drm_display_mode *mode = drm_mode_create(dev); 172 struct omap_video_timings timings = {0}; 173 174 dssdrv->get_timings(dssdev, &timings); 175 176 copy_timings_omap_to_drm(mode, &timings); 177 178 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 179 drm_mode_set_name(mode); 180 drm_mode_probed_add(connector, mode); 181 182 n = 1; 183 } 184 185 return n; 186} 187 188static int omap_connector_mode_valid(struct drm_connector *connector, 189 struct drm_display_mode *mode) 190{ 191 struct omap_connector *omap_connector = to_omap_connector(connector); 192 struct omap_dss_device *dssdev = omap_connector->dssdev; 193 struct omap_dss_driver *dssdrv = dssdev->driver; 194 struct omap_video_timings timings = {0}; 195 struct drm_device *dev = connector->dev; 196 struct drm_display_mode *new_mode; 197 int r, ret = MODE_BAD; 198 199 copy_timings_drm_to_omap(&timings, mode); 200 mode->vrefresh = drm_mode_vrefresh(mode); 201 202 /* 203 * if the panel driver doesn't have a check_timings, it's most likely 204 * a fixed resolution panel, check if the timings match with the 205 * panel's timings 206 */ 207 if (dssdrv->check_timings) { 208 r = dssdrv->check_timings(dssdev, &timings); 209 } else { 210 struct omap_video_timings t = {0}; 211 212 dssdrv->get_timings(dssdev, &t); 213 214 if (memcmp(&timings, &t, sizeof(struct omap_video_timings))) 215 r = -EINVAL; 216 else 217 r = 0; 218 } 219 220 if (!r) { 221 /* check if vrefresh is still valid */ 222 new_mode = drm_mode_duplicate(dev, mode); 223 new_mode->clock = timings.pixel_clock; 224 new_mode->vrefresh = 0; 225 if (mode->vrefresh == drm_mode_vrefresh(new_mode)) 226 ret = MODE_OK; 227 drm_mode_destroy(dev, new_mode); 228 } 229 230 DBG("connector: mode %s: " 231 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", 232 (ret == MODE_OK) ? "valid" : "invalid", 233 mode->base.id, mode->name, mode->vrefresh, mode->clock, 234 mode->hdisplay, mode->hsync_start, 235 mode->hsync_end, mode->htotal, 236 mode->vdisplay, mode->vsync_start, 237 mode->vsync_end, mode->vtotal, mode->type, mode->flags); 238 239 return ret; 240} 241 242struct drm_encoder *omap_connector_attached_encoder( 243 struct drm_connector *connector) 244{ 245 struct omap_connector *omap_connector = to_omap_connector(connector); 246 return omap_connector->encoder; 247} 248 249static const struct drm_connector_funcs omap_connector_funcs = { 250 .dpms = drm_helper_connector_dpms, 251 .detect = omap_connector_detect, 252 .fill_modes = drm_helper_probe_single_connector_modes, 253 .destroy = omap_connector_destroy, 254}; 255 256static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { 257 .get_modes = omap_connector_get_modes, 258 .mode_valid = omap_connector_mode_valid, 259 .best_encoder = omap_connector_attached_encoder, 260}; 261 262/* flush an area of the framebuffer (in case of manual update display that 263 * is not automatically flushed) 264 */ 265void omap_connector_flush(struct drm_connector *connector, 266 int x, int y, int w, int h) 267{ 268 struct omap_connector *omap_connector = to_omap_connector(connector); 269 270 /* TODO: enable when supported in dss */ 271 VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h); 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_sysfs_connector_add(connector); 311 312 return connector; 313 314fail: 315 if (connector) 316 omap_connector_destroy(connector); 317 318 return NULL; 319}