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 Renesas 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 * V4L2 Subdevice Core Operations
  43 */
  44
  45static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
  46{
  47        struct vsp1_rwpf *rpf = to_rwpf(subdev);
  48        const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
  49        const struct v4l2_pix_format_mplane *format = &rpf->video.format;
  50        const struct v4l2_rect *crop = &rpf->crop;
  51        u32 pstride;
  52        u32 infmt;
  53
  54        if (!enable)
  55                return 0;
  56
  57        /* Source size, stride and crop offsets.
  58         *
  59         * The crop offsets correspond to the location of the crop rectangle top
  60         * left corner in the plane buffer. Only two offsets are needed, as
  61         * planes 2 and 3 always have identical strides.
  62         */
  63        vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
  64                       (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
  65                       (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
  66        vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
  67                       (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
  68                       (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
  69
  70        rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
  71                        + crop->left * fmtinfo->bpp[0] / 8;
  72        pstride = format->plane_fmt[0].bytesperline
  73                << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
  74        if (format->num_planes > 1) {
  75                rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
  76                                + crop->left * fmtinfo->bpp[1] / 8;
  77                pstride |= format->plane_fmt[1].bytesperline
  78                        << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
  79        }
  80
  81        vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
  82
  83        /* Format */
  84        infmt = VI6_RPF_INFMT_CIPM
  85              | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
  86
  87        if (fmtinfo->swap_yc)
  88                infmt |= VI6_RPF_INFMT_SPYCS;
  89        if (fmtinfo->swap_uv)
  90                infmt |= VI6_RPF_INFMT_SPUVS;
  91
  92        if (rpf->entity.formats[RWPF_PAD_SINK].code !=
  93            rpf->entity.formats[RWPF_PAD_SOURCE].code)
  94                infmt |= VI6_RPF_INFMT_CSC;
  95
  96        vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
  97        vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
  98
  99        /* Output location. Composing isn't supported yet. */
 100        vsp1_rpf_write(rpf, VI6_RPF_LOC, 0);
 101
 102        /* Disable alpha, mask and color key. Set the alpha channel to a fixed
 103         * value of 255.
 104         */
 105        vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
 106        vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
 107                       255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
 108        vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
 109        vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
 110
 111        return 0;
 112}
 113
 114/* -----------------------------------------------------------------------------
 115 * V4L2 Subdevice Operations
 116 */
 117
 118static struct v4l2_subdev_video_ops rpf_video_ops = {
 119        .s_stream = rpf_s_stream,
 120};
 121
 122static struct v4l2_subdev_pad_ops rpf_pad_ops = {
 123        .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
 124        .enum_frame_size = vsp1_rwpf_enum_frame_size,
 125        .get_fmt = vsp1_rwpf_get_format,
 126        .set_fmt = vsp1_rwpf_set_format,
 127        .get_selection = vsp1_rwpf_get_selection,
 128        .set_selection = vsp1_rwpf_set_selection,
 129};
 130
 131static struct v4l2_subdev_ops rpf_ops = {
 132        .video  = &rpf_video_ops,
 133        .pad    = &rpf_pad_ops,
 134};
 135
 136/* -----------------------------------------------------------------------------
 137 * Video Device Operations
 138 */
 139
 140static void rpf_vdev_queue(struct vsp1_video *video,
 141                           struct vsp1_video_buffer *buf)
 142{
 143        struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
 144
 145        vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
 146                       buf->addr[0] + rpf->offsets[0]);
 147        if (buf->buf.num_planes > 1)
 148                vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
 149                               buf->addr[1] + rpf->offsets[1]);
 150        if (buf->buf.num_planes > 2)
 151                vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
 152                               buf->addr[2] + rpf->offsets[1]);
 153}
 154
 155static const struct vsp1_video_operations rpf_vdev_ops = {
 156        .queue = rpf_vdev_queue,
 157};
 158
 159/* -----------------------------------------------------------------------------
 160 * Initialization and Cleanup
 161 */
 162
 163struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
 164{
 165        struct v4l2_subdev *subdev;
 166        struct vsp1_video *video;
 167        struct vsp1_rwpf *rpf;
 168        int ret;
 169
 170        rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
 171        if (rpf == NULL)
 172                return ERR_PTR(-ENOMEM);
 173
 174        rpf->max_width = RPF_MAX_WIDTH;
 175        rpf->max_height = RPF_MAX_HEIGHT;
 176
 177        rpf->entity.type = VSP1_ENTITY_RPF;
 178        rpf->entity.index = index;
 179        rpf->entity.id = VI6_DPR_NODE_RPF(index);
 180
 181        ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
 182        if (ret < 0)
 183                return ERR_PTR(ret);
 184
 185        /* Initialize the V4L2 subdev. */
 186        subdev = &rpf->entity.subdev;
 187        v4l2_subdev_init(subdev, &rpf_ops);
 188
 189        subdev->entity.ops = &vsp1_media_ops;
 190        subdev->internal_ops = &vsp1_subdev_internal_ops;
 191        snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
 192                 dev_name(vsp1->dev), index);
 193        v4l2_set_subdevdata(subdev, rpf);
 194        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 195
 196        vsp1_entity_init_formats(subdev, NULL);
 197
 198        /* Initialize the video device. */
 199        video = &rpf->video;
 200
 201        video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 202        video->vsp1 = vsp1;
 203        video->ops = &rpf_vdev_ops;
 204
 205        ret = vsp1_video_init(video, &rpf->entity);
 206        if (ret < 0)
 207                goto error_video;
 208
 209        /* Connect the video device to the RPF. */
 210        ret = media_entity_create_link(&rpf->video.video.entity, 0,
 211                                       &rpf->entity.subdev.entity,
 212                                       RWPF_PAD_SINK,
 213                                       MEDIA_LNK_FL_ENABLED |
 214                                       MEDIA_LNK_FL_IMMUTABLE);
 215        if (ret < 0)
 216                goto error_link;
 217
 218        return rpf;
 219
 220error_link:
 221        vsp1_video_cleanup(video);
 222error_video:
 223        media_entity_cleanup(&rpf->entity.subdev.entity);
 224        return ERR_PTR(ret);
 225}
 226