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