Commit | Line | Data |
---|---|---|
26e0ca22 LP |
1 | /* |
2 | * vsp1_rpf.c -- R-Car VSP1 Read 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 RPF_MAX_WIDTH 8190 | |
23 | #define RPF_MAX_HEIGHT 8190 | |
24 | ||
25 | /* ----------------------------------------------------------------------------- | |
26 | * Device Access | |
27 | */ | |
28 | ||
26e0ca22 LP |
29 | static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) |
30 | { | |
1517b039 TS |
31 | vsp1_mod_write(&rpf->entity, reg + rpf->entity.index * VI6_RPF_OFFSET, |
32 | data); | |
26e0ca22 LP |
33 | } |
34 | ||
35 | /* ----------------------------------------------------------------------------- | |
36 | * V4L2 Subdevice Core Operations | |
37 | */ | |
38 | ||
39 | static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) | |
40 | { | |
3dbb6100 | 41 | struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); |
26e0ca22 | 42 | struct vsp1_rwpf *rpf = to_rwpf(subdev); |
86960eec LP |
43 | const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; |
44 | const struct v4l2_pix_format_mplane *format = &rpf->format; | |
e790c3cb LP |
45 | const struct v4l2_mbus_framefmt *source_format; |
46 | const struct v4l2_mbus_framefmt *sink_format; | |
b7e5107e LP |
47 | const struct v4l2_rect *crop; |
48 | unsigned int left = 0; | |
49 | unsigned int top = 0; | |
26e0ca22 LP |
50 | u32 pstride; |
51 | u32 infmt; | |
7578c204 | 52 | |
26e0ca22 LP |
53 | if (!enable) |
54 | return 0; | |
55 | ||
e5ad37b6 LP |
56 | /* Source size, stride and crop offsets. |
57 | * | |
58 | * The crop offsets correspond to the location of the crop rectangle top | |
59 | * left corner in the plane buffer. Only two offsets are needed, as | |
60 | * planes 2 and 3 always have identical strides. | |
61 | */ | |
b7e5107e LP |
62 | crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); |
63 | ||
26e0ca22 | 64 | vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, |
e5ad37b6 LP |
65 | (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | |
66 | (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); | |
26e0ca22 | 67 | vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, |
e5ad37b6 LP |
68 | (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | |
69 | (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); | |
26e0ca22 | 70 | |
e5ad37b6 LP |
71 | rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline |
72 | + crop->left * fmtinfo->bpp[0] / 8; | |
26e0ca22 LP |
73 | pstride = format->plane_fmt[0].bytesperline |
74 | << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; | |
857161fc | 75 | |
e5ad37b6 LP |
76 | if (format->num_planes > 1) { |
77 | rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline | |
78 | + crop->left * fmtinfo->bpp[1] / 8; | |
26e0ca22 LP |
79 | pstride |= format->plane_fmt[1].bytesperline |
80 | << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; | |
4d346be5 LP |
81 | } else { |
82 | rpf->offsets[1] = 0; | |
e5ad37b6 | 83 | } |
26e0ca22 LP |
84 | |
85 | vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); | |
86 | ||
87 | /* Format */ | |
e790c3cb LP |
88 | sink_format = vsp1_entity_get_pad_format(&rpf->entity, |
89 | rpf->entity.config, | |
90 | RWPF_PAD_SINK); | |
91 | source_format = vsp1_entity_get_pad_format(&rpf->entity, | |
92 | rpf->entity.config, | |
93 | RWPF_PAD_SOURCE); | |
94 | ||
26e0ca22 LP |
95 | infmt = VI6_RPF_INFMT_CIPM |
96 | | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); | |
97 | ||
98 | if (fmtinfo->swap_yc) | |
99 | infmt |= VI6_RPF_INFMT_SPYCS; | |
100 | if (fmtinfo->swap_uv) | |
101 | infmt |= VI6_RPF_INFMT_SPUVS; | |
102 | ||
e790c3cb | 103 | if (sink_format->code != source_format->code) |
26e0ca22 LP |
104 | infmt |= VI6_RPF_INFMT_CSC; |
105 | ||
106 | vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); | |
107 | vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); | |
108 | ||
629bb6d4 | 109 | /* Output location */ |
b7e5107e LP |
110 | if (pipe->bru) { |
111 | const struct v4l2_rect *compose; | |
112 | ||
113 | compose = vsp1_entity_get_pad_compose(pipe->bru, | |
114 | pipe->bru->config, | |
115 | rpf->bru_input); | |
116 | left = compose->left; | |
117 | top = compose->top; | |
118 | } | |
119 | ||
629bb6d4 | 120 | vsp1_rpf_write(rpf, VI6_RPF_LOC, |
b7e5107e LP |
121 | (left << VI6_RPF_LOC_HCOORD_SHIFT) | |
122 | (top << VI6_RPF_LOC_VCOORD_SHIFT)); | |
26e0ca22 | 123 | |
7578c204 LP |
124 | /* Use the alpha channel (extended to 8 bits) when available or an |
125 | * alpha value set through the V4L2_CID_ALPHA_COMPONENT control | |
126 | * otherwise. Disable color keying. | |
26e0ca22 | 127 | */ |
7a52b6de LP |
128 | vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | |
129 | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED | |
130 | : VI6_RPF_ALPH_SEL_ASEL_FIXED)); | |
3dbb6100 | 131 | |
3dbb6100 | 132 | vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, |
bd2fdd5a LP |
133 | rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); |
134 | ||
135 | vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha); | |
3dbb6100 | 136 | |
26e0ca22 LP |
137 | vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); |
138 | vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | /* ----------------------------------------------------------------------------- | |
144 | * V4L2 Subdevice Operations | |
145 | */ | |
146 | ||
147 | static struct v4l2_subdev_video_ops rpf_video_ops = { | |
148 | .s_stream = rpf_s_stream, | |
149 | }; | |
150 | ||
151 | static struct v4l2_subdev_pad_ops rpf_pad_ops = { | |
0efdf0f5 | 152 | .init_cfg = vsp1_entity_init_cfg, |
26e0ca22 LP |
153 | .enum_mbus_code = vsp1_rwpf_enum_mbus_code, |
154 | .enum_frame_size = vsp1_rwpf_enum_frame_size, | |
155 | .get_fmt = vsp1_rwpf_get_format, | |
156 | .set_fmt = vsp1_rwpf_set_format, | |
e5ad37b6 LP |
157 | .get_selection = vsp1_rwpf_get_selection, |
158 | .set_selection = vsp1_rwpf_set_selection, | |
26e0ca22 LP |
159 | }; |
160 | ||
161 | static struct v4l2_subdev_ops rpf_ops = { | |
162 | .video = &rpf_video_ops, | |
163 | .pad = &rpf_pad_ops, | |
164 | }; | |
165 | ||
166 | /* ----------------------------------------------------------------------------- | |
52434534 | 167 | * VSP1 Entity Operations |
26e0ca22 LP |
168 | */ |
169 | ||
52434534 | 170 | static void rpf_set_memory(struct vsp1_entity *entity) |
26e0ca22 | 171 | { |
52434534 LP |
172 | struct vsp1_rwpf *rpf = entity_to_rwpf(entity); |
173 | ||
e5ad37b6 | 174 | vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, |
351bbf99 | 175 | rpf->mem.addr[0] + rpf->offsets[0]); |
4d346be5 | 176 | vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, |
351bbf99 | 177 | rpf->mem.addr[1] + rpf->offsets[1]); |
4d346be5 | 178 | vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, |
351bbf99 | 179 | rpf->mem.addr[2] + rpf->offsets[1]); |
26e0ca22 LP |
180 | } |
181 | ||
52434534 | 182 | static const struct vsp1_entity_operations rpf_entity_ops = { |
b58faa95 | 183 | .set_memory = rpf_set_memory, |
26e0ca22 LP |
184 | }; |
185 | ||
186 | /* ----------------------------------------------------------------------------- | |
187 | * Initialization and Cleanup | |
188 | */ | |
189 | ||
190 | struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) | |
191 | { | |
26e0ca22 | 192 | struct vsp1_rwpf *rpf; |
823329df | 193 | char name[6]; |
26e0ca22 LP |
194 | int ret; |
195 | ||
196 | rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); | |
197 | if (rpf == NULL) | |
198 | return ERR_PTR(-ENOMEM); | |
199 | ||
200 | rpf->max_width = RPF_MAX_WIDTH; | |
201 | rpf->max_height = RPF_MAX_HEIGHT; | |
202 | ||
52434534 | 203 | rpf->entity.ops = &rpf_entity_ops; |
26e0ca22 LP |
204 | rpf->entity.type = VSP1_ENTITY_RPF; |
205 | rpf->entity.index = index; | |
26e0ca22 | 206 | |
823329df LP |
207 | sprintf(name, "rpf.%u", index); |
208 | ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops); | |
26e0ca22 LP |
209 | if (ret < 0) |
210 | return ERR_PTR(ret); | |
211 | ||
7578c204 | 212 | /* Initialize the control handler. */ |
bd2fdd5a LP |
213 | ret = vsp1_rwpf_init_ctrls(rpf); |
214 | if (ret < 0) { | |
7578c204 LP |
215 | dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", |
216 | index); | |
7578c204 LP |
217 | goto error; |
218 | } | |
219 | ||
26e0ca22 LP |
220 | return rpf; |
221 | ||
1499be67 LP |
222 | error: |
223 | vsp1_entity_destroy(&rpf->entity); | |
26e0ca22 LP |
224 | return ERR_PTR(ret); |
225 | } |