]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/media/pci/intel/ipu-isys-tpg.c
Revert "UBUNTU: [Config] IPU6: enable OV01A10 sensor"
[mirror_ubuntu-jammy-kernel.git] / drivers / media / pci / intel / ipu-isys-tpg.c
CommitLineData
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
19static 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
31static const u32 *tpg_supported_codes[] = {
32 tpg_supported_codes_pad,
33};
34
35static struct v4l2_subdev_internal_ops tpg_sd_internal_ops = {
36 .open = ipu_isys_subdev_open,
37 .close = ipu_isys_subdev_close,
38};
39
40static const struct v4l2_subdev_video_ops tpg_sd_video_ops = {
41 .s_stream = tpg_set_stream,
42};
43
44static 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
67static const struct v4l2_ctrl_ops ipu_isys_tpg_ctrl_ops = {
68 .s_ctrl = ipu_isys_tpg_s_ctrl,
69};
70
71static 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
76static const char *const tpg_mode_items[] = {
77 "Ramp",
78 "Checkerboard", /* Does not work, disabled. */
79 "Frame Based Colour",
80};
81
82static 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
94static 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
104static 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
138static 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
146static 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
168static const struct ipu_isys_pixelformat *
169ipu_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
187static 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
193static 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 */
209static const struct v4l2_subdev_core_ops tpg_sd_core_ops = {
210 .subscribe_event = subscribe_event,
211 .unsubscribe_event = v4l2_event_subdev_unsubscribe,
212};
213
214static 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
220static struct media_entity_operations tpg_entity_ops = {
221 .link_validate = v4l2_subdev_link_validate,
222};
223
224void 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
231int 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
307fail:
308 ipu_isys_tpg_cleanup(tpg);
309
310 return rval;
311}