1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 4 * Author: Rob Clark <rob@ti.com> 5 */ 6 7#include <drm/drm_atomic_helper.h> 8#include <drm/drm_crtc.h> 9#include <drm/drm_probe_helper.h> 10 11#include "omap_drv.h" 12 13/* 14 * connector funcs 15 */ 16 17#define to_omap_connector(x) container_of(x, struct omap_connector, base) 18 19struct omap_connector { 20 struct drm_connector base; 21 struct omap_dss_device *output; 22}; 23 24static enum drm_connector_status omap_connector_detect( 25 struct drm_connector *connector, bool force) 26{ 27 return connector_status_connected; 28} 29 30static void omap_connector_destroy(struct drm_connector *connector) 31{ 32 struct omap_connector *omap_connector = to_omap_connector(connector); 33 34 DBG("%s", connector->name); 35 36 drm_connector_unregister(connector); 37 drm_connector_cleanup(connector); 38 39 omapdss_device_put(omap_connector->output); 40 41 kfree(omap_connector); 42} 43 44static int omap_connector_get_modes(struct drm_connector *connector) 45{ 46 struct omap_connector *omap_connector = to_omap_connector(connector); 47 struct omap_dss_device *dssdev = NULL; 48 struct omap_dss_device *d; 49 50 DBG("%s", connector->name); 51 52 /* 53 * If the display pipeline reports modes (e.g. with a fixed resolution 54 * panel or an analog TV output), query it. 55 */ 56 for (d = omap_connector->output; d; d = d->next) { 57 if (d->ops_flags & OMAP_DSS_DEVICE_OP_MODES) 58 dssdev = d; 59 } 60 61 if (dssdev) 62 return dssdev->ops->get_modes(dssdev, connector); 63 64 /* We can't retrieve modes. The KMS core will add the default modes. */ 65 return 0; 66} 67 68enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev, 69 const struct drm_display_mode *mode, 70 struct drm_display_mode *adjusted_mode) 71{ 72 int ret; 73 74 drm_mode_copy(adjusted_mode, mode); 75 76 for (; dssdev; dssdev = dssdev->next) { 77 if (!dssdev->ops || !dssdev->ops->check_timings) 78 continue; 79 80 ret = dssdev->ops->check_timings(dssdev, adjusted_mode); 81 if (ret) 82 return MODE_BAD; 83 } 84 85 return MODE_OK; 86} 87 88static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector, 89 struct drm_display_mode *mode) 90{ 91 struct omap_connector *omap_connector = to_omap_connector(connector); 92 struct drm_display_mode new_mode = {}; 93 enum drm_mode_status status; 94 95 status = omap_connector_mode_fixup(omap_connector->output, mode, 96 &new_mode); 97 if (status != MODE_OK) 98 goto done; 99 100 /* Check if vrefresh is still valid. */ 101 if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode)) 102 status = MODE_NOCLOCK; 103 104done: 105 DBG("connector: mode %s: " DRM_MODE_FMT, 106 (status == MODE_OK) ? "valid" : "invalid", 107 DRM_MODE_ARG(mode)); 108 109 return status; 110} 111 112static const struct drm_connector_funcs omap_connector_funcs = { 113 .reset = drm_atomic_helper_connector_reset, 114 .detect = omap_connector_detect, 115 .fill_modes = drm_helper_probe_single_connector_modes, 116 .destroy = omap_connector_destroy, 117 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 118 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 119}; 120 121static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { 122 .get_modes = omap_connector_get_modes, 123 .mode_valid = omap_connector_mode_valid, 124}; 125 126/* initialize connector */ 127struct drm_connector *omap_connector_init(struct drm_device *dev, 128 struct omap_dss_device *output, 129 struct drm_encoder *encoder) 130{ 131 struct drm_connector *connector = NULL; 132 struct omap_connector *omap_connector; 133 134 DBG("%s", output->name); 135 136 omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); 137 if (!omap_connector) 138 goto fail; 139 140 omap_connector->output = omapdss_device_get(output); 141 142 connector = &omap_connector->base; 143 connector->interlace_allowed = 1; 144 connector->doublescan_allowed = 0; 145 146 drm_connector_init(dev, connector, &omap_connector_funcs, 147 DRM_MODE_CONNECTOR_DSI); 148 drm_connector_helper_add(connector, &omap_connector_helper_funcs); 149 150 return connector; 151 152fail: 153 if (connector) 154 omap_connector_destroy(connector); 155 156 return NULL; 157} 158