]>
Commit | Line | Data |
---|---|---|
b278248f HY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2013 - 2020 Intel Corporation | |
3 | ||
4 | #include <linux/device.h> | |
5 | #include <linux/module.h> | |
6 | ||
7 | #include <media/media-entity.h> | |
8 | #include <media/v4l2-device.h> | |
9 | #include <media/v4l2-event.h> | |
10 | ||
11 | #include "ipu.h" | |
12 | #include "ipu-bus.h" | |
13 | #include "ipu-isys.h" | |
14 | #include "ipu-isys-subdev.h" | |
15 | #include "ipu-isys-tpg.h" | |
16 | #include "ipu-isys-video.h" | |
17 | #include "ipu-platform-isys-csi2-reg.h" | |
18 | ||
19 | static const u32 tpg_supported_codes_pad[] = { | |
20 | MEDIA_BUS_FMT_SBGGR8_1X8, | |
21 | MEDIA_BUS_FMT_SGBRG8_1X8, | |
22 | MEDIA_BUS_FMT_SGRBG8_1X8, | |
23 | MEDIA_BUS_FMT_SRGGB8_1X8, | |
24 | MEDIA_BUS_FMT_SBGGR10_1X10, | |
25 | MEDIA_BUS_FMT_SGBRG10_1X10, | |
26 | MEDIA_BUS_FMT_SGRBG10_1X10, | |
27 | MEDIA_BUS_FMT_SRGGB10_1X10, | |
28 | 0, | |
29 | }; | |
30 | ||
31 | static const u32 *tpg_supported_codes[] = { | |
32 | tpg_supported_codes_pad, | |
33 | }; | |
34 | ||
35 | static struct v4l2_subdev_internal_ops tpg_sd_internal_ops = { | |
36 | .open = ipu_isys_subdev_open, | |
37 | .close = ipu_isys_subdev_close, | |
38 | }; | |
39 | ||
40 | static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { | |
41 | .s_stream = tpg_set_stream, | |
42 | }; | |
43 | ||
44 | static int ipu_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) | |
45 | { | |
46 | struct ipu_isys_tpg *tpg = container_of(container_of(ctrl->handler, | |
47 | struct | |
48 | ipu_isys_subdev, | |
49 | ctrl_handler), | |
50 | struct ipu_isys_tpg, asd); | |
51 | ||
52 | switch (ctrl->id) { | |
53 | case V4L2_CID_HBLANK: | |
54 | writel(ctrl->val, tpg->base + MIPI_GEN_REG_SYNG_HBLANK_CYC); | |
55 | break; | |
56 | case V4L2_CID_VBLANK: | |
57 | writel(ctrl->val, tpg->base + MIPI_GEN_REG_SYNG_VBLANK_CYC); | |
58 | break; | |
59 | case V4L2_CID_TEST_PATTERN: | |
60 | writel(ctrl->val, tpg->base + MIPI_GEN_REG_TPG_MODE); | |
61 | break; | |
62 | } | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
67 | static const struct v4l2_ctrl_ops ipu_isys_tpg_ctrl_ops = { | |
68 | .s_ctrl = ipu_isys_tpg_s_ctrl, | |
69 | }; | |
70 | ||
71 | static s64 ipu_isys_tpg_rate(struct ipu_isys_tpg *tpg, unsigned int bpp) | |
72 | { | |
73 | return MIPI_GEN_PPC * IPU_ISYS_FREQ / bpp; | |
74 | } | |
75 | ||
76 | static const char *const tpg_mode_items[] = { | |
77 | "Ramp", | |
78 | "Checkerboard", /* Does not work, disabled. */ | |
79 | "Frame Based Colour", | |
80 | }; | |
81 | ||
82 | static struct v4l2_ctrl_config tpg_mode = { | |
83 | .ops = &ipu_isys_tpg_ctrl_ops, | |
84 | .id = V4L2_CID_TEST_PATTERN, | |
85 | .name = "Test Pattern", | |
86 | .type = V4L2_CTRL_TYPE_MENU, | |
87 | .min = 0, | |
88 | .max = ARRAY_SIZE(tpg_mode_items) - 1, | |
89 | .def = 0, | |
90 | .menu_skip_mask = 0x2, | |
91 | .qmenu = tpg_mode_items, | |
92 | }; | |
93 | ||
94 | static const struct v4l2_ctrl_config csi2_header_cfg = { | |
95 | .id = V4L2_CID_IPU_STORE_CSI2_HEADER, | |
96 | .name = "Store CSI-2 Headers", | |
97 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
98 | .min = 0, | |
99 | .max = 1, | |
100 | .step = 1, | |
101 | .def = 1, | |
102 | }; | |
103 | ||
104 | static void ipu_isys_tpg_init_controls(struct v4l2_subdev *sd) | |
105 | { | |
106 | struct ipu_isys_tpg *tpg = to_ipu_isys_tpg(sd); | |
107 | int hblank; | |
108 | u64 default_pixel_rate; | |
109 | ||
110 | hblank = 1024; | |
111 | ||
112 | tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, | |
113 | &ipu_isys_tpg_ctrl_ops, | |
114 | V4L2_CID_HBLANK, 8, 65535, 1, hblank); | |
115 | ||
116 | tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, | |
117 | &ipu_isys_tpg_ctrl_ops, | |
118 | V4L2_CID_VBLANK, 8, 65535, 1, 1024); | |
119 | ||
120 | default_pixel_rate = ipu_isys_tpg_rate(tpg, 8); | |
121 | tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, | |
122 | &ipu_isys_tpg_ctrl_ops, | |
123 | V4L2_CID_PIXEL_RATE, | |
124 | default_pixel_rate, | |
125 | default_pixel_rate, | |
126 | 1, default_pixel_rate); | |
127 | if (tpg->pixel_rate) { | |
128 | tpg->pixel_rate->cur.val = default_pixel_rate; | |
129 | tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; | |
130 | } | |
131 | ||
132 | v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); | |
133 | tpg->store_csi2_header = | |
134 | v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, | |
135 | &csi2_header_cfg, NULL); | |
136 | } | |
137 | ||
138 | static void tpg_set_ffmt(struct v4l2_subdev *sd, | |
139 | struct v4l2_subdev_state *sd_state, | |
140 | struct v4l2_subdev_format *fmt) | |
141 | { | |
142 | fmt->format.field = V4L2_FIELD_NONE; | |
143 | *__ipu_isys_get_ffmt(sd, sd_state, fmt->pad, fmt->which) = fmt->format; | |
144 | } | |
145 | ||
146 | static int ipu_isys_tpg_set_ffmt(struct v4l2_subdev *sd, | |
147 | struct v4l2_subdev_state *sd_state, | |
148 | struct v4l2_subdev_format *fmt) | |
149 | { | |
150 | struct ipu_isys_tpg *tpg = to_ipu_isys_tpg(sd); | |
151 | __u32 code = tpg->asd.ffmt[TPG_PAD_SOURCE].code; | |
152 | unsigned int bpp = ipu_isys_mbus_code_to_bpp(code); | |
153 | s64 tpg_rate = ipu_isys_tpg_rate(tpg, bpp); | |
154 | int rval; | |
155 | ||
156 | mutex_lock(&tpg->asd.mutex); | |
157 | rval = __ipu_isys_subdev_set_ffmt(sd, sd_state, fmt); | |
158 | mutex_unlock(&tpg->asd.mutex); | |
159 | ||
160 | if (rval || fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) | |
161 | return rval; | |
162 | ||
163 | v4l2_ctrl_s_ctrl_int64(tpg->pixel_rate, tpg_rate); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static const struct ipu_isys_pixelformat * | |
169 | ipu_isys_tpg_try_fmt(struct ipu_isys_video *av, | |
170 | struct v4l2_pix_format_mplane *mpix) | |
171 | { | |
172 | struct media_link *link = list_first_entry(&av->vdev.entity.links, | |
173 | struct media_link, list); | |
174 | struct v4l2_subdev *sd = | |
175 | media_entity_to_v4l2_subdev(link->source->entity); | |
176 | struct ipu_isys_tpg *tpg; | |
177 | ||
178 | if (!sd) | |
179 | return NULL; | |
180 | ||
181 | tpg = to_ipu_isys_tpg(sd); | |
182 | ||
183 | return ipu_isys_video_try_fmt_vid_mplane(av, mpix, | |
184 | v4l2_ctrl_g_ctrl(tpg->store_csi2_header)); | |
185 | } | |
186 | ||
187 | static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { | |
188 | .get_fmt = ipu_isys_subdev_get_ffmt, | |
189 | .set_fmt = ipu_isys_tpg_set_ffmt, | |
190 | .enum_mbus_code = ipu_isys_subdev_enum_mbus_code, | |
191 | }; | |
192 | ||
193 | static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | |
194 | struct v4l2_event_subscription *sub) | |
195 | { | |
196 | switch (sub->type) { | |
197 | #ifdef IPU_TPG_FRAME_SYNC | |
198 | case V4L2_EVENT_FRAME_SYNC: | |
199 | return v4l2_event_subscribe(fh, sub, 10, NULL); | |
200 | #endif | |
201 | case V4L2_EVENT_CTRL: | |
202 | return v4l2_ctrl_subscribe_event(fh, sub); | |
203 | default: | |
204 | return -EINVAL; | |
205 | } | |
206 | }; | |
207 | ||
208 | /* V4L2 subdev core operations */ | |
209 | static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { | |
210 | .subscribe_event = subscribe_event, | |
211 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, | |
212 | }; | |
213 | ||
214 | static struct v4l2_subdev_ops tpg_sd_ops = { | |
215 | .core = &tpg_sd_core_ops, | |
216 | .video = &tpg_sd_video_ops, | |
217 | .pad = &tpg_sd_pad_ops, | |
218 | }; | |
219 | ||
220 | static struct media_entity_operations tpg_entity_ops = { | |
221 | .link_validate = v4l2_subdev_link_validate, | |
222 | }; | |
223 | ||
224 | void ipu_isys_tpg_cleanup(struct ipu_isys_tpg *tpg) | |
225 | { | |
226 | v4l2_device_unregister_subdev(&tpg->asd.sd); | |
227 | ipu_isys_subdev_cleanup(&tpg->asd); | |
228 | ipu_isys_video_cleanup(&tpg->av); | |
229 | } | |
230 | ||
231 | int ipu_isys_tpg_init(struct ipu_isys_tpg *tpg, | |
232 | struct ipu_isys *isys, | |
233 | void __iomem *base, void __iomem *sel, | |
234 | unsigned int index) | |
235 | { | |
236 | struct v4l2_subdev_format fmt = { | |
237 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
238 | .pad = TPG_PAD_SOURCE, | |
239 | .format = { | |
240 | .width = 4096, | |
241 | .height = 3072, | |
242 | }, | |
243 | }; | |
244 | int rval; | |
245 | ||
246 | tpg->isys = isys; | |
247 | tpg->base = base; | |
248 | tpg->sel = sel; | |
249 | tpg->index = index; | |
250 | ||
251 | tpg->asd.sd.entity.ops = &tpg_entity_ops; | |
252 | tpg->asd.ctrl_init = ipu_isys_tpg_init_controls; | |
253 | tpg->asd.isys = isys; | |
254 | ||
255 | rval = ipu_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, | |
256 | NR_OF_TPG_PADS, | |
257 | NR_OF_TPG_SOURCE_PADS, | |
258 | NR_OF_TPG_SINK_PADS, | |
259 | V4L2_SUBDEV_FL_HAS_EVENTS); | |
260 | if (rval) | |
261 | return rval; | |
262 | ||
263 | tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; | |
264 | tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | |
265 | ||
266 | tpg->asd.source = IPU_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 + index; | |
267 | tpg->asd.supported_codes = tpg_supported_codes; | |
268 | tpg->asd.set_ffmt = tpg_set_ffmt; | |
269 | ipu_isys_subdev_set_ffmt(&tpg->asd.sd, NULL, &fmt); | |
270 | ||
271 | tpg->asd.sd.internal_ops = &tpg_sd_internal_ops; | |
272 | snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), | |
273 | IPU_ISYS_ENTITY_PREFIX " TPG %u", index); | |
274 | v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); | |
275 | rval = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); | |
276 | if (rval) { | |
277 | dev_info(&isys->adev->dev, "can't register v4l2 subdev\n"); | |
278 | goto fail; | |
279 | } | |
280 | ||
281 | snprintf(tpg->av.vdev.name, sizeof(tpg->av.vdev.name), | |
282 | IPU_ISYS_ENTITY_PREFIX " TPG %u capture", index); | |
283 | tpg->av.isys = isys; | |
284 | tpg->av.aq.css_pin_type = IPU_FW_ISYS_PIN_TYPE_MIPI; | |
285 | tpg->av.pfmts = ipu_isys_pfmts_packed; | |
286 | tpg->av.try_fmt_vid_mplane = ipu_isys_tpg_try_fmt; | |
287 | tpg->av.prepare_fw_stream = | |
288 | ipu_isys_prepare_fw_cfg_default; | |
289 | tpg->av.packed = true; | |
290 | tpg->av.line_header_length = IPU_ISYS_CSI2_LONG_PACKET_HEADER_SIZE; | |
291 | tpg->av.line_footer_length = IPU_ISYS_CSI2_LONG_PACKET_FOOTER_SIZE; | |
292 | tpg->av.aq.buf_prepare = ipu_isys_buf_prepare; | |
293 | tpg->av.aq.fill_frame_buff_set_pin = | |
294 | ipu_isys_buffer_to_fw_frame_buff_pin; | |
295 | tpg->av.aq.link_fmt_validate = ipu_isys_link_fmt_validate; | |
296 | tpg->av.aq.vbq.buf_struct_size = sizeof(struct ipu_isys_video_buffer); | |
297 | ||
298 | rval = ipu_isys_video_init(&tpg->av, &tpg->asd.sd.entity, | |
299 | TPG_PAD_SOURCE, MEDIA_PAD_FL_SINK, 0); | |
300 | if (rval) { | |
301 | dev_info(&isys->adev->dev, "can't init video node\n"); | |
302 | goto fail; | |
303 | } | |
304 | ||
305 | return 0; | |
306 | ||
307 | fail: | |
308 | ipu_isys_tpg_cleanup(tpg); | |
309 | ||
310 | return rval; | |
311 | } |