Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] v4l: vsp1: uds: Fix scaling of alpha layer

Pixel color components can be scaled using either bilinear interpolation
or a multitap filter. The multitap filter provides better results, but
can't be selected when the alpha layer need to be scaled down by more
than 1/2.

Disable alpha scaling when the input has a fixed alpha value, and
program the UDS to output a fixed alpha value in that case. This ensures
the multitap filter will be used whenever possible.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>

authored by

Laurent Pinchart and committed by
Mauro Carvalho Chehab
bdc2df62 a16e2794

+122 -35
+4
drivers/media/platform/vsp1/vsp1_rpf.c
··· 46 46 { 47 47 struct vsp1_rwpf *rpf = 48 48 container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 49 + struct vsp1_pipeline *pipe; 49 50 50 51 if (!vsp1_entity_is_streaming(&rpf->entity)) 51 52 return 0; ··· 55 54 case V4L2_CID_ALPHA_COMPONENT: 56 55 vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, 57 56 ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); 57 + 58 + pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity); 59 + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val); 58 60 break; 59 61 } 60 62
+36 -27
drivers/media/platform/vsp1/vsp1_uds.c
··· 45 45 * Scaling Computation 46 46 */ 47 47 48 + void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha) 49 + { 50 + vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); 51 + } 52 + 48 53 /* 49 54 * uds_output_size - Return the output size for an input size and scaling ratio 50 55 * @input: input size in pixels ··· 110 105 return (input - 1) * 4096 / (output - 1); 111 106 } 112 107 113 - static void uds_compute_ratios(struct vsp1_uds *uds) 114 - { 115 - struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK]; 116 - struct v4l2_mbus_framefmt *output = 117 - &uds->entity.formats[UDS_PAD_SOURCE]; 118 - 119 - uds->hscale = uds_compute_ratio(input->width, output->width); 120 - uds->vscale = uds_compute_ratio(input->height, output->height); 121 - 122 - dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", 123 - uds->hscale, uds->vscale); 124 - } 125 - 126 108 /* ----------------------------------------------------------------------------- 127 109 * V4L2 Subdevice Core Operations 128 110 */ 129 111 130 112 static int uds_s_stream(struct v4l2_subdev *subdev, int enable) 131 113 { 132 - const struct v4l2_mbus_framefmt *format; 133 114 struct vsp1_uds *uds = to_uds(subdev); 115 + const struct v4l2_mbus_framefmt *output; 116 + const struct v4l2_mbus_framefmt *input; 117 + unsigned int hscale; 118 + unsigned int vscale; 119 + bool multitap; 134 120 135 121 if (!enable) 136 122 return 0; 137 123 138 - /* Enable multi-tap scaling. */ 139 - vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC); 124 + input = &uds->entity.formats[UDS_PAD_SINK]; 125 + output = &uds->entity.formats[UDS_PAD_SOURCE]; 126 + 127 + hscale = uds_compute_ratio(input->width, output->width); 128 + vscale = uds_compute_ratio(input->height, output->height); 129 + 130 + dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale); 131 + 132 + /* Multi-tap scaling can't be enabled along with alpha scaling when 133 + * scaling down with a factor lower than or equal to 1/2 in either 134 + * direction. 135 + */ 136 + if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192)) 137 + multitap = false; 138 + else 139 + multitap = true; 140 + 141 + vsp1_uds_write(uds, VI6_UDS_CTRL, 142 + (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | 143 + (multitap ? VI6_UDS_CTRL_BC : 0)); 140 144 141 145 vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, 142 - (uds_passband_width(uds->hscale) 146 + (uds_passband_width(hscale) 143 147 << VI6_UDS_PASS_BWIDTH_H_SHIFT) | 144 - (uds_passband_width(uds->vscale) 148 + (uds_passband_width(vscale) 145 149 << VI6_UDS_PASS_BWIDTH_V_SHIFT)); 146 150 147 151 /* Set the scaling ratios and the output size. */ 148 - format = &uds->entity.formats[UDS_PAD_SOURCE]; 149 - 150 152 vsp1_uds_write(uds, VI6_UDS_SCALE, 151 - (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | 152 - (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); 153 + (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | 154 + (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); 153 155 vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, 154 - (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | 155 - (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); 156 + (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | 157 + (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); 156 158 157 159 return 0; 158 160 } ··· 291 279 292 280 uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); 293 281 } 294 - 295 - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) 296 - uds_compute_ratios(uds); 297 282 298 283 return 0; 299 284 }
+3 -3
drivers/media/platform/vsp1/vsp1_uds.h
··· 25 25 26 26 struct vsp1_uds { 27 27 struct vsp1_entity entity; 28 - 29 - unsigned int hscale; 30 - unsigned int vscale; 28 + bool scale_alpha; 31 29 }; 32 30 33 31 static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) ··· 34 36 } 35 37 36 38 struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); 39 + 40 + void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha); 37 41 38 42 #endif /* __VSP1_UDS_H__ */
+73 -5
drivers/media/platform/vsp1/vsp1_video.c
··· 31 31 #include "vsp1_bru.h" 32 32 #include "vsp1_entity.h" 33 33 #include "vsp1_rwpf.h" 34 + #include "vsp1_uds.h" 34 35 #include "vsp1_video.h" 35 36 36 37 #define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV ··· 307 306 * Pipeline Management 308 307 */ 309 308 310 - static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, 309 + static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, 310 + struct vsp1_rwpf *input, 311 311 struct vsp1_rwpf *output) 312 312 { 313 313 struct vsp1_entity *entity; 314 314 unsigned int entities = 0; 315 315 struct media_pad *pad; 316 - bool uds_found = false; 316 + bool bru_found = false; 317 317 318 318 input->location.left = 0; 319 319 input->location.top = 0; ··· 343 341 344 342 input->location.left = rect->left; 345 343 input->location.top = rect->top; 344 + 345 + bru_found = true; 346 346 } 347 347 348 348 /* We've reached the WPF, we're done. */ ··· 359 355 360 356 /* UDS can't be chained. */ 361 357 if (entity->type == VSP1_ENTITY_UDS) { 362 - if (uds_found) 358 + if (pipe->uds) 363 359 return -EPIPE; 364 - uds_found = true; 360 + 361 + pipe->uds = entity; 362 + pipe->uds_input = bru_found ? pipe->bru 363 + : &input->entity; 365 364 } 366 365 367 366 /* Follow the source link. The link setup operations ensure ··· 401 394 pipe->output = NULL; 402 395 pipe->bru = NULL; 403 396 pipe->lif = NULL; 397 + pipe->uds = NULL; 404 398 } 405 399 406 400 static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, ··· 459 451 * contains no loop and that all branches end at the output WPF. 460 452 */ 461 453 for (i = 0; i < pipe->num_inputs; ++i) { 462 - ret = vsp1_pipeline_validate_branch(pipe->inputs[i], 454 + ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i], 463 455 pipe->output); 464 456 if (ret < 0) 465 457 goto error; ··· 662 654 spin_unlock_irqrestore(&pipe->irqlock, flags); 663 655 } 664 656 657 + /* 658 + * Propagate the alpha value through the pipeline. 659 + * 660 + * As the UDS has restricted scaling capabilities when the alpha component needs 661 + * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha 662 + * value. The UDS then outputs a fixed alpha value which needs to be programmed 663 + * from the input RPF alpha. 664 + */ 665 + void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, 666 + struct vsp1_entity *input, 667 + unsigned int alpha) 668 + { 669 + struct vsp1_entity *entity; 670 + struct media_pad *pad; 671 + 672 + pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); 673 + 674 + while (pad) { 675 + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 676 + break; 677 + 678 + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); 679 + 680 + /* The BRU background color has a fixed alpha value set to 255, 681 + * the output alpha value is thus always equal to 255. 682 + */ 683 + if (entity->type == VSP1_ENTITY_BRU) 684 + alpha = 255; 685 + 686 + if (entity->type == VSP1_ENTITY_UDS) { 687 + struct vsp1_uds *uds = to_uds(&entity->subdev); 688 + 689 + vsp1_uds_set_alpha(uds, alpha); 690 + break; 691 + } 692 + 693 + pad = &entity->pads[entity->source_pad]; 694 + pad = media_entity_remote_pad(pad); 695 + } 696 + } 697 + 665 698 /* ----------------------------------------------------------------------------- 666 699 * videobuf2 Queue Operations 667 700 */ ··· 810 761 811 762 mutex_lock(&pipe->lock); 812 763 if (pipe->stream_count == pipe->num_video - 1) { 764 + if (pipe->uds) { 765 + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); 766 + 767 + /* If a BRU is present in the pipeline before the UDS, 768 + * the alpha component doesn't need to be scaled as the 769 + * BRU output alpha value is fixed to 255. Otherwise we 770 + * need to scale the alpha component only when available 771 + * at the input RPF. 772 + */ 773 + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { 774 + uds->scale_alpha = false; 775 + } else { 776 + struct vsp1_rwpf *rpf = 777 + to_rwpf(&pipe->uds_input->subdev); 778 + 779 + uds->scale_alpha = rpf->video.fmtinfo->alpha; 780 + } 781 + } 782 + 813 783 list_for_each_entry(entity, &pipe->entities, list_pipe) { 814 784 vsp1_entity_route_setup(entity); 815 785
+6
drivers/media/platform/vsp1/vsp1_video.h
··· 79 79 struct vsp1_rwpf *output; 80 80 struct vsp1_entity *bru; 81 81 struct vsp1_entity *lif; 82 + struct vsp1_entity *uds; 83 + struct vsp1_entity *uds_input; 82 84 83 85 struct list_head entities; 84 86 }; ··· 144 142 void vsp1_video_cleanup(struct vsp1_video *video); 145 143 146 144 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); 145 + 146 + void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, 147 + struct vsp1_entity *input, 148 + unsigned int alpha); 147 149 148 150 #endif /* __VSP1_VIDEO_H__ */