]> git.proxmox.com Git - mirror_ubuntu-kernels.git/blob - drivers/media/i2c/adv748x/adv748x-csi2.c
HID: logitech-dj: fix spelling in printk
[mirror_ubuntu-kernels.git] / drivers / media / i2c / adv748x / adv748x-csi2.c
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 static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
18 unsigned int vc)
19 {
20 return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT);
21 }
22
23 /**
24 * adv748x_csi2_register_link : Register and link internal entities
25 *
26 * @tx: CSI2 private entity
27 * @v4l2_dev: Video registration device
28 * @src: Source subdevice to establish link
29 * @src_pad: Pad number of source to link to this @tx
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 {
39 int enabled = MEDIA_LNK_FL_ENABLED;
40 int ret;
41
42 /*
43 * Dynamic linking of the AFE is not supported.
44 * Register the links as immutable.
45 */
46 enabled |= MEDIA_LNK_FL_IMMUTABLE;
47
48 if (!src->v4l2_dev) {
49 ret = v4l2_device_register_subdev(v4l2_dev, src);
50 if (ret)
51 return ret;
52 }
53
54 return media_create_pad_link(&src->entity, src_pad,
55 &tx->sd.entity, ADV748X_CSI2_SINK,
56 enabled);
57 }
58
59 /* -----------------------------------------------------------------------------
60 * v4l2_subdev_internal_ops
61 *
62 * We use the internal registered operation to be able to ensure that our
63 * incremental subdevices (not connected in the forward path) can be registered
64 * against the resulting video path and media device.
65 */
66
67 static int adv748x_csi2_registered(struct v4l2_subdev *sd)
68 {
69 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
70 struct adv748x_state *state = tx->state;
71
72 adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
73 sd->name);
74
75 /*
76 * The adv748x hardware allows the AFE to route through the TXA, however
77 * this is not currently supported in this driver.
78 *
79 * Link HDMI->TXA, and AFE->TXB directly.
80 */
81 if (is_txa(tx) && is_hdmi_enabled(state))
82 return adv748x_csi2_register_link(tx, sd->v4l2_dev,
83 &state->hdmi.sd,
84 ADV748X_HDMI_SOURCE);
85 if (!is_txa(tx) && is_afe_enabled(state))
86 return adv748x_csi2_register_link(tx, sd->v4l2_dev,
87 &state->afe.sd,
88 ADV748X_AFE_SOURCE);
89 return 0;
90 }
91
92 static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = {
93 .registered = adv748x_csi2_registered,
94 };
95
96 /* -----------------------------------------------------------------------------
97 * v4l2_subdev_video_ops
98 */
99
100 static int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable)
101 {
102 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
103 struct v4l2_subdev *src;
104
105 src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]);
106 if (!src)
107 return -EPIPE;
108
109 return v4l2_subdev_call(src, video, s_stream, enable);
110 }
111
112 static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = {
113 .s_stream = adv748x_csi2_s_stream,
114 };
115
116 /* -----------------------------------------------------------------------------
117 * v4l2_subdev_pad_ops
118 *
119 * The CSI2 bus pads are ignorant to the data sizes or formats.
120 * But we must support setting the pad formats for format propagation.
121 */
122
123 static struct v4l2_mbus_framefmt *
124 adv748x_csi2_get_pad_format(struct v4l2_subdev *sd,
125 struct v4l2_subdev_pad_config *cfg,
126 unsigned int pad, u32 which)
127 {
128 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
129
130 if (which == V4L2_SUBDEV_FORMAT_TRY)
131 return v4l2_subdev_get_try_format(sd, cfg, pad);
132
133 return &tx->format;
134 }
135
136 static int adv748x_csi2_get_format(struct v4l2_subdev *sd,
137 struct v4l2_subdev_pad_config *cfg,
138 struct v4l2_subdev_format *sdformat)
139 {
140 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
141 struct adv748x_state *state = tx->state;
142 struct v4l2_mbus_framefmt *mbusformat;
143
144 mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad,
145 sdformat->which);
146 if (!mbusformat)
147 return -EINVAL;
148
149 mutex_lock(&state->mutex);
150
151 sdformat->format = *mbusformat;
152
153 mutex_unlock(&state->mutex);
154
155 return 0;
156 }
157
158 static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
159 struct v4l2_subdev_pad_config *cfg,
160 struct v4l2_subdev_format *sdformat)
161 {
162 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
163 struct adv748x_state *state = tx->state;
164 struct v4l2_mbus_framefmt *mbusformat;
165 int ret = 0;
166
167 mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad,
168 sdformat->which);
169 if (!mbusformat)
170 return -EINVAL;
171
172 mutex_lock(&state->mutex);
173
174 if (sdformat->pad == ADV748X_CSI2_SOURCE) {
175 const struct v4l2_mbus_framefmt *sink_fmt;
176
177 sink_fmt = adv748x_csi2_get_pad_format(sd, cfg,
178 ADV748X_CSI2_SINK,
179 sdformat->which);
180
181 if (!sink_fmt) {
182 ret = -EINVAL;
183 goto unlock;
184 }
185
186 sdformat->format = *sink_fmt;
187 }
188
189 *mbusformat = sdformat->format;
190
191 unlock:
192 mutex_unlock(&state->mutex);
193
194 return ret;
195 }
196
197 static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = {
198 .get_fmt = adv748x_csi2_get_format,
199 .set_fmt = adv748x_csi2_set_format,
200 };
201
202 /* -----------------------------------------------------------------------------
203 * v4l2_subdev_ops
204 */
205
206 static const struct v4l2_subdev_ops adv748x_csi2_ops = {
207 .video = &adv748x_csi2_video_ops,
208 .pad = &adv748x_csi2_pad_ops,
209 };
210
211 /* -----------------------------------------------------------------------------
212 * Subdev module and controls
213 */
214
215 int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate)
216 {
217 struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd);
218
219 if (!tx->pixel_rate)
220 return -EINVAL;
221
222 return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate);
223 }
224
225 static int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl)
226 {
227 switch (ctrl->id) {
228 case V4L2_CID_PIXEL_RATE:
229 return 0;
230 default:
231 return -EINVAL;
232 }
233 }
234
235 static const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = {
236 .s_ctrl = adv748x_csi2_s_ctrl,
237 };
238
239 static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx)
240 {
241
242 v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1);
243
244 tx->pixel_rate = v4l2_ctrl_new_std(&tx->ctrl_hdl,
245 &adv748x_csi2_ctrl_ops,
246 V4L2_CID_PIXEL_RATE, 1, INT_MAX,
247 1, 1);
248
249 tx->sd.ctrl_handler = &tx->ctrl_hdl;
250 if (tx->ctrl_hdl.error) {
251 v4l2_ctrl_handler_free(&tx->ctrl_hdl);
252 return tx->ctrl_hdl.error;
253 }
254
255 return v4l2_ctrl_handler_setup(&tx->ctrl_hdl);
256 }
257
258 int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
259 {
260 int ret;
261
262 if (!is_tx_enabled(tx))
263 return 0;
264
265 /* Initialise the virtual channel */
266 adv748x_csi2_set_virtual_channel(tx, 0);
267
268 adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops,
269 MEDIA_ENT_F_VID_IF_BRIDGE,
270 is_txa(tx) ? "txa" : "txb");
271
272 /* Ensure that matching is based upon the endpoint fwnodes */
273 tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]);
274
275 /* Register internal ops for incremental subdev registration */
276 tx->sd.internal_ops = &adv748x_csi2_internal_ops;
277
278 tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
279 tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
280
281 ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS,
282 tx->pads);
283 if (ret)
284 return ret;
285
286 ret = adv748x_csi2_init_controls(tx);
287 if (ret)
288 goto err_free_media;
289
290 ret = v4l2_async_register_subdev(&tx->sd);
291 if (ret)
292 goto err_free_ctrl;
293
294 return 0;
295
296 err_free_ctrl:
297 v4l2_ctrl_handler_free(&tx->ctrl_hdl);
298 err_free_media:
299 media_entity_cleanup(&tx->sd.entity);
300
301 return ret;
302 }
303
304 void adv748x_csi2_cleanup(struct adv748x_csi2 *tx)
305 {
306 if (!is_tx_enabled(tx))
307 return;
308
309 v4l2_async_unregister_subdev(&tx->sd);
310 media_entity_cleanup(&tx->sd.entity);
311 v4l2_ctrl_handler_free(&tx->ctrl_hdl);
312 }