1/* exynos_drm_encoder.c 2 * 3 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 4 * Authors: 5 * Inki Dae <inki.dae@samsung.com> 6 * Joonyoung Shim <jy0922.shim@samsung.com> 7 * Seung-Woo Kim <sw0312.kim@samsung.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15#include <drm/drmP.h> 16#include <drm/drm_crtc_helper.h> 17 18#include "exynos_drm_drv.h" 19#include "exynos_drm_encoder.h" 20 21#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ 22 drm_encoder) 23 24/* 25 * exynos specific encoder structure. 26 * 27 * @drm_encoder: encoder object. 28 * @display: the display structure that maps to this encoder 29 */ 30struct exynos_drm_encoder { 31 struct drm_encoder drm_encoder; 32 struct exynos_drm_display *display; 33}; 34 35static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) 36{ 37 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 38 struct exynos_drm_display *display = exynos_encoder->display; 39 40 DRM_DEBUG_KMS("encoder dpms: %d\n", mode); 41 42 if (display->ops->dpms) 43 display->ops->dpms(display, mode); 44} 45 46static bool 47exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, 48 const struct drm_display_mode *mode, 49 struct drm_display_mode *adjusted_mode) 50{ 51 struct drm_device *dev = encoder->dev; 52 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 53 struct exynos_drm_display *display = exynos_encoder->display; 54 struct drm_connector *connector; 55 56 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 57 if (connector->encoder != encoder) 58 continue; 59 60 if (display->ops->mode_fixup) 61 display->ops->mode_fixup(display, connector, mode, 62 adjusted_mode); 63 } 64 65 return true; 66} 67 68static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, 69 struct drm_display_mode *mode, 70 struct drm_display_mode *adjusted_mode) 71{ 72 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 73 struct exynos_drm_display *display = exynos_encoder->display; 74 75 if (display->ops->mode_set) 76 display->ops->mode_set(display, adjusted_mode); 77} 78 79static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) 80{ 81 /* drm framework doesn't check NULL. */ 82} 83 84static void exynos_drm_encoder_commit(struct drm_encoder *encoder) 85{ 86 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 87 struct exynos_drm_display *display = exynos_encoder->display; 88 89 if (display->ops->dpms) 90 display->ops->dpms(display, DRM_MODE_DPMS_ON); 91 92 if (display->ops->commit) 93 display->ops->commit(display); 94} 95 96static void exynos_drm_encoder_disable(struct drm_encoder *encoder) 97{ 98 struct drm_plane *plane; 99 struct drm_device *dev = encoder->dev; 100 101 exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); 102 103 /* all planes connected to this encoder should be also disabled. */ 104 drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) { 105 if (plane->crtc && (plane->crtc == encoder->crtc)) 106 plane->funcs->disable_plane(plane); 107 } 108} 109 110static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { 111 .dpms = exynos_drm_encoder_dpms, 112 .mode_fixup = exynos_drm_encoder_mode_fixup, 113 .mode_set = exynos_drm_encoder_mode_set, 114 .prepare = exynos_drm_encoder_prepare, 115 .commit = exynos_drm_encoder_commit, 116 .disable = exynos_drm_encoder_disable, 117}; 118 119static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) 120{ 121 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 122 123 drm_encoder_cleanup(encoder); 124 kfree(exynos_encoder); 125} 126 127static struct drm_encoder_funcs exynos_encoder_funcs = { 128 .destroy = exynos_drm_encoder_destroy, 129}; 130 131static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) 132{ 133 struct drm_encoder *clone; 134 struct drm_device *dev = encoder->dev; 135 struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); 136 struct exynos_drm_display *display = exynos_encoder->display; 137 unsigned int clone_mask = 0; 138 int cnt = 0; 139 140 list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { 141 switch (display->type) { 142 case EXYNOS_DISPLAY_TYPE_LCD: 143 case EXYNOS_DISPLAY_TYPE_HDMI: 144 case EXYNOS_DISPLAY_TYPE_VIDI: 145 clone_mask |= (1 << (cnt++)); 146 break; 147 default: 148 continue; 149 } 150 } 151 152 return clone_mask; 153} 154 155void exynos_drm_encoder_setup(struct drm_device *dev) 156{ 157 struct drm_encoder *encoder; 158 159 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 160 encoder->possible_clones = exynos_drm_encoder_clones(encoder); 161} 162 163struct drm_encoder * 164exynos_drm_encoder_create(struct drm_device *dev, 165 struct exynos_drm_display *display, 166 unsigned long possible_crtcs) 167{ 168 struct drm_encoder *encoder; 169 struct exynos_drm_encoder *exynos_encoder; 170 171 if (!possible_crtcs) 172 return NULL; 173 174 exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); 175 if (!exynos_encoder) 176 return NULL; 177 178 exynos_encoder->display = display; 179 encoder = &exynos_encoder->drm_encoder; 180 encoder->possible_crtcs = possible_crtcs; 181 182 DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); 183 184 drm_encoder_init(dev, encoder, &exynos_encoder_funcs, 185 DRM_MODE_ENCODER_TMDS); 186 187 drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); 188 189 DRM_DEBUG_KMS("encoder has been created\n"); 190 191 return encoder; 192} 193 194struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder) 195{ 196 return to_exynos_encoder(encoder)->display; 197} 198