277 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters4 *5 * Copyright (C) 2013-2014 Renesas Electronics Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <media/v4l2-subdev.h>11 12#include "vsp1.h"13#include "vsp1_rwpf.h"14#include "vsp1_video.h"15 16#define RWPF_MIN_WIDTH 117#define RWPF_MIN_HEIGHT 118 19/* -----------------------------------------------------------------------------20 * V4L2 Subdevice Operations21 */22 23static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,24 struct v4l2_subdev_state *sd_state,25 struct v4l2_subdev_mbus_code_enum *code)26{27 static const unsigned int codes[] = {28 MEDIA_BUS_FMT_ARGB8888_1X32,29 MEDIA_BUS_FMT_AHSV8888_1X32,30 MEDIA_BUS_FMT_AYUV8_1X32,31 };32 33 if (code->index >= ARRAY_SIZE(codes))34 return -EINVAL;35 36 code->code = codes[code->index];37 38 return 0;39}40 41static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,42 struct v4l2_subdev_state *sd_state,43 struct v4l2_subdev_frame_size_enum *fse)44{45 struct vsp1_rwpf *rwpf = to_rwpf(subdev);46 47 return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,48 RWPF_MIN_WIDTH,49 RWPF_MIN_HEIGHT, rwpf->max_width,50 rwpf->max_height);51}52 53static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,54 struct v4l2_subdev_state *sd_state,55 struct v4l2_subdev_format *fmt)56{57 struct vsp1_rwpf *rwpf = to_rwpf(subdev);58 struct v4l2_subdev_state *state;59 struct v4l2_mbus_framefmt *format;60 int ret = 0;61 62 mutex_lock(&rwpf->entity.lock);63 64 state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which);65 if (!state) {66 ret = -EINVAL;67 goto done;68 }69 70 /* Default to YUV if the requested format is not supported. */71 if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&72 fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&73 fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)74 fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;75 76 format = v4l2_subdev_state_get_format(state, fmt->pad);77 78 if (fmt->pad == RWPF_PAD_SOURCE) {79 /*80 * The RWPF performs format conversion but can't scale, only the81 * format code can be changed on the source pad.82 */83 format->code = fmt->format.code;84 fmt->format = *format;85 goto done;86 }87 88 format->code = fmt->format.code;89 format->width = clamp_t(unsigned int, fmt->format.width,90 RWPF_MIN_WIDTH, rwpf->max_width);91 format->height = clamp_t(unsigned int, fmt->format.height,92 RWPF_MIN_HEIGHT, rwpf->max_height);93 format->field = V4L2_FIELD_NONE;94 format->colorspace = V4L2_COLORSPACE_SRGB;95 96 fmt->format = *format;97 98 if (rwpf->entity.type == VSP1_ENTITY_RPF) {99 struct v4l2_rect *crop;100 101 /* Update the sink crop rectangle. */102 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);103 crop->left = 0;104 crop->top = 0;105 crop->width = fmt->format.width;106 crop->height = fmt->format.height;107 }108 109 /* Propagate the format to the source pad. */110 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);111 *format = fmt->format;112 113 if (rwpf->flip.rotate) {114 format->width = fmt->format.height;115 format->height = fmt->format.width;116 }117 118done:119 mutex_unlock(&rwpf->entity.lock);120 return ret;121}122 123static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,124 struct v4l2_subdev_state *sd_state,125 struct v4l2_subdev_selection *sel)126{127 struct vsp1_rwpf *rwpf = to_rwpf(subdev);128 struct v4l2_subdev_state *state;129 struct v4l2_mbus_framefmt *format;130 int ret = 0;131 132 /*133 * Cropping is only supported on the RPF and is implemented on the sink134 * pad.135 */136 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)137 return -EINVAL;138 139 mutex_lock(&rwpf->entity.lock);140 141 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);142 if (!state) {143 ret = -EINVAL;144 goto done;145 }146 147 switch (sel->target) {148 case V4L2_SEL_TGT_CROP:149 sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);150 break;151 152 case V4L2_SEL_TGT_CROP_BOUNDS:153 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);154 sel->r.left = 0;155 sel->r.top = 0;156 sel->r.width = format->width;157 sel->r.height = format->height;158 break;159 160 default:161 ret = -EINVAL;162 break;163 }164 165done:166 mutex_unlock(&rwpf->entity.lock);167 return ret;168}169 170static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,171 struct v4l2_subdev_state *sd_state,172 struct v4l2_subdev_selection *sel)173{174 struct vsp1_rwpf *rwpf = to_rwpf(subdev);175 struct v4l2_subdev_state *state;176 struct v4l2_mbus_framefmt *format;177 struct v4l2_rect *crop;178 int ret = 0;179 180 /*181 * Cropping is only supported on the RPF and is implemented on the sink182 * pad.183 */184 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)185 return -EINVAL;186 187 if (sel->target != V4L2_SEL_TGT_CROP)188 return -EINVAL;189 190 mutex_lock(&rwpf->entity.lock);191 192 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);193 if (!state) {194 ret = -EINVAL;195 goto done;196 }197 198 /* Make sure the crop rectangle is entirely contained in the image. */199 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);200 201 /*202 * Restrict the crop rectangle coordinates to multiples of 2 to avoid203 * shifting the color plane.204 */205 if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {206 sel->r.left = ALIGN(sel->r.left, 2);207 sel->r.top = ALIGN(sel->r.top, 2);208 sel->r.width = round_down(sel->r.width, 2);209 sel->r.height = round_down(sel->r.height, 2);210 }211 212 sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);213 sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);214 sel->r.width = min_t(unsigned int, sel->r.width,215 format->width - sel->r.left);216 sel->r.height = min_t(unsigned int, sel->r.height,217 format->height - sel->r.top);218 219 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);220 *crop = sel->r;221 222 /* Propagate the format to the source pad. */223 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);224 format->width = crop->width;225 format->height = crop->height;226 227done:228 mutex_unlock(&rwpf->entity.lock);229 return ret;230}231 232static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {233 .enum_mbus_code = vsp1_rwpf_enum_mbus_code,234 .enum_frame_size = vsp1_rwpf_enum_frame_size,235 .get_fmt = vsp1_subdev_get_pad_format,236 .set_fmt = vsp1_rwpf_set_format,237 .get_selection = vsp1_rwpf_get_selection,238 .set_selection = vsp1_rwpf_set_selection,239};240 241const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = {242 .pad = &vsp1_rwpf_pad_ops,243};244 245/* -----------------------------------------------------------------------------246 * Controls247 */248 249static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)250{251 struct vsp1_rwpf *rwpf =252 container_of(ctrl->handler, struct vsp1_rwpf, ctrls);253 254 switch (ctrl->id) {255 case V4L2_CID_ALPHA_COMPONENT:256 rwpf->alpha = ctrl->val;257 break;258 }259 260 return 0;261}262 263static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {264 .s_ctrl = vsp1_rwpf_s_ctrl,265};266 267int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)268{269 v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);270 v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,271 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);272 273 rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;274 275 return rwpf->ctrls.error;276}277