1 /* 2 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 3 * Author: Rob Clark <rob@ti.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published by 7 * the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <linux/list.h> 19 20 #include <drm/drm_crtc.h> 21 #include <drm/drm_modeset_helper_vtables.h> 22 #include <drm/drm_edid.h> 23 24 #include "omap_drv.h" 25 26 /* 27 * encoder funcs 28 */ 29 30 #define to_omap_encoder(x) container_of(x, struct omap_encoder, base) 31 32 /* The encoder and connector both map to same dssdev.. the encoder 33 * handles the 'active' parts, ie. anything the modifies the state 34 * of the hw, and the connector handles the 'read-only' parts, like 35 * detecting connection and reading edid. 36 */ 37 struct omap_encoder { 38 struct drm_encoder base; 39 struct omap_dss_device *output; 40 struct omap_dss_device *display; 41 }; 42 43 static void omap_encoder_destroy(struct drm_encoder *encoder) 44 { 45 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 46 47 drm_encoder_cleanup(encoder); 48 kfree(omap_encoder); 49 } 50 51 static const struct drm_encoder_funcs omap_encoder_funcs = { 52 .destroy = omap_encoder_destroy, 53 }; 54 55 static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder, 56 struct drm_display_mode *adjusted_mode) 57 { 58 struct drm_device *dev = encoder->dev; 59 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 60 struct omap_dss_device *dssdev = omap_encoder->output; 61 struct drm_connector *connector; 62 bool hdmi_mode; 63 64 hdmi_mode = false; 65 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 66 if (connector->encoder == encoder) { 67 hdmi_mode = omap_connector_get_hdmi_mode(connector); 68 break; 69 } 70 } 71 72 if (dssdev->ops->hdmi.set_hdmi_mode) 73 dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode); 74 75 if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) { 76 struct hdmi_avi_infoframe avi; 77 int r; 78 79 r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, 80 adjusted_mode); 81 if (r == 0) 82 dssdev->ops->hdmi.set_infoframe(dssdev, &avi); 83 } 84 } 85 86 static void omap_encoder_mode_set(struct drm_encoder *encoder, 87 struct drm_display_mode *mode, 88 struct drm_display_mode *adjusted_mode) 89 { 90 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 91 struct omap_dss_device *dssdev; 92 struct videomode vm = { 0 }; 93 94 drm_display_mode_to_videomode(adjusted_mode, &vm); 95 96 /* 97 * HACK: This fixes the vm flags. 98 * struct drm_display_mode does not contain the VSYNC/HSYNC/DE flags and 99 * they get lost when converting back and forth between struct 100 * drm_display_mode and struct videomode. The hack below goes and 101 * fetches the missing flags. 102 * 103 * A better solution is to use DRM's bus-flags through the whole driver. 104 */ 105 for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { 106 unsigned long bus_flags = dssdev->bus_flags; 107 108 if (!(vm.flags & (DISPLAY_FLAGS_DE_LOW | 109 DISPLAY_FLAGS_DE_HIGH))) { 110 if (bus_flags & DRM_BUS_FLAG_DE_LOW) 111 vm.flags |= DISPLAY_FLAGS_DE_LOW; 112 else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) 113 vm.flags |= DISPLAY_FLAGS_DE_HIGH; 114 } 115 116 if (!(vm.flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | 117 DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { 118 if (bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) 119 vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; 120 else if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) 121 vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; 122 } 123 124 if (!(vm.flags & (DISPLAY_FLAGS_SYNC_POSEDGE | 125 DISPLAY_FLAGS_SYNC_NEGEDGE))) { 126 if (bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE) 127 vm.flags |= DISPLAY_FLAGS_SYNC_POSEDGE; 128 else if (bus_flags & DRM_BUS_FLAG_SYNC_NEGEDGE) 129 vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; 130 } 131 } 132 133 /* Set timings for all devices in the display pipeline. */ 134 dss_mgr_set_timings(omap_encoder->output, &vm); 135 136 for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { 137 if (dssdev->ops->set_timings) 138 dssdev->ops->set_timings(dssdev, &vm); 139 } 140 141 /* Set the HDMI mode and HDMI infoframe if applicable. */ 142 if (omap_encoder->output->output_type == OMAP_DISPLAY_TYPE_HDMI) 143 omap_encoder_hdmi_mode_set(encoder, adjusted_mode); 144 } 145 146 static void omap_encoder_disable(struct drm_encoder *encoder) 147 { 148 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 149 struct omap_dss_device *dssdev = omap_encoder->display; 150 151 dssdev->ops->disable(dssdev); 152 } 153 154 static void omap_encoder_enable(struct drm_encoder *encoder) 155 { 156 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 157 struct omap_dss_device *dssdev = omap_encoder->display; 158 int r; 159 160 r = dssdev->ops->enable(dssdev); 161 if (r) 162 dev_err(encoder->dev->dev, 163 "Failed to enable display '%s': %d\n", 164 dssdev->name, r); 165 } 166 167 static int omap_encoder_atomic_check(struct drm_encoder *encoder, 168 struct drm_crtc_state *crtc_state, 169 struct drm_connector_state *conn_state) 170 { 171 struct omap_encoder *omap_encoder = to_omap_encoder(encoder); 172 enum omap_channel channel = omap_encoder->output->dispc_channel; 173 struct drm_device *dev = encoder->dev; 174 struct omap_drm_private *priv = dev->dev_private; 175 struct omap_dss_device *dssdev; 176 struct videomode vm = { 0 }; 177 int ret; 178 179 drm_display_mode_to_videomode(&crtc_state->mode, &vm); 180 181 ret = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm); 182 if (ret) 183 goto done; 184 185 for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) { 186 if (!dssdev->ops->check_timings) 187 continue; 188 189 ret = dssdev->ops->check_timings(dssdev, &vm); 190 if (ret) 191 goto done; 192 } 193 194 drm_display_mode_from_videomode(&vm, &crtc_state->adjusted_mode); 195 196 done: 197 if (ret) 198 dev_err(dev->dev, "invalid timings: %d\n", ret); 199 200 return ret; 201 } 202 203 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { 204 .mode_set = omap_encoder_mode_set, 205 .disable = omap_encoder_disable, 206 .enable = omap_encoder_enable, 207 .atomic_check = omap_encoder_atomic_check, 208 }; 209 210 /* initialize encoder */ 211 struct drm_encoder *omap_encoder_init(struct drm_device *dev, 212 struct omap_dss_device *output, 213 struct omap_dss_device *display) 214 { 215 struct drm_encoder *encoder = NULL; 216 struct omap_encoder *omap_encoder; 217 218 omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); 219 if (!omap_encoder) 220 goto fail; 221 222 omap_encoder->output = output; 223 omap_encoder->display = display; 224 225 encoder = &omap_encoder->base; 226 227 drm_encoder_init(dev, encoder, &omap_encoder_funcs, 228 DRM_MODE_ENCODER_TMDS, NULL); 229 drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs); 230 231 return encoder; 232 233 fail: 234 if (encoder) 235 omap_encoder_destroy(encoder); 236 237 return NULL; 238 } 239