1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Driver for Analog Devices ADV748X CSI-2 Transmitter |
4 | * |
5 | * Copyright (C) 2017 Renesas Electronics Corp. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/mutex.h> |
10 | |
11 | #include <media/v4l2-ctrls.h> |
12 | #include <media/v4l2-device.h> |
13 | #include <media/v4l2-ioctl.h> |
14 | |
15 | #include "adv748x.h" |
16 | |
17 | int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) |
18 | { |
19 | return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT); |
20 | } |
21 | |
22 | /** |
23 | * adv748x_csi2_register_link : Register and link internal entities |
24 | * |
25 | * @tx: CSI2 private entity |
26 | * @v4l2_dev: Video registration device |
27 | * @src: Source subdevice to establish link |
28 | * @src_pad: Pad number of source to link to this @tx |
29 | * @enable: Link enabled flag |
30 | * |
31 | * Ensure that the subdevice is registered against the v4l2_device, and link the |
32 | * source pad to the sink pad of the CSI2 bus entity. |
33 | */ |
34 | static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, |
35 | struct v4l2_device *v4l2_dev, |
36 | struct v4l2_subdev *src, |
37 | unsigned int src_pad, |
38 | bool enable) |
39 | { |
40 | int ret; |
41 | |
42 | if (!src->v4l2_dev) { |
43 | ret = v4l2_device_register_subdev(v4l2_dev, sd: src); |
44 | if (ret) |
45 | return ret; |
46 | } |
47 | |
48 | ret = media_create_pad_link(source: &src->entity, source_pad: src_pad, |
49 | sink: &tx->sd.entity, sink_pad: ADV748X_CSI2_SINK, |
50 | flags: enable ? MEDIA_LNK_FL_ENABLED : 0); |
51 | if (ret) |
52 | return ret; |
53 | |
54 | if (enable) |
55 | tx->src = src; |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | /* ----------------------------------------------------------------------------- |
61 | * v4l2_subdev_internal_ops |
62 | * |
63 | * We use the internal registered operation to be able to ensure that our |
64 | * incremental subdevices (not connected in the forward path) can be registered |
65 | * against the resulting video path and media device. |
66 | */ |
67 | |
68 | static int adv748x_csi2_registered(struct v4l2_subdev *sd) |
69 | { |
70 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
71 | struct adv748x_state *state = tx->state; |
72 | int ret; |
73 | |
74 | adv_dbg(state, "Registered %s (%s)" , is_txa(tx) ? "TXA" :"TXB" , |
75 | sd->name); |
76 | |
77 | /* |
78 | * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output |
79 | * HDMI. |
80 | * |
81 | * The HDMI->TXA link is enabled by default, as is the AFE->TXB one. |
82 | */ |
83 | if (is_afe_enabled(state)) { |
84 | ret = adv748x_csi2_register_link(tx, v4l2_dev: sd->v4l2_dev, |
85 | src: &state->afe.sd, |
86 | src_pad: ADV748X_AFE_SOURCE, |
87 | is_txb(tx)); |
88 | if (ret) |
89 | return ret; |
90 | |
91 | /* TXB can output AFE signals only. */ |
92 | if (is_txb(tx)) |
93 | state->afe.tx = tx; |
94 | } |
95 | |
96 | /* Register link to HDMI for TXA only. */ |
97 | if (is_txb(tx) || !is_hdmi_enabled(state)) |
98 | return 0; |
99 | |
100 | ret = adv748x_csi2_register_link(tx, v4l2_dev: sd->v4l2_dev, src: &state->hdmi.sd, |
101 | src_pad: ADV748X_HDMI_SOURCE, enable: true); |
102 | if (ret) |
103 | return ret; |
104 | |
105 | /* The default HDMI output is TXA. */ |
106 | state->hdmi.tx = tx; |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { |
112 | .registered = adv748x_csi2_registered, |
113 | }; |
114 | |
115 | /* ----------------------------------------------------------------------------- |
116 | * v4l2_subdev_video_ops |
117 | */ |
118 | |
119 | static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable) |
120 | { |
121 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
122 | struct v4l2_subdev *src; |
123 | |
124 | src = adv748x_get_remote_sd(pad: &tx->pads[ADV748X_CSI2_SINK]); |
125 | if (!src) |
126 | return -EPIPE; |
127 | |
128 | return v4l2_subdev_call(src, video, s_stream, enable); |
129 | } |
130 | |
131 | static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = { |
132 | .s_stream = adv748x_csi2_s_stream, |
133 | }; |
134 | |
135 | /* ----------------------------------------------------------------------------- |
136 | * v4l2_subdev_pad_ops |
137 | * |
138 | * The CSI2 bus pads are ignorant to the data sizes or formats. |
139 | * But we must support setting the pad formats for format propagation. |
140 | */ |
141 | |
142 | static struct v4l2_mbus_framefmt * |
143 | adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, |
144 | struct v4l2_subdev_state *sd_state, |
145 | unsigned int pad, u32 which) |
146 | { |
147 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
148 | |
149 | if (which == V4L2_SUBDEV_FORMAT_TRY) |
150 | return v4l2_subdev_state_get_format(sd_state, pad); |
151 | |
152 | return &tx->format; |
153 | } |
154 | |
155 | static int adv748x_csi2_get_format(struct v4l2_subdev *sd, |
156 | struct v4l2_subdev_state *sd_state, |
157 | struct v4l2_subdev_format *sdformat) |
158 | { |
159 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
160 | struct adv748x_state *state = tx->state; |
161 | struct v4l2_mbus_framefmt *mbusformat; |
162 | |
163 | mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, pad: sdformat->pad, |
164 | which: sdformat->which); |
165 | if (!mbusformat) |
166 | return -EINVAL; |
167 | |
168 | mutex_lock(&state->mutex); |
169 | |
170 | sdformat->format = *mbusformat; |
171 | |
172 | mutex_unlock(lock: &state->mutex); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | static int adv748x_csi2_set_format(struct v4l2_subdev *sd, |
178 | struct v4l2_subdev_state *sd_state, |
179 | struct v4l2_subdev_format *sdformat) |
180 | { |
181 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
182 | struct adv748x_state *state = tx->state; |
183 | struct v4l2_mbus_framefmt *mbusformat; |
184 | int ret = 0; |
185 | |
186 | mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, pad: sdformat->pad, |
187 | which: sdformat->which); |
188 | if (!mbusformat) |
189 | return -EINVAL; |
190 | |
191 | mutex_lock(&state->mutex); |
192 | |
193 | if (sdformat->pad == ADV748X_CSI2_SOURCE) { |
194 | const struct v4l2_mbus_framefmt *sink_fmt; |
195 | |
196 | sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state, |
197 | pad: ADV748X_CSI2_SINK, |
198 | which: sdformat->which); |
199 | |
200 | if (!sink_fmt) { |
201 | ret = -EINVAL; |
202 | goto unlock; |
203 | } |
204 | |
205 | sdformat->format = *sink_fmt; |
206 | } |
207 | |
208 | *mbusformat = sdformat->format; |
209 | |
210 | unlock: |
211 | mutex_unlock(lock: &state->mutex); |
212 | |
213 | return ret; |
214 | } |
215 | |
216 | static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, |
217 | struct v4l2_mbus_config *config) |
218 | { |
219 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
220 | |
221 | if (pad != ADV748X_CSI2_SOURCE) |
222 | return -EINVAL; |
223 | |
224 | config->type = V4L2_MBUS_CSI2_DPHY; |
225 | config->bus.mipi_csi2.num_data_lanes = tx->active_lanes; |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { |
231 | .get_fmt = adv748x_csi2_get_format, |
232 | .set_fmt = adv748x_csi2_set_format, |
233 | .get_mbus_config = adv748x_csi2_get_mbus_config, |
234 | }; |
235 | |
236 | /* ----------------------------------------------------------------------------- |
237 | * v4l2_subdev_ops |
238 | */ |
239 | |
240 | static const struct v4l2_subdev_ops adv748x_csi2_ops = { |
241 | .video = &adv748x_csi2_video_ops, |
242 | .pad = &adv748x_csi2_pad_ops, |
243 | }; |
244 | |
245 | /* ----------------------------------------------------------------------------- |
246 | * Subdev module and controls |
247 | */ |
248 | |
249 | int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate) |
250 | { |
251 | struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
252 | |
253 | if (!tx->pixel_rate) |
254 | return -EINVAL; |
255 | |
256 | return v4l2_ctrl_s_ctrl_int64(ctrl: tx->pixel_rate, val: rate); |
257 | } |
258 | |
259 | static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl) |
260 | { |
261 | switch (ctrl->id) { |
262 | case V4L2_CID_PIXEL_RATE: |
263 | return 0; |
264 | default: |
265 | return -EINVAL; |
266 | } |
267 | } |
268 | |
269 | static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = { |
270 | .s_ctrl = adv748x_csi2_s_ctrl, |
271 | }; |
272 | |
273 | static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx) |
274 | { |
275 | |
276 | v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1); |
277 | |
278 | tx->pixel_rate = v4l2_ctrl_new_std(hdl: &tx->ctrl_hdl, |
279 | ops: &adv748x_csi2_ctrl_ops, |
280 | V4L2_CID_PIXEL_RATE, min: 1, INT_MAX, |
281 | step: 1, def: 1); |
282 | |
283 | tx->sd.ctrl_handler = &tx->ctrl_hdl; |
284 | if (tx->ctrl_hdl.error) { |
285 | v4l2_ctrl_handler_free(hdl: &tx->ctrl_hdl); |
286 | return tx->ctrl_hdl.error; |
287 | } |
288 | |
289 | return v4l2_ctrl_handler_setup(hdl: &tx->ctrl_hdl); |
290 | } |
291 | |
292 | int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) |
293 | { |
294 | int ret; |
295 | |
296 | if (!is_tx_enabled(tx)) |
297 | return 0; |
298 | |
299 | adv748x_subdev_init(sd: &tx->sd, state, ops: &adv748x_csi2_ops, |
300 | MEDIA_ENT_F_VID_IF_BRIDGE, |
301 | is_txa(tx) ? "txa" : "txb" ); |
302 | |
303 | /* Register internal ops for incremental subdev registration */ |
304 | tx->sd.internal_ops = &adv748x_csi2_internal_ops; |
305 | |
306 | tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; |
307 | tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; |
308 | |
309 | ret = media_entity_pads_init(entity: &tx->sd.entity, num_pads: ADV748X_CSI2_NR_PADS, |
310 | pads: tx->pads); |
311 | if (ret) |
312 | return ret; |
313 | |
314 | ret = v4l2_async_subdev_endpoint_add(sd: &tx->sd, |
315 | of_fwnode_handle(state->endpoints[tx->port])); |
316 | if (ret) |
317 | goto err_free_media; |
318 | |
319 | ret = adv748x_csi2_init_controls(tx); |
320 | if (ret) |
321 | goto err_cleanup_subdev; |
322 | |
323 | ret = v4l2_async_register_subdev(sd: &tx->sd); |
324 | if (ret) |
325 | goto err_free_ctrl; |
326 | |
327 | return 0; |
328 | |
329 | err_free_ctrl: |
330 | v4l2_ctrl_handler_free(hdl: &tx->ctrl_hdl); |
331 | err_cleanup_subdev: |
332 | v4l2_subdev_cleanup(sd: &tx->sd); |
333 | err_free_media: |
334 | media_entity_cleanup(entity: &tx->sd.entity); |
335 | |
336 | return ret; |
337 | } |
338 | |
339 | void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) |
340 | { |
341 | if (!is_tx_enabled(tx)) |
342 | return; |
343 | |
344 | v4l2_async_unregister_subdev(sd: &tx->sd); |
345 | media_entity_cleanup(entity: &tx->sd.entity); |
346 | v4l2_ctrl_handler_free(hdl: &tx->ctrl_hdl); |
347 | v4l2_subdev_cleanup(sd: &tx->sd); |
348 | } |
349 | |