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.14 286 lines 8.0 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 38static void omap_connector_hpd_cb(void *cb_data, 39 enum drm_connector_status status) 40{ 41 struct omap_connector *omap_connector = cb_data; 42 struct drm_connector *connector = &omap_connector->base; 43 struct drm_device *dev = connector->dev; 44 enum drm_connector_status old_status; 45 46 mutex_lock(&dev->mode_config.mutex); 47 old_status = connector->status; 48 connector->status = status; 49 mutex_unlock(&dev->mode_config.mutex); 50 51 if (old_status != status) 52 drm_kms_helper_hotplug_event(dev); 53} 54 55bool omap_connector_get_hdmi_mode(struct drm_connector *connector) 56{ 57 struct omap_connector *omap_connector = to_omap_connector(connector); 58 59 return omap_connector->hdmi_mode; 60} 61 62static enum drm_connector_status omap_connector_detect( 63 struct drm_connector *connector, bool force) 64{ 65 struct omap_connector *omap_connector = to_omap_connector(connector); 66 struct omap_dss_device *dssdev = omap_connector->dssdev; 67 struct omap_dss_driver *dssdrv = dssdev->driver; 68 enum drm_connector_status ret; 69 70 if (dssdrv->detect) { 71 if (dssdrv->detect(dssdev)) 72 ret = connector_status_connected; 73 else 74 ret = connector_status_disconnected; 75 } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI || 76 dssdev->type == OMAP_DISPLAY_TYPE_DBI || 77 dssdev->type == OMAP_DISPLAY_TYPE_SDI || 78 dssdev->type == OMAP_DISPLAY_TYPE_DSI) { 79 ret = connector_status_connected; 80 } else { 81 ret = connector_status_unknown; 82 } 83 84 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); 85 86 return ret; 87} 88 89static void omap_connector_destroy(struct drm_connector *connector) 90{ 91 struct omap_connector *omap_connector = to_omap_connector(connector); 92 struct omap_dss_device *dssdev = omap_connector->dssdev; 93 94 DBG("%s", omap_connector->dssdev->name); 95 if (connector->polled == DRM_CONNECTOR_POLL_HPD && 96 dssdev->driver->unregister_hpd_cb) { 97 dssdev->driver->unregister_hpd_cb(dssdev); 98 } 99 drm_connector_unregister(connector); 100 drm_connector_cleanup(connector); 101 kfree(omap_connector); 102 103 omap_dss_put_device(dssdev); 104} 105 106#define MAX_EDID 512 107 108static int omap_connector_get_modes(struct drm_connector *connector) 109{ 110 struct omap_connector *omap_connector = to_omap_connector(connector); 111 struct omap_dss_device *dssdev = omap_connector->dssdev; 112 struct omap_dss_driver *dssdrv = dssdev->driver; 113 struct drm_device *dev = connector->dev; 114 int n = 0; 115 116 DBG("%s", omap_connector->dssdev->name); 117 118 /* if display exposes EDID, then we parse that in the normal way to 119 * build table of supported modes.. otherwise (ie. fixed resolution 120 * LCD panels) we just return a single mode corresponding to the 121 * currently configured timings: 122 */ 123 if (dssdrv->read_edid) { 124 void *edid = kzalloc(MAX_EDID, GFP_KERNEL); 125 126 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) && 127 drm_edid_is_valid(edid)) { 128 drm_mode_connector_update_edid_property( 129 connector, edid); 130 n = drm_add_edid_modes(connector, edid); 131 132 omap_connector->hdmi_mode = 133 drm_detect_hdmi_monitor(edid); 134 } else { 135 drm_mode_connector_update_edid_property( 136 connector, NULL); 137 } 138 139 kfree(edid); 140 } else { 141 struct drm_display_mode *mode = drm_mode_create(dev); 142 struct videomode vm = {0}; 143 144 dssdrv->get_timings(dssdev, &vm); 145 146 drm_display_mode_from_videomode(&vm, mode); 147 148 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 149 drm_mode_set_name(mode); 150 drm_mode_probed_add(connector, mode); 151 152 n = 1; 153 } 154 155 return n; 156} 157 158static int omap_connector_mode_valid(struct drm_connector *connector, 159 struct drm_display_mode *mode) 160{ 161 struct omap_connector *omap_connector = to_omap_connector(connector); 162 struct omap_dss_device *dssdev = omap_connector->dssdev; 163 struct omap_dss_driver *dssdrv = dssdev->driver; 164 struct videomode vm = {0}; 165 struct drm_device *dev = connector->dev; 166 struct drm_display_mode *new_mode; 167 int r, ret = MODE_BAD; 168 169 drm_display_mode_to_videomode(mode, &vm); 170 mode->vrefresh = drm_mode_vrefresh(mode); 171 172 /* 173 * if the panel driver doesn't have a check_timings, it's most likely 174 * a fixed resolution panel, check if the timings match with the 175 * panel's timings 176 */ 177 if (dssdrv->check_timings) { 178 r = dssdrv->check_timings(dssdev, &vm); 179 } else { 180 struct videomode t = {0}; 181 182 dssdrv->get_timings(dssdev, &t); 183 184 /* 185 * Ignore the flags, as we don't get them from 186 * drm_display_mode_to_videomode. 187 */ 188 t.flags = 0; 189 190 if (memcmp(&vm, &t, sizeof(vm))) 191 r = -EINVAL; 192 else 193 r = 0; 194 } 195 196 if (!r) { 197 /* check if vrefresh is still valid */ 198 new_mode = drm_mode_duplicate(dev, mode); 199 new_mode->clock = vm.pixelclock / 1000; 200 new_mode->vrefresh = 0; 201 if (mode->vrefresh == drm_mode_vrefresh(new_mode)) 202 ret = MODE_OK; 203 drm_mode_destroy(dev, new_mode); 204 } 205 206 DBG("connector: mode %s: " 207 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", 208 (ret == MODE_OK) ? "valid" : "invalid", 209 mode->base.id, mode->name, mode->vrefresh, mode->clock, 210 mode->hdisplay, mode->hsync_start, 211 mode->hsync_end, mode->htotal, 212 mode->vdisplay, mode->vsync_start, 213 mode->vsync_end, mode->vtotal, mode->type, mode->flags); 214 215 return ret; 216} 217 218static const struct drm_connector_funcs omap_connector_funcs = { 219 .reset = drm_atomic_helper_connector_reset, 220 .detect = omap_connector_detect, 221 .fill_modes = drm_helper_probe_single_connector_modes, 222 .destroy = omap_connector_destroy, 223 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 224 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 225}; 226 227static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { 228 .get_modes = omap_connector_get_modes, 229 .mode_valid = omap_connector_mode_valid, 230}; 231 232/* initialize connector */ 233struct drm_connector *omap_connector_init(struct drm_device *dev, 234 int connector_type, struct omap_dss_device *dssdev, 235 struct drm_encoder *encoder) 236{ 237 struct drm_connector *connector = NULL; 238 struct omap_connector *omap_connector; 239 bool hpd_supported = false; 240 241 DBG("%s", dssdev->name); 242 243 omap_dss_get_device(dssdev); 244 245 omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); 246 if (!omap_connector) 247 goto fail; 248 249 omap_connector->dssdev = dssdev; 250 251 connector = &omap_connector->base; 252 253 drm_connector_init(dev, connector, &omap_connector_funcs, 254 connector_type); 255 drm_connector_helper_add(connector, &omap_connector_helper_funcs); 256 257 if (dssdev->driver->register_hpd_cb) { 258 int ret = dssdev->driver->register_hpd_cb(dssdev, 259 omap_connector_hpd_cb, 260 omap_connector); 261 if (!ret) 262 hpd_supported = true; 263 else if (ret != -ENOTSUPP) 264 DBG("%s: Failed to register HPD callback (%d).", 265 dssdev->name, ret); 266 } 267 268 if (hpd_supported) 269 connector->polled = DRM_CONNECTOR_POLL_HPD; 270 else if (dssdev->driver->detect) 271 connector->polled = DRM_CONNECTOR_POLL_CONNECT | 272 DRM_CONNECTOR_POLL_DISCONNECT; 273 else 274 connector->polled = 0; 275 276 connector->interlace_allowed = 1; 277 connector->doublescan_allowed = 0; 278 279 return connector; 280 281fail: 282 if (connector) 283 omap_connector_destroy(connector); 284 285 return NULL; 286}