blob: 576779f2332b92b3654af2afa01fc2a517684725 [file] [log] [blame]
Laurent Pinchart26e0ca22013-06-04 11:22:30 -03001/*
2 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
3 *
Laurent Pinchart8a1edc52014-02-06 14:42:31 -03004 * Copyright (C) 2013-2014 Renesas Electronics Corporation
Laurent Pinchart26e0ca22013-06-04 11:22:30 -03005 *
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/* -----------------------------------------------------------------------------
Laurent Pinchart7578c202014-05-21 19:00:05 -030042 * 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
50 if (!vsp1_entity_is_streaming(&rpf->entity))
51 return 0;
52
53 switch (ctrl->id) {
54 case V4L2_CID_ALPHA_COMPONENT:
55 vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
56 ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
57 break;
58 }
59
60 return 0;
61}
62
63static const struct v4l2_ctrl_ops rpf_ctrl_ops = {
64 .s_ctrl = rpf_s_ctrl,
65};
66
67/* -----------------------------------------------------------------------------
Laurent Pinchart26e0ca22013-06-04 11:22:30 -030068 * V4L2 Subdevice Core Operations
69 */
70
71static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
72{
73 struct vsp1_rwpf *rpf = to_rwpf(subdev);
74 const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
75 const struct v4l2_pix_format_mplane *format = &rpf->video.format;
Laurent Pincharte5ad37b2013-08-24 20:49:58 -030076 const struct v4l2_rect *crop = &rpf->crop;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -030077 u32 pstride;
78 u32 infmt;
Laurent Pinchart7578c202014-05-21 19:00:05 -030079 int ret;
80
81 ret = vsp1_entity_set_streaming(&rpf->entity, enable);
82 if (ret < 0)
83 return ret;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -030084
85 if (!enable)
86 return 0;
87
Laurent Pincharte5ad37b2013-08-24 20:49:58 -030088 /* Source size, stride and crop offsets.
89 *
90 * The crop offsets correspond to the location of the crop rectangle top
91 * left corner in the plane buffer. Only two offsets are needed, as
92 * planes 2 and 3 always have identical strides.
93 */
Laurent Pinchart26e0ca22013-06-04 11:22:30 -030094 vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
Laurent Pincharte5ad37b2013-08-24 20:49:58 -030095 (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
96 (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
Laurent Pinchart26e0ca22013-06-04 11:22:30 -030097 vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
Laurent Pincharte5ad37b2013-08-24 20:49:58 -030098 (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
99 (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300100
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300101 rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
102 + crop->left * fmtinfo->bpp[0] / 8;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300103 pstride = format->plane_fmt[0].bytesperline
104 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300105 if (format->num_planes > 1) {
106 rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
107 + crop->left * fmtinfo->bpp[1] / 8;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300108 pstride |= format->plane_fmt[1].bytesperline
109 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300110 }
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300111
112 vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
113
114 /* Format */
115 infmt = VI6_RPF_INFMT_CIPM
116 | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
117
118 if (fmtinfo->swap_yc)
119 infmt |= VI6_RPF_INFMT_SPYCS;
120 if (fmtinfo->swap_uv)
121 infmt |= VI6_RPF_INFMT_SPUVS;
122
123 if (rpf->entity.formats[RWPF_PAD_SINK].code !=
124 rpf->entity.formats[RWPF_PAD_SOURCE].code)
125 infmt |= VI6_RPF_INFMT_CSC;
126
127 vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
128 vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
129
Laurent Pinchart629bb6d2013-07-10 18:03:46 -0300130 /* Output location */
131 vsp1_rpf_write(rpf, VI6_RPF_LOC,
132 (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
133 (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300134
Laurent Pinchart7578c202014-05-21 19:00:05 -0300135 /* Use the alpha channel (extended to 8 bits) when available or an
136 * alpha value set through the V4L2_CID_ALPHA_COMPONENT control
137 * otherwise. Disable color keying.
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300138 */
Laurent Pinchart7a52b6d2014-05-26 20:12:53 -0300139 vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
140 (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
141 : VI6_RPF_ALPH_SEL_ASEL_FIXED));
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300142 vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
143 vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
144
145 return 0;
146}
147
148/* -----------------------------------------------------------------------------
149 * V4L2 Subdevice Operations
150 */
151
152static struct v4l2_subdev_video_ops rpf_video_ops = {
153 .s_stream = rpf_s_stream,
154};
155
156static struct v4l2_subdev_pad_ops rpf_pad_ops = {
157 .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
158 .enum_frame_size = vsp1_rwpf_enum_frame_size,
159 .get_fmt = vsp1_rwpf_get_format,
160 .set_fmt = vsp1_rwpf_set_format,
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300161 .get_selection = vsp1_rwpf_get_selection,
162 .set_selection = vsp1_rwpf_set_selection,
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300163};
164
165static struct v4l2_subdev_ops rpf_ops = {
166 .video = &rpf_video_ops,
167 .pad = &rpf_pad_ops,
168};
169
170/* -----------------------------------------------------------------------------
171 * Video Device Operations
172 */
173
174static void rpf_vdev_queue(struct vsp1_video *video,
175 struct vsp1_video_buffer *buf)
176{
177 struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
178
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300179 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
180 buf->addr[0] + rpf->offsets[0]);
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300181 if (buf->buf.num_planes > 1)
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300182 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
183 buf->addr[1] + rpf->offsets[1]);
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300184 if (buf->buf.num_planes > 2)
Laurent Pincharte5ad37b2013-08-24 20:49:58 -0300185 vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
186 buf->addr[2] + rpf->offsets[1]);
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300187}
188
189static const struct vsp1_video_operations rpf_vdev_ops = {
190 .queue = rpf_vdev_queue,
191};
192
193/* -----------------------------------------------------------------------------
194 * Initialization and Cleanup
195 */
196
197struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
198{
199 struct v4l2_subdev *subdev;
200 struct vsp1_video *video;
201 struct vsp1_rwpf *rpf;
202 int ret;
203
204 rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
205 if (rpf == NULL)
206 return ERR_PTR(-ENOMEM);
207
208 rpf->max_width = RPF_MAX_WIDTH;
209 rpf->max_height = RPF_MAX_HEIGHT;
210
211 rpf->entity.type = VSP1_ENTITY_RPF;
212 rpf->entity.index = index;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300213
214 ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
215 if (ret < 0)
216 return ERR_PTR(ret);
217
218 /* Initialize the V4L2 subdev. */
219 subdev = &rpf->entity.subdev;
220 v4l2_subdev_init(subdev, &rpf_ops);
221
222 subdev->entity.ops = &vsp1_media_ops;
223 subdev->internal_ops = &vsp1_subdev_internal_ops;
224 snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
225 dev_name(vsp1->dev), index);
226 v4l2_set_subdevdata(subdev, rpf);
227 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
228
229 vsp1_entity_init_formats(subdev, NULL);
230
Laurent Pinchart7578c202014-05-21 19:00:05 -0300231 /* Initialize the control handler. */
232 v4l2_ctrl_handler_init(&rpf->ctrls, 1);
233 v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
234 0, 255, 1, 255);
235
236 rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
237
238 if (rpf->ctrls.error) {
239 dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
240 index);
241 ret = rpf->ctrls.error;
242 goto error;
243 }
244
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300245 /* Initialize the video device. */
246 video = &rpf->video;
247
248 video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
249 video->vsp1 = vsp1;
250 video->ops = &rpf_vdev_ops;
251
252 ret = vsp1_video_init(video, &rpf->entity);
253 if (ret < 0)
Laurent Pinchart1499be62014-05-28 12:49:13 -0300254 goto error;
255
256 rpf->entity.video = video;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300257
258 /* Connect the video device to the RPF. */
259 ret = media_entity_create_link(&rpf->video.video.entity, 0,
260 &rpf->entity.subdev.entity,
261 RWPF_PAD_SINK,
262 MEDIA_LNK_FL_ENABLED |
263 MEDIA_LNK_FL_IMMUTABLE);
264 if (ret < 0)
Laurent Pinchart1499be62014-05-28 12:49:13 -0300265 goto error;
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300266
267 return rpf;
268
Laurent Pinchart1499be62014-05-28 12:49:13 -0300269error:
270 vsp1_entity_destroy(&rpf->entity);
Laurent Pinchart26e0ca22013-06-04 11:22:30 -0300271 return ERR_PTR(ret);
272}