1/* 2 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter 3 * 4 * Copyright (C) 2013-2014 Renesas Electronics Corporation 5 * 6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14#include <linux/device.h> 15 16#include <media/v4l2-subdev.h> 17 18#include "vsp1.h" 19#include "vsp1_rwpf.h" 20#include "vsp1_video.h" 21 22#define RPF_MAX_WIDTH 8190 23#define RPF_MAX_HEIGHT 8190 24 25/* ----------------------------------------------------------------------------- 26 * Device Access 27 */ 28 29static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg) 30{ 31 return vsp1_read(rpf->entity.vsp1, 32 reg + rpf->entity.index * VI6_RPF_OFFSET); 33} 34 35static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) 36{ 37 vsp1_write(rpf->entity.vsp1, 38 reg + rpf->entity.index * VI6_RPF_OFFSET, data); 39} 40 41/* ----------------------------------------------------------------------------- 42 * Controls 43 */ 44 45static int rpf_s_ctrl(struct v4l2_ctrl *ctrl) 46{ 47 struct vsp1_rwpf *rpf = 48 container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 49 struct vsp1_pipeline *pipe; 50 51 if (!vsp1_entity_is_streaming(&rpf->entity)) 52 return 0; 53 54 switch (ctrl->id) { 55 case V4L2_CID_ALPHA_COMPONENT: 56 vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, 57 ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); 58 59 pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity); 60 vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val); 61 break; 62 } 63 64 return 0; 65} 66 67static const struct v4l2_ctrl_ops rpf_ctrl_ops = { 68 .s_ctrl = rpf_s_ctrl, 69}; 70 71/* ----------------------------------------------------------------------------- 72 * V4L2 Subdevice Core Operations 73 */ 74 75static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) 76{ 77 struct vsp1_rwpf *rpf = to_rwpf(subdev); 78 const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo; 79 const struct v4l2_pix_format_mplane *format = &rpf->video.format; 80 const struct v4l2_rect *crop = &rpf->crop; 81 u32 pstride; 82 u32 infmt; 83 int ret; 84 85 ret = vsp1_entity_set_streaming(&rpf->entity, enable); 86 if (ret < 0) 87 return ret; 88 89 if (!enable) 90 return 0; 91 92 /* Source size, stride and crop offsets. 93 * 94 * The crop offsets correspond to the location of the crop rectangle top 95 * left corner in the plane buffer. Only two offsets are needed, as 96 * planes 2 and 3 always have identical strides. 97 */ 98 vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, 99 (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | 100 (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); 101 vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, 102 (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | 103 (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); 104 105 rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline 106 + crop->left * fmtinfo->bpp[0] / 8; 107 pstride = format->plane_fmt[0].bytesperline 108 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; 109 110 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, 111 rpf->buf_addr[0] + rpf->offsets[0]); 112 113 if (format->num_planes > 1) { 114 rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline 115 + crop->left * fmtinfo->bpp[1] / 8; 116 pstride |= format->plane_fmt[1].bytesperline 117 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; 118 119 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, 120 rpf->buf_addr[1] + rpf->offsets[1]); 121 122 if (format->num_planes > 2) 123 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, 124 rpf->buf_addr[2] + rpf->offsets[1]); 125 } 126 127 vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); 128 129 /* Format */ 130 infmt = VI6_RPF_INFMT_CIPM 131 | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); 132 133 if (fmtinfo->swap_yc) 134 infmt |= VI6_RPF_INFMT_SPYCS; 135 if (fmtinfo->swap_uv) 136 infmt |= VI6_RPF_INFMT_SPUVS; 137 138 if (rpf->entity.formats[RWPF_PAD_SINK].code != 139 rpf->entity.formats[RWPF_PAD_SOURCE].code) 140 infmt |= VI6_RPF_INFMT_CSC; 141 142 vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); 143 vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); 144 145 /* Output location */ 146 vsp1_rpf_write(rpf, VI6_RPF_LOC, 147 (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | 148 (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); 149 150 /* Use the alpha channel (extended to 8 bits) when available or an 151 * alpha value set through the V4L2_CID_ALPHA_COMPONENT control 152 * otherwise. Disable color keying. 153 */ 154 vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | 155 (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED 156 : VI6_RPF_ALPH_SEL_ASEL_FIXED)); 157 vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); 158 vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); 159 160 return 0; 161} 162 163/* ----------------------------------------------------------------------------- 164 * V4L2 Subdevice Operations 165 */ 166 167static struct v4l2_subdev_video_ops rpf_video_ops = { 168 .s_stream = rpf_s_stream, 169}; 170 171static struct v4l2_subdev_pad_ops rpf_pad_ops = { 172 .enum_mbus_code = vsp1_rwpf_enum_mbus_code, 173 .enum_frame_size = vsp1_rwpf_enum_frame_size, 174 .get_fmt = vsp1_rwpf_get_format, 175 .set_fmt = vsp1_rwpf_set_format, 176 .get_selection = vsp1_rwpf_get_selection, 177 .set_selection = vsp1_rwpf_set_selection, 178}; 179 180static struct v4l2_subdev_ops rpf_ops = { 181 .video = &rpf_video_ops, 182 .pad = &rpf_pad_ops, 183}; 184 185/* ----------------------------------------------------------------------------- 186 * Video Device Operations 187 */ 188 189static void rpf_vdev_queue(struct vsp1_video *video, 190 struct vsp1_video_buffer *buf) 191{ 192 struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); 193 unsigned int i; 194 195 for (i = 0; i < 3; ++i) 196 rpf->buf_addr[i] = buf->addr[i]; 197 198 if (!vsp1_entity_is_streaming(&rpf->entity)) 199 return; 200 201 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, 202 buf->addr[0] + rpf->offsets[0]); 203 if (buf->buf.num_planes > 1) 204 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, 205 buf->addr[1] + rpf->offsets[1]); 206 if (buf->buf.num_planes > 2) 207 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, 208 buf->addr[2] + rpf->offsets[1]); 209} 210 211static const struct vsp1_video_operations rpf_vdev_ops = { 212 .queue = rpf_vdev_queue, 213}; 214 215/* ----------------------------------------------------------------------------- 216 * Initialization and Cleanup 217 */ 218 219struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) 220{ 221 struct v4l2_subdev *subdev; 222 struct vsp1_video *video; 223 struct vsp1_rwpf *rpf; 224 int ret; 225 226 rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); 227 if (rpf == NULL) 228 return ERR_PTR(-ENOMEM); 229 230 rpf->max_width = RPF_MAX_WIDTH; 231 rpf->max_height = RPF_MAX_HEIGHT; 232 233 rpf->entity.type = VSP1_ENTITY_RPF; 234 rpf->entity.index = index; 235 236 ret = vsp1_entity_init(vsp1, &rpf->entity, 2); 237 if (ret < 0) 238 return ERR_PTR(ret); 239 240 /* Initialize the V4L2 subdev. */ 241 subdev = &rpf->entity.subdev; 242 v4l2_subdev_init(subdev, &rpf_ops); 243 244 subdev->entity.ops = &vsp1_media_ops; 245 subdev->internal_ops = &vsp1_subdev_internal_ops; 246 snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", 247 dev_name(vsp1->dev), index); 248 v4l2_set_subdevdata(subdev, rpf); 249 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 250 251 vsp1_entity_init_formats(subdev, NULL); 252 253 /* Initialize the control handler. */ 254 v4l2_ctrl_handler_init(&rpf->ctrls, 1); 255 v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 256 0, 255, 1, 255); 257 258 rpf->entity.subdev.ctrl_handler = &rpf->ctrls; 259 260 if (rpf->ctrls.error) { 261 dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", 262 index); 263 ret = rpf->ctrls.error; 264 goto error; 265 } 266 267 /* Initialize the video device. */ 268 video = &rpf->video; 269 270 video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 271 video->vsp1 = vsp1; 272 video->ops = &rpf_vdev_ops; 273 274 ret = vsp1_video_init(video, &rpf->entity); 275 if (ret < 0) 276 goto error; 277 278 rpf->entity.video = video; 279 280 /* Connect the video device to the RPF. */ 281 ret = media_entity_create_link(&rpf->video.video.entity, 0, 282 &rpf->entity.subdev.entity, 283 RWPF_PAD_SINK, 284 MEDIA_LNK_FL_ENABLED | 285 MEDIA_LNK_FL_IMMUTABLE); 286 if (ret < 0) 287 goto error; 288 289 return rpf; 290 291error: 292 vsp1_entity_destroy(&rpf->entity); 293 return ERR_PTR(ret); 294} 295