1 /* 2 * Copyright (c) 2015 MediaTek Inc. 3 * Author: CK Hu <ck.hu@mediatek.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <drm/drmP.h> 16 #include <drm/drm_atomic.h> 17 #include <drm/drm_atomic_helper.h> 18 #include <drm/drm_plane_helper.h> 19 20 #include "mtk_drm_crtc.h" 21 #include "mtk_drm_ddp_comp.h" 22 #include "mtk_drm_drv.h" 23 #include "mtk_drm_fb.h" 24 #include "mtk_drm_gem.h" 25 #include "mtk_drm_plane.h" 26 27 static const u32 formats[] = { 28 DRM_FORMAT_XRGB8888, 29 DRM_FORMAT_ARGB8888, 30 DRM_FORMAT_RGB565, 31 }; 32 33 static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable, 34 dma_addr_t addr, struct drm_rect *dest) 35 { 36 struct drm_plane *plane = &mtk_plane->base; 37 struct mtk_plane_state *state = to_mtk_plane_state(plane->state); 38 unsigned int pitch, format; 39 int x, y; 40 41 if (WARN_ON(!plane->state || (enable && !plane->state->fb))) 42 return; 43 44 if (plane->state->fb) { 45 pitch = plane->state->fb->pitches[0]; 46 format = plane->state->fb->pixel_format; 47 } else { 48 pitch = 0; 49 format = DRM_FORMAT_RGBA8888; 50 } 51 52 x = plane->state->crtc_x; 53 y = plane->state->crtc_y; 54 55 if (x < 0) { 56 addr -= x * 4; 57 x = 0; 58 } 59 60 if (y < 0) { 61 addr -= y * pitch; 62 y = 0; 63 } 64 65 state->pending.enable = enable; 66 state->pending.pitch = pitch; 67 state->pending.format = format; 68 state->pending.addr = addr; 69 state->pending.x = x; 70 state->pending.y = y; 71 state->pending.width = dest->x2 - dest->x1; 72 state->pending.height = dest->y2 - dest->y1; 73 wmb(); /* Make sure the above parameters are set before update */ 74 state->pending.dirty = true; 75 } 76 77 static void mtk_plane_reset(struct drm_plane *plane) 78 { 79 struct mtk_plane_state *state; 80 81 if (plane->state) { 82 if (plane->state->fb) 83 drm_framebuffer_unreference(plane->state->fb); 84 85 state = to_mtk_plane_state(plane->state); 86 memset(state, 0, sizeof(*state)); 87 } else { 88 state = kzalloc(sizeof(*state), GFP_KERNEL); 89 if (!state) 90 return; 91 plane->state = &state->base; 92 } 93 94 state->base.plane = plane; 95 state->pending.format = DRM_FORMAT_RGB565; 96 } 97 98 static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane) 99 { 100 struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state); 101 struct mtk_plane_state *state; 102 103 state = kzalloc(sizeof(*state), GFP_KERNEL); 104 if (!state) 105 return NULL; 106 107 __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 108 109 WARN_ON(state->base.plane != plane); 110 111 state->pending = old_state->pending; 112 113 return &state->base; 114 } 115 116 static void mtk_drm_plane_destroy_state(struct drm_plane *plane, 117 struct drm_plane_state *state) 118 { 119 __drm_atomic_helper_plane_destroy_state(state); 120 kfree(to_mtk_plane_state(state)); 121 } 122 123 static const struct drm_plane_funcs mtk_plane_funcs = { 124 .update_plane = drm_atomic_helper_update_plane, 125 .disable_plane = drm_atomic_helper_disable_plane, 126 .destroy = drm_plane_cleanup, 127 .reset = mtk_plane_reset, 128 .atomic_duplicate_state = mtk_plane_duplicate_state, 129 .atomic_destroy_state = mtk_drm_plane_destroy_state, 130 }; 131 132 static int mtk_plane_atomic_check(struct drm_plane *plane, 133 struct drm_plane_state *state) 134 { 135 struct drm_framebuffer *fb = state->fb; 136 struct drm_crtc_state *crtc_state; 137 bool visible; 138 struct drm_rect dest = { 139 .x1 = state->crtc_x, 140 .y1 = state->crtc_y, 141 .x2 = state->crtc_x + state->crtc_w, 142 .y2 = state->crtc_y + state->crtc_h, 143 }; 144 struct drm_rect src = { 145 /* 16.16 fixed point */ 146 .x1 = state->src_x, 147 .y1 = state->src_y, 148 .x2 = state->src_x + state->src_w, 149 .y2 = state->src_y + state->src_h, 150 }; 151 struct drm_rect clip = { 0, }; 152 153 if (!fb) 154 return 0; 155 156 if (!mtk_fb_get_gem_obj(fb)) { 157 DRM_DEBUG_KMS("buffer is null\n"); 158 return -EFAULT; 159 } 160 161 if (!state->crtc) 162 return 0; 163 164 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 165 if (IS_ERR(crtc_state)) 166 return PTR_ERR(crtc_state); 167 168 clip.x2 = crtc_state->mode.hdisplay; 169 clip.y2 = crtc_state->mode.vdisplay; 170 171 return drm_plane_helper_check_update(plane, state->crtc, fb, 172 &src, &dest, &clip, 173 DRM_PLANE_HELPER_NO_SCALING, 174 DRM_PLANE_HELPER_NO_SCALING, 175 true, true, &visible); 176 } 177 178 static void mtk_plane_atomic_update(struct drm_plane *plane, 179 struct drm_plane_state *old_state) 180 { 181 struct mtk_plane_state *state = to_mtk_plane_state(plane->state); 182 struct drm_crtc *crtc = state->base.crtc; 183 struct drm_gem_object *gem; 184 struct mtk_drm_gem_obj *mtk_gem; 185 struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane); 186 struct drm_rect dest = { 187 .x1 = state->base.crtc_x, 188 .y1 = state->base.crtc_y, 189 .x2 = state->base.crtc_x + state->base.crtc_w, 190 .y2 = state->base.crtc_y + state->base.crtc_h, 191 }; 192 struct drm_rect clip = { 0, }; 193 194 if (!crtc) 195 return; 196 197 clip.x2 = state->base.crtc->state->mode.hdisplay; 198 clip.y2 = state->base.crtc->state->mode.vdisplay; 199 drm_rect_intersect(&dest, &clip); 200 201 gem = mtk_fb_get_gem_obj(state->base.fb); 202 mtk_gem = to_mtk_gem_obj(gem); 203 mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest); 204 } 205 206 static void mtk_plane_atomic_disable(struct drm_plane *plane, 207 struct drm_plane_state *old_state) 208 { 209 struct mtk_plane_state *state = to_mtk_plane_state(plane->state); 210 211 state->pending.enable = false; 212 wmb(); /* Make sure the above parameter is set before update */ 213 state->pending.dirty = true; 214 } 215 216 static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { 217 .atomic_check = mtk_plane_atomic_check, 218 .atomic_update = mtk_plane_atomic_update, 219 .atomic_disable = mtk_plane_atomic_disable, 220 }; 221 222 int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, 223 unsigned long possible_crtcs, enum drm_plane_type type, 224 unsigned int zpos) 225 { 226 int err; 227 228 err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs, 229 &mtk_plane_funcs, formats, 230 ARRAY_SIZE(formats), type, NULL); 231 if (err) { 232 DRM_ERROR("failed to initialize plane\n"); 233 return err; 234 } 235 236 drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs); 237 mtk_plane->idx = zpos; 238 239 return 0; 240 } 241