]>
Commit | Line | Data |
---|---|---|
26e0ca22 LP |
1 | /* |
2 | * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter | |
3 | * | |
8a1edc55 | 4 | * Copyright (C) 2013-2014 Renesas Electronics Corporation |
26e0ca22 LP |
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 WPF_MAX_WIDTH 2048 | |
23 | #define WPF_MAX_HEIGHT 2048 | |
24 | ||
25 | /* ----------------------------------------------------------------------------- | |
26 | * Device Access | |
27 | */ | |
28 | ||
29 | static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg) | |
30 | { | |
31 | return vsp1_read(wpf->entity.vsp1, | |
32 | reg + wpf->entity.index * VI6_WPF_OFFSET); | |
33 | } | |
34 | ||
35 | static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) | |
36 | { | |
37 | vsp1_write(wpf->entity.vsp1, | |
38 | reg + wpf->entity.index * VI6_WPF_OFFSET, data); | |
39 | } | |
40 | ||
7578c204 LP |
41 | /* ----------------------------------------------------------------------------- |
42 | * Controls | |
43 | */ | |
44 | ||
45 | static int wpf_s_ctrl(struct v4l2_ctrl *ctrl) | |
46 | { | |
47 | struct vsp1_rwpf *wpf = | |
48 | container_of(ctrl->handler, struct vsp1_rwpf, ctrls); | |
49 | u32 value; | |
50 | ||
51 | if (!vsp1_entity_is_streaming(&wpf->entity)) | |
52 | return 0; | |
53 | ||
54 | switch (ctrl->id) { | |
55 | case V4L2_CID_ALPHA_COMPONENT: | |
56 | value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT); | |
57 | value &= ~VI6_WPF_OUTFMT_PDV_MASK; | |
58 | value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT; | |
59 | vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value); | |
60 | break; | |
61 | } | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static const struct v4l2_ctrl_ops wpf_ctrl_ops = { | |
67 | .s_ctrl = wpf_s_ctrl, | |
68 | }; | |
69 | ||
26e0ca22 LP |
70 | /* ----------------------------------------------------------------------------- |
71 | * V4L2 Subdevice Core Operations | |
72 | */ | |
73 | ||
74 | static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) | |
75 | { | |
5aeb01ad | 76 | struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); |
26e0ca22 | 77 | struct vsp1_rwpf *wpf = to_rwpf(subdev); |
26e0ca22 | 78 | struct vsp1_device *vsp1 = wpf->entity.vsp1; |
e5ad37b6 | 79 | const struct v4l2_rect *crop = &wpf->crop; |
26e0ca22 LP |
80 | unsigned int i; |
81 | u32 srcrpf = 0; | |
82 | u32 outfmt = 0; | |
7578c204 LP |
83 | int ret; |
84 | ||
85 | ret = vsp1_entity_set_streaming(&wpf->entity, enable); | |
86 | if (ret < 0) | |
87 | return ret; | |
26e0ca22 LP |
88 | |
89 | if (!enable) { | |
90 | vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); | |
d6c71e8c | 91 | vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0); |
26e0ca22 LP |
92 | return 0; |
93 | } | |
94 | ||
5d0beeec TH |
95 | /* Sources. If the pipeline has a single input and BRU is not used, |
96 | * configure it as the master layer. Otherwise configure all | |
97 | * inputs as sub-layers and select the virtual RPF as the master | |
98 | * layer. | |
629bb6d4 | 99 | */ |
26e0ca22 LP |
100 | for (i = 0; i < pipe->num_inputs; ++i) { |
101 | struct vsp1_rwpf *input = pipe->inputs[i]; | |
102 | ||
5d0beeec | 103 | srcrpf |= (!pipe->bru && pipe->num_inputs == 1) |
629bb6d4 LP |
104 | ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) |
105 | : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); | |
26e0ca22 LP |
106 | } |
107 | ||
5d0beeec | 108 | if (pipe->bru || pipe->num_inputs > 1) |
629bb6d4 LP |
109 | srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; |
110 | ||
26e0ca22 LP |
111 | vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); |
112 | ||
e5ad37b6 | 113 | /* Destination stride. */ |
26e0ca22 LP |
114 | if (!pipe->lif) { |
115 | struct v4l2_pix_format_mplane *format = &wpf->video.format; | |
116 | ||
117 | vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, | |
118 | format->plane_fmt[0].bytesperline); | |
119 | if (format->num_planes > 1) | |
120 | vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, | |
121 | format->plane_fmt[1].bytesperline); | |
122 | } | |
123 | ||
e5ad37b6 LP |
124 | vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | |
125 | (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | | |
126 | (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); | |
127 | vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | | |
128 | (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | | |
129 | (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); | |
26e0ca22 LP |
130 | |
131 | /* Format */ | |
132 | if (!pipe->lif) { | |
133 | const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo; | |
134 | ||
135 | outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; | |
136 | ||
7a52b6de LP |
137 | if (fmtinfo->alpha) |
138 | outfmt |= VI6_WPF_OUTFMT_PXA; | |
26e0ca22 LP |
139 | if (fmtinfo->swap_yc) |
140 | outfmt |= VI6_WPF_OUTFMT_SPYCS; | |
141 | if (fmtinfo->swap_uv) | |
142 | outfmt |= VI6_WPF_OUTFMT_SPUVS; | |
143 | ||
144 | vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); | |
145 | } | |
146 | ||
147 | if (wpf->entity.formats[RWPF_PAD_SINK].code != | |
148 | wpf->entity.formats[RWPF_PAD_SOURCE].code) | |
149 | outfmt |= VI6_WPF_OUTFMT_CSC; | |
150 | ||
7578c204 LP |
151 | /* Take the control handler lock to ensure that the PDV value won't be |
152 | * changed behind our back by a set control operation. | |
153 | */ | |
154 | mutex_lock(wpf->ctrls.lock); | |
155 | outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK; | |
26e0ca22 | 156 | vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); |
7578c204 | 157 | mutex_unlock(wpf->ctrls.lock); |
26e0ca22 LP |
158 | |
159 | vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), | |
160 | VI6_DPR_WPF_FPORCH_FP_WPFN); | |
161 | ||
162 | vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0); | |
163 | ||
164 | /* Enable interrupts */ | |
165 | vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0); | |
166 | vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), | |
167 | VI6_WFP_IRQ_ENB_FREE); | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | /* ----------------------------------------------------------------------------- | |
173 | * V4L2 Subdevice Operations | |
174 | */ | |
175 | ||
176 | static struct v4l2_subdev_video_ops wpf_video_ops = { | |
177 | .s_stream = wpf_s_stream, | |
178 | }; | |
179 | ||
180 | static struct v4l2_subdev_pad_ops wpf_pad_ops = { | |
181 | .enum_mbus_code = vsp1_rwpf_enum_mbus_code, | |
182 | .enum_frame_size = vsp1_rwpf_enum_frame_size, | |
183 | .get_fmt = vsp1_rwpf_get_format, | |
184 | .set_fmt = vsp1_rwpf_set_format, | |
e5ad37b6 LP |
185 | .get_selection = vsp1_rwpf_get_selection, |
186 | .set_selection = vsp1_rwpf_set_selection, | |
26e0ca22 LP |
187 | }; |
188 | ||
189 | static struct v4l2_subdev_ops wpf_ops = { | |
190 | .video = &wpf_video_ops, | |
191 | .pad = &wpf_pad_ops, | |
192 | }; | |
193 | ||
194 | /* ----------------------------------------------------------------------------- | |
195 | * Video Device Operations | |
196 | */ | |
197 | ||
198 | static void wpf_vdev_queue(struct vsp1_video *video, | |
199 | struct vsp1_video_buffer *buf) | |
200 | { | |
201 | struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video); | |
202 | ||
203 | vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]); | |
204 | if (buf->buf.num_planes > 1) | |
205 | vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]); | |
206 | if (buf->buf.num_planes > 2) | |
207 | vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]); | |
208 | } | |
209 | ||
210 | static const struct vsp1_video_operations wpf_vdev_ops = { | |
211 | .queue = wpf_vdev_queue, | |
212 | }; | |
213 | ||
214 | /* ----------------------------------------------------------------------------- | |
215 | * Initialization and Cleanup | |
216 | */ | |
217 | ||
218 | struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) | |
219 | { | |
220 | struct v4l2_subdev *subdev; | |
221 | struct vsp1_video *video; | |
222 | struct vsp1_rwpf *wpf; | |
223 | unsigned int flags; | |
224 | int ret; | |
225 | ||
226 | wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); | |
227 | if (wpf == NULL) | |
228 | return ERR_PTR(-ENOMEM); | |
229 | ||
230 | wpf->max_width = WPF_MAX_WIDTH; | |
231 | wpf->max_height = WPF_MAX_HEIGHT; | |
232 | ||
233 | wpf->entity.type = VSP1_ENTITY_WPF; | |
234 | wpf->entity.index = index; | |
26e0ca22 LP |
235 | |
236 | ret = vsp1_entity_init(vsp1, &wpf->entity, 2); | |
237 | if (ret < 0) | |
238 | return ERR_PTR(ret); | |
239 | ||
240 | /* Initialize the V4L2 subdev. */ | |
241 | subdev = &wpf->entity.subdev; | |
242 | v4l2_subdev_init(subdev, &wpf_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 wpf.%u", | |
247 | dev_name(vsp1->dev), index); | |
248 | v4l2_set_subdevdata(subdev, wpf); | |
249 | subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
250 | ||
251 | vsp1_entity_init_formats(subdev, NULL); | |
252 | ||
7578c204 LP |
253 | /* Initialize the control handler. */ |
254 | v4l2_ctrl_handler_init(&wpf->ctrls, 1); | |
255 | v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, | |
256 | 0, 255, 1, 255); | |
257 | ||
258 | wpf->entity.subdev.ctrl_handler = &wpf->ctrls; | |
259 | ||
260 | if (wpf->ctrls.error) { | |
261 | dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", | |
262 | index); | |
263 | ret = wpf->ctrls.error; | |
264 | goto error; | |
265 | } | |
266 | ||
26e0ca22 LP |
267 | /* Initialize the video device. */ |
268 | video = &wpf->video; | |
269 | ||
270 | video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
271 | video->vsp1 = vsp1; | |
272 | video->ops = &wpf_vdev_ops; | |
273 | ||
274 | ret = vsp1_video_init(video, &wpf->entity); | |
275 | if (ret < 0) | |
1499be67 LP |
276 | goto error; |
277 | ||
278 | wpf->entity.video = video; | |
26e0ca22 LP |
279 | |
280 | /* Connect the video device to the WPF. All connections are immutable | |
281 | * except for the WPF0 source link if a LIF is present. | |
282 | */ | |
283 | flags = MEDIA_LNK_FL_ENABLED; | |
32d17597 | 284 | if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0) |
26e0ca22 LP |
285 | flags |= MEDIA_LNK_FL_IMMUTABLE; |
286 | ||
287 | ret = media_entity_create_link(&wpf->entity.subdev.entity, | |
288 | RWPF_PAD_SOURCE, | |
289 | &wpf->video.video.entity, 0, flags); | |
290 | if (ret < 0) | |
1499be67 | 291 | goto error; |
26e0ca22 LP |
292 | |
293 | wpf->entity.sink = &wpf->video.video.entity; | |
294 | ||
295 | return wpf; | |
296 | ||
1499be67 LP |
297 | error: |
298 | vsp1_entity_destroy(&wpf->entity); | |
26e0ca22 LP |
299 | return ERR_PTR(ret); |
300 | } |