]>
Commit | Line | Data |
---|---|---|
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 | ||
29 | static 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 | ||
35 | static 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 | ||
45 | static 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 | ||
67 | static const struct v4l2_ctrl_ops rpf_ctrl_ops = { | |
68 | .s_ctrl = rpf_s_ctrl, | |
69 | }; | |
70 | ||
71 | /* ----------------------------------------------------------------------------- | |
72 | * V4L2 Subdevice Core Operations | |
73 | */ | |
74 | ||
75 | static 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 | ||
167 | static struct v4l2_subdev_video_ops rpf_video_ops = { | |
168 | .s_stream = rpf_s_stream, | |
169 | }; | |
170 | ||
171 | static 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 | ||
180 | static 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 | ||
189 | static 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.vb2_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.vb2_buf.num_planes > 2) | |
207 | vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, | |
208 | buf->addr[2] + rpf->offsets[1]); | |
209 | } | |
210 | ||
211 | static const struct vsp1_video_operations rpf_vdev_ops = { | |
212 | .queue = rpf_vdev_queue, | |
213 | }; | |
214 | ||
215 | /* ----------------------------------------------------------------------------- | |
216 | * Initialization and Cleanup | |
217 | */ | |
218 | ||
219 | struct 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 | ||
291 | error: | |
292 | vsp1_entity_destroy(&rpf->entity); | |
293 | return ERR_PTR(ret); | |
294 | } |