0001
0002
0003 #include <linux/crc32.h>
0004
0005 #include <drm/drm_atomic.h>
0006 #include <drm/drm_atomic_helper.h>
0007 #include <drm/drm_fourcc.h>
0008 #include <drm/drm_gem_framebuffer_helper.h>
0009 #include <drm/drm_vblank.h>
0010
0011 #include "vkms_drv.h"
0012
0013 static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer,
0014 const struct vkms_composer *composer)
0015 {
0016 u32 pixel;
0017 int src_offset = composer->offset + (y * composer->pitch)
0018 + (x * composer->cpp);
0019
0020 pixel = *(u32 *)&buffer[src_offset];
0021
0022 return pixel;
0023 }
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 static uint32_t compute_crc(const u8 *vaddr,
0035 const struct vkms_composer *composer)
0036 {
0037 int x, y;
0038 u32 crc = 0, pixel = 0;
0039 int x_src = composer->src.x1 >> 16;
0040 int y_src = composer->src.y1 >> 16;
0041 int h_src = drm_rect_height(&composer->src) >> 16;
0042 int w_src = drm_rect_width(&composer->src) >> 16;
0043
0044 for (y = y_src; y < y_src + h_src; ++y) {
0045 for (x = x_src; x < x_src + w_src; ++x) {
0046 pixel = get_pixel_from_buffer(x, y, vaddr, composer);
0047 crc = crc32_le(crc, (void *)&pixel, sizeof(u32));
0048 }
0049 }
0050
0051 return crc;
0052 }
0053
0054 static u8 blend_channel(u8 src, u8 dst, u8 alpha)
0055 {
0056 u32 pre_blend;
0057 u8 new_color;
0058
0059 pre_blend = (src * 255 + dst * (255 - alpha));
0060
0061
0062 new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
0063
0064 return new_color;
0065 }
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 static void alpha_blend(const u8 *argb_src, u8 *argb_dst)
0078 {
0079 u8 alpha;
0080
0081 alpha = argb_src[3];
0082 argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
0083 argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
0084 argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
0085 }
0086
0087
0088
0089
0090
0091
0092 static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst)
0093 {
0094 memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3);
0095 }
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113 static void blend(void *vaddr_dst, void *vaddr_src,
0114 struct vkms_composer *dst_composer,
0115 struct vkms_composer *src_composer,
0116 void (*pixel_blend)(const u8 *, u8 *))
0117 {
0118 int i, j, j_dst, i_dst;
0119 int offset_src, offset_dst;
0120 u8 *pixel_dst, *pixel_src;
0121
0122 int x_src = src_composer->src.x1 >> 16;
0123 int y_src = src_composer->src.y1 >> 16;
0124
0125 int x_dst = src_composer->dst.x1;
0126 int y_dst = src_composer->dst.y1;
0127 int h_dst = drm_rect_height(&src_composer->dst);
0128 int w_dst = drm_rect_width(&src_composer->dst);
0129
0130 int y_limit = y_src + h_dst;
0131 int x_limit = x_src + w_dst;
0132
0133 for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
0134 for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
0135 offset_dst = dst_composer->offset
0136 + (i_dst * dst_composer->pitch)
0137 + (j_dst++ * dst_composer->cpp);
0138 offset_src = src_composer->offset
0139 + (i * src_composer->pitch)
0140 + (j * src_composer->cpp);
0141
0142 pixel_src = (u8 *)(vaddr_src + offset_src);
0143 pixel_dst = (u8 *)(vaddr_dst + offset_dst);
0144 pixel_blend(pixel_src, pixel_dst);
0145
0146 pixel_dst[3] = 0xff;
0147 }
0148 i_dst++;
0149 }
0150 }
0151
0152 static void compose_plane(struct vkms_composer *primary_composer,
0153 struct vkms_composer *plane_composer,
0154 void *vaddr_out)
0155 {
0156 struct drm_framebuffer *fb = &plane_composer->fb;
0157 void *vaddr;
0158 void (*pixel_blend)(const u8 *p_src, u8 *p_dst);
0159
0160 if (WARN_ON(iosys_map_is_null(&plane_composer->map[0])))
0161 return;
0162
0163 vaddr = plane_composer->map[0].vaddr;
0164
0165 if (fb->format->format == DRM_FORMAT_ARGB8888)
0166 pixel_blend = &alpha_blend;
0167 else
0168 pixel_blend = &x_blend;
0169
0170 blend(vaddr_out, vaddr, primary_composer, plane_composer, pixel_blend);
0171 }
0172
0173 static int compose_active_planes(void **vaddr_out,
0174 struct vkms_composer *primary_composer,
0175 struct vkms_crtc_state *crtc_state)
0176 {
0177 struct drm_framebuffer *fb = &primary_composer->fb;
0178 struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0);
0179 const void *vaddr;
0180 int i;
0181
0182 if (!*vaddr_out) {
0183 *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL);
0184 if (!*vaddr_out) {
0185 DRM_ERROR("Cannot allocate memory for output frame.");
0186 return -ENOMEM;
0187 }
0188 }
0189
0190 if (WARN_ON(iosys_map_is_null(&primary_composer->map[0])))
0191 return -EINVAL;
0192
0193 vaddr = primary_composer->map[0].vaddr;
0194
0195 memcpy(*vaddr_out, vaddr, gem_obj->size);
0196
0197
0198
0199
0200
0201 for (i = 1; i < crtc_state->num_active_planes; i++)
0202 compose_plane(primary_composer,
0203 crtc_state->active_planes[i]->composer,
0204 *vaddr_out);
0205
0206 return 0;
0207 }
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218 void vkms_composer_worker(struct work_struct *work)
0219 {
0220 struct vkms_crtc_state *crtc_state = container_of(work,
0221 struct vkms_crtc_state,
0222 composer_work);
0223 struct drm_crtc *crtc = crtc_state->base.crtc;
0224 struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0225 struct vkms_composer *primary_composer = NULL;
0226 struct vkms_plane_state *act_plane = NULL;
0227 bool crc_pending, wb_pending;
0228 void *vaddr_out = NULL;
0229 u32 crc32 = 0;
0230 u64 frame_start, frame_end;
0231 int ret;
0232
0233 spin_lock_irq(&out->composer_lock);
0234 frame_start = crtc_state->frame_start;
0235 frame_end = crtc_state->frame_end;
0236 crc_pending = crtc_state->crc_pending;
0237 wb_pending = crtc_state->wb_pending;
0238 crtc_state->frame_start = 0;
0239 crtc_state->frame_end = 0;
0240 crtc_state->crc_pending = false;
0241 spin_unlock_irq(&out->composer_lock);
0242
0243
0244
0245
0246
0247 if (!crc_pending)
0248 return;
0249
0250 if (crtc_state->num_active_planes >= 1) {
0251 act_plane = crtc_state->active_planes[0];
0252 if (act_plane->base.base.plane->type == DRM_PLANE_TYPE_PRIMARY)
0253 primary_composer = act_plane->composer;
0254 }
0255
0256 if (!primary_composer)
0257 return;
0258
0259 if (wb_pending)
0260 vaddr_out = crtc_state->active_writeback->data[0].vaddr;
0261
0262 ret = compose_active_planes(&vaddr_out, primary_composer,
0263 crtc_state);
0264 if (ret) {
0265 if (ret == -EINVAL && !wb_pending)
0266 kvfree(vaddr_out);
0267 return;
0268 }
0269
0270 crc32 = compute_crc(vaddr_out, primary_composer);
0271
0272 if (wb_pending) {
0273 drm_writeback_signal_completion(&out->wb_connector, 0);
0274 spin_lock_irq(&out->composer_lock);
0275 crtc_state->wb_pending = false;
0276 spin_unlock_irq(&out->composer_lock);
0277 } else {
0278 kvfree(vaddr_out);
0279 }
0280
0281
0282
0283
0284 while (frame_start <= frame_end)
0285 drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32);
0286 }
0287
0288 static const char * const pipe_crc_sources[] = {"auto"};
0289
0290 const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
0291 size_t *count)
0292 {
0293 *count = ARRAY_SIZE(pipe_crc_sources);
0294 return pipe_crc_sources;
0295 }
0296
0297 static int vkms_crc_parse_source(const char *src_name, bool *enabled)
0298 {
0299 int ret = 0;
0300
0301 if (!src_name) {
0302 *enabled = false;
0303 } else if (strcmp(src_name, "auto") == 0) {
0304 *enabled = true;
0305 } else {
0306 *enabled = false;
0307 ret = -EINVAL;
0308 }
0309
0310 return ret;
0311 }
0312
0313 int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
0314 size_t *values_cnt)
0315 {
0316 bool enabled;
0317
0318 if (vkms_crc_parse_source(src_name, &enabled) < 0) {
0319 DRM_DEBUG_DRIVER("unknown source %s\n", src_name);
0320 return -EINVAL;
0321 }
0322
0323 *values_cnt = 1;
0324
0325 return 0;
0326 }
0327
0328 void vkms_set_composer(struct vkms_output *out, bool enabled)
0329 {
0330 bool old_enabled;
0331
0332 if (enabled)
0333 drm_crtc_vblank_get(&out->crtc);
0334
0335 spin_lock_irq(&out->lock);
0336 old_enabled = out->composer_enabled;
0337 out->composer_enabled = enabled;
0338 spin_unlock_irq(&out->lock);
0339
0340 if (old_enabled)
0341 drm_crtc_vblank_put(&out->crtc);
0342 }
0343
0344 int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name)
0345 {
0346 struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0347 bool enabled = false;
0348 int ret = 0;
0349
0350 ret = vkms_crc_parse_source(src_name, &enabled);
0351
0352 vkms_set_composer(out, enabled);
0353
0354 return ret;
0355 }