Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-only
2 : /*
3 : * Synthetic CSI2 sensor subdev for the IPU4 QEMU harness.
4 : *
5 : * Under CONFIG_VIDEO_IPU4_VIRT_SENSOR, `ipu4_virt_sensor_install()`
6 : * replaces the ambu_ipu_bridge_init() call in the IPU4 probe path. It
7 : * does exactly two things that matter to ipu6-isys:
8 : *
9 : * 1. Builds a software_node graph on `pdev->dev.fwnode->secondary`
10 : * shaped like the ambu bridge's: an IPU HID node with port@0 /
11 : * endpoint@0, linked to a sensor node with its own port@0 /
12 : * endpoint@0. This is what `fwnode_graph_get_endpoint_by_id()`
13 : * in isys_notifier_init() (ipu6-isys.c:824) walks.
14 : *
15 : * 2. Registers a v4l2_subdev with a SOURCE pad advertising
16 : * RGB888_1X24 at 800x800, with its fwnode set to the sensor HID
17 : * node's fwnode so the async matcher can bind it.
18 : *
19 : * `.s_stream()` is a no-op — frame generation is M5's QEMU work. What
20 : * this file delivers is: probe completes, `/dev/video*` nodes appear.
21 : */
22 :
23 : #include <linux/module.h>
24 : #include <linux/property.h>
25 : #include <linux/slab.h>
26 : #include <linux/types.h>
27 :
28 : #include <media/media-entity.h>
29 : #include <media/v4l2-ctrls.h>
30 : #include <media/v4l2-fwnode.h>
31 : #include <media/v4l2-subdev.h>
32 :
33 : #include "ipu4-compat.h"
34 : #include "virt-sensor.h"
35 :
36 : #define VIRT_SENSOR_WIDTH 800
37 : #define VIRT_SENSOR_HEIGHT 800
38 : #define VIRT_SENSOR_MBUS_CODE MEDIA_BUS_FMT_RGB888_1X24
39 : #define VIRT_SENSOR_LANES 4
40 : #define VIRT_SENSOR_LINK_FREQ 450000000ULL
41 :
42 : /* Match ambu-bridge's IPU HID; ipu6-isys does not care about the string,
43 : * but using the same ACPI HID keeps the register/cleanup symmetric with
44 : * the downstream bridge code the harness is replacing. */
45 : #define IPU_HID "INT343E"
46 : #define SENSOR_NAME "ipu4-virt-sensor"
47 :
48 : struct virt_sensor {
49 : struct v4l2_subdev sd;
50 : struct media_pad pad;
51 : struct v4l2_ctrl_handler ctrl_handler;
52 :
53 : /* Nodes are in the order expected by software_node_register_node_group. */
54 : struct software_node ipu_node;
55 : struct software_node ipu_port;
56 : struct software_node ipu_endpoint;
57 : struct software_node sensor_node;
58 : struct software_node sensor_port;
59 : struct software_node sensor_endpoint;
60 : const struct software_node *group[7]; /* 6 nodes + NULL */
61 :
62 : u32 data_lanes[VIRT_SENSOR_LANES];
63 : u64 link_freqs[1];
64 :
65 : struct property_entry ipu_ep_props[3]; /* data-lanes, remote-ep, NULL */
66 : struct property_entry sensor_ep_props[5]; /* bus-type, data-lanes, remote-ep, link-freqs, NULL */
67 :
68 : /* PROPERTY_ENTRY_REF_ARRAY uses ARRAY_SIZE internally, so these
69 : * must be actual arrays (size 1) rather than plain structs. */
70 : struct software_node_ref_args ipu_remote_ref[1];
71 : struct software_node_ref_args sensor_remote_ref[1];
72 :
73 : bool fwnode_secondary_set;
74 : };
75 :
76 : /* Only one virt-sensor per system; matches the one-IPU-device reality. */
77 : static struct virt_sensor *g_virt_sensor;
78 :
79 : static const struct v4l2_mbus_framefmt virt_sensor_default_fmt = {
80 : .width = VIRT_SENSOR_WIDTH,
81 : .height = VIRT_SENSOR_HEIGHT,
82 : .code = VIRT_SENSOR_MBUS_CODE,
83 : .field = V4L2_FIELD_NONE,
84 : .colorspace = V4L2_COLORSPACE_SRGB,
85 : };
86 :
87 0 : static int virt_sensor_enum_mbus_code(struct v4l2_subdev *sd,
88 : struct v4l2_subdev_state *state,
89 : struct v4l2_subdev_mbus_code_enum *code)
90 : {
91 0 : if (code->index != 0)
92 : return -EINVAL;
93 0 : code->code = VIRT_SENSOR_MBUS_CODE;
94 0 : return 0;
95 : }
96 :
97 0 : static int virt_sensor_enum_frame_size(struct v4l2_subdev *sd,
98 : struct v4l2_subdev_state *state,
99 : struct v4l2_subdev_frame_size_enum *fse)
100 : {
101 0 : if (fse->index != 0 || fse->code != VIRT_SENSOR_MBUS_CODE)
102 : return -EINVAL;
103 0 : fse->min_width = fse->max_width = VIRT_SENSOR_WIDTH;
104 0 : fse->min_height = fse->max_height = VIRT_SENSOR_HEIGHT;
105 0 : return 0;
106 : }
107 :
108 1 : static int virt_sensor_get_fmt(struct v4l2_subdev *sd,
109 : struct v4l2_subdev_state *state,
110 : struct v4l2_subdev_format *fmt)
111 : {
112 1 : fmt->format = virt_sensor_default_fmt;
113 1 : return 0;
114 : }
115 :
116 0 : static int virt_sensor_set_fmt(struct v4l2_subdev *sd,
117 : struct v4l2_subdev_state *state,
118 : struct v4l2_subdev_format *fmt)
119 : {
120 : /* Single format — negotiation is a no-op. */
121 0 : fmt->format = virt_sensor_default_fmt;
122 0 : return 0;
123 : }
124 :
125 2 : static int virt_sensor_s_stream(struct v4l2_subdev *sd, int enable)
126 : {
127 3 : dev_info(sd->dev, "virt-sensor s_stream %s (no-op, M5 delivers frames)\n",
128 : enable ? "on" : "off");
129 2 : return 0;
130 : }
131 :
132 : static const struct v4l2_subdev_pad_ops virt_sensor_pad_ops = {
133 : .enum_mbus_code = virt_sensor_enum_mbus_code,
134 : .enum_frame_size = virt_sensor_enum_frame_size,
135 : .get_fmt = virt_sensor_get_fmt,
136 : .set_fmt = virt_sensor_set_fmt,
137 : };
138 :
139 : static const struct v4l2_subdev_video_ops virt_sensor_video_ops = {
140 : .s_stream = virt_sensor_s_stream,
141 : };
142 :
143 : static const struct v4l2_subdev_ops virt_sensor_subdev_ops = {
144 : .pad = &virt_sensor_pad_ops,
145 : .video = &virt_sensor_video_ops,
146 : };
147 :
148 1 : static void virt_sensor_build_swnodes(struct virt_sensor *vs)
149 : {
150 : unsigned int i;
151 :
152 5 : for (i = 0; i < VIRT_SENSOR_LANES; i++)
153 4 : vs->data_lanes[i] = i + 1;
154 1 : vs->link_freqs[0] = VIRT_SENSOR_LINK_FREQ;
155 :
156 1 : vs->ipu_node.name = IPU_HID;
157 1 : vs->ipu_port.name = "port@0";
158 1 : vs->ipu_port.parent = &vs->ipu_node;
159 1 : vs->ipu_endpoint.name = "endpoint@0";
160 1 : vs->ipu_endpoint.parent = &vs->ipu_port;
161 :
162 1 : vs->sensor_node.name = SENSOR_NAME;
163 1 : vs->sensor_port.name = "port@0";
164 1 : vs->sensor_port.parent = &vs->sensor_node;
165 1 : vs->sensor_endpoint.name = "endpoint@0";
166 1 : vs->sensor_endpoint.parent = &vs->sensor_port;
167 :
168 1 : vs->ipu_remote_ref[0] = SOFTWARE_NODE_REFERENCE(&vs->sensor_endpoint);
169 1 : vs->sensor_remote_ref[0] = SOFTWARE_NODE_REFERENCE(&vs->ipu_endpoint);
170 :
171 1 : vs->ipu_ep_props[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
172 : vs->data_lanes,
173 : VIRT_SENSOR_LANES);
174 1 : vs->ipu_ep_props[1] = PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
175 : vs->ipu_remote_ref);
176 : /* ipu_ep_props[2] is the zero-init terminator. */
177 :
178 1 : vs->sensor_ep_props[0] = PROPERTY_ENTRY_U32("bus-type",
179 : V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
180 1 : vs->sensor_ep_props[1] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
181 : vs->data_lanes,
182 : VIRT_SENSOR_LANES);
183 1 : vs->sensor_ep_props[2] = PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
184 : vs->sensor_remote_ref);
185 1 : vs->sensor_ep_props[3] = PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies",
186 : vs->link_freqs, 1);
187 : /* sensor_ep_props[4] is the terminator. */
188 :
189 1 : vs->ipu_endpoint.properties = vs->ipu_ep_props;
190 1 : vs->sensor_endpoint.properties = vs->sensor_ep_props;
191 :
192 1 : vs->group[0] = &vs->ipu_node;
193 1 : vs->group[1] = &vs->ipu_port;
194 1 : vs->group[2] = &vs->ipu_endpoint;
195 1 : vs->group[3] = &vs->sensor_node;
196 1 : vs->group[4] = &vs->sensor_port;
197 1 : vs->group[5] = &vs->sensor_endpoint;
198 1 : vs->group[6] = NULL;
199 1 : }
200 :
201 1 : int ipu4_virt_sensor_install(struct pci_dev *pdev)
202 : {
203 1 : struct device *dev = &pdev->dev;
204 : struct virt_sensor *vs;
205 : struct fwnode_handle *ipu_fwnode;
206 : int ret;
207 :
208 1 : if (g_virt_sensor)
209 : return -EEXIST;
210 1 : if (!dev->fwnode) {
211 0 : dev_err(dev, "virt-sensor: IPU device has no primary fwnode\n");
212 0 : return -ENODEV;
213 : }
214 1 : if (dev->fwnode->secondary && !IS_ERR(dev->fwnode->secondary)) {
215 0 : dev_err(dev, "virt-sensor: fwnode->secondary already set\n");
216 0 : return -EBUSY;
217 : }
218 :
219 1 : vs = kzalloc(sizeof(*vs), GFP_KERNEL);
220 1 : if (!vs)
221 : return -ENOMEM;
222 :
223 1 : virt_sensor_build_swnodes(vs);
224 :
225 1 : ret = software_node_register_node_group(vs->group);
226 1 : if (ret) {
227 0 : dev_err(dev, "virt-sensor: sw-node register failed %d\n", ret);
228 0 : goto err_free;
229 : }
230 :
231 1 : ipu_fwnode = software_node_fwnode(&vs->ipu_node);
232 1 : if (!ipu_fwnode) {
233 0 : dev_err(dev, "virt-sensor: no fwnode for IPU swnode\n");
234 : ret = -ENODEV;
235 0 : goto err_nodes;
236 : }
237 :
238 : /* Mirror ambu_ipu_bridge_init: there is no public inverse of
239 : * set_secondary_fwnode(), so set it directly. */
240 1 : dev->fwnode->secondary = ipu_fwnode;
241 1 : vs->fwnode_secondary_set = true;
242 :
243 1 : v4l2_subdev_init(&vs->sd, &virt_sensor_subdev_ops);
244 1 : vs->sd.dev = dev;
245 1 : vs->sd.fwnode = software_node_fwnode(&vs->sensor_node);
246 1 : vs->sd.owner = THIS_MODULE;
247 1 : vs->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
248 1 : strscpy(vs->sd.name, SENSOR_NAME, sizeof(vs->sd.name));
249 :
250 : /*
251 : * Expose V4L2_CID_LINK_FREQ so ipu6_isys_csi2_get_link_freq()
252 : * (kernel/ipu4/ipu6-isys-csi2.c:108) can read a sane value from
253 : * `v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0)`. Without the
254 : * ctrl, that helper returns -ENOENT, ipu6_isys_csi2_calc_timing()
255 : * propagates the error back, and CSI2 enable_streams aborts
256 : * before set_stream() ever writes the per-port RX_ENABLE /
257 : * DLY_CNT_* / IRQ_* registers — exactly the silicon writes the
258 : * Step-2 mmio-trace coverage report wants to drop from
259 : * `unimplemented`.
260 : */
261 1 : ret = v4l2_ctrl_handler_init(&vs->ctrl_handler, 1);
262 1 : if (ret) {
263 0 : dev_err(dev, "virt-sensor: ctrl handler init failed %d\n", ret);
264 0 : goto err_fwnode;
265 : }
266 1 : v4l2_ctrl_new_int_menu(&vs->ctrl_handler, NULL, V4L2_CID_LINK_FREQ,
267 1 : 0, 0, vs->link_freqs);
268 1 : if (vs->ctrl_handler.error) {
269 : ret = vs->ctrl_handler.error;
270 0 : dev_err(dev, "virt-sensor: failed to add LINK_FREQ ctrl %d\n",
271 : ret);
272 0 : v4l2_ctrl_handler_free(&vs->ctrl_handler);
273 0 : goto err_fwnode;
274 : }
275 1 : vs->sd.ctrl_handler = &vs->ctrl_handler;
276 :
277 1 : vs->pad.flags = MEDIA_PAD_FL_SOURCE;
278 1 : vs->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
279 1 : vs->sd.entity.name = vs->sd.name;
280 :
281 1 : ret = media_entity_pads_init(&vs->sd.entity, 1, &vs->pad);
282 1 : if (ret) {
283 0 : dev_err(dev, "virt-sensor: entity pads init failed %d\n", ret);
284 0 : goto err_fwnode;
285 : }
286 :
287 1 : ret = v4l2_async_register_subdev(&vs->sd);
288 1 : if (ret) {
289 0 : dev_err(dev, "virt-sensor: async register failed %d\n", ret);
290 0 : goto err_entity;
291 : }
292 :
293 1 : g_virt_sensor = vs;
294 1 : dev_info(dev, "virt-sensor installed: %ux%u RGB888, %d lanes\n",
295 : VIRT_SENSOR_WIDTH, VIRT_SENSOR_HEIGHT, VIRT_SENSOR_LANES);
296 1 : return 0;
297 :
298 : err_entity:
299 : media_entity_cleanup(&vs->sd.entity);
300 0 : v4l2_ctrl_handler_free(&vs->ctrl_handler);
301 0 : err_fwnode:
302 0 : dev->fwnode->secondary = NULL;
303 0 : vs->fwnode_secondary_set = false;
304 0 : err_nodes:
305 0 : software_node_unregister_node_group(vs->group);
306 0 : err_free:
307 0 : kfree(vs);
308 0 : return ret;
309 : }
310 : EXPORT_SYMBOL_NS_GPL(ipu4_virt_sensor_install, INTEL_IPU6);
311 :
312 0 : void ipu4_virt_sensor_remove(struct pci_dev *pdev)
313 : {
314 0 : struct virt_sensor *vs = g_virt_sensor;
315 :
316 0 : if (!vs)
317 : return;
318 0 : v4l2_async_unregister_subdev(&vs->sd);
319 : media_entity_cleanup(&vs->sd.entity);
320 0 : v4l2_ctrl_handler_free(&vs->ctrl_handler);
321 0 : if (vs->fwnode_secondary_set)
322 0 : pdev->dev.fwnode->secondary = NULL;
323 0 : software_node_unregister_node_group(vs->group);
324 0 : kfree(vs);
325 0 : g_virt_sensor = NULL;
326 : }
327 : EXPORT_SYMBOL_NS_GPL(ipu4_virt_sensor_remove, INTEL_IPU6);
|