1 /* 2 * Copyright (C) 2011 Samsung Electronics Co.Ltd 3 * Authors: Joonyoung Shim <jy0922.shim@samsung.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 as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 * 10 */ 11 12 #include <drm/drmP.h> 13 14 #include <drm/exynos_drm.h> 15 #include <drm/drm_plane_helper.h> 16 #include "exynos_drm_drv.h" 17 #include "exynos_drm_crtc.h" 18 #include "exynos_drm_fb.h" 19 #include "exynos_drm_gem.h" 20 #include "exynos_drm_plane.h" 21 22 static const uint32_t formats[] = { 23 DRM_FORMAT_XRGB8888, 24 DRM_FORMAT_ARGB8888, 25 DRM_FORMAT_NV12, 26 }; 27 28 /* 29 * This function is to get X or Y size shown via screen. This needs length and 30 * start position of CRTC. 31 * 32 * <--- length ---> 33 * CRTC ---------------- 34 * ^ start ^ end 35 * 36 * There are six cases from a to f. 37 * 38 * <----- SCREEN -----> 39 * 0 last 40 * ----------|------------------|---------- 41 * CRTCs 42 * a ------- 43 * b ------- 44 * c -------------------------- 45 * d -------- 46 * e ------- 47 * f ------- 48 */ 49 static int exynos_plane_get_size(int start, unsigned length, unsigned last) 50 { 51 int end = start + length; 52 int size = 0; 53 54 if (start <= 0) { 55 if (end > 0) 56 size = min_t(unsigned, end, last); 57 } else if (start <= last) { 58 size = min_t(unsigned, last - start, length); 59 } 60 61 return size; 62 } 63 64 int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb) 65 { 66 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 67 int nr; 68 int i; 69 70 nr = exynos_drm_fb_get_buf_cnt(fb); 71 for (i = 0; i < nr; i++) { 72 struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i); 73 74 if (!buffer) { 75 DRM_DEBUG_KMS("buffer is null\n"); 76 return -EFAULT; 77 } 78 79 exynos_plane->dma_addr[i] = buffer->dma_addr; 80 81 DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n", 82 i, (unsigned long)exynos_plane->dma_addr[i]); 83 } 84 85 return 0; 86 } 87 88 void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, 89 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 90 unsigned int crtc_w, unsigned int crtc_h, 91 uint32_t src_x, uint32_t src_y, 92 uint32_t src_w, uint32_t src_h) 93 { 94 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 95 unsigned int actual_w; 96 unsigned int actual_h; 97 98 actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay); 99 actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay); 100 101 if (crtc_x < 0) { 102 if (actual_w) 103 src_x -= crtc_x; 104 crtc_x = 0; 105 } 106 107 if (crtc_y < 0) { 108 if (actual_h) 109 src_y -= crtc_y; 110 crtc_y = 0; 111 } 112 113 /* set ratio */ 114 exynos_plane->h_ratio = (src_w << 16) / crtc_w; 115 exynos_plane->v_ratio = (src_h << 16) / crtc_h; 116 117 /* set drm framebuffer data. */ 118 exynos_plane->src_x = src_x; 119 exynos_plane->src_y = src_y; 120 exynos_plane->src_width = (actual_w * exynos_plane->h_ratio) >> 16; 121 exynos_plane->src_height = (actual_h * exynos_plane->v_ratio) >> 16; 122 exynos_plane->fb_width = fb->width; 123 exynos_plane->fb_height = fb->height; 124 exynos_plane->bpp = fb->bits_per_pixel; 125 exynos_plane->pitch = fb->pitches[0]; 126 exynos_plane->pixel_format = fb->pixel_format; 127 128 /* set plane range to be displayed. */ 129 exynos_plane->crtc_x = crtc_x; 130 exynos_plane->crtc_y = crtc_y; 131 exynos_plane->crtc_width = actual_w; 132 exynos_plane->crtc_height = actual_h; 133 134 /* set drm mode data. */ 135 exynos_plane->mode_width = crtc->mode.hdisplay; 136 exynos_plane->mode_height = crtc->mode.vdisplay; 137 exynos_plane->refresh = crtc->mode.vrefresh; 138 exynos_plane->scan_flag = crtc->mode.flags; 139 140 DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)", 141 exynos_plane->crtc_x, exynos_plane->crtc_y, 142 exynos_plane->crtc_width, exynos_plane->crtc_height); 143 144 plane->crtc = crtc; 145 } 146 147 int 148 exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, 149 struct drm_framebuffer *fb, int crtc_x, int crtc_y, 150 unsigned int crtc_w, unsigned int crtc_h, 151 uint32_t src_x, uint32_t src_y, 152 uint32_t src_w, uint32_t src_h) 153 { 154 155 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 156 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 157 int ret; 158 159 ret = exynos_check_plane(plane, fb); 160 if (ret < 0) 161 return ret; 162 163 exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, 164 crtc_w, crtc_h, src_x >> 16, src_y >> 16, 165 src_w >> 16, src_h >> 16); 166 167 if (exynos_crtc->ops->win_commit) 168 exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos); 169 170 return 0; 171 } 172 173 static int exynos_disable_plane(struct drm_plane *plane) 174 { 175 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 176 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc); 177 178 if (exynos_crtc && exynos_crtc->ops->win_disable) 179 exynos_crtc->ops->win_disable(exynos_crtc, 180 exynos_plane->zpos); 181 182 return 0; 183 } 184 185 static struct drm_plane_funcs exynos_plane_funcs = { 186 .update_plane = exynos_update_plane, 187 .disable_plane = exynos_disable_plane, 188 .destroy = drm_plane_cleanup, 189 }; 190 191 static void exynos_plane_attach_zpos_property(struct drm_plane *plane, 192 unsigned int zpos) 193 { 194 struct drm_device *dev = plane->dev; 195 struct exynos_drm_private *dev_priv = dev->dev_private; 196 struct drm_property *prop; 197 198 prop = dev_priv->plane_zpos_property; 199 if (!prop) { 200 prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, 201 "zpos", 0, MAX_PLANE - 1); 202 if (!prop) 203 return; 204 205 dev_priv->plane_zpos_property = prop; 206 } 207 208 drm_object_attach_property(&plane->base, prop, zpos); 209 } 210 211 int exynos_plane_init(struct drm_device *dev, 212 struct exynos_drm_plane *exynos_plane, 213 unsigned long possible_crtcs, enum drm_plane_type type, 214 unsigned int zpos) 215 { 216 int err; 217 218 err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, 219 &exynos_plane_funcs, formats, 220 ARRAY_SIZE(formats), type); 221 if (err) { 222 DRM_ERROR("failed to initialize plane\n"); 223 return err; 224 } 225 226 exynos_plane->zpos = zpos; 227 228 if (type == DRM_PLANE_TYPE_OVERLAY) 229 exynos_plane_attach_zpos_property(&exynos_plane->base, zpos); 230 231 return 0; 232 } 233