linux/drivers/media/platform/vsp1/vsp1_rpf.c
<<
>>
Prefs
   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