diff options
Diffstat (limited to 'drivers/media/platform/tegra')
47 files changed, 14154 insertions, 0 deletions
diff --git a/drivers/media/platform/tegra/Makefile b/drivers/media/platform/tegra/Makefile new file mode 100644 index 000000000..414f22f69 --- /dev/null +++ b/drivers/media/platform/tegra/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | |||
3 | #subdir-ccflags-y := -Werror | ||
4 | # | ||
5 | # Makefile for the video capture/playback device drivers. | ||
6 | # | ||
7 | obj-y += camera/ | ||
8 | obj-y += vi/ | ||
9 | obj-$(CONFIG_VIDEO_CAMERA) += regmap_util.o | ||
10 | obj-$(CONFIG_VIDEO_TEGRA_VI_TPG) += tpg/ | ||
11 | |||
12 | obj-$(CONFIG_TEGRA_MIPI_CAL) += mipical/ | ||
diff --git a/drivers/media/platform/tegra/camera/Makefile b/drivers/media/platform/tegra/camera/Makefile new file mode 100644 index 000000000..8474f451a --- /dev/null +++ b/drivers/media/platform/tegra/camera/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | obj-y += vi/ | ||
3 | obj-y += csi/ | ||
4 | ccflags-y += -I../nvhost/drivers/video/tegra/host | ||
5 | ccflags-y += -Idrivers/video/tegra/host | ||
6 | ccflags-y += -Idrivers/video/tegra/camera | ||
7 | ccflags-y += -Idrivers/media/platform/tegra | ||
8 | ccflags-y += -Werror | ||
9 | |||
10 | obj-y += camera_common.o camera_gpio.o sensor_common.o | ||
diff --git a/drivers/media/platform/tegra/camera/camera_common.c b/drivers/media/platform/tegra/camera/camera_common.c new file mode 100644 index 000000000..17a253640 --- /dev/null +++ b/drivers/media/platform/tegra/camera/camera_common.c | |||
@@ -0,0 +1,858 @@ | |||
1 | /* | ||
2 | * camera_common.c - utilities for tegra camera driver | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | #include <media/camera_common.h> | ||
19 | #include <linux/of_graph.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <soc/tegra/pmc.h> | ||
22 | #include "camera/vi/mc_common.h" | ||
23 | |||
24 | #define has_s_op(master, op) \ | ||
25 | (master->ops && master->ops->op) | ||
26 | #define call_s_op(master, op) \ | ||
27 | (has_s_op(master, op) ? \ | ||
28 | master->ops->op(master) : 0) | ||
29 | #define call_s_ops(master, op, ...) \ | ||
30 | (has_s_op(master, op) ? \ | ||
31 | master->ops->op(master, __VA_ARGS__) : 0) | ||
32 | |||
33 | static const struct camera_common_colorfmt camera_common_color_fmts[] = { | ||
34 | { | ||
35 | MEDIA_BUS_FMT_SRGGB12_1X12, | ||
36 | V4L2_COLORSPACE_SRGB, | ||
37 | V4L2_PIX_FMT_SRGGB12, | ||
38 | }, | ||
39 | { | ||
40 | MEDIA_BUS_FMT_SRGGB10_1X10, | ||
41 | V4L2_COLORSPACE_SRGB, | ||
42 | V4L2_PIX_FMT_SRGGB10, | ||
43 | }, | ||
44 | { | ||
45 | MEDIA_BUS_FMT_SBGGR10_1X10, | ||
46 | V4L2_COLORSPACE_SRGB, | ||
47 | V4L2_PIX_FMT_SBGGR10, | ||
48 | }, | ||
49 | { | ||
50 | MEDIA_BUS_FMT_SRGGB8_1X8, | ||
51 | V4L2_COLORSPACE_SRGB, | ||
52 | V4L2_PIX_FMT_SRGGB8, | ||
53 | }, | ||
54 | /* | ||
55 | * The below two formats are not supported by VI4, | ||
56 | * keep them at the last to ensure they get discarded | ||
57 | */ | ||
58 | { | ||
59 | MEDIA_BUS_FMT_XRGGB10P_3X10, | ||
60 | V4L2_COLORSPACE_SRGB, | ||
61 | V4L2_PIX_FMT_XRGGB10P, | ||
62 | }, | ||
63 | { | ||
64 | MEDIA_BUS_FMT_XBGGR10P_3X10, | ||
65 | V4L2_COLORSPACE_SRGB, | ||
66 | V4L2_PIX_FMT_XRGGB10P, | ||
67 | }, | ||
68 | }; | ||
69 | |||
70 | static const char *camera_common_csi_io_pads[] = { | ||
71 | "csia", | ||
72 | "csib", | ||
73 | "csic", | ||
74 | "csid", | ||
75 | "csie", | ||
76 | "csif", | ||
77 | }; | ||
78 | |||
79 | bool camera_common_verify_code(struct tegra_channel *chan, unsigned int code) | ||
80 | { | ||
81 | int i; | ||
82 | |||
83 | for (i = 0; i < chan->num_video_formats; i++) { | ||
84 | if (chan->video_formats[i]->code == code) | ||
85 | return true; | ||
86 | } | ||
87 | |||
88 | return false; | ||
89 | } | ||
90 | |||
91 | int camera_common_g_ctrl(struct camera_common_data *s_data, | ||
92 | struct v4l2_control *control) | ||
93 | { | ||
94 | int i; | ||
95 | |||
96 | for (i = 0; i < s_data->numctrls; i++) { | ||
97 | if (s_data->ctrls[i]->id == control->id) { | ||
98 | control->value = s_data->ctrls[i]->val; | ||
99 | dev_dbg(&s_data->i2c_client->dev, | ||
100 | "%s: found control %s\n", __func__, | ||
101 | s_data->ctrls[i]->name); | ||
102 | return 0; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | return -EFAULT; | ||
107 | } | ||
108 | |||
109 | int camera_common_regulator_get(struct i2c_client *client, | ||
110 | struct regulator **vreg, const char *vreg_name) | ||
111 | { | ||
112 | struct regulator *reg = NULL; | ||
113 | int err = 0; | ||
114 | |||
115 | reg = devm_regulator_get(&client->dev, vreg_name); | ||
116 | if (unlikely(IS_ERR(reg))) { | ||
117 | dev_err(&client->dev, "%s %s ERR: %p\n", | ||
118 | __func__, vreg_name, reg); | ||
119 | err = PTR_ERR(reg); | ||
120 | reg = NULL; | ||
121 | } else | ||
122 | dev_dbg(&client->dev, "%s: %s\n", | ||
123 | __func__, vreg_name); | ||
124 | |||
125 | *vreg = reg; | ||
126 | return err; | ||
127 | } | ||
128 | |||
129 | int camera_common_parse_clocks(struct i2c_client *client, | ||
130 | struct camera_common_pdata *pdata) | ||
131 | { | ||
132 | struct device_node *np = client->dev.of_node; | ||
133 | const char *prop; | ||
134 | int proplen = 0; | ||
135 | int i = 0; | ||
136 | int numclocks = 0; | ||
137 | int mclk_index = 0; | ||
138 | int parentclk_index = -1; | ||
139 | int err = 0; | ||
140 | |||
141 | |||
142 | pdata->mclk_name = NULL; | ||
143 | pdata->parentclk_name = NULL; | ||
144 | err = of_property_read_string(np, "mclk", &pdata->mclk_name); | ||
145 | if (!err) { | ||
146 | dev_dbg(&client->dev, "mclk in DT %s\n", pdata->mclk_name); | ||
147 | of_property_read_string(np, "parent-clk", | ||
148 | &pdata->parentclk_name); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | prop = (const char *)of_get_property(np, "clock-names", &proplen); | ||
153 | if (!prop) | ||
154 | return -ENODATA; | ||
155 | |||
156 | /* find length of clock-names string array */ | ||
157 | for (i = 0; i < proplen; i++) { | ||
158 | if (prop[i] == '\0') | ||
159 | numclocks++; | ||
160 | } | ||
161 | |||
162 | if (numclocks > 1) { | ||
163 | err = of_property_read_u32(np, "mclk-index", &mclk_index); | ||
164 | if (err) { | ||
165 | dev_err(&client->dev, "Failed to find mclk index\n"); | ||
166 | return err; | ||
167 | } | ||
168 | err = of_property_read_u32(np, "parent-clk-index", | ||
169 | &parentclk_index); | ||
170 | } | ||
171 | |||
172 | for (i = 0; i < numclocks; i++) { | ||
173 | if (i == mclk_index) { | ||
174 | pdata->mclk_name = prop; | ||
175 | dev_dbg(&client->dev, "%s: mclk_name is %s\n", | ||
176 | __func__, pdata->mclk_name); | ||
177 | } else if (i == parentclk_index) { | ||
178 | pdata->parentclk_name = prop; | ||
179 | dev_dbg(&client->dev, "%s: parentclk_name is %s\n", | ||
180 | __func__, pdata->parentclk_name); | ||
181 | } else | ||
182 | dev_dbg(&client->dev, "%s: %s\n", __func__, prop); | ||
183 | prop += strlen(prop) + 1; | ||
184 | } | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | int camera_common_parse_ports(struct i2c_client *client, | ||
190 | struct camera_common_data *s_data) | ||
191 | { | ||
192 | struct device_node *node = client->dev.of_node; | ||
193 | struct device_node *ep = NULL; | ||
194 | struct device_node *next; | ||
195 | int bus_width = 0; | ||
196 | int err = 0; | ||
197 | int port = 0; | ||
198 | |||
199 | /* Parse all the remote entities and put them into the list */ | ||
200 | next = of_graph_get_next_endpoint(node, ep); | ||
201 | if (!next) | ||
202 | return -ENODATA; | ||
203 | |||
204 | of_node_put(ep); | ||
205 | ep = next; | ||
206 | |||
207 | err = of_property_read_u32(ep, "bus-width", &bus_width); | ||
208 | if (err) { | ||
209 | dev_err(&client->dev, | ||
210 | "Failed to find num of lanes\n"); | ||
211 | return err; | ||
212 | } | ||
213 | s_data->numlanes = bus_width; | ||
214 | |||
215 | err = of_property_read_u32(ep, "csi-port", &port); | ||
216 | if (err) { | ||
217 | dev_err(&client->dev, | ||
218 | "Failed to find CSI port\n"); | ||
219 | return err; | ||
220 | } | ||
221 | s_data->csi_port = port; | ||
222 | |||
223 | dev_dbg(&client->dev, "%s: csi port %d num of lanes %d\n", | ||
224 | __func__, s_data->csi_port, s_data->numlanes); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | int camera_common_debugfs_show(struct seq_file *s, void *unused) | ||
230 | { | ||
231 | struct camera_common_data *s_data = s->private; | ||
232 | |||
233 | dev_dbg(&s_data->i2c_client->dev, "%s: ++\n", __func__); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | ssize_t camera_common_debugfs_write( | ||
239 | struct file *file, | ||
240 | char const __user *buf, | ||
241 | size_t count, | ||
242 | loff_t *offset) | ||
243 | { | ||
244 | struct camera_common_data *s_data = | ||
245 | ((struct seq_file *)file->private_data)->private; | ||
246 | struct i2c_client *client = s_data->i2c_client; | ||
247 | int err = 0; | ||
248 | char buffer[MAX_BUFFER_SIZE]; | ||
249 | u32 address; | ||
250 | u32 data; | ||
251 | u8 readback = 0; | ||
252 | |||
253 | dev_dbg(&client->dev, "%s: ++\n", __func__); | ||
254 | |||
255 | if (copy_from_user(&buffer, buf, sizeof(buffer))) | ||
256 | goto debugfs_write_fail; | ||
257 | |||
258 | if (sscanf(buf, "0x%x 0x%x", &address, &data) == 2) | ||
259 | goto set_attr; | ||
260 | if (sscanf(buf, "0X%x 0X%x", &address, &data) == 2) | ||
261 | goto set_attr; | ||
262 | if (sscanf(buf, "%d %d", &address, &data) == 2) | ||
263 | goto set_attr; | ||
264 | |||
265 | if (sscanf(buf, "0x%x 0x%x", &address, &data) == 1) | ||
266 | goto read; | ||
267 | if (sscanf(buf, "0X%x 0X%x", &address, &data) == 1) | ||
268 | goto read; | ||
269 | if (sscanf(buf, "%d %d", &address, &data) == 1) | ||
270 | goto read; | ||
271 | |||
272 | dev_err(&client->dev, "SYNTAX ERROR: %s\n", buf); | ||
273 | return -EFAULT; | ||
274 | |||
275 | set_attr: | ||
276 | dev_dbg(&client->dev, | ||
277 | "new address = %x, data = %x\n", address, data); | ||
278 | err |= call_s_ops(s_data, write_reg, address, data); | ||
279 | read: | ||
280 | err |= call_s_ops(s_data, read_reg, address, &readback); | ||
281 | dev_dbg(&client->dev, | ||
282 | "wrote to address 0x%x with value 0x%x\n", | ||
283 | address, readback); | ||
284 | |||
285 | if (err) | ||
286 | goto debugfs_write_fail; | ||
287 | |||
288 | return count; | ||
289 | |||
290 | debugfs_write_fail: | ||
291 | dev_err(&client->dev, | ||
292 | "%s: test pattern write failed\n", __func__); | ||
293 | return -EFAULT; | ||
294 | } | ||
295 | |||
296 | int camera_common_debugfs_open(struct inode *inode, struct file *file) | ||
297 | { | ||
298 | struct camera_common_data *s_data = inode->i_private; | ||
299 | struct i2c_client *client = s_data->i2c_client; | ||
300 | |||
301 | dev_dbg(&client->dev, "%s: ++\n", __func__); | ||
302 | |||
303 | return single_open(file, camera_common_debugfs_show, inode->i_private); | ||
304 | } | ||
305 | |||
306 | static const struct file_operations camera_common_debugfs_fops = { | ||
307 | .open = camera_common_debugfs_open, | ||
308 | .read = seq_read, | ||
309 | .write = camera_common_debugfs_write, | ||
310 | .llseek = seq_lseek, | ||
311 | .release = single_release, | ||
312 | }; | ||
313 | |||
314 | void camera_common_remove_debugfs( | ||
315 | struct camera_common_data *s_data) | ||
316 | { | ||
317 | struct i2c_client *client = s_data->i2c_client; | ||
318 | |||
319 | dev_dbg(&client->dev, "%s: ++\n", __func__); | ||
320 | |||
321 | debugfs_remove_recursive(s_data->debugdir); | ||
322 | s_data->debugdir = NULL; | ||
323 | } | ||
324 | |||
325 | void camera_common_create_debugfs( | ||
326 | struct camera_common_data *s_data, | ||
327 | const char *name) | ||
328 | { | ||
329 | struct dentry *err; | ||
330 | struct i2c_client *client = s_data->i2c_client; | ||
331 | |||
332 | dev_dbg(&client->dev, "%s %s\n", __func__, name); | ||
333 | |||
334 | s_data->debugdir = | ||
335 | debugfs_create_dir(name, NULL); | ||
336 | if (!s_data->debugdir) | ||
337 | goto remove_debugfs; | ||
338 | |||
339 | err = debugfs_create_file("d", | ||
340 | S_IWUSR | S_IRUGO, | ||
341 | s_data->debugdir, s_data, | ||
342 | &camera_common_debugfs_fops); | ||
343 | if (!err) | ||
344 | goto remove_debugfs; | ||
345 | |||
346 | return; | ||
347 | remove_debugfs: | ||
348 | dev_err(&client->dev, "couldn't create debugfs\n"); | ||
349 | camera_common_remove_debugfs(s_data); | ||
350 | } | ||
351 | |||
352 | /* Find a data format by a pixel code in an array */ | ||
353 | const struct camera_common_colorfmt *camera_common_find_datafmt( | ||
354 | unsigned int code) | ||
355 | { | ||
356 | int i; | ||
357 | |||
358 | for (i = 0; i < ARRAY_SIZE(camera_common_color_fmts); i++) | ||
359 | if (camera_common_color_fmts[i].code == code) | ||
360 | return camera_common_color_fmts + i; | ||
361 | |||
362 | return NULL; | ||
363 | } | ||
364 | EXPORT_SYMBOL(camera_common_find_datafmt); | ||
365 | |||
366 | int camera_common_enum_mbus_code(struct v4l2_subdev *sd, | ||
367 | struct v4l2_subdev_pad_config *cfg, | ||
368 | struct v4l2_subdev_mbus_code_enum *code) | ||
369 | { | ||
370 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
371 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
372 | struct tegra_channel *chan = v4l2_get_subdev_hostdata(sd); | ||
373 | unsigned int mbus_code; | ||
374 | |||
375 | if (s_data->num_color_fmts < 1 || !s_data->color_fmts) { | ||
376 | s_data->color_fmts = camera_common_color_fmts; | ||
377 | s_data->num_color_fmts = ARRAY_SIZE(camera_common_color_fmts); | ||
378 | } | ||
379 | |||
380 | if ((unsigned int)code->index >= s_data->num_color_fmts) | ||
381 | return -EINVAL; | ||
382 | |||
383 | mbus_code = s_data->color_fmts[code->index].code; | ||
384 | if (!camera_common_verify_code(chan, mbus_code)) | ||
385 | return -EINVAL; | ||
386 | |||
387 | code->code = mbus_code; | ||
388 | return 0; | ||
389 | } | ||
390 | EXPORT_SYMBOL(camera_common_enum_mbus_code); | ||
391 | |||
392 | int camera_common_enum_fmt(struct v4l2_subdev *sd, unsigned int index, | ||
393 | unsigned int *code) | ||
394 | { | ||
395 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
396 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
397 | |||
398 | if (s_data->num_color_fmts < 1 || !s_data->color_fmts) { | ||
399 | s_data->color_fmts = camera_common_color_fmts; | ||
400 | s_data->num_color_fmts = ARRAY_SIZE(camera_common_color_fmts); | ||
401 | } | ||
402 | |||
403 | if ((unsigned int)index >= s_data->num_color_fmts) | ||
404 | return -EINVAL; | ||
405 | *code = s_data->color_fmts[index].code; | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | int camera_common_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) | ||
410 | { | ||
411 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
412 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
413 | struct tegra_channel *chan = v4l2_get_subdev_hostdata(sd); | ||
414 | struct v4l2_control hdr_control; | ||
415 | const struct camera_common_frmfmt *frmfmt = s_data->frmfmt; | ||
416 | int hdr_en; | ||
417 | int err = 0; | ||
418 | int i; | ||
419 | |||
420 | dev_dbg(&client->dev, "%s: size %i x %i\n", __func__, | ||
421 | mf->width, mf->height); | ||
422 | |||
423 | /* check hdr enable ctrl */ | ||
424 | hdr_control.id = V4L2_CID_HDR_EN; | ||
425 | |||
426 | err = v4l2_g_ctrl(s_data->ctrl_handler, &hdr_control); | ||
427 | if (err < 0) { | ||
428 | dev_err(&client->dev, "could not find device ctrl.\n"); | ||
429 | return err; | ||
430 | } | ||
431 | |||
432 | hdr_en = switch_ctrl_qmenu[hdr_control.value]; | ||
433 | |||
434 | s_data->mode = s_data->def_mode; | ||
435 | s_data->fmt_width = s_data->def_width; | ||
436 | s_data->fmt_height = s_data->def_height; | ||
437 | |||
438 | if (s_data->use_sensor_mode_id && | ||
439 | s_data->sensor_mode_id >= 0 && | ||
440 | s_data->sensor_mode_id < s_data->numfmts) { | ||
441 | dev_dbg(&client->dev, "%s: use_sensor_mode_id %d\n", | ||
442 | __func__, s_data->sensor_mode_id); | ||
443 | s_data->mode = frmfmt[s_data->sensor_mode_id].mode; | ||
444 | s_data->fmt_width = mf->width; | ||
445 | s_data->fmt_height = mf->height; | ||
446 | } else { | ||
447 | for (i = 0; i < s_data->numfmts; i++) { | ||
448 | if (mf->width == frmfmt[i].size.width && | ||
449 | mf->height == frmfmt[i].size.height && | ||
450 | hdr_en == frmfmt[i].hdr_en) { | ||
451 | s_data->mode = frmfmt[i].mode; | ||
452 | s_data->fmt_width = mf->width; | ||
453 | s_data->fmt_height = mf->height; | ||
454 | break; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | if (i == s_data->numfmts) { | ||
459 | mf->width = s_data->fmt_width; | ||
460 | mf->height = s_data->fmt_height; | ||
461 | dev_dbg(&client->dev, | ||
462 | "%s: invalid resolution supplied to set mode %d %d\n", | ||
463 | __func__, mf->width, mf->height); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | if (!camera_common_verify_code(chan, mf->code)) | ||
468 | err = -EINVAL; | ||
469 | |||
470 | mf->field = V4L2_FIELD_NONE; | ||
471 | mf->colorspace = V4L2_COLORSPACE_SRGB; | ||
472 | |||
473 | return err; | ||
474 | } | ||
475 | |||
476 | int camera_common_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) | ||
477 | { | ||
478 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
479 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
480 | int ret; | ||
481 | |||
482 | dev_dbg(&client->dev, "%s(%u) size %i x %i\n", __func__, | ||
483 | mf->code, mf->width, mf->height); | ||
484 | |||
485 | /* MIPI CSI could have changed the format, double-check */ | ||
486 | if (!camera_common_find_datafmt(mf->code)) | ||
487 | return -EINVAL; | ||
488 | |||
489 | ret = camera_common_try_fmt(sd, mf); | ||
490 | |||
491 | s_data->colorfmt = camera_common_find_datafmt(mf->code); | ||
492 | |||
493 | return ret; | ||
494 | } | ||
495 | |||
496 | int camera_common_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) | ||
497 | { | ||
498 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
499 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
500 | const struct camera_common_colorfmt *fmt = s_data->colorfmt; | ||
501 | |||
502 | dev_dbg(&client->dev, "%s++\n", __func__); | ||
503 | |||
504 | mf->code = fmt->code; | ||
505 | mf->colorspace = fmt->colorspace; | ||
506 | mf->width = s_data->fmt_width; | ||
507 | mf->height = s_data->fmt_height; | ||
508 | mf->field = V4L2_FIELD_NONE; | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static int camera_common_evaluate_color_format(struct v4l2_subdev *sd, | ||
514 | int pixelformat) | ||
515 | { | ||
516 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
517 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
518 | int i; | ||
519 | |||
520 | if (!s_data) | ||
521 | return -EINVAL; | ||
522 | |||
523 | if (s_data->num_color_fmts < 1 || !s_data->color_fmts) { | ||
524 | s_data->color_fmts = camera_common_color_fmts; | ||
525 | s_data->num_color_fmts = ARRAY_SIZE(camera_common_color_fmts); | ||
526 | } | ||
527 | |||
528 | for (i = 0; i < s_data->num_color_fmts; i++) { | ||
529 | if (s_data->color_fmts[i].pix_fmt == pixelformat) | ||
530 | break; | ||
531 | } | ||
532 | |||
533 | if (i >= s_data->num_color_fmts) | ||
534 | return -EINVAL; | ||
535 | |||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | int camera_common_enum_framesizes(struct v4l2_subdev *sd, | ||
540 | struct v4l2_subdev_pad_config *cfg, | ||
541 | struct v4l2_subdev_frame_size_enum *fse) | ||
542 | { | ||
543 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
544 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
545 | int ret; | ||
546 | |||
547 | if (!s_data || !s_data->frmfmt) | ||
548 | return -EINVAL; | ||
549 | |||
550 | if (fse->index >= s_data->numfmts) | ||
551 | return -EINVAL; | ||
552 | |||
553 | ret = camera_common_evaluate_color_format(sd, fse->code); | ||
554 | if (ret) | ||
555 | return ret; | ||
556 | |||
557 | fse->min_width = fse->max_width = | ||
558 | s_data->frmfmt[fse->index].size.width; | ||
559 | fse->min_height = fse->max_height = | ||
560 | s_data->frmfmt[fse->index].size.height; | ||
561 | return 0; | ||
562 | } | ||
563 | EXPORT_SYMBOL(camera_common_enum_framesizes); | ||
564 | |||
565 | int camera_common_enum_frameintervals(struct v4l2_subdev *sd, | ||
566 | struct v4l2_subdev_pad_config *cfg, | ||
567 | struct v4l2_subdev_frame_interval_enum *fie) | ||
568 | { | ||
569 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
570 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
571 | int i, ret; | ||
572 | |||
573 | if (!s_data || !s_data->frmfmt) | ||
574 | return -EINVAL; | ||
575 | |||
576 | /* Check color format */ | ||
577 | ret = camera_common_evaluate_color_format(sd, fie->code); | ||
578 | if (ret) | ||
579 | return ret; | ||
580 | |||
581 | /* Check resolution sizes */ | ||
582 | for (i = 0; i < s_data->numfmts; i++) { | ||
583 | if (s_data->frmfmt[i].size.width == fie->width && | ||
584 | s_data->frmfmt[i].size.height == fie->height) | ||
585 | break; | ||
586 | } | ||
587 | if (i >= s_data->numfmts) | ||
588 | return -EINVAL; | ||
589 | |||
590 | /* Check index is in the rage of framerates array index */ | ||
591 | if (fie->index >= s_data->frmfmt[i].num_framerates) | ||
592 | return -EINVAL; | ||
593 | |||
594 | fie->interval.numerator = 1; | ||
595 | fie->interval.denominator = | ||
596 | s_data->frmfmt[i].framerates[fie->index]; | ||
597 | |||
598 | return 0; | ||
599 | } | ||
600 | EXPORT_SYMBOL(camera_common_enum_frameintervals); | ||
601 | |||
602 | static void camera_common_mclk_disable(struct camera_common_data *s_data) | ||
603 | { | ||
604 | struct camera_common_power_rail *pw = s_data->power; | ||
605 | |||
606 | if (!pw) { | ||
607 | dev_err(&s_data->i2c_client->dev, "%s: no device power rail\n", | ||
608 | __func__); | ||
609 | return; | ||
610 | } | ||
611 | |||
612 | dev_dbg(&s_data->i2c_client->dev, "%s: disable MCLK\n", __func__); | ||
613 | clk_disable_unprepare(pw->mclk); | ||
614 | } | ||
615 | |||
616 | static int camera_common_mclk_enable(struct camera_common_data *s_data) | ||
617 | { | ||
618 | int err; | ||
619 | struct camera_common_power_rail *pw = s_data->power; | ||
620 | unsigned long mclk_init_rate = s_data->def_clk_freq; | ||
621 | |||
622 | if (!pw) { | ||
623 | dev_err(s_data->dev, "%s: no device power rail\n", | ||
624 | __func__); | ||
625 | return -ENODEV; | ||
626 | } | ||
627 | |||
628 | dev_dbg(s_data->dev, "%s: enable MCLK with %lu Hz\n", | ||
629 | __func__, mclk_init_rate); | ||
630 | |||
631 | err = clk_set_rate(pw->mclk, mclk_init_rate); | ||
632 | if (!err) | ||
633 | err = clk_prepare_enable(pw->mclk); | ||
634 | |||
635 | return err; | ||
636 | } | ||
637 | |||
638 | void camera_common_dpd_disable(struct camera_common_data *s_data) | ||
639 | { | ||
640 | int i; | ||
641 | int io_idx; | ||
642 | /* 2 lanes per port, divide by two to get numports */ | ||
643 | int numports = (s_data->numlanes + 1) >> 1; | ||
644 | |||
645 | /* disable CSI IOs DPD mode to turn on camera */ | ||
646 | for (i = 0; i < numports; i++) { | ||
647 | io_idx = s_data->csi_port + i; | ||
648 | tegra_pmc_io_pad_low_power_disable( | ||
649 | camera_common_csi_io_pads[io_idx]); | ||
650 | dev_dbg(s_data->dev, | ||
651 | "%s: csi %d\n", __func__, io_idx); | ||
652 | } | ||
653 | } | ||
654 | |||
655 | void camera_common_dpd_enable(struct camera_common_data *s_data) | ||
656 | { | ||
657 | int i; | ||
658 | int io_idx; | ||
659 | /* 2 lanes per port, divide by two to get numports */ | ||
660 | int numports = (s_data->numlanes + 1) >> 1; | ||
661 | |||
662 | /* disable CSI IOs DPD mode to turn on camera */ | ||
663 | for (i = 0; i < numports; i++) { | ||
664 | io_idx = s_data->csi_port + i; | ||
665 | tegra_pmc_io_pad_low_power_enable( | ||
666 | camera_common_csi_io_pads[io_idx]); | ||
667 | dev_dbg(s_data->dev, | ||
668 | "%s: csi %d\n", __func__, io_idx); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | int camera_common_s_power(struct v4l2_subdev *sd, int on) | ||
673 | { | ||
674 | int err = 0; | ||
675 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
676 | struct camera_common_data *s_data = to_camera_common_data(client); | ||
677 | |||
678 | if (on) { | ||
679 | err = camera_common_mclk_enable(s_data); | ||
680 | if (err) | ||
681 | return err; | ||
682 | |||
683 | camera_common_dpd_disable(s_data); | ||
684 | |||
685 | err = call_s_op(s_data, power_on); | ||
686 | if (err) { | ||
687 | dev_err(s_data->dev, | ||
688 | "%s: error power on\n", __func__); | ||
689 | camera_common_dpd_enable(s_data); | ||
690 | camera_common_mclk_disable(s_data); | ||
691 | } | ||
692 | } else { | ||
693 | call_s_op(s_data, power_off); | ||
694 | camera_common_dpd_enable(s_data); | ||
695 | camera_common_mclk_disable(s_data); | ||
696 | } | ||
697 | |||
698 | return err; | ||
699 | } | ||
700 | |||
701 | int camera_common_g_mbus_config(struct v4l2_subdev *sd, | ||
702 | struct v4l2_mbus_config *cfg) | ||
703 | { | ||
704 | cfg->type = V4L2_MBUS_CSI2; | ||
705 | cfg->flags = V4L2_MBUS_CSI2_4_LANE | | ||
706 | V4L2_MBUS_CSI2_CHANNEL_0 | | ||
707 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | int camera_common_focuser_s_power(struct v4l2_subdev *sd, int on) | ||
713 | { | ||
714 | int err = 0; | ||
715 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
716 | struct camera_common_focuser_data *s_data = | ||
717 | to_camera_common_focuser_data(client); | ||
718 | |||
719 | if (on) { | ||
720 | err = call_s_op(s_data, power_on); | ||
721 | if (err) | ||
722 | dev_err(&s_data->i2c_client->dev, | ||
723 | "%s: error power on\n", __func__); | ||
724 | } else | ||
725 | err = call_s_op(s_data, power_off); | ||
726 | |||
727 | return err; | ||
728 | } | ||
729 | |||
730 | int camera_common_focuser_init(struct camera_common_focuser_data *s_data) | ||
731 | { | ||
732 | int err = 0; | ||
733 | |||
734 | /* power on */ | ||
735 | err = call_s_op(s_data, power_on); | ||
736 | if (err) { | ||
737 | dev_err(&s_data->i2c_client->dev, | ||
738 | "%s: error power on\n", __func__); | ||
739 | return err; | ||
740 | } | ||
741 | |||
742 | /* load default configuration */ | ||
743 | err = call_s_op(s_data, load_config); | ||
744 | if (err) { | ||
745 | dev_err(&s_data->i2c_client->dev, | ||
746 | "%s: error loading config\n", __func__); | ||
747 | goto fail; | ||
748 | } | ||
749 | |||
750 | /* set controls */ | ||
751 | err = call_s_op(s_data, ctrls_init); | ||
752 | if (err) | ||
753 | dev_err(&s_data->i2c_client->dev, | ||
754 | "%s: error initializing controls\n", __func__); | ||
755 | |||
756 | fail: | ||
757 | /* power off */ | ||
758 | err |= call_s_op(s_data, power_off); | ||
759 | |||
760 | return err; | ||
761 | } | ||
762 | |||
763 | int camera_common_parse_sensor_mode(struct i2c_client *client, | ||
764 | struct camera_common_pdata *pdata) | ||
765 | { | ||
766 | struct device_node *np = client->dev.of_node; | ||
767 | char temp_str[OF_MAX_STR_LEN]; | ||
768 | const char *str; | ||
769 | struct device_node *node; | ||
770 | int num_modes = 0; | ||
771 | int err, i; | ||
772 | |||
773 | /* get number of modes */ | ||
774 | for (i = 0; num_modes < MAX_NUM_SENSOR_MODES; i++) { | ||
775 | snprintf(temp_str, sizeof(temp_str), "%s%d", | ||
776 | OF_SENSORMODE_PREFIX, i); | ||
777 | of_node_get(np); | ||
778 | node = of_find_node_by_name(np, temp_str); | ||
779 | if (node == NULL) | ||
780 | break; | ||
781 | num_modes++; | ||
782 | } | ||
783 | |||
784 | pdata->mode_info = devm_kzalloc(&client->dev, | ||
785 | num_modes * sizeof(struct camera_common_mode_info), | ||
786 | GFP_KERNEL); | ||
787 | if (!pdata->mode_info) { | ||
788 | dev_err(&client->dev, "Failed to allocate memory for mode info\n"); | ||
789 | return -ENOMEM; | ||
790 | } | ||
791 | memset(pdata->mode_info, 0, num_modes * | ||
792 | sizeof(struct camera_common_mode_info)); | ||
793 | |||
794 | /* parse mode info */ | ||
795 | for (i = 0; i < num_modes; i++) { | ||
796 | snprintf(temp_str, sizeof(temp_str), "%s%d", | ||
797 | OF_SENSORMODE_PREFIX, i); | ||
798 | of_node_get(np); | ||
799 | node = of_find_node_by_name(np, temp_str); | ||
800 | if (node == NULL) { | ||
801 | dev_err(&client->dev, "Failed to find mode\n"); | ||
802 | return -ENODATA; | ||
803 | }; | ||
804 | |||
805 | /* read mode width */ | ||
806 | of_property_read_string(node, "active_w", &str); | ||
807 | if (str == NULL) { | ||
808 | dev_err(&client->dev, "Failed to read mode width\n"); | ||
809 | return -ENODATA; | ||
810 | }; | ||
811 | err = kstrtoint(str, 10, &pdata->mode_info[i].width); | ||
812 | if (err) { | ||
813 | dev_err(&client->dev, "Failed to convert mode width\n"); | ||
814 | return -EFAULT; | ||
815 | } | ||
816 | /* read mode height */ | ||
817 | of_property_read_string(node, "active_h", &str); | ||
818 | if (str == NULL) { | ||
819 | dev_err(&client->dev, "Failed to read mode height\n"); | ||
820 | return -ENODATA; | ||
821 | }; | ||
822 | err = kstrtoint(str, 10, &pdata->mode_info[i].height); | ||
823 | if (err) { | ||
824 | dev_err(&client->dev, "Failed to convert mode height\n"); | ||
825 | return -EFAULT; | ||
826 | } | ||
827 | dev_info(&client->dev, "%s: mode %d x %d:\n", __func__, | ||
828 | pdata->mode_info[i].width, pdata->mode_info[i].height); | ||
829 | |||
830 | of_property_read_string(node, "line_length", &str); | ||
831 | if (str == NULL) { | ||
832 | dev_err(&client->dev, "Failed to read mode line_length\n"); | ||
833 | return -ENODATA; | ||
834 | }; | ||
835 | err = kstrtoint(str, 10, &pdata->mode_info[i].line_length); | ||
836 | if (err) { | ||
837 | dev_err(&client->dev, "Failed to convert mode line_length\n"); | ||
838 | return -EFAULT; | ||
839 | } | ||
840 | |||
841 | of_property_read_string(node, "pix_clk_hz", &str); | ||
842 | if (str == NULL) { | ||
843 | dev_err(&client->dev, "Failed to read mode pix_clk_hz\n"); | ||
844 | return -ENODATA; | ||
845 | }; | ||
846 | err = kstrtoll(str, 10, &pdata->mode_info[i].pixel_clock); | ||
847 | if (err) { | ||
848 | dev_err(&client->dev, "Failed to convert mode pix_clk_hz\n"); | ||
849 | return -EFAULT; | ||
850 | } | ||
851 | dev_info(&client->dev, "%s: line_length = %d, pixel_clock = %llu\n", | ||
852 | __func__, pdata->mode_info[i].line_length, | ||
853 | pdata->mode_info[i].pixel_clock); | ||
854 | } | ||
855 | |||
856 | return 0; | ||
857 | } | ||
858 | EXPORT_SYMBOL(camera_common_parse_sensor_mode); | ||
diff --git a/drivers/media/platform/tegra/camera/camera_gpio.c b/drivers/media/platform/tegra/camera/camera_gpio.c new file mode 100644 index 000000000..fcd53e3be --- /dev/null +++ b/drivers/media/platform/tegra/camera/camera_gpio.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * virtual.c - Camera GPIO driver | ||
3 | * | ||
4 | * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. | ||
5 | |||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | |||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | |||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/list.h> | ||
20 | #include <linux/debugfs.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/gpio.h> | ||
24 | |||
25 | #include "camera_gpio.h" | ||
26 | |||
27 | struct camera_gpio { | ||
28 | struct list_head list; | ||
29 | unsigned gpio_num; | ||
30 | struct mutex mutex; | ||
31 | atomic_t state_cnt; | ||
32 | atomic_t use_cnt; | ||
33 | }; | ||
34 | |||
35 | static DEFINE_MUTEX(g_mutex); | ||
36 | static LIST_HEAD(cam_gpio_list); | ||
37 | |||
38 | int cam_gpio_register(struct i2c_client *client, | ||
39 | unsigned pin_num) { | ||
40 | struct camera_gpio *new_gpio; | ||
41 | struct camera_gpio *next_gpio; | ||
42 | |||
43 | mutex_lock(&g_mutex); | ||
44 | |||
45 | |||
46 | list_for_each_entry(next_gpio, &cam_gpio_list, list) { | ||
47 | if (next_gpio->gpio_num == pin_num) { | ||
48 | dev_dbg(&client->dev, | ||
49 | "%s: gpio pin %u already registered.\n", | ||
50 | __func__, pin_num); | ||
51 | |||
52 | atomic_inc(&next_gpio->use_cnt); | ||
53 | |||
54 | mutex_unlock(&g_mutex); | ||
55 | return 0; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /* gpio is not present in the cam_gpio_list, add it */ | ||
60 | new_gpio = kzalloc(sizeof(*new_gpio), GFP_KERNEL); | ||
61 | if (!new_gpio) { | ||
62 | dev_err(&client->dev, "%s: memory low!\n", __func__); | ||
63 | mutex_unlock(&g_mutex); | ||
64 | return -EFAULT; | ||
65 | } | ||
66 | |||
67 | dev_dbg(&client->dev, "%s: adding cam gpio %u\n", | ||
68 | __func__, pin_num); | ||
69 | |||
70 | new_gpio->gpio_num = pin_num; | ||
71 | mutex_init(&new_gpio->mutex); | ||
72 | atomic_inc(&new_gpio->use_cnt); | ||
73 | |||
74 | list_add(&new_gpio->list, &cam_gpio_list); | ||
75 | |||
76 | mutex_unlock(&g_mutex); | ||
77 | return 0; | ||
78 | } | ||
79 | EXPORT_SYMBOL(cam_gpio_register); | ||
80 | |||
81 | void cam_gpio_deregister(struct i2c_client *client, | ||
82 | unsigned pin_num) { | ||
83 | struct camera_gpio *next_gpio; | ||
84 | |||
85 | mutex_lock(&g_mutex); | ||
86 | |||
87 | |||
88 | list_for_each_entry(next_gpio, &cam_gpio_list, list) { | ||
89 | if (next_gpio->gpio_num == pin_num) { | ||
90 | atomic_dec(&next_gpio->use_cnt); | ||
91 | |||
92 | if (atomic_read(&next_gpio->use_cnt) == 0) { | ||
93 | list_del(&next_gpio->list); | ||
94 | kfree(next_gpio); | ||
95 | |||
96 | dev_dbg(&client->dev, | ||
97 | "%s: removing cam gpio %u\n", | ||
98 | __func__, pin_num); | ||
99 | } | ||
100 | |||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | mutex_unlock(&g_mutex); | ||
106 | return; | ||
107 | } | ||
108 | EXPORT_SYMBOL(cam_gpio_deregister); | ||
109 | |||
110 | int cam_gpio_ctrl(struct i2c_client *client, | ||
111 | unsigned pin_num, int val, | ||
112 | bool active_high) /* val: 0=deassert, 1=assert */ | ||
113 | { | ||
114 | struct camera_gpio *next_gpio; | ||
115 | int err = -EINVAL; | ||
116 | int pin_val; | ||
117 | bool found = false; | ||
118 | |||
119 | list_for_each_entry(next_gpio, &cam_gpio_list, list) { | ||
120 | mutex_lock(&next_gpio->mutex); | ||
121 | if (next_gpio->gpio_num == pin_num) { | ||
122 | found = true; | ||
123 | |||
124 | if (!atomic_read(&next_gpio->state_cnt) && | ||
125 | !val) { | ||
126 | dev_err(&client->dev, | ||
127 | "%s: state cnt can't be < 0\n", | ||
128 | __func__); | ||
129 | mutex_unlock(&next_gpio->mutex); | ||
130 | return err; | ||
131 | } | ||
132 | |||
133 | if (val) | ||
134 | atomic_inc(&next_gpio->state_cnt); | ||
135 | else | ||
136 | atomic_dec(&next_gpio->state_cnt); | ||
137 | |||
138 | pin_val = active_high ? val : !val; | ||
139 | pin_val &= 1; | ||
140 | err = pin_val; | ||
141 | |||
142 | /* subtract val allows a 0 check to be | ||
143 | * used to indicate that gpio can be written to*/ | ||
144 | if (atomic_read(&next_gpio->state_cnt) - val == 0) { | ||
145 | gpio_set_value_cansleep(pin_num, pin_val); | ||
146 | dev_dbg(&client->dev, "%s %u %d\n", | ||
147 | __func__, pin_num, pin_val); | ||
148 | } | ||
149 | } | ||
150 | mutex_unlock(&next_gpio->mutex); | ||
151 | } | ||
152 | |||
153 | if (!found) | ||
154 | dev_dbg(&client->dev, | ||
155 | "WARNING %s: gpio %u not in list\n", | ||
156 | __func__, pin_num); | ||
157 | |||
158 | return err; /* return value written or error */ | ||
159 | } | ||
160 | EXPORT_SYMBOL(cam_gpio_ctrl); | ||
diff --git a/drivers/media/platform/tegra/camera/camera_gpio.h b/drivers/media/platform/tegra/camera/camera_gpio.h new file mode 100644 index 000000000..b388e178d --- /dev/null +++ b/drivers/media/platform/tegra/camera/camera_gpio.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | ||
3 | |||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | |||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | |||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #ifndef __CAMERA_GPIO_H__ | ||
18 | #define __CAMERA_GPIO_H__ | ||
19 | |||
20 | int cam_gpio_register(struct i2c_client *client, | ||
21 | unsigned pin_num); | ||
22 | |||
23 | void cam_gpio_deregister(struct i2c_client *client, | ||
24 | unsigned pin_num); | ||
25 | |||
26 | int cam_gpio_ctrl(struct i2c_client *client, | ||
27 | unsigned pin_num, int ref_inc, bool active_high); | ||
28 | |||
29 | #endif | ||
30 | /* __CAMERA_GPIO_H__ */ | ||
diff --git a/drivers/media/platform/tegra/camera/csi/Makefile b/drivers/media/platform/tegra/camera/csi/Makefile new file mode 100644 index 000000000..3d307cb83 --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | ccflags-y += -I../nvhost/drivers/video/tegra/host | ||
3 | ccflags-y += -I../nvhost/include | ||
4 | ccflags-y += -Idrivers/video/tegra/camera | ||
5 | ccflags-y += -Idrivers/media/platform/tegra | ||
6 | ccflags-y += -Werror | ||
7 | |||
8 | obj-y += csi.o csi2_fops.o csi4_fops.o | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi.c b/drivers/media/platform/tegra/camera/csi/csi.c new file mode 100644 index 000000000..f6c838ff2 --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi.c | |||
@@ -0,0 +1,784 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra CSI Device | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/gpio/consumer.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_graph.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/of_platform.h> | ||
21 | |||
22 | #include <media/media-entity.h> | ||
23 | #include <media/v4l2-async.h> | ||
24 | #include <media/v4l2-ctrls.h> | ||
25 | #include <media/camera_common.h> | ||
26 | |||
27 | #include "dev.h" | ||
28 | #include "vi/vi.h" | ||
29 | #include "camera/csi/csi.h" | ||
30 | #include "camera/vi/mc_common.h" | ||
31 | #include "mipical/mipi_cal.h" | ||
32 | #include "linux/nvhost.h" | ||
33 | |||
34 | static int set_csi_properties(struct tegra_csi_device *csi, | ||
35 | struct platform_device *pdev) | ||
36 | { | ||
37 | struct camera_common_data *s_data = &csi->s_data[0]; | ||
38 | |||
39 | /* | ||
40 | * These values are only used for tpg mode | ||
41 | * With sensor, CSI power and clock info are provided | ||
42 | * by the sensor sub device | ||
43 | */ | ||
44 | s_data->csi_port = 0; | ||
45 | s_data->numlanes = 12; | ||
46 | csi->clk_freq = TEGRA_CLOCK_CSI_PORT_MAX; | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static void update_blank_intervals(struct tegra_csi_channel *chan, | ||
52 | int portnum, int fmtindex) | ||
53 | { | ||
54 | struct tegra_csi_port *port = &chan->ports[portnum]; | ||
55 | const struct tpg_frmfmt *tegra_csi_tpg_frmfmt = | ||
56 | chan->csi->tpg_frmfmt_table; | ||
57 | |||
58 | port->framerate = tegra_csi_tpg_frmfmt[fmtindex].framerate; | ||
59 | port->h_blank = tegra_csi_tpg_frmfmt[fmtindex].h_blank; | ||
60 | port->v_blank = tegra_csi_tpg_frmfmt[fmtindex].v_blank; | ||
61 | } | ||
62 | |||
63 | void set_csi_portinfo(struct tegra_csi_device *csi, | ||
64 | unsigned int port, unsigned int numlanes) | ||
65 | { | ||
66 | struct camera_common_data *s_data = &csi->s_data[port]; | ||
67 | |||
68 | s_data->csi_port = port; | ||
69 | s_data->numlanes = numlanes; | ||
70 | s_data->def_clk_freq = TEGRA_CLOCK_CSI_PORT_MAX; | ||
71 | } | ||
72 | EXPORT_SYMBOL(set_csi_portinfo); | ||
73 | |||
74 | int tegra_csi_power(struct tegra_csi_device *csi, int enable) | ||
75 | { | ||
76 | int err = 0; | ||
77 | |||
78 | if (enable) { | ||
79 | err = csi->fops->csi_power_on(csi); | ||
80 | if (!err) | ||
81 | atomic_inc(&csi->power_ref); | ||
82 | } else { | ||
83 | err = csi->fops->csi_power_off(csi); | ||
84 | if (!err) | ||
85 | atomic_dec(&csi->power_ref); | ||
86 | } | ||
87 | return err; | ||
88 | } | ||
89 | EXPORT_SYMBOL(tegra_csi_power); | ||
90 | |||
91 | int tegra_csi_s_power(struct v4l2_subdev *subdev, int enable) | ||
92 | { | ||
93 | int err = 0; | ||
94 | struct tegra_csi_device *csi = to_csi(subdev); | ||
95 | |||
96 | err = tegra_csi_power(csi, enable); | ||
97 | |||
98 | return err; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * ----------------------------------------------------------------------------- | ||
103 | * CSI Subdevice Video Operations | ||
104 | * ----------------------------------------------------------------------------- | ||
105 | */ | ||
106 | |||
107 | int tegra_csi_start_streaming(struct tegra_csi_channel *chan, | ||
108 | enum tegra_csi_port_num port_num) | ||
109 | { | ||
110 | struct tegra_csi_device *csi = chan->csi; | ||
111 | |||
112 | return csi->fops->csi_start_streaming(chan, port_num); | ||
113 | } | ||
114 | EXPORT_SYMBOL(tegra_csi_start_streaming); | ||
115 | |||
116 | void tegra_csi_stop_streaming(struct tegra_csi_channel *chan, | ||
117 | enum tegra_csi_port_num port_num) | ||
118 | { | ||
119 | struct tegra_csi_device *csi = chan->csi; | ||
120 | |||
121 | csi->fops->csi_stop_streaming(chan, port_num); | ||
122 | } | ||
123 | EXPORT_SYMBOL(tegra_csi_stop_streaming); | ||
124 | |||
125 | static int tegra_csi_s_stream(struct v4l2_subdev *subdev, int enable) | ||
126 | { | ||
127 | struct tegra_csi_device *csi; | ||
128 | struct tegra_csi_channel *chan = to_csi_chan(subdev); | ||
129 | struct tegra_channel *tegra_chan = v4l2_get_subdev_hostdata(subdev); | ||
130 | int i, ret = 0; | ||
131 | |||
132 | csi = to_csi(subdev); | ||
133 | if (!csi) | ||
134 | return -EINVAL; | ||
135 | if (!chan->pg_mode) { | ||
136 | if (enable) { | ||
137 | tegra_mipi_bias_pad_enable(); | ||
138 | csi->fops->mipical(chan); | ||
139 | } else | ||
140 | tegra_mipi_bias_pad_disable(); | ||
141 | } | ||
142 | if (tegra_chan->bypass) | ||
143 | return 0; | ||
144 | |||
145 | for (i = 0; i < tegra_chan->valid_ports; i++) { | ||
146 | if (enable) { | ||
147 | ret = tegra_csi_start_streaming(chan, i); | ||
148 | if (ret) | ||
149 | tegra_csi_stop_streaming(chan, i); | ||
150 | } else | ||
151 | tegra_csi_stop_streaming(chan, i); | ||
152 | } | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * Only use this subdevice media bus ops for test pattern generator, | ||
158 | * because CSI device is an separated subdevice which has 6 source | ||
159 | * pads to generate test pattern. | ||
160 | */ | ||
161 | static struct v4l2_mbus_framefmt tegra_csi_tpg_fmts[] = { | ||
162 | { | ||
163 | TEGRA_DEF_WIDTH, | ||
164 | TEGRA_DEF_HEIGHT, | ||
165 | MEDIA_BUS_FMT_SRGGB10_1X10, | ||
166 | V4L2_FIELD_NONE, | ||
167 | V4L2_COLORSPACE_SRGB | ||
168 | }, | ||
169 | { | ||
170 | TEGRA_DEF_WIDTH, | ||
171 | TEGRA_DEF_HEIGHT, | ||
172 | MEDIA_BUS_FMT_RGB888_1X32_PADHI, | ||
173 | V4L2_FIELD_NONE, | ||
174 | V4L2_COLORSPACE_SRGB | ||
175 | } | ||
176 | |||
177 | }; | ||
178 | |||
179 | static struct v4l2_frmsize_discrete tegra_csi_tpg_sizes[] = { | ||
180 | {1280, 720}, | ||
181 | {1920, 1080}, | ||
182 | {3840, 2160} | ||
183 | }; | ||
184 | |||
185 | static int tegra_csi_enum_framesizes(struct v4l2_subdev *sd, | ||
186 | struct v4l2_subdev_pad_config *cfg, | ||
187 | struct v4l2_subdev_frame_size_enum *fse) | ||
188 | { | ||
189 | int i; | ||
190 | struct tegra_csi_channel *chan = to_csi_chan(sd); | ||
191 | struct tegra_channel *vi_chan = v4l2_get_subdev_hostdata(sd); | ||
192 | |||
193 | if (!chan->pg_mode) | ||
194 | return -ENOIOCTLCMD; | ||
195 | |||
196 | if (fse->index >= ARRAY_SIZE(tegra_csi_tpg_sizes)) | ||
197 | return -EINVAL; | ||
198 | |||
199 | for (i = 0; i < ARRAY_SIZE(tegra_csi_tpg_fmts); i++) { | ||
200 | const struct tegra_video_format *format = | ||
201 | tegra_core_get_format_by_code(vi_chan, | ||
202 | tegra_csi_tpg_fmts[i].code, 0); | ||
203 | if (format && format->fourcc == fse->code) | ||
204 | break; | ||
205 | } | ||
206 | if (i == ARRAY_SIZE(tegra_csi_tpg_fmts)) | ||
207 | return -EINVAL; | ||
208 | |||
209 | fse->min_width = fse->max_width = | ||
210 | tegra_csi_tpg_sizes[fse->index].width; | ||
211 | fse->min_height = fse->max_height = | ||
212 | tegra_csi_tpg_sizes[fse->index].height; | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int tegra_csi_get_fmtindex(struct tegra_csi_channel *chan, | ||
217 | int width, int height, int pixel_format) | ||
218 | { | ||
219 | int i; | ||
220 | const struct tpg_frmfmt *tegra_csi_tpg_frmfmt = | ||
221 | chan->csi->tpg_frmfmt_table; | ||
222 | |||
223 | for (i = 0; i < chan->csi->tpg_frmfmt_table_size; i++) { | ||
224 | if (tegra_csi_tpg_frmfmt[i].frmsize.width == width && | ||
225 | tegra_csi_tpg_frmfmt[i].frmsize.height == height && | ||
226 | tegra_csi_tpg_frmfmt[i].pixel_format == pixel_format) | ||
227 | break; | ||
228 | } | ||
229 | |||
230 | if (i == chan->csi->tpg_frmfmt_table_size) | ||
231 | return -EINVAL; | ||
232 | |||
233 | return i; | ||
234 | } | ||
235 | |||
236 | static int tegra_csi_enum_frameintervals(struct v4l2_subdev *sd, | ||
237 | struct v4l2_subdev_pad_config *cfg, | ||
238 | struct v4l2_subdev_frame_interval_enum *fie) | ||
239 | { | ||
240 | int index; | ||
241 | struct tegra_csi_channel *chan = to_csi_chan(sd); | ||
242 | const struct tegra_video_format *format; | ||
243 | const struct tpg_frmfmt *tegra_csi_tpg_frmfmt = | ||
244 | chan->csi->tpg_frmfmt_table; | ||
245 | struct tegra_channel *vi_chan = v4l2_get_subdev_hostdata(sd); | ||
246 | |||
247 | if (!chan->pg_mode) | ||
248 | return -ENOIOCTLCMD; | ||
249 | |||
250 | /* One resolution just one framerate */ | ||
251 | if (fie->index > 0) | ||
252 | return -EINVAL; | ||
253 | format = tegra_core_get_format_by_fourcc(vi_chan, fie->code); | ||
254 | if (!format) | ||
255 | return -EINVAL; | ||
256 | index = tegra_csi_get_fmtindex(chan, fie->width, fie->height, | ||
257 | format->fourcc); | ||
258 | if (index < 0) | ||
259 | return -EINVAL; | ||
260 | |||
261 | fie->interval.numerator = 1; | ||
262 | fie->interval.denominator = tegra_csi_tpg_frmfmt[index].framerate; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int tegra_csi_try_mbus_fmt(struct v4l2_subdev *sd, | ||
268 | struct v4l2_mbus_framefmt *mf) | ||
269 | { | ||
270 | int i, j; | ||
271 | struct tegra_csi_channel *chan = to_csi_chan(sd); | ||
272 | static struct v4l2_frmsize_discrete *sizes; | ||
273 | |||
274 | if (!chan->pg_mode) | ||
275 | return -ENOIOCTLCMD; | ||
276 | |||
277 | for (i = 0; i < ARRAY_SIZE(tegra_csi_tpg_fmts); i++) { | ||
278 | struct v4l2_mbus_framefmt *fmt = &tegra_csi_tpg_fmts[i]; | ||
279 | |||
280 | if (mf->code == fmt->code && mf->field == fmt->field && | ||
281 | mf->colorspace == fmt->colorspace) { | ||
282 | for (j = 0; j < ARRAY_SIZE(tegra_csi_tpg_sizes); j++) { | ||
283 | sizes = &tegra_csi_tpg_sizes[j]; | ||
284 | if (mf->width == sizes->width && | ||
285 | mf->height == sizes->height) | ||
286 | return 0; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | memcpy(mf, tegra_csi_tpg_fmts, sizeof(struct v4l2_mbus_framefmt)); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int tegra_csi_g_mbus_fmt(struct v4l2_subdev *sd, | ||
297 | struct v4l2_mbus_framefmt *fmt) | ||
298 | { | ||
299 | struct tegra_csi_channel *chan = to_csi_chan(sd); | ||
300 | struct v4l2_mbus_framefmt *format = &chan->ports[0].format; | ||
301 | |||
302 | if (!chan->pg_mode) { | ||
303 | dev_err(chan->csi->dev, "CSI is not in TPG mode\n"); | ||
304 | return -EINVAL; | ||
305 | } | ||
306 | |||
307 | mutex_lock(&chan->format_lock); | ||
308 | memcpy(fmt, format, sizeof(struct v4l2_mbus_framefmt)); | ||
309 | mutex_unlock(&chan->format_lock); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static int csi_is_power_on(struct tegra_csi_device *csi) | ||
315 | { | ||
316 | return atomic_read(&csi->power_ref); | ||
317 | } | ||
318 | static int tegra_csi_g_input_status(struct v4l2_subdev *sd, u32 *status) | ||
319 | { | ||
320 | struct tegra_csi_device *csi = to_csi(sd); | ||
321 | |||
322 | /* Set status to 0 if power is on | ||
323 | * Set status to 1 if power is off | ||
324 | */ | ||
325 | *status = !csi_is_power_on(csi); | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | /* ----------------------------------------------------------------------------- | ||
331 | * V4L2 Subdevice Pad Operations | ||
332 | */ | ||
333 | |||
334 | static int tegra_csi_get_format(struct v4l2_subdev *subdev, | ||
335 | struct v4l2_subdev_pad_config *cfg, | ||
336 | struct v4l2_subdev_format *fmt) | ||
337 | { | ||
338 | struct tegra_csi_channel *chan = to_csi_chan(subdev); | ||
339 | struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; | ||
340 | int ret; | ||
341 | |||
342 | if (!chan->pg_mode) | ||
343 | return -ENOIOCTLCMD; | ||
344 | ret = tegra_csi_g_mbus_fmt(subdev, mbus_fmt); | ||
345 | if (ret) | ||
346 | return ret; | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int tegra_csi_set_format(struct v4l2_subdev *subdev, | ||
352 | struct v4l2_subdev_pad_config *cfg, | ||
353 | struct v4l2_subdev_format *fmt) | ||
354 | { | ||
355 | int ret; | ||
356 | struct tegra_csi_channel *chan = to_csi_chan(subdev); | ||
357 | struct v4l2_mbus_framefmt *format = &fmt->format; | ||
358 | const struct tegra_video_format *vf; | ||
359 | struct tegra_channel *vi_chan = v4l2_get_subdev_hostdata(subdev); | ||
360 | int index, i; | ||
361 | |||
362 | if (!chan->pg_mode) | ||
363 | return -ENOIOCTLCMD; | ||
364 | |||
365 | ret = tegra_csi_try_mbus_fmt(subdev, format); | ||
366 | if (ret) | ||
367 | return ret; | ||
368 | |||
369 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) | ||
370 | return 0; | ||
371 | |||
372 | vf = tegra_core_get_format_by_code(vi_chan, format->code, 0); | ||
373 | if (!vf) { | ||
374 | dev_err(chan->csi->dev, "Fail to find tegra video fmt"); | ||
375 | mutex_unlock(&chan->format_lock); | ||
376 | return -EINVAL; | ||
377 | } | ||
378 | index = tegra_csi_get_fmtindex(chan, format->width, | ||
379 | format->height, vf->fourcc); | ||
380 | if (index < 0) { | ||
381 | dev_err(chan->csi->dev, "Fail to find matching fmt"); | ||
382 | return -EINVAL; | ||
383 | } | ||
384 | |||
385 | mutex_lock(&chan->format_lock); | ||
386 | for (i = 0; i < vi_chan->valid_ports; i++) { | ||
387 | memcpy(&chan->ports[i].format, | ||
388 | &fmt->format, sizeof(struct v4l2_mbus_framefmt)); | ||
389 | chan->ports[i].core_format = vf; | ||
390 | update_blank_intervals(chan, i, index); | ||
391 | } | ||
392 | mutex_unlock(&chan->format_lock); | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int tegra_csi_g_frame_interval(struct v4l2_subdev *sd, | ||
398 | struct v4l2_subdev_frame_interval *vfi) | ||
399 | { | ||
400 | struct tegra_csi_channel *chan = to_csi_chan(sd); | ||
401 | struct tegra_csi_port *port = &chan->ports[0]; | ||
402 | |||
403 | if (!port->framerate) | ||
404 | return -EINVAL; | ||
405 | |||
406 | vfi->interval.numerator = 1; | ||
407 | vfi->interval.denominator = port->framerate; | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | /* ----------------------------------------------------------------------------- | ||
413 | * V4L2 Subdevice Operations | ||
414 | */ | ||
415 | static struct v4l2_subdev_video_ops tegra_csi_video_ops = { | ||
416 | .s_stream = tegra_csi_s_stream, | ||
417 | .g_input_status = tegra_csi_g_input_status, | ||
418 | .g_frame_interval = tegra_csi_g_frame_interval, | ||
419 | }; | ||
420 | |||
421 | static struct v4l2_subdev_pad_ops tegra_csi_pad_ops = { | ||
422 | .get_fmt = tegra_csi_get_format, | ||
423 | .set_fmt = tegra_csi_set_format, | ||
424 | .enum_frame_size = tegra_csi_enum_framesizes, | ||
425 | .enum_frame_interval = tegra_csi_enum_frameintervals, | ||
426 | }; | ||
427 | |||
428 | static struct v4l2_subdev_core_ops tegra_csi_core_ops = { | ||
429 | .s_power = tegra_csi_s_power, | ||
430 | }; | ||
431 | |||
432 | static struct v4l2_subdev_ops tegra_csi_ops = { | ||
433 | .core = &tegra_csi_core_ops, | ||
434 | .video = &tegra_csi_video_ops, | ||
435 | .pad = &tegra_csi_pad_ops, | ||
436 | }; | ||
437 | |||
438 | /* ----------------------------------------------------------------------------- | ||
439 | * Media Operations | ||
440 | */ | ||
441 | |||
442 | static const struct media_entity_operations tegra_csi_media_ops = { | ||
443 | .link_validate = v4l2_subdev_link_validate, | ||
444 | }; | ||
445 | |||
446 | /* ----------------------------------------------------------------------------- | ||
447 | * Platform Device Driver | ||
448 | */ | ||
449 | |||
450 | static int tegra_csi_get_port_info(struct tegra_csi_channel *chan, | ||
451 | struct device_node *node, unsigned int index) | ||
452 | { | ||
453 | struct device_node *ep = NULL; | ||
454 | struct device_node *ports; | ||
455 | struct device_node *port; | ||
456 | struct device_node *chan_dt; | ||
457 | |||
458 | int value = 0xFFFF; | ||
459 | int ret = 0, i; | ||
460 | |||
461 | memset(&chan->port[0], INVALID_CSI_PORT, TEGRA_CSI_BLOCKS); | ||
462 | for_each_child_of_node(node, chan_dt) { | ||
463 | if (!chan_dt->name || of_node_cmp(chan_dt->name, "channel")) | ||
464 | continue; | ||
465 | ret = of_property_read_u32(chan_dt, "reg", &value); | ||
466 | if (ret < 0) | ||
467 | return -EINVAL; | ||
468 | if (value == index) | ||
469 | break; | ||
470 | } | ||
471 | |||
472 | chan->subdev.of_node = chan_dt; | ||
473 | ports = of_get_child_by_name(chan_dt, "ports"); | ||
474 | if (ports == NULL) | ||
475 | return -EINVAL; | ||
476 | |||
477 | for_each_child_of_node(ports, port) { | ||
478 | if (!port->name || of_node_cmp(port->name, "port")) | ||
479 | continue; | ||
480 | ret = of_property_read_u32(port, "reg", &value); | ||
481 | if (ret < 0) | ||
482 | continue; | ||
483 | if (value != 0) | ||
484 | continue; | ||
485 | for_each_child_of_node(port, ep) { | ||
486 | if (!ep->name || of_node_cmp(ep->name, "endpoint")) | ||
487 | continue; | ||
488 | ret = of_property_read_u32(ep, "csi-port", &value); | ||
489 | if (ret < 0) | ||
490 | dev_err(chan->csi->dev, "No csi port info\n"); | ||
491 | chan->port[0] = value; | ||
492 | |||
493 | ret = of_property_read_u32(ep, "bus-width", &value); | ||
494 | if (ret < 0) | ||
495 | dev_err(chan->csi->dev, "No bus width info\n"); | ||
496 | chan->numlanes = value; | ||
497 | if (value > 12) { | ||
498 | dev_err(chan->csi->dev, "Invalid num lanes\n"); | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | /* | ||
502 | * for numlanes greater than 4 multiple CSI bricks | ||
503 | * are needed to capture the image, the logic below | ||
504 | * checks for numlanes > 4 and add a new CSI brick | ||
505 | * as a valid port. Loops around the three CSI | ||
506 | * bricks to add as many ports necessary. | ||
507 | */ | ||
508 | value -= 4; | ||
509 | for (i = 1; value > 0; i++, value -= 4) { | ||
510 | int next_port = chan->port[i-1] + 2; | ||
511 | |||
512 | next_port = (next_port % (PORT_F + 1)); | ||
513 | chan->port[i] = next_port; | ||
514 | } | ||
515 | } | ||
516 | } | ||
517 | |||
518 | for (i = 0; csi_port_is_valid(chan->port[i]); i++) | ||
519 | chan->numports++; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | int tegra_csi_init(struct tegra_csi_device *csi, | ||
525 | struct platform_device *pdev) | ||
526 | { | ||
527 | int err = 0; | ||
528 | struct nvhost_device_data *pdata = platform_get_drvdata(pdev); | ||
529 | |||
530 | csi->dev = &pdev->dev; | ||
531 | err = set_csi_properties(csi, pdev); | ||
532 | if (err) | ||
533 | return err; | ||
534 | |||
535 | csi->iomem_base = pdata->aperture[0]; | ||
536 | csi->fops->hw_init(csi); | ||
537 | return err; | ||
538 | } | ||
539 | |||
540 | static int tegra_csi_channel_init_one(struct tegra_csi_channel *chan) | ||
541 | { | ||
542 | struct v4l2_subdev *sd; | ||
543 | int numlanes = 0; | ||
544 | struct tegra_csi_device *csi = chan->csi; | ||
545 | int i, ret; | ||
546 | const struct tegra_video_format *vf; | ||
547 | |||
548 | mutex_init(&chan->format_lock); | ||
549 | |||
550 | vf = tegra_core_get_default_format(); | ||
551 | if (vf == NULL) { | ||
552 | dev_err(csi->dev, "Fail to find tegra video fmt"); | ||
553 | return -EINVAL; | ||
554 | } | ||
555 | |||
556 | sd = &chan->subdev; | ||
557 | /* Initialize V4L2 subdevice and media entity */ | ||
558 | v4l2_subdev_init(sd, &tegra_csi_ops); | ||
559 | sd->dev = chan->csi->dev; | ||
560 | v4l2_set_subdevdata(sd, csi); | ||
561 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
562 | sd->entity.ops = &tegra_csi_media_ops; | ||
563 | chan->ports = devm_kzalloc(csi->dev, | ||
564 | chan->numports * sizeof(struct tegra_csi_port), | ||
565 | GFP_KERNEL); | ||
566 | if (!chan->ports) | ||
567 | return -ENOMEM; | ||
568 | |||
569 | /* Initialize the default format */ | ||
570 | for (i = 0; i < chan->numports; i++) { | ||
571 | chan->ports[i].format.code = vf->vf_code; | ||
572 | chan->ports[i].format.field = V4L2_FIELD_NONE; | ||
573 | chan->ports[i].format.colorspace = V4L2_COLORSPACE_SRGB; | ||
574 | chan->ports[i].format.width = TEGRA_DEF_WIDTH; | ||
575 | chan->ports[i].format.height = TEGRA_DEF_HEIGHT; | ||
576 | chan->ports[i].core_format = vf; | ||
577 | } | ||
578 | if (chan->pg_mode) { | ||
579 | /* If CSI has 2 existing channels, chan->id will start | ||
580 | * from 2 for the first TPG channel, which uses PORT_A(0). | ||
581 | * To get the correct PORT number, subtract existing number of | ||
582 | * channels from chan->id. | ||
583 | */ | ||
584 | chan->port[0] = chan->id - csi->num_channels; | ||
585 | WARN_ON(chan->port[0] > TPG_CHANNELS); | ||
586 | chan->ports[0].num = chan->id - csi->num_channels; | ||
587 | chan->ports->lanes = 2; | ||
588 | chan->pads = devm_kzalloc(csi->dev, sizeof(*chan->pads), | ||
589 | GFP_KERNEL); | ||
590 | if (!chan->pads) | ||
591 | return -ENOMEM; | ||
592 | chan->pads[0].flags = MEDIA_PAD_FL_SOURCE; | ||
593 | } else { | ||
594 | chan->pads = devm_kzalloc(csi->dev, 2 * sizeof(*chan->pads), | ||
595 | GFP_KERNEL); | ||
596 | if (!chan->pads) | ||
597 | return -ENOMEM; | ||
598 | chan->pads[0].flags = MEDIA_PAD_FL_SINK; | ||
599 | chan->pads[1].flags = MEDIA_PAD_FL_SOURCE; | ||
600 | } | ||
601 | snprintf(sd->name, sizeof(sd->name), "%s-%d", | ||
602 | chan->pg_mode ? "tpg" : | ||
603 | (strlen(csi->devname) == 0 ? | ||
604 | dev_name(csi->dev) : csi->devname), | ||
605 | chan->port[0]); | ||
606 | /* Initialize media entity */ | ||
607 | ret = media_entity_init(&sd->entity, | ||
608 | chan->pg_mode ? 1 : 2, | ||
609 | chan->pads, 0); | ||
610 | if (ret < 0) | ||
611 | return ret; | ||
612 | |||
613 | for (i = 0; i < chan->numports; i++) { | ||
614 | numlanes = chan->numlanes - (i * MAX_CSI_BLOCK_LANES); | ||
615 | WARN_ON(numlanes < 0); | ||
616 | numlanes = numlanes > MAX_CSI_BLOCK_LANES ? | ||
617 | MAX_CSI_BLOCK_LANES : numlanes; | ||
618 | chan->ports[i].lanes = numlanes; | ||
619 | chan->ports[i].num = chan->port[i]; | ||
620 | } | ||
621 | |||
622 | if (!chan->pg_mode) { | ||
623 | ret = v4l2_async_register_subdev(sd); | ||
624 | if (ret < 0) { | ||
625 | dev_err(csi->dev, "failed to register subdev\n"); | ||
626 | media_entity_cleanup(&sd->entity); | ||
627 | } | ||
628 | } | ||
629 | return ret; | ||
630 | } | ||
631 | |||
632 | static int tegra_csi_channels_init(struct tegra_csi_device *csi) | ||
633 | { | ||
634 | int ret; | ||
635 | struct tegra_csi_channel *it; | ||
636 | |||
637 | list_for_each_entry(it, &csi->csi_chans, list) { | ||
638 | ret = tegra_csi_channel_init_one(it); | ||
639 | if (ret) | ||
640 | return ret; | ||
641 | } | ||
642 | |||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static int csi_parse_dt(struct tegra_csi_device *csi, | ||
647 | struct platform_device *pdev) | ||
648 | { | ||
649 | int err = 0, i; | ||
650 | int num_channels = 0; | ||
651 | struct device_node *node = pdev->dev.of_node; | ||
652 | struct tegra_csi_channel *item; | ||
653 | |||
654 | if (strncmp(node->name, "nvcsi", 5)) { | ||
655 | node = of_find_node_by_name(node, "nvcsi"); | ||
656 | strncpy(csi->devname, "nvcsi", 6); | ||
657 | } | ||
658 | if (!node) | ||
659 | return -EINVAL; | ||
660 | err = of_property_read_u32(node, "num-channels", &num_channels); | ||
661 | if (err) { | ||
662 | dev_dbg(csi->dev, " Failed to find num of channels, set to 0\n"); | ||
663 | num_channels = 0; | ||
664 | } | ||
665 | |||
666 | csi->num_channels = num_channels; | ||
667 | for (i = 0; i < num_channels; i++) { | ||
668 | item = devm_kzalloc(csi->dev, sizeof(*item), GFP_KERNEL); | ||
669 | if (!item) | ||
670 | return -ENOMEM; | ||
671 | list_add_tail(&item->list, &csi->csi_chans); | ||
672 | item->csi = csi; | ||
673 | item->id = i; | ||
674 | err = tegra_csi_get_port_info(item, node, item->id); | ||
675 | if (err) | ||
676 | return err; | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | int tpg_csi_media_controller_init(struct tegra_csi_device *csi, int pg_mode) | ||
683 | { | ||
684 | int i, err; | ||
685 | struct tegra_csi_channel *item; | ||
686 | |||
687 | if (!csi) | ||
688 | return -EINVAL; | ||
689 | for (i = 0; i < TPG_CHANNELS; i++) { | ||
690 | item = devm_kzalloc(csi->dev, sizeof(*item), GFP_KERNEL); | ||
691 | if (!item) { | ||
692 | err = -ENOMEM; | ||
693 | goto channel_init_error; | ||
694 | } | ||
695 | if (i == 0) | ||
696 | csi->tpg_start = item; | ||
697 | list_add_tail(&item->list, &csi->csi_chans); | ||
698 | item->numlanes = 2; | ||
699 | item->numports = 1; | ||
700 | item->csi = csi; | ||
701 | item->pg_mode = pg_mode; | ||
702 | item->id = csi->num_channels + i; | ||
703 | err = tegra_csi_channel_init_one(item); | ||
704 | if (err) | ||
705 | goto channel_init_error; | ||
706 | } | ||
707 | csi->fops->hw_init(csi); | ||
708 | csi->num_channels += TPG_CHANNELS; | ||
709 | |||
710 | return err; | ||
711 | |||
712 | channel_init_error: | ||
713 | if (csi->tpg_start) | ||
714 | tpg_csi_media_controller_cleanup(csi); | ||
715 | dev_err(csi->dev, "%s: Error\n", __func__); | ||
716 | return err; | ||
717 | } | ||
718 | EXPORT_SYMBOL(tpg_csi_media_controller_init); | ||
719 | |||
720 | void tpg_csi_media_controller_cleanup(struct tegra_csi_device *csi) | ||
721 | { | ||
722 | struct tegra_csi_channel *item; | ||
723 | struct tegra_csi_channel *itemn; | ||
724 | struct v4l2_subdev *sd; | ||
725 | |||
726 | list_for_each_entry_safe(item, itemn, &csi->csi_chans, list) { | ||
727 | if (!item->pg_mode) | ||
728 | continue; | ||
729 | sd = &item->subdev; | ||
730 | /* decrement media device entity count */ | ||
731 | if (sd->entity.parent) | ||
732 | sd->entity.parent->entity_id--; | ||
733 | v4l2_device_unregister_subdev(sd); | ||
734 | media_entity_cleanup(&sd->entity); | ||
735 | list_del(&item->list); | ||
736 | devm_kfree(csi->dev, item); | ||
737 | } | ||
738 | csi->num_channels -= TPG_CHANNELS; | ||
739 | csi->tpg_start = NULL; | ||
740 | } | ||
741 | EXPORT_SYMBOL(tpg_csi_media_controller_cleanup); | ||
742 | int tegra_csi_media_controller_init(struct tegra_csi_device *csi, | ||
743 | struct platform_device *pdev) | ||
744 | { | ||
745 | int ret; | ||
746 | |||
747 | csi->dev = &pdev->dev; | ||
748 | csi->pdev = pdev; | ||
749 | atomic_set(&csi->power_ref, 0); | ||
750 | atomic_set(&csi->tpg_enabled, 0); | ||
751 | INIT_LIST_HEAD(&csi->csi_chans); | ||
752 | ret = csi_parse_dt(csi, pdev); | ||
753 | if (ret < 0) | ||
754 | return ret; | ||
755 | |||
756 | /* | ||
757 | * if there is no csi channels listed in DT, | ||
758 | * no need to init the channel and graph | ||
759 | */ | ||
760 | if (csi->num_channels == 0) | ||
761 | return 0; | ||
762 | |||
763 | ret = tegra_csi_channels_init(csi); | ||
764 | ret = tegra_csi_init(csi, pdev); | ||
765 | if (ret < 0) | ||
766 | dev_err(&pdev->dev, "Failed to init csi property,clks\n"); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | EXPORT_SYMBOL(tegra_csi_media_controller_init); | ||
771 | |||
772 | int tegra_csi_media_controller_remove(struct tegra_csi_device *csi) | ||
773 | { | ||
774 | struct tegra_csi_channel *chan; | ||
775 | struct v4l2_subdev *sd; | ||
776 | |||
777 | list_for_each_entry(chan, &csi->csi_chans, list) { | ||
778 | sd = &chan->subdev; | ||
779 | v4l2_async_unregister_subdev(sd); | ||
780 | media_entity_cleanup(&sd->entity); | ||
781 | } | ||
782 | return 0; | ||
783 | } | ||
784 | EXPORT_SYMBOL(tegra_csi_media_controller_remove); | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi.h b/drivers/media/platform/tegra/camera/csi/csi.h new file mode 100644 index 000000000..208e15e42 --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi.h | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra CSI Device Header | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __CSI_H_ | ||
14 | #define __CSI_H_ | ||
15 | |||
16 | #include <media/media-entity.h> | ||
17 | #include <media/v4l2-async.h> | ||
18 | #include <media/v4l2-ctrls.h> | ||
19 | #include <media/v4l2-subdev.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | |||
22 | #include <media/camera_common.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | |||
25 | #include "camera/vi/registers.h" | ||
26 | #include "camera/csi/csi4_registers.h" | ||
27 | |||
28 | #define MAX_CSI_BLOCK_LANES 4 | ||
29 | |||
30 | enum tegra_csi_port_num { | ||
31 | PORT_A = 0, | ||
32 | PORT_B = 1, | ||
33 | PORT_C = 2, | ||
34 | PORT_D = 3, | ||
35 | PORT_E = 4, | ||
36 | PORT_F = 5, | ||
37 | }; | ||
38 | |||
39 | #define csi_port_is_valid(port) (port > PORT_F ? 0 : 1) | ||
40 | |||
41 | enum camera_gang_mode { | ||
42 | CAMERA_NO_GANG_MODE = 0, | ||
43 | CAMERA_GANG_L_R = 1, | ||
44 | CAMERA_GANG_T_B, | ||
45 | CAMERA_GANG_R_L, | ||
46 | CAMERA_GANG_B_T | ||
47 | }; | ||
48 | |||
49 | struct tegra_channel; | ||
50 | |||
51 | struct tpg_frmfmt { | ||
52 | struct v4l2_frmsize_discrete frmsize; | ||
53 | int pixel_format; | ||
54 | int framerate; | ||
55 | int h_blank; | ||
56 | int v_blank; | ||
57 | }; | ||
58 | |||
59 | struct tegra_csi_port { | ||
60 | void __iomem *pixel_parser; | ||
61 | void __iomem *cil; | ||
62 | void __iomem *tpg; | ||
63 | |||
64 | /* One pair of sink/source pad has one format */ | ||
65 | struct v4l2_mbus_framefmt format; | ||
66 | const struct tegra_video_format *core_format; | ||
67 | unsigned int lanes; | ||
68 | unsigned int framerate; | ||
69 | unsigned int h_blank; | ||
70 | unsigned int v_blank; | ||
71 | |||
72 | enum tegra_csi_port_num num; | ||
73 | }; | ||
74 | |||
75 | struct tegra_csi_device { | ||
76 | struct device *dev; | ||
77 | struct platform_device *pdev; | ||
78 | char devname[32]; | ||
79 | void __iomem *iomem_base; | ||
80 | void __iomem *iomem[3]; | ||
81 | struct clk *plld_dsi; | ||
82 | struct clk *plld; | ||
83 | |||
84 | struct camera_common_data s_data[6]; | ||
85 | struct tegra_csi_port *ports; | ||
86 | struct media_pad *pads; | ||
87 | |||
88 | unsigned int clk_freq; | ||
89 | int num_ports; | ||
90 | int num_channels; | ||
91 | struct list_head csi_chans; | ||
92 | struct tegra_csi_channel *tpg_start; | ||
93 | struct tegra_csi_fops *fops; | ||
94 | const struct tpg_frmfmt *tpg_frmfmt_table; | ||
95 | unsigned int tpg_frmfmt_table_size; | ||
96 | atomic_t power_ref; | ||
97 | |||
98 | struct dentry *debugdir; | ||
99 | atomic_t tpg_enabled; | ||
100 | }; | ||
101 | |||
102 | /* | ||
103 | * subdev: channel subdev | ||
104 | * numports: Number of CSI ports in use for this channel | ||
105 | * numlanes: Number of CIL lanes in use | ||
106 | */ | ||
107 | struct tegra_csi_channel { | ||
108 | struct list_head list; | ||
109 | struct v4l2_subdev subdev; | ||
110 | struct media_pad *pads; | ||
111 | struct media_pipeline pipe; | ||
112 | |||
113 | struct tegra_csi_device *csi; | ||
114 | struct tegra_csi_port *ports; | ||
115 | unsigned char port[TEGRA_CSI_BLOCKS]; | ||
116 | struct mutex format_lock; | ||
117 | unsigned int numports; | ||
118 | unsigned int numlanes; | ||
119 | unsigned int pg_mode; | ||
120 | struct camera_common_data *s_data; | ||
121 | unsigned int id; | ||
122 | }; | ||
123 | |||
124 | static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev) | ||
125 | { | ||
126 | return container_of(subdev, struct tegra_csi_channel, subdev); | ||
127 | } | ||
128 | |||
129 | static inline struct tegra_csi_device *to_csi(struct v4l2_subdev *subdev) | ||
130 | { | ||
131 | struct tegra_csi_channel *chan = to_csi_chan(subdev); | ||
132 | |||
133 | return chan->csi; | ||
134 | } | ||
135 | |||
136 | void set_csi_portinfo(struct tegra_csi_device *csi, | ||
137 | unsigned int port, unsigned int numlanes); | ||
138 | void tegra_csi_status(struct tegra_csi_channel *chan, | ||
139 | enum tegra_csi_port_num port_num); | ||
140 | int tegra_csi_error(struct tegra_csi_channel *chan, | ||
141 | enum tegra_csi_port_num port_num); | ||
142 | int tegra_csi_start_streaming(struct tegra_csi_channel *chan, | ||
143 | enum tegra_csi_port_num port_num); | ||
144 | void tegra_csi_stop_streaming(struct tegra_csi_channel *chan, | ||
145 | enum tegra_csi_port_num port_num); | ||
146 | void tegra_csi_error_recover(struct tegra_csi_channel *chan, | ||
147 | enum tegra_csi_port_num port_num); | ||
148 | int tegra_csi_power(struct tegra_csi_device *csi, int enable); | ||
149 | #define tegra_csi_power_on(csi) tegra_csi_power(csi, 1) | ||
150 | #define tegra_csi_power_off(csi) tegra_csi_power(csi, 0) | ||
151 | int tegra_csi_init(struct tegra_csi_device *csi, | ||
152 | struct platform_device *pdev); | ||
153 | int tegra_csi_media_controller_init(struct tegra_csi_device *csi, | ||
154 | struct platform_device *pdev); | ||
155 | int tegra_csi_media_controller_remove(struct tegra_csi_device *csi); | ||
156 | int tpg_csi_media_controller_init(struct tegra_csi_device *csi, int pg_mode); | ||
157 | void tpg_csi_media_controller_cleanup(struct tegra_csi_device *csi); | ||
158 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi2_fops.c b/drivers/media/platform/tegra/camera/csi/csi2_fops.c new file mode 100644 index 000000000..d633a6205 --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi2_fops.c | |||
@@ -0,0 +1,455 @@ | |||
1 | /* | ||
2 | * Tegra CSI2 device common APIs | ||
3 | * | ||
4 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/device.h> | ||
14 | #include <linux/clk/tegra.h> | ||
15 | #include "camera/csi/csi.h" | ||
16 | #include "camera/vi/mc_common.h" | ||
17 | #include "mipical/mipi_cal.h" | ||
18 | #include "nvhost_acm.h" | ||
19 | #include <linux/clk/tegra.h> | ||
20 | |||
21 | static void csi_write(struct tegra_csi_channel *chan, unsigned int addr, | ||
22 | u32 val, u8 port) | ||
23 | { | ||
24 | struct tegra_csi_device *csi = chan->csi; | ||
25 | |||
26 | writel(val, (csi->iomem[port] + addr)); | ||
27 | } | ||
28 | |||
29 | static u32 csi_read(struct tegra_csi_channel *chan, unsigned int addr, | ||
30 | u8 port) | ||
31 | { | ||
32 | struct tegra_csi_device *csi = chan->csi; | ||
33 | |||
34 | return readl((csi->iomem[port] + addr)); | ||
35 | } | ||
36 | |||
37 | /* Pixel parser registers accessors */ | ||
38 | static void pp_write(struct tegra_csi_port *port, u32 addr, u32 val) | ||
39 | { | ||
40 | writel(val, port->pixel_parser + addr); | ||
41 | } | ||
42 | |||
43 | static u32 pp_read(struct tegra_csi_port *port, u32 addr) | ||
44 | { | ||
45 | return readl(port->pixel_parser + addr); | ||
46 | } | ||
47 | |||
48 | /* CSI CIL registers accessors */ | ||
49 | static void cil_write(struct tegra_csi_port *port, u32 addr, u32 val) | ||
50 | { | ||
51 | writel(val, port->cil + addr); | ||
52 | } | ||
53 | |||
54 | static u32 cil_read(struct tegra_csi_port *port, u32 addr) | ||
55 | { | ||
56 | return readl(port->cil + addr); | ||
57 | } | ||
58 | |||
59 | /* Test pattern generator registers accessor */ | ||
60 | static void tpg_write(struct tegra_csi_port *port, | ||
61 | unsigned int addr, u32 val) | ||
62 | { | ||
63 | writel(val, port->tpg + addr); | ||
64 | } | ||
65 | |||
66 | int tegra_csi_error(struct tegra_csi_channel *chan, | ||
67 | enum tegra_csi_port_num port_num) | ||
68 | { | ||
69 | struct tegra_csi_port *port; | ||
70 | u32 val; | ||
71 | int err = 0; | ||
72 | |||
73 | port = &chan->ports[port_num]; | ||
74 | /* | ||
75 | * only uncorrectable header error and multi-bit | ||
76 | * transmission errors are checked as they cannot be | ||
77 | * corrected automatically | ||
78 | */ | ||
79 | val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); | ||
80 | err |= val & 0x4000; | ||
81 | pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, val); | ||
82 | |||
83 | val = cil_read(port, TEGRA_CSI_CIL_STATUS); | ||
84 | err |= val & 0x02; | ||
85 | cil_write(port, TEGRA_CSI_CIL_STATUS, val); | ||
86 | |||
87 | val = cil_read(port, TEGRA_CSI_CILX_STATUS); | ||
88 | err |= val & 0x00020020; | ||
89 | cil_write(port, TEGRA_CSI_CILX_STATUS, val); | ||
90 | |||
91 | return err; | ||
92 | } | ||
93 | |||
94 | void tegra_csi_status(struct tegra_csi_channel *chan, | ||
95 | enum tegra_csi_port_num port_num) | ||
96 | { | ||
97 | int i; | ||
98 | u32 val; | ||
99 | struct tegra_csi_port *port; | ||
100 | |||
101 | for (i = 0; i < chan->numports; i++) { | ||
102 | port = &chan->ports[i]; | ||
103 | val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); | ||
104 | |||
105 | dev_dbg(chan->csi->dev, | ||
106 | "TEGRA_CSI_PIXEL_PARSER_STATUS 0x%08x\n", | ||
107 | val); | ||
108 | |||
109 | val = cil_read(port, TEGRA_CSI_CIL_STATUS); | ||
110 | dev_dbg(chan->csi->dev, | ||
111 | "TEGRA_CSI_CIL_STATUS 0x%08x\n", val); | ||
112 | |||
113 | val = cil_read(port, TEGRA_CSI_CILX_STATUS); | ||
114 | dev_dbg(chan->csi->dev, | ||
115 | "TEGRA_CSI_CILX_STATUS 0x%08x\n", val); | ||
116 | } | ||
117 | } | ||
118 | EXPORT_SYMBOL(tegra_csi_status); | ||
119 | |||
120 | void tegra_csi_error_recover(struct tegra_csi_channel *chan, | ||
121 | enum tegra_csi_port_num port_num) | ||
122 | { | ||
123 | struct tegra_csi_port *port; | ||
124 | struct tegra_csi_device *csi; | ||
125 | int i; | ||
126 | |||
127 | csi = chan->csi; | ||
128 | |||
129 | for (i = 0; i < chan->numports; i++) { | ||
130 | port = &chan->ports[i]; | ||
131 | |||
132 | if (port->lanes == 4) { | ||
133 | int port_val = ((port_num >> 1) << 1); | ||
134 | struct tegra_csi_port *port_a = | ||
135 | &chan->ports[port_val]; | ||
136 | struct tegra_csi_port *port_b = | ||
137 | &chan->ports[port_val+1]; | ||
138 | |||
139 | tpg_write(port_a, | ||
140 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); | ||
141 | tpg_write(port_b, | ||
142 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); | ||
143 | cil_write(port_a, | ||
144 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); | ||
145 | cil_write(port_b, | ||
146 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); | ||
147 | csi_write(chan, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, | ||
148 | port_num >> 1); | ||
149 | /* sleep for clock cycles to drain the Rx FIFO */ | ||
150 | usleep_range(10, 20); | ||
151 | cil_write(port_a, | ||
152 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); | ||
153 | cil_write(port_b, | ||
154 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); | ||
155 | csi_write(chan, | ||
156 | TEGRA_CSI_CSI_SW_STATUS_RESET, | ||
157 | 0x0, port_num >> 1); | ||
158 | tpg_write(port_a, | ||
159 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); | ||
160 | tpg_write(port_b, | ||
161 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); | ||
162 | } else { | ||
163 | tpg_write(port, | ||
164 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); | ||
165 | cil_write(port, | ||
166 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); | ||
167 | csi_write(chan, | ||
168 | TEGRA_CSI_CSI_SW_STATUS_RESET, | ||
169 | 0x1, port_num >> 1); | ||
170 | /* sleep for clock cycles to drain the Rx FIFO */ | ||
171 | usleep_range(10, 20); | ||
172 | cil_write(port, | ||
173 | TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); | ||
174 | csi_write(chan, | ||
175 | TEGRA_CSI_CSI_SW_STATUS_RESET, | ||
176 | 0x0, port_num >> 1); | ||
177 | tpg_write(port, | ||
178 | TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | |||
184 | static int tpg_clk_enable(struct tegra_csi_device *csi) | ||
185 | { | ||
186 | int err = 0; | ||
187 | |||
188 | if (atomic_inc_return(&csi->tpg_enabled) != 1) | ||
189 | return 0; | ||
190 | |||
191 | clk_set_rate(csi->plld, TEGRA_CLOCK_TPG); | ||
192 | err = clk_prepare_enable(csi->plld); | ||
193 | if (err) { | ||
194 | dev_err(csi->dev, "pll_d enable failed"); | ||
195 | return err; | ||
196 | } | ||
197 | |||
198 | err = clk_prepare_enable(csi->plld_dsi); | ||
199 | if (err) { | ||
200 | dev_err(csi->dev, "pll_d enable failed"); | ||
201 | goto plld_dsi_err; | ||
202 | } | ||
203 | tegra210_csi_source_from_plld(); | ||
204 | return err; | ||
205 | plld_dsi_err: | ||
206 | clk_disable_unprepare(csi->plld); | ||
207 | return err; | ||
208 | } | ||
209 | |||
210 | static int tpg_clk_disable(struct tegra_csi_device *csi) | ||
211 | { | ||
212 | int err = 0; | ||
213 | |||
214 | if (!atomic_dec_and_test(&csi->tpg_enabled)) | ||
215 | return 0; | ||
216 | tegra210_csi_source_from_brick(); | ||
217 | clk_disable_unprepare(csi->plld_dsi); | ||
218 | clk_disable_unprepare(csi->plld); | ||
219 | |||
220 | return err; | ||
221 | } | ||
222 | |||
223 | static int csi2_tpg_start_streaming(struct tegra_csi_channel *chan, | ||
224 | enum tegra_csi_port_num port_num) | ||
225 | { | ||
226 | struct tegra_csi_port *port = &chan->ports[port_num]; | ||
227 | |||
228 | tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, | ||
229 | ((chan->pg_mode - 1) << PG_MODE_OFFSET) | | ||
230 | PG_ENABLE); | ||
231 | tpg_write(port, TEGRA_CSI_PG_BLANK, | ||
232 | port->v_blank << PG_VBLANK_OFFSET | | ||
233 | port->h_blank); | ||
234 | tpg_write(port, TEGRA_CSI_PG_PHASE, 0x0); | ||
235 | tpg_write(port, TEGRA_CSI_PG_RED_FREQ, | ||
236 | (0x10 << PG_RED_VERT_INIT_FREQ_OFFSET) | | ||
237 | (0x10 << PG_RED_HOR_INIT_FREQ_OFFSET)); | ||
238 | tpg_write(port, TEGRA_CSI_PG_RED_FREQ_RATE, 0x0); | ||
239 | tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ, | ||
240 | (0x10 << PG_GREEN_VERT_INIT_FREQ_OFFSET) | | ||
241 | (0x10 << PG_GREEN_HOR_INIT_FREQ_OFFSET)); | ||
242 | tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ_RATE, 0x0); | ||
243 | tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ, | ||
244 | (0x10 << PG_BLUE_VERT_INIT_FREQ_OFFSET) | | ||
245 | (0x10 << PG_BLUE_HOR_INIT_FREQ_OFFSET)); | ||
246 | tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ_RATE, 0x0); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | int csi2_start_streaming(struct tegra_csi_channel *chan, | ||
251 | enum tegra_csi_port_num port_num) | ||
252 | { | ||
253 | struct tegra_csi_port *port = &chan->ports[port_num]; | ||
254 | int csi_port, csi_lanes; | ||
255 | |||
256 | csi_port = chan->ports[port_num].num; | ||
257 | csi_lanes = chan->ports[port_num].lanes; | ||
258 | |||
259 | csi_write(chan, TEGRA_CSI_CLKEN_OVERRIDE, 0, csi_port >> 1); | ||
260 | |||
261 | /* Clean up status */ | ||
262 | pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, 0xFFFFFFFF); | ||
263 | cil_write(port, TEGRA_CSI_CIL_STATUS, 0xFFFFFFFF); | ||
264 | cil_write(port, TEGRA_CSI_CILX_STATUS, 0xFFFFFFFF); | ||
265 | |||
266 | cil_write(port, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0); | ||
267 | |||
268 | /* CIL PHY registers setup */ | ||
269 | cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); | ||
270 | cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL, | ||
271 | BYPASS_LP_SEQ | 0xA); | ||
272 | |||
273 | /* | ||
274 | * The CSI unit provides for connection of up to six cameras in | ||
275 | * the system and is organized as three identical instances of | ||
276 | * two MIPI support blocks, each with a separate 4-lane | ||
277 | * interface that can be configured as a single camera with 4 | ||
278 | * lanes or as a dual camera with 2 lanes available for each | ||
279 | * camera. | ||
280 | */ | ||
281 | if (csi_lanes == 4) { | ||
282 | unsigned int cilb_offset; | ||
283 | |||
284 | cilb_offset = TEGRA_CSI_CIL_OFFSET + TEGRA_CSI_PORT_OFFSET; | ||
285 | |||
286 | cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, | ||
287 | BRICK_CLOCK_A_4X); | ||
288 | csi_write(chan, cilb_offset + TEGRA_CSI_CIL_PAD_CONFIG0, 0x0, | ||
289 | csi_port >> 1); | ||
290 | csi_write(chan, cilb_offset + TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0, | ||
291 | csi_port >> 1); | ||
292 | cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL, | ||
293 | BYPASS_LP_SEQ | 0xA); | ||
294 | csi_write(chan, cilb_offset + TEGRA_CSI_CIL_PHY_CONTROL, | ||
295 | BYPASS_LP_SEQ | 0xA, csi_port >> 1); | ||
296 | csi_write(chan, TEGRA_CSI_PHY_CIL_COMMAND, | ||
297 | CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE, | ||
298 | csi_port >> 1); | ||
299 | } else { | ||
300 | u32 val = csi_read(chan, TEGRA_CSI_PHY_CIL_COMMAND, | ||
301 | csi_port >> 1); | ||
302 | |||
303 | csi_write(chan, | ||
304 | TEGRA_CSI_CIL_OFFSET + TEGRA_CSI_CIL_PAD_CONFIG0, 0x0, | ||
305 | csi_port >> 1); | ||
306 | val |= ((csi_port & 0x1) == PORT_A) ? CSI_A_PHY_CIL_ENABLE : | ||
307 | CSI_B_PHY_CIL_ENABLE; | ||
308 | csi_write(chan, TEGRA_CSI_PHY_CIL_COMMAND, val, | ||
309 | csi_port >> 1); | ||
310 | } | ||
311 | /* CSI pixel parser registers setup */ | ||
312 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, | ||
313 | (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | | ||
314 | CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_RST); | ||
315 | pp_write(port, TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0); | ||
316 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL0, | ||
317 | CSI_PP_PACKET_HEADER_SENT | | ||
318 | CSI_PP_DATA_IDENTIFIER_ENABLE | | ||
319 | CSI_PP_WORD_COUNT_SELECT_HEADER | | ||
320 | CSI_PP_CRC_CHECK_ENABLE | CSI_PP_WC_CHECK | | ||
321 | CSI_PP_OUTPUT_FORMAT_STORE | CSI_PPA_PAD_LINE_NOPAD | | ||
322 | CSI_PP_HEADER_EC_DISABLE | CSI_PPA_PAD_FRAME_NOPAD | | ||
323 | (csi_port & 1)); | ||
324 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL1, | ||
325 | (0x1 << CSI_PP_TOP_FIELD_FRAME_OFFSET) | | ||
326 | (0x1 << CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET)); | ||
327 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_GAP, | ||
328 | 0x14 << PP_FRAME_MIN_GAP_OFFSET); | ||
329 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME, 0x0); | ||
330 | pp_write(port, TEGRA_CSI_INPUT_STREAM_CONTROL, | ||
331 | (0x3f << CSI_SKIP_PACKET_THRESHOLD_OFFSET) | | ||
332 | (csi_lanes - 1)); | ||
333 | |||
334 | if (chan->pg_mode) { | ||
335 | tpg_clk_enable(chan->csi); | ||
336 | csi2_tpg_start_streaming(chan, port_num); | ||
337 | } | ||
338 | |||
339 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, | ||
340 | (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | | ||
341 | CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_ENABLE); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | void csi2_stop_streaming(struct tegra_csi_channel *chan, | ||
346 | enum tegra_csi_port_num port_num) | ||
347 | { | ||
348 | struct tegra_csi_port *port = &chan->ports[port_num]; | ||
349 | |||
350 | |||
351 | if (chan->pg_mode) { | ||
352 | tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); | ||
353 | tpg_clk_disable(chan->csi); | ||
354 | } | ||
355 | if (!port) { | ||
356 | pr_err("%s:no port\n", __func__); | ||
357 | return; | ||
358 | } | ||
359 | pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, | ||
360 | (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | | ||
361 | CSI_PP_DISABLE); | ||
362 | } | ||
363 | |||
364 | int csi2_hw_init(struct tegra_csi_device *csi) | ||
365 | { | ||
366 | int i, csi_port; | ||
367 | struct tegra_csi_channel *it; | ||
368 | struct tegra_csi_port *port; | ||
369 | |||
370 | csi->iomem[0] = (csi->iomem_base + TEGRA_CSI_PIXEL_PARSER_0_BASE); | ||
371 | csi->iomem[1] = (csi->iomem_base + TEGRA_CSI_PIXEL_PARSER_2_BASE); | ||
372 | csi->iomem[2] = (csi->iomem_base + TEGRA_CSI_PIXEL_PARSER_4_BASE); | ||
373 | list_for_each_entry(it, &csi->csi_chans, list) { | ||
374 | for (i = 0; i < it->numports; i++) { | ||
375 | port = &it->ports[i]; | ||
376 | csi_port = it->ports[i].num; | ||
377 | port->pixel_parser = csi->iomem[csi_port >> 1] + | ||
378 | (csi_port % 2) * TEGRA_CSI_PORT_OFFSET; | ||
379 | port->cil = port->pixel_parser + TEGRA_CSI_CIL_OFFSET; | ||
380 | port->tpg = port->pixel_parser + TEGRA_CSI_TPG_OFFSET; | ||
381 | } | ||
382 | } | ||
383 | csi->plld = devm_clk_get(csi->dev, "pll_d"); | ||
384 | if (IS_ERR(csi->plld)) { | ||
385 | dev_err(csi->dev, "Fail to get pll_d\n"); | ||
386 | return PTR_ERR(csi->plld); | ||
387 | } | ||
388 | csi->plld_dsi = devm_clk_get(csi->dev, "pll_d_dsi_out"); | ||
389 | if (IS_ERR(csi->plld_dsi)) { | ||
390 | dev_err(csi->dev, "Fail to get pll_d_dsi_out\n"); | ||
391 | return PTR_ERR(csi->plld_dsi); | ||
392 | } | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | int csi2_mipi_cal(struct tegra_csi_channel *chan) | ||
397 | { | ||
398 | unsigned int lanes, num_ports, val, csi_port; | ||
399 | struct tegra_csi_port *port; | ||
400 | struct tegra_csi_device *csi = chan->csi; | ||
401 | |||
402 | lanes = 0; | ||
403 | num_ports = 0; | ||
404 | |||
405 | nvhost_module_enable_clk(csi->dev); | ||
406 | while (num_ports < chan->numports) { | ||
407 | port = &chan->ports[num_ports]; | ||
408 | csi_port = port->num; | ||
409 | dev_dbg(csi->dev, "Calibrate csi port %d\n", port->num); | ||
410 | |||
411 | if (chan->numlanes == 2) { | ||
412 | lanes |= CSIA << csi_port; | ||
413 | val = csi_read(chan, TEGRA_CSI_PHY_CIL_COMMAND, | ||
414 | csi_port >> 1); | ||
415 | csi_write(chan, | ||
416 | TEGRA_CSI_CIL_OFFSET + | ||
417 | TEGRA_CSI_CIL_PAD_CONFIG0, 0x0, csi_port >> 1); | ||
418 | val |= ((csi_port & 0x1) == PORT_A) ? | ||
419 | CSI_A_PHY_CIL_ENABLE : CSI_B_PHY_CIL_ENABLE; | ||
420 | csi_write(chan, TEGRA_CSI_PHY_CIL_COMMAND, val, | ||
421 | csi_port >> 1); | ||
422 | } else { | ||
423 | lanes |= (CSIA | CSIB) << port->num; | ||
424 | csi_write(chan, TEGRA_CSI_PHY_CIL_COMMAND, | ||
425 | CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE, | ||
426 | csi_port >> 1); | ||
427 | } | ||
428 | num_ports++; | ||
429 | } | ||
430 | if (!lanes) { | ||
431 | dev_err(csi->dev, | ||
432 | "Selected no CSI lane, cannot do calibration"); | ||
433 | return -EINVAL; | ||
434 | } | ||
435 | nvhost_module_disable_clk(csi->dev); | ||
436 | return tegra_mipi_calibration(lanes); | ||
437 | } | ||
438 | |||
439 | int csi2_power_on(struct tegra_csi_device *csi) | ||
440 | { | ||
441 | return 0; | ||
442 | } | ||
443 | int csi2_power_off(struct tegra_csi_device *csi) | ||
444 | { | ||
445 | return 0; | ||
446 | } | ||
447 | const struct tegra_csi_fops csi2_fops = { | ||
448 | .csi_power_on = csi2_power_on, | ||
449 | .csi_power_off = csi2_power_off, | ||
450 | .csi_start_streaming = csi2_start_streaming, | ||
451 | .csi_stop_streaming = csi2_stop_streaming, | ||
452 | .mipical = csi2_mipi_cal, | ||
453 | .hw_init = csi2_hw_init, | ||
454 | }; | ||
455 | EXPORT_SYMBOL(csi2_fops); | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi2_fops.h b/drivers/media/platform/tegra/camera/csi/csi2_fops.h new file mode 100644 index 000000000..7b801e777 --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi2_fops.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * Tegra CSI2 device common APIs | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * Author: Bryan Wu <pengw@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef __CSI2_H__ | ||
16 | #define __CSI2_H__ | ||
17 | |||
18 | #include "csi.h" | ||
19 | |||
20 | int csi2_start_streaming(struct tegra_csi_channel *chan, | ||
21 | enum tegra_csi_port_num port_num); | ||
22 | void csi2_stop_streaming(struct tegra_csi_channel *chan, | ||
23 | enum tegra_csi_port_num port_num); | ||
24 | |||
25 | extern struct tegra_csi_fops csi2_fops; | ||
26 | |||
27 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi4_fops.c b/drivers/media/platform/tegra/camera/csi/csi4_fops.c new file mode 100644 index 000000000..6fd28156e --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi4_fops.c | |||
@@ -0,0 +1,506 @@ | |||
1 | /* | ||
2 | * Tegra CSI4 device common APIs | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Frank Chen <frankc@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/clk/tegra.h> | ||
13 | #include "nvhost_acm.h" | ||
14 | #include "camera/csi/csi.h" | ||
15 | #include "camera/csi/csi4_registers.h" | ||
16 | #include "camera/vi/core.h" | ||
17 | #include "mipical/mipi_cal.h" | ||
18 | #include "nvcsi/nvcsi.h" | ||
19 | |||
20 | static void csi4_stream_write(struct tegra_csi_channel *chan, | ||
21 | unsigned int index, unsigned int addr, u32 val) | ||
22 | { | ||
23 | struct tegra_csi_device *csi = chan->csi; | ||
24 | u32 cilb_offset = (index & 0x1) ? CSI4_STREAM_OFFSET : 0x0; | ||
25 | |||
26 | writel(val, csi->iomem[index >> 1] + cilb_offset + addr); | ||
27 | } | ||
28 | |||
29 | static u32 csi4_stream_read(struct tegra_csi_channel *chan, | ||
30 | unsigned int index, unsigned int addr) | ||
31 | { | ||
32 | struct tegra_csi_device *csi = chan->csi; | ||
33 | u32 cilb_offset = (index & 0x1) ? CSI4_STREAM_OFFSET : 0x0; | ||
34 | |||
35 | return readl(csi->iomem[index >> 1] + cilb_offset + addr); | ||
36 | } | ||
37 | |||
38 | static void csi4_phy_write(struct tegra_csi_channel *chan, | ||
39 | unsigned int index, unsigned int addr, u32 val) | ||
40 | { | ||
41 | struct tegra_csi_device *csi = chan->csi; | ||
42 | |||
43 | writel(val, csi->iomem_base + | ||
44 | CSI4_BASE_ADDRESS + (CSI4_PHY_OFFSET * index) + addr); | ||
45 | } | ||
46 | |||
47 | static u32 csi4_phy_read(struct tegra_csi_channel *chan, | ||
48 | unsigned int index, unsigned int addr) | ||
49 | { | ||
50 | struct tegra_csi_device *csi = chan->csi; | ||
51 | |||
52 | return readl(csi->iomem_base + | ||
53 | CSI4_BASE_ADDRESS + (CSI4_PHY_OFFSET * index) + addr); | ||
54 | } | ||
55 | |||
56 | static void csi4_stream_init(struct tegra_csi_channel *chan, int port_num) | ||
57 | { | ||
58 | struct tegra_csi_device *csi = chan->csi; | ||
59 | |||
60 | dev_dbg(csi->dev, "%s\n", __func__); | ||
61 | |||
62 | csi4_stream_write(chan, port_num, CIL_INTR_STATUS, 0xffffffff); | ||
63 | csi4_stream_write(chan, port_num, CIL_ERR_INTR_STATUS, 0xffffffff); | ||
64 | csi4_stream_write(chan, port_num, CIL_INTR_MASK, 0xffffffff); | ||
65 | csi4_stream_write(chan, port_num, CIL_ERR_INTR_MASK, 0xffffffff); | ||
66 | csi4_stream_write(chan, port_num, INTR_STATUS, 0x3ffff); | ||
67 | csi4_stream_write(chan, port_num, ERR_INTR_STATUS, 0x7ffff); | ||
68 | csi4_stream_write(chan, port_num, ERROR_STATUS2VI_MASK, 0x0); | ||
69 | csi4_stream_write(chan, port_num, INTR_MASK, 0x0); | ||
70 | csi4_stream_write(chan, port_num, ERR_INTR_MASK, 0x0); | ||
71 | } | ||
72 | |||
73 | static void csi4_stream_config(struct tegra_csi_channel *chan, int port_num) | ||
74 | { | ||
75 | struct tegra_csi_device *csi = chan->csi; | ||
76 | int val; | ||
77 | |||
78 | dev_dbg(csi->dev, "%s\n", __func__); | ||
79 | |||
80 | csi4_stream_write(chan, port_num, PPFSM_TIMEOUT_CTRL, 0); | ||
81 | csi4_stream_write(chan, port_num, PH_CHK_CTRL, | ||
82 | CFG_PH_CRC_CHK_EN | CFG_PH_ECC_CHK_EN); | ||
83 | csi4_stream_write(chan, port_num, VC0_DPCM_CTRL, 0); | ||
84 | csi4_stream_write(chan, port_num, VC0_DT_OVERRIDE, 0); | ||
85 | |||
86 | val = csi4_stream_read(chan, port_num, VC0_DPCM_CTRL); | ||
87 | dev_dbg(csi->dev, "%s (%d) read VC0_DPCM_CTRL = %08x\n", | ||
88 | __func__, port_num, val); | ||
89 | } | ||
90 | |||
91 | |||
92 | static void csi4_phy_config( | ||
93 | struct tegra_csi_channel *chan, int csi_port, | ||
94 | int csi_lanes, bool enable) | ||
95 | { | ||
96 | struct tegra_csi_device *csi = chan->csi; | ||
97 | int phy_num = (csi_port & 0x6) >> 1; | ||
98 | bool cil_a = (csi_port & 0x1) ? false : true; | ||
99 | int cil_config; | ||
100 | |||
101 | dev_dbg(csi->dev, "%s\n", __func__); | ||
102 | |||
103 | /* set to DPHY */ | ||
104 | csi4_phy_write(chan, phy_num, NVCSI_CIL_PHY_CTRL, DPHY); | ||
105 | |||
106 | /* read current NVCSI_CIL_CONFIG setting */ | ||
107 | cil_config = csi4_phy_read(chan, phy_num, NVCSI_CIL_CONFIG); | ||
108 | dev_dbg(csi->dev, "NVCSI_CIL_CONFIG = %08x\n", cil_config); | ||
109 | |||
110 | if (cil_a) { | ||
111 | /* soft reset for data lane */ | ||
112 | csi4_phy_write(chan, phy_num, NVCSI_CIL_A_SW_RESET, | ||
113 | SW_RESET1_EN | SW_RESET0_EN); | ||
114 | /* reset CSI lane number */ | ||
115 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
116 | cil_config & ~DATA_LANE_A); | ||
117 | /* disable clock lane*/ | ||
118 | csi4_phy_write(chan, phy_num, | ||
119 | NVCSI_CIL_A_PAD_CONFIG, | ||
120 | PD_CLK | PD_IO0 | PD_IO1 | SPARE_IO0 | SPARE_IO1); | ||
121 | |||
122 | /* setting up CIL B for 4 lane */ | ||
123 | if (csi_lanes > 2) { | ||
124 | /* soft reset for data lane */ | ||
125 | csi4_phy_write(chan, phy_num, NVCSI_CIL_B_SW_RESET, | ||
126 | SW_RESET1_EN | SW_RESET0_EN); | ||
127 | /* reset CSI lane number */ | ||
128 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
129 | cil_config & ~DATA_LANE_B); | ||
130 | /* disable clock lane*/ | ||
131 | csi4_phy_write(chan, phy_num, | ||
132 | NVCSI_CIL_B_PAD_CONFIG, | ||
133 | PD_CLK | PD_IO0 | PD_IO1 | | ||
134 | SPARE_IO0 | SPARE_IO1); | ||
135 | } | ||
136 | |||
137 | /* power down de-serializer is CIL B is not in use*/ | ||
138 | if ((cil_config & DATA_LANE_B) == 0) | ||
139 | csi4_phy_write(chan, phy_num, | ||
140 | NVCSI_CIL_PAD_CONFIG, PDVCLAMP); | ||
141 | } else { | ||
142 | /* soft reset for data lane */ | ||
143 | csi4_phy_write(chan, phy_num, NVCSI_CIL_B_SW_RESET, | ||
144 | SW_RESET1_EN | SW_RESET0_EN); | ||
145 | /* reset CSI lane number */ | ||
146 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
147 | cil_config & ~DATA_LANE_B); | ||
148 | /* disable clock lane*/ | ||
149 | csi4_phy_write(chan, phy_num, | ||
150 | NVCSI_CIL_B_PAD_CONFIG, | ||
151 | PD_CLK | PD_IO0 | PD_IO1 | SPARE_IO0 | SPARE_IO1); | ||
152 | |||
153 | /* power down de-serializer if CIL A is not in use*/ | ||
154 | if ((cil_config & DATA_LANE_A) == 0) | ||
155 | csi4_phy_write(chan, phy_num, | ||
156 | NVCSI_CIL_PAD_CONFIG, PDVCLAMP); | ||
157 | } | ||
158 | |||
159 | if (!enable) | ||
160 | return; | ||
161 | |||
162 | /* power on de-serializer */ | ||
163 | csi4_phy_write(chan, phy_num, NVCSI_CIL_PAD_CONFIG, 0); | ||
164 | |||
165 | if (cil_a) { | ||
166 | /* set CSI lane number */ | ||
167 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
168 | (cil_config & ~DATA_LANE_A) | | ||
169 | (csi_lanes << DATA_LANE_A_OFFSET)); | ||
170 | /* enable clock lane*/ | ||
171 | csi4_phy_write(chan, phy_num, | ||
172 | NVCSI_CIL_A_PAD_CONFIG, | ||
173 | E_INPUT_LP_CLK | E_INPUT_LP_IO0 | E_INPUT_LP_IO1); | ||
174 | /* setup settle time */ | ||
175 | csi4_phy_write(chan, phy_num, | ||
176 | NVCSI_CIL_A_CONTROL, | ||
177 | DEFAULT_DESKEW_COMPARE | DEFAULT_DESKEW_SETTLE | | ||
178 | DEFAULT_CLK_SETTLE | | ||
179 | T18X_BYPASS_LP_SEQ | DEFAULT_THS_SETTLE); | ||
180 | /* release soft reset */ | ||
181 | csi4_phy_write(chan, phy_num, NVCSI_CIL_A_SW_RESET, 0x0); | ||
182 | |||
183 | /* setting up CIL B for 4 lane */ | ||
184 | if (csi_lanes > 2) { | ||
185 | /* set CSI lane number */ | ||
186 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
187 | csi_lanes << DATA_LANE_A_OFFSET); | ||
188 | /* enable clock lane*/ | ||
189 | csi4_phy_write(chan, phy_num, | ||
190 | NVCSI_CIL_B_PAD_CONFIG, | ||
191 | E_INPUT_LP_IO0 | E_INPUT_LP_IO1 | PD_CLK); | ||
192 | /* setup settle time */ | ||
193 | csi4_phy_write(chan, phy_num, | ||
194 | NVCSI_CIL_B_CONTROL, | ||
195 | DEFAULT_DESKEW_COMPARE | DEFAULT_DESKEW_SETTLE | ||
196 | | DEFAULT_CLK_SETTLE | | ||
197 | T18X_BYPASS_LP_SEQ | DEFAULT_THS_SETTLE); | ||
198 | /* release soft reset */ | ||
199 | csi4_phy_write(chan, phy_num, | ||
200 | NVCSI_CIL_B_SW_RESET, 0x0); | ||
201 | } | ||
202 | } else { | ||
203 | /* set CSI lane number */ | ||
204 | csi4_phy_write(chan, phy_num, NVCSI_CIL_CONFIG, | ||
205 | (cil_config & ~DATA_LANE_B) | | ||
206 | (csi_lanes << DATA_LANE_B_OFFSET)); | ||
207 | /* enable clock lane*/ | ||
208 | csi4_phy_write(chan, phy_num, | ||
209 | NVCSI_CIL_B_PAD_CONFIG, | ||
210 | E_INPUT_LP_CLK | E_INPUT_LP_IO0 | E_INPUT_LP_IO1); | ||
211 | /* setup settle time */ | ||
212 | csi4_phy_write(chan, phy_num, | ||
213 | NVCSI_CIL_B_CONTROL, | ||
214 | DEFAULT_DESKEW_COMPARE | DEFAULT_DESKEW_SETTLE | | ||
215 | DEFAULT_CLK_SETTLE | | ||
216 | T18X_BYPASS_LP_SEQ | DEFAULT_THS_SETTLE); | ||
217 | /* release soft reset */ | ||
218 | csi4_phy_write(chan, phy_num, NVCSI_CIL_B_SW_RESET, 0x0); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | static void csi4_stream_check_status( | ||
223 | struct tegra_csi_channel *chan, int port_num) | ||
224 | { | ||
225 | struct tegra_csi_device *csi = chan->csi; | ||
226 | int status = 0; | ||
227 | |||
228 | dev_dbg(csi->dev, "%s\n", __func__); | ||
229 | if (!chan->pg_mode) { | ||
230 | status = csi4_stream_read(chan, port_num, ERROR_STATUS2VI_VC0); | ||
231 | if (status) | ||
232 | dev_err(csi->dev, | ||
233 | "%s (%d) ERROR_STATUS2VI_VC0 = 0x%08x\n", | ||
234 | __func__, port_num, status); | ||
235 | |||
236 | status = csi4_stream_read(chan, port_num, ERROR_STATUS2VI_VC1); | ||
237 | if (status) | ||
238 | dev_err(csi->dev, | ||
239 | "%s (%d) ERROR_STATUS2VI_VC1 = 0x%08x\n", | ||
240 | __func__, port_num, status); | ||
241 | |||
242 | status = csi4_stream_read(chan, port_num, ERROR_STATUS2VI_VC2); | ||
243 | if (status) | ||
244 | dev_err(csi->dev, | ||
245 | "%s (%d) ERROR_STATUS2VI_VC2 = 0x%08x\n", | ||
246 | __func__, port_num, status); | ||
247 | |||
248 | status = csi4_stream_read(chan, port_num, ERROR_STATUS2VI_VC3); | ||
249 | if (status) | ||
250 | dev_err(csi->dev, | ||
251 | "%s (%d) ERROR_STATUS2VI_VC2 = 0x%08x\n", | ||
252 | __func__, port_num, status); | ||
253 | } | ||
254 | status = csi4_stream_read(chan, port_num, INTR_STATUS); | ||
255 | if (status) | ||
256 | dev_err(csi->dev, | ||
257 | "%s (%d) INTR_STATUS 0x%08x\n", | ||
258 | __func__, port_num, status); | ||
259 | |||
260 | status = csi4_stream_read(chan, port_num, ERR_INTR_STATUS); | ||
261 | if (status) | ||
262 | dev_err(csi->dev, | ||
263 | "%s (%d) ERR_INTR_STATUS 0x%08x\n", | ||
264 | __func__, port_num, status); | ||
265 | } | ||
266 | |||
267 | static void csi4_cil_check_status(struct tegra_csi_channel *chan, int port_num) | ||
268 | { | ||
269 | struct tegra_csi_device *csi = chan->csi; | ||
270 | int status = 0; | ||
271 | |||
272 | dev_dbg(csi->dev, "%s %d\n", __func__, __LINE__); | ||
273 | |||
274 | status = csi4_stream_read(chan, port_num, CIL_INTR_STATUS); | ||
275 | if (status) | ||
276 | dev_err(csi->dev, | ||
277 | "%s (%d) CIL_INTR_STATUS 0x%08x\n", | ||
278 | __func__, port_num, status); | ||
279 | |||
280 | status = csi4_stream_read(chan, port_num, CIL_ERR_INTR_STATUS); | ||
281 | if (status) | ||
282 | dev_err(csi->dev, | ||
283 | "%s (%d) CIL_ERR_INTR_STATUS 0x%08x\n", | ||
284 | __func__, port_num, status); | ||
285 | } | ||
286 | |||
287 | |||
288 | int csi4_power_on(struct tegra_csi_device *csi) | ||
289 | { | ||
290 | int err = 0; | ||
291 | |||
292 | err = nvhost_module_busy(csi->pdev); | ||
293 | if (err) | ||
294 | dev_err(csi->dev, "%s:cannot enable csi\n", __func__); | ||
295 | |||
296 | return err; | ||
297 | } | ||
298 | |||
299 | int csi4_power_off(struct tegra_csi_device *csi) | ||
300 | { | ||
301 | nvhost_module_idle(csi->pdev); | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static void csi4_tpg_stop_streaming(struct tegra_csi_channel *chan, | ||
307 | int ports_index) | ||
308 | { | ||
309 | unsigned int csi_port = chan->ports[ports_index].num; | ||
310 | struct tegra_csi_device *csi = chan->csi; | ||
311 | |||
312 | dev_dbg(csi->dev, "%s\n", __func__); | ||
313 | csi4_stream_check_status(chan, csi_port); | ||
314 | csi4_cil_check_status(chan, csi_port); | ||
315 | csi4_stream_write(chan, csi_port, PP_EN_CTRL, 0); | ||
316 | csi4_stream_write(chan, csi_port, TPG_EN_0, 0); | ||
317 | csi4_stream_write(chan, csi_port, PG_CTRL, PG_DISABLE); | ||
318 | } | ||
319 | static int csi4_tpg_start_streaming(struct tegra_csi_channel *chan, | ||
320 | enum tegra_csi_port_num port_num) | ||
321 | { | ||
322 | struct tegra_csi_port *port = &chan->ports[port_num]; | ||
323 | struct tegra_csi_device *csi = chan->csi; | ||
324 | unsigned int val, csi_port, csi_lanes; | ||
325 | |||
326 | if (!port->core_format) { | ||
327 | dev_err(csi->dev, "Fail to find tegra video fmt"); | ||
328 | return -EINVAL; | ||
329 | } | ||
330 | |||
331 | csi_port = port->num; | ||
332 | csi_lanes = port->lanes; | ||
333 | dev_dbg(csi->dev, "%s CSI port=%d, # lanes=%d\n", | ||
334 | __func__, csi_port, csi_lanes); | ||
335 | |||
336 | csi4_stream_write(chan, csi_port, PH_CHK_CTRL, 0); | ||
337 | csi4_stream_write(chan, csi_port, INTR_MASK, PH_ECC_MULTI_BIT_ERR | | ||
338 | PD_CRC_ERR_VC0 | PH_ECC_SINGLE_BIT_ERR_VC0); | ||
339 | csi4_stream_write(chan, csi_port, ERR_INTR_MASK, PH_ECC_MULTI_BIT_ERR | | ||
340 | PD_CRC_ERR_VC0 | PH_ECC_SINGLE_BIT_ERR_VC0); | ||
341 | csi4_stream_write(chan, csi_port, ERROR_STATUS2VI_MASK, | ||
342 | CFG_ERR_STATUS2VI_MASK_VC0 | | ||
343 | CFG_ERR_STATUS2VI_MASK_VC1 | | ||
344 | CFG_ERR_STATUS2VI_MASK_VC2 | | ||
345 | CFG_ERR_STATUS2VI_MASK_VC3); | ||
346 | /* calculate PG blank */ | ||
347 | csi4_stream_write(chan, csi_port, PG_BLANK, | ||
348 | ((port->v_blank & PG_VBLANK_MASK) << PG_VBLANK_OFFSET) | | ||
349 | ((port->h_blank & PG_HBLANK_MASK) << PG_HBLANK_OFFSET)); | ||
350 | csi4_stream_write(chan, csi_port, PG_PHASE, 0x0); | ||
351 | csi4_stream_write(chan, csi_port, PG_RED_FREQ, | ||
352 | (0x10 << PG_VERT_INIT_FREQ_OFFSET)| | ||
353 | (0x10 << PG_HOR_INIT_FREQ_OFFSET)); | ||
354 | csi4_stream_write(chan, csi_port, PG_RED_FREQ_RATE, 0x0); | ||
355 | csi4_stream_write(chan, csi_port, PG_GREEN_FREQ, | ||
356 | (0x10 << PG_VERT_INIT_FREQ_OFFSET)| | ||
357 | (0x10 << PG_HOR_INIT_FREQ_OFFSET)); | ||
358 | csi4_stream_write(chan, csi_port, PG_GREEN_FREQ_RATE, 0x0); | ||
359 | csi4_stream_write(chan, csi_port, PG_BLUE_FREQ, | ||
360 | (0x10 << PG_VERT_INIT_FREQ_OFFSET)| | ||
361 | (0x10 << PG_HOR_INIT_FREQ_OFFSET)); | ||
362 | csi4_stream_write(chan, csi_port, PG_BLUE_FREQ_RATE, 0x0); | ||
363 | /* calculate PG IMAGE SIZE and DT */ | ||
364 | mutex_lock(&chan->format_lock); | ||
365 | val = port->format.height << HEIGHT_OFFSET | | ||
366 | (port->format.width * | ||
367 | (port->core_format->vf_code == TEGRA_VF_RAW10 ? 10 : 24) / 8); | ||
368 | mutex_unlock(&chan->format_lock); | ||
369 | csi4_stream_write(chan, csi_port, PG_IMAGE_SIZE, val); | ||
370 | csi4_stream_write(chan, csi_port, PG_IMAGE_DT, | ||
371 | port->core_format->img_dt); | ||
372 | csi4_stream_write(chan, csi_port, PP_EN_CTRL, CFG_PP_EN); | ||
373 | csi4_stream_write(chan, csi_port, TPG_EN_0, cfg_tpg_en); | ||
374 | |||
375 | csi4_stream_write(chan, csi_port, PG_CTRL, | ||
376 | ((chan->pg_mode - 1) << PG_MODE_OFFSET) | PG_ENABLE); | ||
377 | return 0; | ||
378 | } | ||
379 | int csi4_hw_init(struct tegra_csi_device *csi) | ||
380 | { | ||
381 | csi->iomem[0] = csi->iomem_base + TEGRA_CSI_STREAM_0_BASE; | ||
382 | csi->iomem[1] = csi->iomem_base + TEGRA_CSI_STREAM_2_BASE; | ||
383 | csi->iomem[2] = csi->iomem_base + TEGRA_CSI_STREAM_4_BASE; | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | int csi4_start_streaming(struct tegra_csi_channel *chan, | ||
388 | enum tegra_csi_port_num port_num) | ||
389 | { | ||
390 | struct tegra_csi_device *csi = chan->csi; | ||
391 | int csi_port, csi_lanes, ret = 0; | ||
392 | |||
393 | csi_port = chan->ports[port_num].num; | ||
394 | csi_lanes = chan->ports[port_num].lanes; | ||
395 | dev_dbg(csi->dev, "%s ports index=%d, lanes=%d\n", | ||
396 | __func__, csi_port, csi_lanes); | ||
397 | |||
398 | if (chan->pg_mode) | ||
399 | ret = csi4_tpg_start_streaming(chan, port_num); | ||
400 | else { | ||
401 | csi4_stream_init(chan, csi_port); | ||
402 | csi4_stream_config(chan, csi_port); | ||
403 | /* enable DPHY */ | ||
404 | csi4_phy_config(chan, csi_port, csi_lanes, true); | ||
405 | csi4_stream_write(chan, csi_port, PP_EN_CTRL, CFG_PP_EN); | ||
406 | } | ||
407 | return ret; | ||
408 | } | ||
409 | |||
410 | void csi4_stop_streaming(struct tegra_csi_channel *chan, | ||
411 | enum tegra_csi_port_num port_num) | ||
412 | { | ||
413 | struct tegra_csi_device *csi = chan->csi; | ||
414 | int csi_port, csi_lanes; | ||
415 | |||
416 | dev_dbg(csi->dev, "%s ports index=%d, lanes=%d\n", | ||
417 | __func__, port_num, chan->numlanes); | ||
418 | |||
419 | csi_port = chan->ports[port_num].num; | ||
420 | csi_lanes = chan->ports[port_num].lanes; | ||
421 | |||
422 | if (chan->pg_mode) | ||
423 | csi4_tpg_stop_streaming(chan, port_num); | ||
424 | else { | ||
425 | /* disable DPHY */ | ||
426 | csi4_phy_config(chan, csi_port, csi_lanes, false); | ||
427 | csi4_stream_check_status(chan, csi_port); | ||
428 | csi4_cil_check_status(chan, csi_port); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | void csi4_override_format(struct tegra_csi_channel *chan, | ||
433 | enum tegra_csi_port_num port_num) | ||
434 | { | ||
435 | struct tegra_csi_port *port = &chan->ports[port_num]; | ||
436 | unsigned int val; | ||
437 | int csi_port; | ||
438 | |||
439 | if (!chan->pg_mode) { | ||
440 | dev_err(chan->csi->dev, "%s non PG format update failed\n", | ||
441 | __func__); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | /* calculate PG IMAGE SIZE and DT */ | ||
446 | mutex_lock(&chan->format_lock); | ||
447 | val = port->format.height << HEIGHT_OFFSET | | ||
448 | (port->format.width * | ||
449 | (port->core_format->vf_code == TEGRA_VF_RAW10 ? 10 : 24) / 8); | ||
450 | mutex_unlock(&chan->format_lock); | ||
451 | |||
452 | csi_port = chan->ports[port_num].num; | ||
453 | csi4_stream_write(chan, csi_port, PG_IMAGE_SIZE, val); | ||
454 | } | ||
455 | |||
456 | int csi4_mipi_cal(struct tegra_csi_channel *chan) | ||
457 | { | ||
458 | unsigned int lanes, num_ports, port, addr; | ||
459 | unsigned int cila, cilb; | ||
460 | struct tegra_csi_device *csi = chan->csi; | ||
461 | |||
462 | lanes = 0; | ||
463 | num_ports = 0; | ||
464 | port = 0; | ||
465 | while (num_ports < chan->numports) { | ||
466 | port = chan->ports[num_ports].num; | ||
467 | dev_dbg(csi->dev, "csi port:%d\n", port); | ||
468 | |||
469 | if (chan->numlanes == 2) { | ||
470 | lanes |= CSIA << port; | ||
471 | cila = (0x01 << E_INPUT_LP_IO0_SHIFT) | | ||
472 | (0x01 << E_INPUT_LP_IO1_SHIFT) | | ||
473 | (0x01 << E_INPUT_LP_CLK_SHIFT) | | ||
474 | (0x00 << PD_CLK_SHIFT) | | ||
475 | (0x00 << PD_IO0_SHIFT) | | ||
476 | (0x00 << PD_IO1_SHIFT); | ||
477 | addr = (port % 2 == 0 ? | ||
478 | NVCSI_CIL_A_BASE : NVCSI_CIL_B_BASE) | ||
479 | + PAD_CONFIG_0; | ||
480 | csi4_phy_write(chan, port >> 1, addr, cila); | ||
481 | } else { | ||
482 | lanes |= (CSIA | CSIB) << port; | ||
483 | cila = (0x01 << E_INPUT_LP_IO0_SHIFT) | | ||
484 | (0x01 << E_INPUT_LP_IO1_SHIFT) | | ||
485 | (0x01 << E_INPUT_LP_CLK_SHIFT) | | ||
486 | (0x00 << PD_CLK_SHIFT) | | ||
487 | (0x00 << PD_IO0_SHIFT) | | ||
488 | (0x00 << PD_IO1_SHIFT); | ||
489 | cilb = (0x01 << E_INPUT_LP_IO0_SHIFT) | | ||
490 | (0x01 << E_INPUT_LP_IO1_SHIFT) | | ||
491 | (0x01 << PD_CLK_SHIFT) | | ||
492 | (0x00 << PD_IO0_SHIFT) | | ||
493 | (0x00 << PD_IO1_SHIFT); | ||
494 | csi4_phy_write(chan, port >> 1, | ||
495 | NVCSI_CIL_A_BASE + PAD_CONFIG_0, cila); | ||
496 | csi4_phy_write(chan, port >> 1, | ||
497 | NVCSI_CIL_B_BASE + PAD_CONFIG_0, cilb); | ||
498 | } | ||
499 | num_ports++; | ||
500 | } | ||
501 | if (!lanes) { | ||
502 | dev_err(csi->dev, "Selected no CSI lane, cannot do calibration"); | ||
503 | return -EINVAL; | ||
504 | } | ||
505 | return tegra_mipi_calibration(lanes); | ||
506 | } | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi4_fops.h b/drivers/media/platform/tegra/camera/csi/csi4_fops.h new file mode 100644 index 000000000..49f0aa8ad --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi4_fops.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * Tegra CSI4 device common APIs | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * Author: Frank Chen <frankc@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef __CSI4_H__ | ||
16 | #define __CSI4_H__ | ||
17 | |||
18 | #include "csi.h" | ||
19 | |||
20 | int csi4_power_on(struct tegra_csi_device *csi); | ||
21 | int csi4_power_off(struct tegra_csi_device *csi); | ||
22 | int csi4_start_streaming(struct tegra_csi_channel *chacsin, | ||
23 | enum tegra_csi_port_num port_num); | ||
24 | void csi4_stop_streaming(struct tegra_csi_channel *chan, | ||
25 | enum tegra_csi_port_num port_num); | ||
26 | void csi4_override_format(struct tegra_csi_channel *chan, | ||
27 | enum tegra_csi_port_num port_num); | ||
28 | int csi4_mipi_cal(struct tegra_csi_channel *chan); | ||
29 | int csi4_hw_init(struct tegra_csi_device *csi); | ||
30 | |||
31 | struct tegra_csi_fops csi4_fops = { | ||
32 | .csi_power_on = csi4_power_on, | ||
33 | .csi_power_off = csi4_power_off, | ||
34 | .csi_start_streaming = csi4_start_streaming, | ||
35 | .csi_stop_streaming = csi4_stop_streaming, | ||
36 | .csi_override_format = csi4_override_format, | ||
37 | .mipical = csi4_mipi_cal, | ||
38 | .hw_init = csi4_hw_init, | ||
39 | }; | ||
40 | |||
41 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/csi/csi4_registers.h b/drivers/media/platform/tegra/camera/csi/csi4_registers.h new file mode 100644 index 000000000..ed4ef936c --- /dev/null +++ b/drivers/media/platform/tegra/camera/csi/csi4_registers.h | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * drivers/media/platform/tegra/camera/csi/csi4_registers.h | ||
3 | * | ||
4 | * Tegra 18x CSI register offsets | ||
5 | * | ||
6 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #ifndef __CSI4_REGISTERS_H__ | ||
22 | #define __CSI4_REGISTERS_H__ | ||
23 | |||
24 | #define CSI4_BASE_ADDRESS 0x18000 | ||
25 | #define CSI4_PHY_OFFSET 0x10000 | ||
26 | #define CSI4_STREAM_OFFSET 0x800 | ||
27 | |||
28 | #define CSI_PORTS (6) | ||
29 | #define PHY_BRICKS (3) | ||
30 | |||
31 | /* NVCSI registers. Starts from 0x0 */ | ||
32 | #define CFG_NVCSI_INCR_SYNCPT_CNTRL 0x04 | ||
33 | |||
34 | /* NVCSI_STREAM registers */ | ||
35 | #define TEGRA_CSI_STREAM_0_BASE 0x010000 | ||
36 | #define TEGRA_CSI_STREAM_1_BASE 0x010800 | ||
37 | #define TEGRA_CSI_STREAM_2_BASE 0x020000 | ||
38 | #define TEGRA_CSI_STREAM_3_BASE 0x020800 | ||
39 | #define TEGRA_CSI_STREAM_4_BASE 0x030000 | ||
40 | #define TEGRA_CSI_STREAM_5_BASE 0x030800 | ||
41 | |||
42 | #define PP_EN_CTRL 0x08 | ||
43 | #define CFG_PP_EN (0x1 << 0) | ||
44 | |||
45 | #define PPFSM_TIMEOUT_CTRL 0x6c | ||
46 | #define CFG_TIMEOUT_EN (0x1 << 31) | ||
47 | #define CFG_TIMEOUT_PERIOD (0x7fffffff << 0) | ||
48 | |||
49 | #define VC0_DT_OVERRIDE 0x20 | ||
50 | #define CFG_VC0_DT_OVERRIDE_EN (0x1 << 31) | ||
51 | #define CFG_VC0_DT_OVERRIDE (0x3f << 0) | ||
52 | |||
53 | #define PH_CHK_CTRL 0x70 | ||
54 | #define CFG_PH_CRC_CHK_EN (0x1 << 1) | ||
55 | #define CFG_PH_ECC_CHK_EN (0x1 << 0) | ||
56 | |||
57 | #define VC0_DPCM_CTRL 0x74 | ||
58 | #define CFG_VC0_DPCM_COMPRESSION_RATIO (0xf << 0) | ||
59 | |||
60 | #define ERROR_STATUS2VI_MASK 0x90 | ||
61 | |||
62 | /* T186 TPG */ | ||
63 | #define TPG_EN_0 0x0b8 | ||
64 | #define cfg_tpg_en 0x1 | ||
65 | /* NVCSI_STREAM Legacy T210 PG*/ | ||
66 | #define PG_CTRL 0x194 | ||
67 | #define PG_MODE_OFFSET 2 | ||
68 | #define PG_ENABLE 0x1 | ||
69 | #define PG_DISABLE 0x0 | ||
70 | #define PG_BLANK 0x198 | ||
71 | #define PG_VBLANK_MASK 0xffff | ||
72 | #define PG_HBLANK_MASK 0xffff | ||
73 | #define PG_VBLANK_OFFSET 16 | ||
74 | #define PG_HBLANK_OFFSET 0 | ||
75 | #define PG_PHASE 0x19c | ||
76 | #define PG_RED_FREQ 0x1a0 | ||
77 | #define PG_VERT_INIT_FREQ_OFFSET 16 | ||
78 | #define PG_HOR_INIT_FREQ_OFFSET 0 | ||
79 | #define PG_RED_FREQ_RATE 0x1a4 | ||
80 | #define PG_GREEN_FREQ 0x1a8 | ||
81 | #define PG_GREEN_FREQ_RATE 0x1ac | ||
82 | #define PG_BLUE_FREQ 0x1b0 | ||
83 | #define PG_BLUE_FREQ_RATE 0X1b4 | ||
84 | #define PG_AOHDR 0x1b8 | ||
85 | #define PG_IMAGE_SIZE 0x1bc | ||
86 | #define HEIGHT_OFFSET 16 | ||
87 | #define PG_IMAGE_DT 0x1c0 | ||
88 | |||
89 | /* TODO - double check if rr_status2vi_vc0:[0] means bit or value */ | ||
90 | #define ERROR_STATUS2VI_VC0 0x94 | ||
91 | #define ERROR_STATUS2VI_VC1 0x98 | ||
92 | #define ERROR_STATUS2VI_VC2 0x9c | ||
93 | #define ERROR_STATUS2VI_VC3 0xa0 | ||
94 | #define ERR_STATUS2VI_VC (0xf << 0) | ||
95 | #define ERR_PP_FSM_TIMEOUT (0) | ||
96 | #define ERR_PH_ECC_SINGLE_BIT (1) | ||
97 | #define ERR_PACKET_PAYLOAD_CRC (2) | ||
98 | #define ERR_PACKET_PAYLOAD_LESS (3) | ||
99 | |||
100 | #define INTR_STATUS 0xa4 | ||
101 | #define INTR_MASK 0xa8 | ||
102 | #define PD_CRC_ERR_VC0 (0x1 << 2) | ||
103 | #define PH_ECC_SINGLE_BIT_ERR_VC0 (0x1 << 1) | ||
104 | #define PH_ECC_MULTI_BIT_ERR (0x1 << 16) | ||
105 | #define ERR_INTR_STATUS 0xac | ||
106 | #define ERR_INTR_MASK 0xb0 | ||
107 | #define MASK_PH_CRC_ERR (0x1 << 17) | ||
108 | #define MASK_PH_ECC_MULTI_BIT_ERR (0x1 << 16) | ||
109 | #define MASK_PD_WC_SHORT_ERR_VC3 (0x1 << 15) | ||
110 | #define MASK_PD_CRC_ERR_VC3 (0x1 << 14) | ||
111 | #define MASK_PH_ECC_SINGLE_BIT_ERR_VC3 (0x1 << 13) | ||
112 | #define MASK_PPFSM_TIMEOUT_VC3 (0x1 << 12) | ||
113 | #define MASK_PD_WC_SHORT_ERR_VC2 (0x1 << 11) | ||
114 | #define MASK_PD_CRC_ERR_VC2 (0x1 << 10) | ||
115 | #define MASK_PH_ECC_SINGLE_BIT_ERR_VC2 (0x1 << 9) | ||
116 | #define MASK_PPFSM_TIMEOUT_VC2 (0x1 << 8) | ||
117 | #define MASK_PD_WC_SHORT_ERR_VC1 (0x1 << 7) | ||
118 | #define MASK_PD_CRC_ERR_VC1 (0x1 << 6) | ||
119 | #define MASK_PH_ECC_SINGLE_BIT_ERR_VC1 (0x1 << 5) | ||
120 | #define MASK_PPFSM_TIMEOUT_VC1 (0x1 << 4) | ||
121 | #define MASK_PD_WC_SHORT_ERR_VC0 (0x1 << 3) | ||
122 | #define MASK_PD_CRC_ERR_VC0 (0x1 << 2) | ||
123 | #define MASK_PH_ECC_SINGLE_BIT_ERR_VC0 (0x1 << 1) | ||
124 | #define MASK_PPFSM_TIMEOUT_VC0 (0x1 << 0) | ||
125 | /* For ERR_INTR_MASK and ERR_INTR_MASK */ | ||
126 | #define MASK_HSM_INTR_SW_TRIGGER (0x1 << 18) | ||
127 | |||
128 | /* NVCSI_PHY CIL registers */ | ||
129 | #define NVCSI_PHY_0_CILA_BASE 0x010400 | ||
130 | #define NVCSI_PHY_0_CILB_BASE 0x010C00 | ||
131 | #define NVCSI_PHY_1_CILA_BASE 0x020400 | ||
132 | #define NVCSI_PHY_1_CILB_BASE 0x020C00 | ||
133 | #define NVCSI_PHY_2_CILA_BASE 0x030400 | ||
134 | #define NVCSI_PHY_2_CILB_BASE 0x030C00 | ||
135 | |||
136 | #define CIL_INTR_STATUS 0x400 | ||
137 | #define CIL_INTR_MASK 0x404 | ||
138 | #define CIL_ERR_INTR_STATUS 0x408 | ||
139 | #define CIL_ERR_INTR_MASK 0x40c | ||
140 | |||
141 | /* NVCSI_PHY registers */ | ||
142 | #define NVCSI_CIL_PHY_CTRL 0x00 | ||
143 | #define CFG_PHY_MODE (0x1 << 0) | ||
144 | #define DPHY (0) | ||
145 | #define CPHY (1) | ||
146 | |||
147 | #define NVCSI_CIL_CONFIG 0x04 | ||
148 | #define DATA_LANE_B_OFFSET 0x8 | ||
149 | #define DATA_LANE_A_OFFSET 0x0 | ||
150 | #define DATA_LANE_B (0x7 << DATA_LANE_B_OFFSET) | ||
151 | #define DATA_LANE_A (0x7 << DATA_LANE_A_OFFSET) | ||
152 | |||
153 | #define NVCSI_CIL_PAD_CONFIG 0x0c | ||
154 | #define LOADADJ (0xf << 12) | ||
155 | #define PDVCLAMP (0x1 << 9) | ||
156 | #define E_VCLAMP (0x1 << 8) | ||
157 | #define SPARE_TOP (0xff << 0) | ||
158 | |||
159 | #define NVCSI_CIL_A_SW_RESET 0x18 | ||
160 | #define NVCSI_CIL_B_SW_RESET 0x7c | ||
161 | #define SW_RESET1_EN (0x1 << 1) | ||
162 | #define SW_RESET0_EN (0x1 << 0) | ||
163 | |||
164 | #define NVCSI_CIL_A_PAD_CONFIG 0x20 | ||
165 | #define NVCSI_CIL_B_PAD_CONFIG 0x84 | ||
166 | #define E_INPUT_LP_IO1_SHIFT 22 | ||
167 | #define E_INPUT_LP_IO0_SHIFT 21 | ||
168 | #define E_INPUT_LP_CLK_SHIFT 20 | ||
169 | #define E_INPUT_LP_IO1 (0x1 << 22) | ||
170 | #define E_INPUT_LP_IO0 (0x1 << 21) | ||
171 | #define E_INPUT_LP_CLK (0x1 << 20) | ||
172 | #define BANDWD_IN (0x1 << 19) | ||
173 | #define PD_CLK (0x1 << 18) | ||
174 | #define PD_IO1 (0x1 << 17) | ||
175 | #define PD_IO0 (0x1 << 16) | ||
176 | #define PD_CLK_SHIFT 18 | ||
177 | #define PD_IO1_SHIFT 17 | ||
178 | #define PD_IO0_SHIFT 16 | ||
179 | #define SPARE_CLK (0x1 << 8) | ||
180 | #define SPARE_IO1 (0x1 << 4) | ||
181 | #define SPARE_IO0 (0x1 << 0) | ||
182 | |||
183 | #define NVCSI_CIL_A_CONTROL 0x5c | ||
184 | #define NVCSI_CIL_B_CONTROL 0xc0 | ||
185 | #define DEFAULT_DESKEW_COMPARE (0x4 << 20) | ||
186 | #define DEFAULT_DESKEW_SETTLE (0x6 << 16) | ||
187 | #define DEFAULT_CLK_SETTLE (0x21 << 8) | ||
188 | #define T18X_BYPASS_LP_SEQ (0x1 << 7) | ||
189 | #define DEFAULT_THS_SETTLE (0x14 << 0) | ||
190 | |||
191 | /* MIPICAL */ | ||
192 | #define NVCSI_CIL_A_BASE 0x18 | ||
193 | #define NVCSI_CIL_B_BASE 0x7c | ||
194 | #define PAD_CONFIG_0 0x8 | ||
195 | |||
196 | #endif /* __CSI4_REGISTERS_H__ */ | ||
diff --git a/drivers/media/platform/tegra/camera/sensor_common.c b/drivers/media/platform/tegra/camera/sensor_common.c new file mode 100644 index 000000000..b1504e61d --- /dev/null +++ b/drivers/media/platform/tegra/camera/sensor_common.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * sensor_common.c - utilities for tegra sensor drivers | ||
3 | * | ||
4 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <media/sensor_common.h> | ||
20 | #include <linux/of_graph.h> | ||
21 | #include <linux/string.h> | ||
22 | |||
23 | static int read_property_u32( | ||
24 | struct device_node *node, const char *name, u32 *value) | ||
25 | { | ||
26 | const char *str; | ||
27 | int err = 0; | ||
28 | |||
29 | err = of_property_read_string(node, name, &str); | ||
30 | if (err) | ||
31 | return -ENODATA; | ||
32 | |||
33 | err = kstrtou32(str, 10, value); | ||
34 | if (err) | ||
35 | return -EFAULT; | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int read_property_u64( | ||
41 | struct device_node *node, const char *name, u64 *value) | ||
42 | { | ||
43 | const char *str; | ||
44 | int err = 0; | ||
45 | |||
46 | err = of_property_read_string(node, name, &str); | ||
47 | if (err) | ||
48 | return -ENODATA; | ||
49 | |||
50 | err = kstrtou64(str, 10, value); | ||
51 | if (err) | ||
52 | return -EFAULT; | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static int sensor_common_parse_signal_props( | ||
58 | struct device *dev, struct device_node *node, | ||
59 | struct sensor_signal_properties *signal) | ||
60 | { | ||
61 | const char *temp_str; | ||
62 | int err = 0; | ||
63 | |||
64 | /* Do not report error for these properties yet */ | ||
65 | read_property_u32(node, "readout_orientation", | ||
66 | &signal->readout_orientation); | ||
67 | read_property_u32(node, "num_lanes", | ||
68 | &signal->num_lanes); | ||
69 | read_property_u32(node, "mclk_khz", | ||
70 | &signal->mclk_freq); | ||
71 | read_property_u64(node, "pix_clk_hz", | ||
72 | &signal->pixel_clock.val); | ||
73 | read_property_u32(node, "cil_settletime", | ||
74 | &signal->cil_settletime); | ||
75 | /* initialize default if this prop not available */ | ||
76 | err = of_property_read_string(node, "discontinuous_clk", | ||
77 | &temp_str); | ||
78 | if (!err) | ||
79 | signal->discontinuous_clk = | ||
80 | !strncmp(temp_str, "yes", sizeof("yes")); | ||
81 | else | ||
82 | signal->discontinuous_clk = 1; | ||
83 | /* initialize default if this prop not available */ | ||
84 | err = of_property_read_string(node, "dpcm_enable", | ||
85 | &temp_str); | ||
86 | if (!err) | ||
87 | signal->dpcm_enable = | ||
88 | !strncmp(temp_str, "true", sizeof("true")); | ||
89 | else | ||
90 | signal->dpcm_enable = 0; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int extract_pixel_format( | ||
96 | const char *pixel_t, u32 *format) | ||
97 | { | ||
98 | size_t size = strnlen(pixel_t, OF_MAX_STR_LEN); | ||
99 | |||
100 | if (strncmp(pixel_t, "bayer_bggr10", size) == 0) | ||
101 | *format = V4L2_PIX_FMT_SBGGR10; | ||
102 | else if (strncmp(pixel_t, "bayer_rggb10", size) == 0) | ||
103 | *format = V4L2_PIX_FMT_SRGGB10; | ||
104 | else if (strncmp(pixel_t, "bayer_bggr12", size) == 0) | ||
105 | *format = V4L2_PIX_FMT_SBGGR12; | ||
106 | else if (strncmp(pixel_t, "bayer_rggb12", size) == 0) | ||
107 | *format = V4L2_PIX_FMT_SRGGB12; | ||
108 | else if (strncmp(pixel_t, "bayer_xbggr10p", size) == 0) | ||
109 | *format = V4L2_PIX_FMT_XBGGR10P; | ||
110 | else if (strncmp(pixel_t, "bayer_xrggb10p", size) == 0) | ||
111 | *format = V4L2_PIX_FMT_XRGGB10P; | ||
112 | else { | ||
113 | pr_err("%s: Need to extend format%s\n", __func__, pixel_t); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int sensor_common_parse_image_props( | ||
121 | struct device *dev, struct device_node *node, | ||
122 | struct sensor_image_properties *image) | ||
123 | { | ||
124 | const char *temp_str; | ||
125 | int err = 0; | ||
126 | |||
127 | err = read_property_u32(node, "active_w", | ||
128 | &image->width); | ||
129 | if (err) { | ||
130 | dev_err(dev, "%s:active_w property missing\n", __func__); | ||
131 | goto fail; | ||
132 | } | ||
133 | |||
134 | err = read_property_u32(node, "active_h", | ||
135 | &image->height); | ||
136 | if (err) { | ||
137 | dev_err(dev, "%s:active_h property missing\n", __func__); | ||
138 | goto fail; | ||
139 | } | ||
140 | |||
141 | err = read_property_u32(node, "line_length", | ||
142 | &image->line_length); | ||
143 | if (err) { | ||
144 | dev_err(dev, "%s:Line length property missing\n", __func__); | ||
145 | goto fail; | ||
146 | } | ||
147 | |||
148 | err = of_property_read_string(node, "pixel_t", | ||
149 | &temp_str); | ||
150 | if (err) { | ||
151 | dev_err(dev, "%s:pixel_t property missing\n", __func__); | ||
152 | goto fail; | ||
153 | } | ||
154 | |||
155 | err = extract_pixel_format(temp_str, &image->pixel_format); | ||
156 | if (err) { | ||
157 | dev_err(dev, "Unsupported pixel format\n"); | ||
158 | goto fail; | ||
159 | } | ||
160 | |||
161 | /* ignore err for this prop */ | ||
162 | read_property_u32(node, "embedded_metadata_height", | ||
163 | &image->embedded_metadata_height); | ||
164 | |||
165 | fail: | ||
166 | return err; | ||
167 | } | ||
168 | |||
169 | static int sensor_common_parse_dv_timings( | ||
170 | struct device *dev, struct device_node *node, | ||
171 | struct sensor_dv_timings *timings) | ||
172 | { | ||
173 | /* Do not report error for these properties yet */ | ||
174 | read_property_u32(node, "horz_front_porch", | ||
175 | &timings->hfrontporch); | ||
176 | read_property_u32(node, "horz_sync", | ||
177 | &timings->hsync); | ||
178 | read_property_u32(node, "horz_back_porch", | ||
179 | &timings->hbackporch); | ||
180 | read_property_u32(node, "vert_front_porch", | ||
181 | &timings->vfrontporch); | ||
182 | read_property_u32(node, "vert_sync", | ||
183 | &timings->vsync); | ||
184 | read_property_u32(node, "vert_back_porch", | ||
185 | &timings->vbackporch); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int sensor_common_parse_control_props( | ||
191 | struct device *dev, struct device_node *node, | ||
192 | struct sensor_control_properties *control) | ||
193 | { | ||
194 | int err = 0; | ||
195 | |||
196 | err = read_property_u32(node, "gain_factor", | ||
197 | &control->gain_factor); | ||
198 | if (err) | ||
199 | dev_err(dev, "%s:%s:property missing\n", | ||
200 | __func__, "gain_factor"); | ||
201 | |||
202 | err = read_property_u32(node, "framerate_factor", | ||
203 | &control->framerate_factor); | ||
204 | if (err) | ||
205 | dev_err(dev, "%s:%s:property missing\n", | ||
206 | __func__, "framerate_factor"); | ||
207 | |||
208 | /* ignore err for this prop */ | ||
209 | err = read_property_u32(node, "inherent_gain", | ||
210 | &control->inherent_gain); | ||
211 | |||
212 | err = read_property_u32(node, "min_gain_val", | ||
213 | &control->min_gain_val); | ||
214 | if (err) | ||
215 | dev_err(dev, "%s:%s:property missing\n", | ||
216 | __func__, "min_gain_val"); | ||
217 | |||
218 | err = read_property_u32(node, "max_gain_val", | ||
219 | &control->max_gain_val); | ||
220 | if (err) | ||
221 | dev_err(dev, "%s:%s:property missing\n", | ||
222 | __func__, "max_gain_val"); | ||
223 | |||
224 | /* ignore err for this prop */ | ||
225 | err = read_property_u32(node, "min_hdr_ratio", | ||
226 | &control->min_hdr_ratio); | ||
227 | err = read_property_u32(node, "max_hdr_ratio", | ||
228 | &control->max_hdr_ratio); | ||
229 | |||
230 | err = read_property_u32(node, "min_framerate", | ||
231 | &control->min_framerate); | ||
232 | if (err) | ||
233 | dev_err(dev, "%s:%s:property missing\n", | ||
234 | __func__, "min_framerate"); | ||
235 | |||
236 | err = read_property_u32(node, "max_framerate", | ||
237 | &control->max_framerate); | ||
238 | if (err) | ||
239 | dev_err(dev, "%s:%s:property missing\n", | ||
240 | __func__, "max_framerate"); | ||
241 | |||
242 | err = read_property_u64(node, "min_exp_time", | ||
243 | &control->min_exp_time.val); | ||
244 | if (err) | ||
245 | dev_err(dev, "%s:%s:property missing\n", | ||
246 | __func__, "min_exp_time"); | ||
247 | |||
248 | err = read_property_u64(node, "max_exp_time", | ||
249 | &control->max_exp_time.val); | ||
250 | if (err) | ||
251 | dev_err(dev, "%s:%s:property missing\n", | ||
252 | __func__, "max_exp_time"); | ||
253 | |||
254 | |||
255 | return err; | ||
256 | } | ||
257 | |||
258 | int sensor_common_init_sensor_properties( | ||
259 | struct device *dev, struct device_node *node, | ||
260 | struct sensor_properties *sensor) | ||
261 | { | ||
262 | char temp_str[OF_MAX_STR_LEN]; | ||
263 | struct device_node *temp_node; | ||
264 | int num_modes = 0; | ||
265 | int err, i; | ||
266 | |||
267 | /* get number of modes */ | ||
268 | for (i = 0; num_modes < MAX_NUM_SENSOR_MODES; i++) { | ||
269 | snprintf(temp_str, sizeof(temp_str), "%s%d", | ||
270 | OF_SENSORMODE_PREFIX, i); | ||
271 | temp_node = of_find_node_by_name(node, temp_str); | ||
272 | if (temp_node == NULL) | ||
273 | break; | ||
274 | num_modes++; | ||
275 | } | ||
276 | sensor->num_modes = num_modes; | ||
277 | |||
278 | sensor->sensor_modes = devm_kzalloc(dev, | ||
279 | num_modes * sizeof(struct sensor_mode_properties), | ||
280 | GFP_KERNEL); | ||
281 | if (!sensor->sensor_modes) { | ||
282 | dev_err(dev, "Failed to allocate memory for sensor modes\n"); | ||
283 | return -ENOMEM; | ||
284 | } | ||
285 | memset(sensor->sensor_modes, 0, num_modes * | ||
286 | sizeof(struct sensor_mode_properties)); | ||
287 | |||
288 | for (i = 0; i < num_modes; i++) { | ||
289 | snprintf(temp_str, sizeof(temp_str), "%s%d", | ||
290 | OF_SENSORMODE_PREFIX, i); | ||
291 | |||
292 | temp_node = of_find_node_by_name(node, temp_str); | ||
293 | if (temp_node == NULL) { | ||
294 | dev_err(dev, "Failed to find mode\n"); | ||
295 | return -ENODATA; | ||
296 | }; | ||
297 | |||
298 | err = sensor_common_parse_signal_props(dev, temp_node, | ||
299 | &sensor->sensor_modes[i].signal_properties); | ||
300 | if (err) { | ||
301 | dev_err(dev, "Failed to read signal properties\n"); | ||
302 | return err; | ||
303 | } | ||
304 | |||
305 | err = sensor_common_parse_image_props(dev, temp_node, | ||
306 | &sensor->sensor_modes[i].image_properties); | ||
307 | if (err) { | ||
308 | dev_err(dev, "Failed to read image properties\n"); | ||
309 | return err; | ||
310 | } | ||
311 | |||
312 | err = sensor_common_parse_dv_timings(dev, temp_node, | ||
313 | &sensor->sensor_modes[i].dv_timings); | ||
314 | if (err) { | ||
315 | dev_err(dev, "Failed to read DV timings\n"); | ||
316 | return err; | ||
317 | } | ||
318 | |||
319 | err = sensor_common_parse_control_props(dev, temp_node, | ||
320 | &sensor->sensor_modes[i].control_properties); | ||
321 | if (err) { | ||
322 | dev_err(dev, "Failed to read control properties\n"); | ||
323 | return err; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | EXPORT_SYMBOL(sensor_common_init_sensor_properties); | ||
diff --git a/drivers/media/platform/tegra/camera/vi/Makefile b/drivers/media/platform/tegra/camera/vi/Makefile new file mode 100644 index 000000000..11bce8f3b --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/Makefile | |||
@@ -0,0 +1,21 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | ccflags-y += -I../nvhost/drivers/video/tegra/host | ||
3 | ccflags-y += -Idrivers/video/tegra/host | ||
4 | ccflags-y += -Idrivers/video/tegra/camera | ||
5 | ccflags-y += -Idrivers/media/platform/tegra | ||
6 | ccflags-y += -Werror | ||
7 | |||
8 | ifeq ($(CONFIG_ARCH_TEGRA_18x_SOC),y) | ||
9 | ccflags-y += -I../t18x/drivers/video/tegra/host/ | ||
10 | ccflags-y += -I../t18x/include | ||
11 | ccflags-y += -I../nvhost/include | ||
12 | obj-y += mc_common.o core.o channel.o graph.o vi2_fops.o vi4_fops.o | ||
13 | endif | ||
14 | |||
15 | obj-$(CONFIG_TEGRA_CAMERA_RTCPU) += capture.o | ||
16 | |||
17 | ifeq ($(CONFIG_ARCH_TEGRA_210_SOC),y) | ||
18 | ccflags-y += -I../nvhost/include | ||
19 | ccflags-y += -I../t18x/drivers/video/tegra/host/ | ||
20 | obj-y += mc_common.o core.o channel.o graph.o vi2_fops.o | ||
21 | endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/capture.c b/drivers/media/platform/tegra/camera/vi/capture.c new file mode 100644 index 000000000..7a8083098 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/capture.c | |||
@@ -0,0 +1,880 @@ | |||
1 | /* | ||
2 | * Tegra Video Input capture operations | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * Author: David Wang <davidw@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/completion.h> | ||
16 | #include <linux/dma-buf.h> | ||
17 | #include <linux/nvhost.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/printk.h> | ||
20 | #include <linux/scatterlist.h> | ||
21 | #include <linux/tegra-capture-ivc.h> | ||
22 | #include <media/capture.h> | ||
23 | |||
24 | #include "soc/tegra/camrtc-capture.h" | ||
25 | #include "soc/tegra/camrtc-capture-messages.h" | ||
26 | #include "mc_common.h" | ||
27 | |||
28 | |||
29 | #define CAPTURE_CHANNEL_UNKNOWN_RESP 0xFFFFFFFF | ||
30 | #define CAPTURE_CHANNEL_INVALID_ID 0xFFFF | ||
31 | #define CAPTURE_CHANNEL_INVALID_MASK 0llu | ||
32 | #define PROGRESS_SP_IDX 0 | ||
33 | #define EMBDATA_SP_IDX 1 | ||
34 | #define LINETIMER_SP_IDX 2 | ||
35 | #define CAPTURE_CHANNEL_MAX_NUM_SPS 3 | ||
36 | |||
37 | struct vi_capture_buf { | ||
38 | struct dma_buf *buf; | ||
39 | struct dma_buf_attachment *attach; | ||
40 | struct sg_table *sgt; | ||
41 | dma_addr_t iova; | ||
42 | }; | ||
43 | |||
44 | struct vi_capture_unpins { | ||
45 | uint32_t num_unpins; | ||
46 | struct vi_capture_buf data[]; | ||
47 | }; | ||
48 | |||
49 | struct vi_capture { | ||
50 | uint16_t channel_id; | ||
51 | struct device *rtcpu_dev; | ||
52 | struct tegra_channel *vi_channel; | ||
53 | struct vi_capture_buf requests; | ||
54 | uint32_t request_size; | ||
55 | |||
56 | uint32_t syncpts[CAPTURE_CHANNEL_MAX_NUM_SPS]; | ||
57 | |||
58 | struct completion control_resp; | ||
59 | struct completion capture_resp; | ||
60 | struct mutex control_msg_lock; | ||
61 | struct CAPTURE_CONTROL_MSG control_resp_msg; | ||
62 | |||
63 | struct mutex unpins_list_lock; | ||
64 | struct vi_capture_unpins *unpins_list; | ||
65 | }; | ||
66 | |||
67 | static void vi_capture_ivc_control_callback(const void *ivc_resp, | ||
68 | const void *pcontext) | ||
69 | { | ||
70 | const struct CAPTURE_CONTROL_MSG *control_msg = ivc_resp; | ||
71 | struct vi_capture *capture = (struct vi_capture *)pcontext; | ||
72 | struct tegra_channel *chan = capture->vi_channel; | ||
73 | |||
74 | if (unlikely(capture == NULL)) { | ||
75 | dev_err(chan->vi->dev, "%s: invalid context", __func__); | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | if (unlikely(control_msg == NULL)) { | ||
80 | dev_err(chan->vi->dev, "%s: invalid response", __func__); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | switch (control_msg->header.msg_id) { | ||
85 | case CAPTURE_CHANNEL_SETUP_RESP: | ||
86 | case CAPTURE_CHANNEL_RESET_RESP: | ||
87 | case CAPTURE_CHANNEL_RELEASE_RESP: | ||
88 | case CAPTURE_COMPAND_CONFIG_RESP: | ||
89 | case CAPTURE_PDAF_CONFIG_RESP: | ||
90 | case CAPTURE_SYNCGEN_ENABLE_RESP: | ||
91 | case CAPTURE_SYNCGEN_DISABLE_RESP: | ||
92 | memcpy(&capture->control_resp_msg, control_msg, | ||
93 | sizeof(*control_msg)); | ||
94 | complete(&capture->control_resp); | ||
95 | break; | ||
96 | default: | ||
97 | dev_err(chan->vi->dev, | ||
98 | "%s: unknown capture control resp", __func__); | ||
99 | break; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | static void vi_capture_request_unpin(struct tegra_channel *chan, | ||
104 | uint32_t buffer_index); | ||
105 | static void vi_capture_ivc_status_callback(const void *ivc_resp, | ||
106 | const void *pcontext) | ||
107 | { | ||
108 | struct CAPTURE_MSG *status_msg = (struct CAPTURE_MSG *)ivc_resp; | ||
109 | struct vi_capture *capture = (struct vi_capture *)pcontext; | ||
110 | struct tegra_channel *chan = capture->vi_channel; | ||
111 | |||
112 | if (unlikely(capture == NULL)) { | ||
113 | dev_err(chan->vi->dev, "%s: invalid context", __func__); | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | if (unlikely(status_msg == NULL)) { | ||
118 | dev_err(chan->vi->dev, "%s: invalid response", __func__); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | switch (status_msg->header.msg_id) { | ||
123 | case CAPTURE_STATUS_IND: | ||
124 | vi_capture_request_unpin(chan, | ||
125 | status_msg->capture_status_ind.buffer_index); | ||
126 | complete(&capture->capture_resp); | ||
127 | dev_dbg(chan->vi->dev, "%s: status chan_id %u msg_id %u\n", | ||
128 | __func__, status_msg->header.channel_id, | ||
129 | status_msg->header.msg_id); | ||
130 | break; | ||
131 | default: | ||
132 | dev_err(chan->vi->dev, | ||
133 | "%s: unknown capture resp", __func__); | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | int vi_capture_init(struct tegra_channel *chan) | ||
139 | { | ||
140 | struct vi_capture *capture; | ||
141 | struct device_node *dn; | ||
142 | struct platform_device *rtc_pdev; | ||
143 | |||
144 | dev_dbg(chan->vi->dev, "%s++\n", __func__); | ||
145 | dn = of_find_node_by_path("tegra-camera-rtcpu"); | ||
146 | if (of_device_is_available(dn) == 0) { | ||
147 | dev_err(chan->vi->dev, "failed to find rtcpu device node\n"); | ||
148 | return -ENODEV; | ||
149 | } | ||
150 | rtc_pdev = of_find_device_by_node(dn); | ||
151 | if (rtc_pdev == NULL) { | ||
152 | dev_err(chan->vi->dev, "failed to find rtcpu platform\n"); | ||
153 | return -ENODEV; | ||
154 | } | ||
155 | |||
156 | capture = devm_kzalloc(chan->vi->dev, | ||
157 | sizeof(*capture), GFP_KERNEL); | ||
158 | if (unlikely(capture == NULL)) { | ||
159 | dev_err(chan->vi->dev, "failed to allocate capture channel\n"); | ||
160 | return -ENOMEM; | ||
161 | } | ||
162 | |||
163 | capture->rtcpu_dev = &rtc_pdev->dev; | ||
164 | |||
165 | init_completion(&capture->control_resp); | ||
166 | init_completion(&capture->capture_resp); | ||
167 | |||
168 | mutex_init(&capture->control_msg_lock); | ||
169 | mutex_init(&capture->unpins_list_lock); | ||
170 | |||
171 | capture->vi_channel = chan; | ||
172 | chan->capture_data = capture; | ||
173 | |||
174 | capture->channel_id = CAPTURE_CHANNEL_INVALID_ID; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | void vi_capture_shutdown(struct tegra_channel *chan) | ||
180 | { | ||
181 | struct vi_capture *capture = chan->capture_data; | ||
182 | |||
183 | dev_dbg(chan->vi->dev, "%s--\n", __func__); | ||
184 | if (capture == NULL) | ||
185 | return; | ||
186 | |||
187 | if (capture->channel_id != CAPTURE_CHANNEL_INVALID_ID) | ||
188 | vi_capture_release(chan, 0); | ||
189 | |||
190 | devm_kfree(chan->vi->dev, capture); | ||
191 | chan->capture_data = NULL; | ||
192 | } | ||
193 | |||
194 | static int vi_capture_ivc_send_control(struct tegra_channel *chan, | ||
195 | const struct CAPTURE_CONTROL_MSG *msg, size_t size, | ||
196 | uint32_t resp_id) | ||
197 | { | ||
198 | struct vi_capture *capture = chan->capture_data; | ||
199 | struct CAPTURE_MSG_HEADER resp_header = msg->header; | ||
200 | uint32_t timeout = HZ; | ||
201 | int err = 0; | ||
202 | |||
203 | dev_dbg(chan->vi->dev, "%s: sending chan_id %u msg_id %u\n", | ||
204 | __func__, resp_header.channel_id, resp_header.msg_id); | ||
205 | resp_header.msg_id = resp_id; | ||
206 | /* Send capture control IVC message */ | ||
207 | mutex_lock(&capture->control_msg_lock); | ||
208 | err = tegra_capture_ivc_control_submit(msg, size); | ||
209 | if (err < 0) { | ||
210 | dev_err(chan->vi->dev, "IVC control submit failed\n"); | ||
211 | goto fail; | ||
212 | } | ||
213 | |||
214 | timeout = wait_for_completion_killable_timeout( | ||
215 | &capture->control_resp, timeout); | ||
216 | if (timeout <= 0) { | ||
217 | dev_err(chan->vi->dev, | ||
218 | "no reply from camera processor\n"); | ||
219 | err = -ETIMEDOUT; | ||
220 | goto fail; | ||
221 | } | ||
222 | |||
223 | if (memcmp(&resp_header, &capture->control_resp_msg.header, | ||
224 | sizeof(resp_header)) != 0) { | ||
225 | dev_err(chan->vi->dev, | ||
226 | "unexpected response from camera processor\n"); | ||
227 | err = -EINVAL; | ||
228 | goto fail; | ||
229 | } | ||
230 | |||
231 | mutex_unlock(&capture->control_msg_lock); | ||
232 | dev_dbg(chan->vi->dev, "%s: response chan_id %u msg_id %u\n", | ||
233 | __func__, capture->control_resp_msg.header.channel_id, | ||
234 | capture->control_resp_msg.header.msg_id); | ||
235 | return 0; | ||
236 | |||
237 | fail: | ||
238 | mutex_unlock(&capture->control_msg_lock); | ||
239 | return err; | ||
240 | } | ||
241 | |||
242 | static int pin_memory(struct device *dev, | ||
243 | uint32_t mem, struct vi_capture_buf *unpin_data); | ||
244 | static void unpin_memory(struct vi_capture_buf *unpin_data); | ||
245 | |||
246 | static int pin_memory(struct device *dev, | ||
247 | uint32_t mem, struct vi_capture_buf *unpin_data) | ||
248 | { | ||
249 | struct dma_buf *buf; | ||
250 | struct dma_buf_attachment *attach; | ||
251 | struct sg_table *sgt; | ||
252 | int err = 0; | ||
253 | |||
254 | buf = dma_buf_get(mem); | ||
255 | if (IS_ERR(buf)) { | ||
256 | err = PTR_ERR(buf); | ||
257 | goto fail; | ||
258 | } | ||
259 | |||
260 | attach = dma_buf_attach(buf, dev); | ||
261 | if (IS_ERR(attach)) { | ||
262 | err = PTR_ERR(attach); | ||
263 | goto fail; | ||
264 | } | ||
265 | |||
266 | sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); | ||
267 | if (IS_ERR(sgt)) { | ||
268 | err = PTR_ERR(sgt); | ||
269 | goto fail; | ||
270 | } | ||
271 | |||
272 | if (sg_dma_address(sgt->sgl) == 0) | ||
273 | sg_dma_address(sgt->sgl) = sg_phys(sgt->sgl); | ||
274 | |||
275 | unpin_data->iova = sg_dma_address(sgt->sgl); | ||
276 | unpin_data->buf = buf; | ||
277 | unpin_data->attach = attach; | ||
278 | unpin_data->sgt = sgt; | ||
279 | |||
280 | return 0; | ||
281 | |||
282 | fail: | ||
283 | unpin_memory(unpin_data); | ||
284 | return err; | ||
285 | } | ||
286 | |||
287 | static void unpin_memory(struct vi_capture_buf *unpin_data) | ||
288 | { | ||
289 | if (unpin_data->sgt != NULL) | ||
290 | dma_buf_unmap_attachment(unpin_data->attach, unpin_data->sgt, | ||
291 | DMA_BIDIRECTIONAL); | ||
292 | if (unpin_data->attach != NULL) | ||
293 | dma_buf_detach(unpin_data->buf, unpin_data->attach); | ||
294 | if (unpin_data->buf != NULL) | ||
295 | dma_buf_put(unpin_data->buf); | ||
296 | |||
297 | unpin_data->sgt = NULL; | ||
298 | unpin_data->attach = NULL; | ||
299 | unpin_data->buf = NULL; | ||
300 | unpin_data->iova = 0; | ||
301 | } | ||
302 | |||
303 | static int vi_capture_setup_syncpts(struct tegra_channel *chan); | ||
304 | static void vi_capture_release_syncpts(struct tegra_channel *chan); | ||
305 | |||
306 | static int vi_capture_setup_syncpts(struct tegra_channel *chan) | ||
307 | { | ||
308 | struct vi_capture *capture = chan->capture_data; | ||
309 | int i; | ||
310 | int err = 0; | ||
311 | |||
312 | for (i = 0; i < CAPTURE_CHANNEL_MAX_NUM_SPS; i++) { | ||
313 | capture->syncpts[i] = nvhost_get_syncpt_client_managed( | ||
314 | chan->vi->ndev, "vi-capture"); | ||
315 | if (capture->syncpts[i] == 0) { | ||
316 | dev_err(chan->vi->dev, "failed to get syncpt %i!\n", i); | ||
317 | err = -ENODEV; | ||
318 | goto fail; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | return 0; | ||
323 | |||
324 | fail: | ||
325 | vi_capture_release_syncpts(chan); | ||
326 | return err; | ||
327 | } | ||
328 | |||
329 | static void vi_capture_release_syncpts(struct tegra_channel *chan) | ||
330 | { | ||
331 | struct vi_capture *capture = chan->capture_data; | ||
332 | int i; | ||
333 | |||
334 | for (i = 0; i < CAPTURE_CHANNEL_MAX_NUM_SPS; i++) { | ||
335 | if (capture->syncpts[i] != 0) | ||
336 | nvhost_syncpt_put_ref_ext(chan->vi->ndev, | ||
337 | capture->syncpts[i]); | ||
338 | capture->syncpts[i] = 0; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | int vi_capture_setup(struct tegra_channel *chan, | ||
343 | struct vi_capture_setup *setup) | ||
344 | { | ||
345 | struct vi_capture *capture = chan->capture_data; | ||
346 | uint32_t transaction; | ||
347 | struct CAPTURE_CONTROL_MSG control_desc; | ||
348 | struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg; | ||
349 | struct capture_channel_config *config = | ||
350 | &control_desc.channel_setup_req.channel_config; | ||
351 | int err = 0; | ||
352 | |||
353 | if (capture == NULL) { | ||
354 | dev_err(chan->vi->dev, | ||
355 | "%s: vi capture uninitialized\n", __func__); | ||
356 | return -ENODEV; | ||
357 | } | ||
358 | |||
359 | if (capture->channel_id != CAPTURE_CHANNEL_INVALID_ID) { | ||
360 | dev_err(chan->vi->dev, | ||
361 | "%s: already setup, release first\n", __func__); | ||
362 | return -EEXIST; | ||
363 | } | ||
364 | |||
365 | dev_dbg(chan->vi->dev, "chan flags %u\n", setup->channel_flags); | ||
366 | dev_dbg(chan->vi->dev, "chan mask %llx\n", setup->vi_channel_mask); | ||
367 | dev_dbg(chan->vi->dev, "queue depth %u\n", setup->queue_depth); | ||
368 | dev_dbg(chan->vi->dev, "request size %u\n", setup->request_size); | ||
369 | |||
370 | if (setup->vi_channel_mask == CAPTURE_CHANNEL_INVALID_MASK || | ||
371 | setup->channel_flags == 0 || | ||
372 | setup->queue_depth == 0 || | ||
373 | setup->request_size == 0) | ||
374 | return -EINVAL; | ||
375 | |||
376 | /* pin the capture descriptor ring buffer */ | ||
377 | dev_dbg(chan->vi->dev, "%s: descr buffer handle %u\n", | ||
378 | __func__, setup->mem); | ||
379 | err = pin_memory(capture->rtcpu_dev, setup->mem, &capture->requests); | ||
380 | if (err < 0) { | ||
381 | dev_err(chan->vi->dev, "%s: memory setup failed\n", __func__); | ||
382 | return -EFAULT; | ||
383 | } | ||
384 | capture->request_size = setup->request_size; | ||
385 | |||
386 | /* allocate for unpin list based on queue depth */ | ||
387 | capture->unpins_list = devm_kzalloc(chan->vi->dev, | ||
388 | sizeof(struct vi_capture_unpins) * setup->queue_depth, | ||
389 | GFP_KERNEL); | ||
390 | if (unlikely(capture->unpins_list == NULL)) { | ||
391 | dev_err(chan->vi->dev, "failed to allocate unpins array\n"); | ||
392 | goto unpins_list_fail; | ||
393 | } | ||
394 | |||
395 | err = vi_capture_setup_syncpts(chan); | ||
396 | if (err < 0) { | ||
397 | dev_err(chan->vi->dev, "%s: syncpt setup failed\n", __func__); | ||
398 | goto syncpt_fail; | ||
399 | } | ||
400 | |||
401 | err = tegra_capture_ivc_register_control_cb( | ||
402 | &vi_capture_ivc_control_callback, | ||
403 | &transaction, capture); | ||
404 | if (err < 0) { | ||
405 | dev_err(chan->vi->dev, "failed to register control callback\n"); | ||
406 | goto control_cb_fail; | ||
407 | } | ||
408 | |||
409 | memset(&control_desc, 0, sizeof(control_desc)); | ||
410 | control_desc.header.msg_id = CAPTURE_CHANNEL_SETUP_REQ; | ||
411 | control_desc.header.transaction = transaction; | ||
412 | |||
413 | config->channel_flags = setup->channel_flags; | ||
414 | config->vi_channel_mask = setup->vi_channel_mask; | ||
415 | |||
416 | config->queue_depth = setup->queue_depth; | ||
417 | config->request_size = setup->request_size; | ||
418 | config->requests = capture->requests.iova; | ||
419 | |||
420 | config->progress_sp.id = capture->syncpts[PROGRESS_SP_IDX]; | ||
421 | config->embdata_sp.id = capture->syncpts[EMBDATA_SP_IDX]; | ||
422 | config->linetimer_sp.id = capture->syncpts[LINETIMER_SP_IDX]; | ||
423 | |||
424 | err = vi_capture_ivc_send_control(chan, &control_desc, | ||
425 | sizeof(control_desc), CAPTURE_CHANNEL_SETUP_RESP); | ||
426 | if (err < 0) | ||
427 | goto submit_fail; | ||
428 | |||
429 | if (resp_msg->channel_setup_resp.result != CAPTURE_OK) { | ||
430 | dev_err(chan->vi->dev, "%s: control failed, errno %d", __func__, | ||
431 | resp_msg->channel_setup_resp.result); | ||
432 | err = -EINVAL; | ||
433 | goto resp_fail; | ||
434 | } | ||
435 | |||
436 | capture->channel_id = resp_msg->channel_setup_resp.channel_id; | ||
437 | |||
438 | err = tegra_capture_ivc_notify_chan_id(capture->channel_id, | ||
439 | transaction); | ||
440 | if (err < 0) { | ||
441 | dev_err(chan->vi->dev, "failed to update control callback\n"); | ||
442 | goto cb_fail; | ||
443 | } | ||
444 | |||
445 | err = tegra_capture_ivc_register_capture_cb( | ||
446 | &vi_capture_ivc_status_callback, | ||
447 | capture->channel_id, capture); | ||
448 | if (err < 0) { | ||
449 | dev_err(chan->vi->dev, "failed to register capture callback\n"); | ||
450 | goto cb_fail; | ||
451 | } | ||
452 | |||
453 | return 0; | ||
454 | |||
455 | cb_fail: | ||
456 | vi_capture_release(chan, CAPTURE_CHANNEL_RESET_FLAG_IMMEDIATE); | ||
457 | resp_fail: | ||
458 | submit_fail: | ||
459 | tegra_capture_ivc_unregister_control_cb(transaction); | ||
460 | control_cb_fail: | ||
461 | vi_capture_release_syncpts(chan); | ||
462 | syncpt_fail: | ||
463 | devm_kfree(chan->vi->dev, capture->unpins_list); | ||
464 | unpins_list_fail: | ||
465 | unpin_memory(&capture->requests); | ||
466 | return err; | ||
467 | } | ||
468 | |||
469 | int vi_capture_reset(struct tegra_channel *chan, | ||
470 | uint32_t reset_flags) | ||
471 | { | ||
472 | struct vi_capture *capture = chan->capture_data; | ||
473 | struct CAPTURE_CONTROL_MSG control_desc; | ||
474 | struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg; | ||
475 | int err = 0; | ||
476 | |||
477 | if (capture == NULL) { | ||
478 | dev_err(chan->vi->dev, | ||
479 | "%s: vi capture uninitialized\n", __func__); | ||
480 | return -ENODEV; | ||
481 | } | ||
482 | |||
483 | if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) { | ||
484 | dev_err(chan->vi->dev, | ||
485 | "%s: setup channel first\n", __func__); | ||
486 | return -ENODEV; | ||
487 | } | ||
488 | |||
489 | memset(&control_desc, 0, sizeof(control_desc)); | ||
490 | control_desc.header.msg_id = CAPTURE_CHANNEL_RESET_REQ; | ||
491 | control_desc.header.channel_id = capture->channel_id; | ||
492 | control_desc.channel_reset_req.reset_flags = reset_flags; | ||
493 | |||
494 | err = vi_capture_ivc_send_control(chan, &control_desc, | ||
495 | sizeof(control_desc), CAPTURE_CHANNEL_RESET_RESP); | ||
496 | if (err < 0) | ||
497 | goto submit_fail; | ||
498 | |||
499 | if (resp_msg->channel_reset_resp.result != CAPTURE_OK) { | ||
500 | dev_err(chan->vi->dev, "%s: control failed, errno %d", __func__, | ||
501 | resp_msg->channel_reset_resp.result); | ||
502 | err = -EINVAL; | ||
503 | } | ||
504 | |||
505 | return 0; | ||
506 | |||
507 | submit_fail: | ||
508 | return err; | ||
509 | } | ||
510 | |||
511 | int vi_capture_release(struct tegra_channel *chan, | ||
512 | uint32_t reset_flags) | ||
513 | { | ||
514 | struct vi_capture *capture = chan->capture_data; | ||
515 | struct CAPTURE_CONTROL_MSG control_desc; | ||
516 | struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg; | ||
517 | int err = 0; | ||
518 | int ret = 0; | ||
519 | |||
520 | if (capture == NULL) { | ||
521 | dev_err(chan->vi->dev, | ||
522 | "%s: vi capture uninitialized\n", __func__); | ||
523 | return -ENODEV; | ||
524 | } | ||
525 | |||
526 | if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) { | ||
527 | dev_err(chan->vi->dev, | ||
528 | "%s: setup channel first\n", __func__); | ||
529 | return -ENODEV; | ||
530 | } | ||
531 | |||
532 | memset(&control_desc, 0, sizeof(control_desc)); | ||
533 | control_desc.header.msg_id = CAPTURE_CHANNEL_RELEASE_REQ; | ||
534 | control_desc.header.channel_id = capture->channel_id; | ||
535 | control_desc.channel_release_req.reset_flags = reset_flags; | ||
536 | |||
537 | err = vi_capture_ivc_send_control(chan, &control_desc, | ||
538 | sizeof(control_desc), CAPTURE_CHANNEL_RELEASE_RESP); | ||
539 | if (err < 0) | ||
540 | goto submit_fail; | ||
541 | |||
542 | if (resp_msg->channel_release_resp.result != CAPTURE_OK) { | ||
543 | dev_err(chan->vi->dev, "%s: control failed, errno %d", __func__, | ||
544 | resp_msg->channel_release_resp.result); | ||
545 | err = -EINVAL; | ||
546 | } | ||
547 | |||
548 | vi_capture_release_syncpts(chan); | ||
549 | unpin_memory(&capture->requests); | ||
550 | |||
551 | ret = tegra_capture_ivc_unregister_capture_cb(capture->channel_id); | ||
552 | if (ret < 0 && err == 0) { | ||
553 | dev_err(chan->vi->dev, | ||
554 | "failed to unregister capture callback\n"); | ||
555 | err = ret; | ||
556 | } | ||
557 | |||
558 | ret = tegra_capture_ivc_unregister_control_cb(capture->channel_id); | ||
559 | if (ret < 0 && err == 0) { | ||
560 | dev_err(chan->vi->dev, | ||
561 | "failed to unregister control callback\n"); | ||
562 | err = ret; | ||
563 | } | ||
564 | |||
565 | capture->channel_id = CAPTURE_CHANNEL_INVALID_ID; | ||
566 | |||
567 | return 0; | ||
568 | |||
569 | submit_fail: | ||
570 | return err; | ||
571 | } | ||
572 | |||
573 | int vi_capture_get_info(struct tegra_channel *chan, | ||
574 | struct vi_capture_info *info) | ||
575 | { | ||
576 | struct vi_capture *capture = chan->capture_data; | ||
577 | |||
578 | if (capture == NULL) { | ||
579 | dev_err(chan->vi->dev, | ||
580 | "%s: vi capture uninitialized\n", __func__); | ||
581 | return -ENODEV; | ||
582 | } | ||
583 | |||
584 | if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) { | ||
585 | dev_err(chan->vi->dev, | ||
586 | "%s: setup channel first\n", __func__); | ||
587 | return -ENODEV; | ||
588 | } | ||
589 | |||
590 | if (info == NULL) | ||
591 | return -EINVAL; | ||
592 | |||
593 | info->syncpts.progress_syncpt = capture->syncpts[PROGRESS_SP_IDX]; | ||
594 | info->syncpts.emb_data_syncpt = capture->syncpts[EMBDATA_SP_IDX]; | ||
595 | info->syncpts.line_timer_syncpt = capture->syncpts[LINETIMER_SP_IDX]; | ||
596 | |||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | int vi_capture_control_message(struct tegra_channel *chan, | ||
601 | struct vi_capture_control_msg *msg) | ||
602 | { | ||
603 | struct vi_capture *capture = chan->capture_data; | ||
604 | const void __user *msg_ptr = | ||
605 | (const void __user *)(uintptr_t)msg->ptr; | ||
606 | void __user *response = | ||
607 | (void __user *)(uintptr_t)msg->response; | ||
608 | void *msg_cpy; | ||
609 | struct CAPTURE_MSG_HEADER *header; | ||
610 | uint32_t resp_id; | ||
611 | struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg; | ||
612 | int err = 0; | ||
613 | |||
614 | if (capture == NULL) { | ||
615 | dev_err(chan->vi->dev, | ||
616 | "%s: vi capture uninitialized\n", __func__); | ||
617 | return -ENODEV; | ||
618 | } | ||
619 | |||
620 | if (msg->ptr == 0ull || msg->response == 0ull || msg->size == 0) | ||
621 | return -EINVAL; | ||
622 | |||
623 | msg_cpy = devm_kzalloc(chan->vi->dev, msg->size, GFP_KERNEL); | ||
624 | if (unlikely(msg_cpy == NULL)) | ||
625 | return -ENOMEM; | ||
626 | |||
627 | err = copy_from_user(msg_cpy, msg_ptr, msg->size) ? -EFAULT : 0; | ||
628 | if (err < 0) | ||
629 | goto fail; | ||
630 | header = (struct CAPTURE_MSG_HEADER *)msg_cpy; | ||
631 | header->channel_id = capture->channel_id; | ||
632 | |||
633 | switch (header->msg_id) { | ||
634 | case CAPTURE_COMPAND_CONFIG_REQ: | ||
635 | resp_id = CAPTURE_COMPAND_CONFIG_RESP; | ||
636 | break; | ||
637 | case CAPTURE_PDAF_CONFIG_REQ: | ||
638 | resp_id = CAPTURE_PDAF_CONFIG_RESP; | ||
639 | break; | ||
640 | case CAPTURE_SYNCGEN_ENABLE_REQ: | ||
641 | resp_id = CAPTURE_SYNCGEN_ENABLE_RESP; | ||
642 | break; | ||
643 | case CAPTURE_SYNCGEN_DISABLE_REQ: | ||
644 | resp_id = CAPTURE_SYNCGEN_DISABLE_RESP; | ||
645 | break; | ||
646 | default: | ||
647 | dev_err(chan->vi->dev, | ||
648 | "%s: unknown capture control resp", __func__); | ||
649 | err = -EINVAL; | ||
650 | goto fail; | ||
651 | } | ||
652 | |||
653 | err = vi_capture_ivc_send_control(chan, msg_cpy, msg->size, resp_id); | ||
654 | if (err < 0) | ||
655 | goto fail; | ||
656 | |||
657 | err = copy_to_user(response, resp_msg, | ||
658 | sizeof(*resp_msg)) ? -EFAULT : 0; | ||
659 | |||
660 | fail: | ||
661 | devm_kfree(chan->vi->dev, msg_cpy); | ||
662 | return err; | ||
663 | } | ||
664 | |||
665 | struct surface_t { | ||
666 | uint32_t offset; | ||
667 | uint32_t offset_hi; | ||
668 | }; | ||
669 | |||
670 | static int vi_capture_request_pin_and_reloc(struct tegra_channel *chan, | ||
671 | struct vi_capture_req *req) | ||
672 | { | ||
673 | struct vi_capture *capture = chan->capture_data; | ||
674 | uint32_t num_relocs = req->num_relocs; | ||
675 | uint32_t __user *reloc_relatives = | ||
676 | (uint32_t __user *)(uintptr_t)req->reloc_relatives; | ||
677 | uint32_t local_reloc_relatives[num_relocs]; | ||
678 | struct vi_capture_unpins *unpins; | ||
679 | uint32_t request_offset = req->buffer_index * capture->request_size; | ||
680 | void *reloc_page_addr = NULL; | ||
681 | uint32_t prev_mem = 0; | ||
682 | int last_page = -1; | ||
683 | dma_addr_t surface_phys_addr = 0; | ||
684 | dma_addr_t surface_prev_addr = 0; | ||
685 | int i, pin_count = 0; | ||
686 | int err = 0; | ||
687 | |||
688 | err = copy_from_user(local_reloc_relatives, reloc_relatives, | ||
689 | num_relocs * sizeof(uint32_t)) ? -EFAULT : 0; | ||
690 | if (err < 0) | ||
691 | return err; | ||
692 | |||
693 | unpins = devm_kzalloc(chan->vi->dev, | ||
694 | sizeof(struct vi_capture_unpins) + | ||
695 | sizeof(struct vi_capture_buf) * num_relocs, | ||
696 | GFP_KERNEL); | ||
697 | if (unpins == NULL) | ||
698 | return -ENOMEM; | ||
699 | |||
700 | dev_dbg(chan->vi->dev, "%s: relocating %u surfaces\n", | ||
701 | __func__, num_relocs); | ||
702 | for (i = 0; i < num_relocs; i++) { | ||
703 | uint32_t reloc_offset = | ||
704 | request_offset + local_reloc_relatives[i]; | ||
705 | uint64_t surface_raw; | ||
706 | struct surface_t *surface; | ||
707 | uint32_t mem; | ||
708 | uint32_t target_offset; | ||
709 | dma_addr_t target_phys_addr; | ||
710 | |||
711 | dev_dbg(chan->vi->dev, | ||
712 | "%s: idx:%i reloc:%u reloc_offset:%u", __func__, | ||
713 | i, local_reloc_relatives[i], reloc_offset); | ||
714 | |||
715 | /* locate page of the request descr buffer relocation is on */ | ||
716 | if (last_page != reloc_offset >> PAGE_SHIFT) { | ||
717 | if (reloc_page_addr != NULL) | ||
718 | dma_buf_kunmap(capture->requests.buf, last_page, | ||
719 | reloc_page_addr); | ||
720 | |||
721 | reloc_page_addr = dma_buf_kmap(capture->requests.buf, | ||
722 | reloc_offset >> PAGE_SHIFT); | ||
723 | last_page = reloc_offset >> PAGE_SHIFT; | ||
724 | |||
725 | if (unlikely(reloc_page_addr == NULL)) { | ||
726 | dev_err(chan->vi->dev, | ||
727 | "%s: couldn't map request\n", __func__); | ||
728 | goto fail; | ||
729 | } | ||
730 | } | ||
731 | |||
732 | /* read surface offset and memory handle from request descr */ | ||
733 | surface_raw = __raw_readq( | ||
734 | (void __iomem *)(reloc_page_addr + | ||
735 | (reloc_offset & ~PAGE_MASK))); | ||
736 | surface = (struct surface_t *)&surface_raw; | ||
737 | target_offset = surface->offset; | ||
738 | mem = surface->offset_hi; | ||
739 | dev_dbg(chan->vi->dev, "%s: hmem:%u offset:%u\n", __func__, | ||
740 | target_offset, mem); | ||
741 | |||
742 | if (mem != prev_mem) { | ||
743 | err = pin_memory(capture->rtcpu_dev, | ||
744 | mem, &unpins->data[pin_count]); | ||
745 | if (err < 0) { | ||
746 | unpins->num_unpins = pin_count; | ||
747 | goto fail; | ||
748 | } | ||
749 | surface_prev_addr = unpins->data[i].iova; | ||
750 | surface_phys_addr = unpins->data[i].iova; | ||
751 | |||
752 | mutex_lock(&capture->unpins_list_lock); | ||
753 | memcpy(&capture->unpins_list[req->buffer_index], | ||
754 | unpins, sizeof(*unpins)); | ||
755 | mutex_unlock(&capture->unpins_list_lock); | ||
756 | |||
757 | pin_count++; | ||
758 | } else | ||
759 | surface_phys_addr = surface_prev_addr; | ||
760 | |||
761 | target_phys_addr = surface_phys_addr + target_offset; | ||
762 | /* write relocated physical address to request descr */ | ||
763 | __raw_writeq( | ||
764 | target_phys_addr, | ||
765 | (void __iomem *)(reloc_page_addr + | ||
766 | (reloc_offset & ~PAGE_MASK))); | ||
767 | } | ||
768 | |||
769 | unpins->num_unpins = pin_count; | ||
770 | |||
771 | return 0; | ||
772 | |||
773 | fail: | ||
774 | if (reloc_page_addr != NULL) | ||
775 | dma_buf_kunmap(capture->requests.buf, last_page, | ||
776 | reloc_page_addr); | ||
777 | |||
778 | for (i = 0; i < unpins->num_unpins; i++) | ||
779 | unpin_memory(&unpins->data[i]); | ||
780 | devm_kfree(chan->vi->dev, unpins); | ||
781 | |||
782 | return err; | ||
783 | } | ||
784 | |||
785 | static void vi_capture_request_unpin(struct tegra_channel *chan, | ||
786 | uint32_t buffer_index) | ||
787 | { | ||
788 | struct vi_capture *capture = chan->capture_data; | ||
789 | struct vi_capture_unpins *unpins; | ||
790 | int i = 0; | ||
791 | |||
792 | mutex_lock(&capture->unpins_list_lock); | ||
793 | unpins = &capture->unpins_list[buffer_index]; | ||
794 | for (i = 0; i < unpins->num_unpins; i++) | ||
795 | unpin_memory(&unpins->data[i]); | ||
796 | unpins->num_unpins = 0u; | ||
797 | mutex_unlock(&capture->unpins_list_lock); | ||
798 | } | ||
799 | |||
800 | int vi_capture_request(struct tegra_channel *chan, | ||
801 | struct vi_capture_req *req) | ||
802 | { | ||
803 | struct vi_capture *capture = chan->capture_data; | ||
804 | struct CAPTURE_MSG capture_desc; | ||
805 | int err = 0; | ||
806 | |||
807 | if (capture == NULL) { | ||
808 | dev_err(chan->vi->dev, | ||
809 | "%s: vi capture uninitialized\n", __func__); | ||
810 | return -ENODEV; | ||
811 | } | ||
812 | |||
813 | if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) { | ||
814 | dev_err(chan->vi->dev, | ||
815 | "%s: setup channel first\n", __func__); | ||
816 | return -ENODEV; | ||
817 | } | ||
818 | |||
819 | memset(&capture_desc, 0, sizeof(capture_desc)); | ||
820 | capture_desc.header.msg_id = CAPTURE_REQUEST_REQ; | ||
821 | capture_desc.header.channel_id = capture->channel_id; | ||
822 | capture_desc.capture_request_req.buffer_index = req->buffer_index; | ||
823 | |||
824 | /* perform surface pinning and relocation */ | ||
825 | err = vi_capture_request_pin_and_reloc(chan, req); | ||
826 | if (err < 0) { | ||
827 | dev_err(chan->vi->dev, "relocation failed\n"); | ||
828 | return err; | ||
829 | } | ||
830 | |||
831 | dev_dbg(chan->vi->dev, "%s: sending chan_id %u msg_id %u buf:%u\n", | ||
832 | __func__, capture_desc.header.channel_id, | ||
833 | capture_desc.header.msg_id, req->buffer_index); | ||
834 | err = tegra_capture_ivc_capture_submit(&capture_desc, | ||
835 | sizeof(capture_desc)); | ||
836 | if (err < 0) { | ||
837 | dev_err(chan->vi->dev, "IVC capture submit failed\n"); | ||
838 | goto fail; | ||
839 | } | ||
840 | |||
841 | return 0; | ||
842 | |||
843 | fail: | ||
844 | vi_capture_request_unpin(chan, req->buffer_index); | ||
845 | return err; | ||
846 | } | ||
847 | |||
848 | int vi_capture_status(struct tegra_channel *chan, | ||
849 | int32_t timeout_ms) | ||
850 | { | ||
851 | struct vi_capture *capture = chan->capture_data; | ||
852 | int ret = 0; | ||
853 | int err = 0; | ||
854 | |||
855 | if (capture == NULL) { | ||
856 | dev_err(chan->vi->dev, | ||
857 | "%s: vi capture uninitialized\n", __func__); | ||
858 | return -ENODEV; | ||
859 | } | ||
860 | |||
861 | if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) { | ||
862 | dev_err(chan->vi->dev, | ||
863 | "%s: setup channel first\n", __func__); | ||
864 | return -ENODEV; | ||
865 | } | ||
866 | |||
867 | dev_dbg(chan->vi->dev, "%s: waiting for status, timeout:%d ms\n", | ||
868 | __func__, timeout_ms); | ||
869 | |||
870 | ret = wait_for_completion_killable_timeout( | ||
871 | &capture->capture_resp, | ||
872 | (unsigned long)(timeout_ms/1000*HZ)); | ||
873 | if (ret <= 0) { | ||
874 | dev_err(chan->vi->dev, | ||
875 | "no reply from camera processor\n"); | ||
876 | return -ETIMEDOUT; | ||
877 | } | ||
878 | |||
879 | return err; | ||
880 | } | ||
diff --git a/drivers/media/platform/tegra/camera/vi/channel.c b/drivers/media/platform/tegra/camera/vi/channel.c new file mode 100644 index 000000000..5c8e066be --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/channel.c | |||
@@ -0,0 +1,1650 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra Video Input Device | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/atomic.h> | ||
14 | #include <linux/bitmap.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/nvhost.h> | ||
18 | #include <linux/lcm.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | #include <media/v4l2-ctrls.h> | ||
26 | #include <media/v4l2-event.h> | ||
27 | #include <media/v4l2-dev.h> | ||
28 | #include <media/v4l2-fh.h> | ||
29 | #include <media/v4l2-ioctl.h> | ||
30 | #include <media/videobuf2-core.h> | ||
31 | #include <media/videobuf2-dma-contig.h> | ||
32 | #include <media/camera_common.h> | ||
33 | #include <media/tegra_camera_platform.h> | ||
34 | #include <media/v4l2-dv-timings.h> | ||
35 | |||
36 | #include <linux/clk/tegra.h> | ||
37 | |||
38 | #include "mc_common.h" | ||
39 | #include "vi/vi.h" | ||
40 | #include "mipical/mipi_cal.h" | ||
41 | |||
42 | #define TPG_CSI_GROUP_ID 10 | ||
43 | |||
44 | static void gang_buffer_offsets(struct tegra_channel *chan) | ||
45 | { | ||
46 | int i; | ||
47 | u32 offset = 0; | ||
48 | |||
49 | for (i = 0; i < chan->total_ports; i++) { | ||
50 | switch (chan->gang_mode) { | ||
51 | case CAMERA_NO_GANG_MODE: | ||
52 | case CAMERA_GANG_L_R: | ||
53 | case CAMERA_GANG_R_L: | ||
54 | offset = chan->gang_bytesperline; | ||
55 | break; | ||
56 | case CAMERA_GANG_T_B: | ||
57 | case CAMERA_GANG_B_T: | ||
58 | offset = chan->gang_sizeimage; | ||
59 | break; | ||
60 | default: | ||
61 | offset = 0; | ||
62 | } | ||
63 | offset = ((offset + TEGRA_SURFACE_ALIGNMENT - 1) & | ||
64 | ~(TEGRA_SURFACE_ALIGNMENT - 1)); | ||
65 | chan->buffer_offset[i] = i * offset; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static u32 gang_mode_width(enum camera_gang_mode gang_mode, | ||
70 | unsigned int width) | ||
71 | { | ||
72 | if ((gang_mode == CAMERA_GANG_L_R) || | ||
73 | (gang_mode == CAMERA_GANG_R_L)) | ||
74 | return width >> 1; | ||
75 | else | ||
76 | return width; | ||
77 | } | ||
78 | |||
79 | static u32 gang_mode_height(enum camera_gang_mode gang_mode, | ||
80 | unsigned int height) | ||
81 | { | ||
82 | if ((gang_mode == CAMERA_GANG_T_B) || | ||
83 | (gang_mode == CAMERA_GANG_B_T)) | ||
84 | return height >> 1; | ||
85 | else | ||
86 | return height; | ||
87 | } | ||
88 | |||
89 | static void update_gang_mode_params(struct tegra_channel *chan) | ||
90 | { | ||
91 | chan->gang_width = gang_mode_width(chan->gang_mode, | ||
92 | chan->format.width); | ||
93 | chan->gang_height = gang_mode_height(chan->gang_mode, | ||
94 | chan->format.height); | ||
95 | chan->gang_bytesperline = ((chan->gang_width * | ||
96 | chan->fmtinfo->bpp.numerator) / | ||
97 | chan->fmtinfo->bpp.denominator); | ||
98 | chan->gang_sizeimage = chan->gang_bytesperline * | ||
99 | chan->format.height; | ||
100 | gang_buffer_offsets(chan); | ||
101 | } | ||
102 | |||
103 | static void update_gang_mode(struct tegra_channel *chan) | ||
104 | { | ||
105 | int width = chan->format.width; | ||
106 | int height = chan->format.height; | ||
107 | |||
108 | /* | ||
109 | * At present only 720p, 1080p and 4k resolutions | ||
110 | * are supported and only 4K requires gang mode | ||
111 | * Update this code with CID for future extensions | ||
112 | * Also, validate width and height of images based | ||
113 | * on gang mode and surface stride alignment | ||
114 | */ | ||
115 | if ((width > 1920) && (height > 1080)) { | ||
116 | chan->gang_mode = CAMERA_GANG_L_R; | ||
117 | chan->valid_ports = chan->total_ports; | ||
118 | } else { | ||
119 | chan->gang_mode = CAMERA_NO_GANG_MODE; | ||
120 | chan->valid_ports = 1; | ||
121 | } | ||
122 | |||
123 | update_gang_mode_params(chan); | ||
124 | } | ||
125 | |||
126 | static u32 get_aligned_buffer_size(struct tegra_channel *chan, | ||
127 | u32 bytesperline, u32 height) | ||
128 | { | ||
129 | u32 height_aligned; | ||
130 | u32 temp_size, size; | ||
131 | |||
132 | height_aligned = roundup(height, chan->height_align); | ||
133 | temp_size = bytesperline * height_aligned; | ||
134 | size = roundup(temp_size, chan->size_align); | ||
135 | |||
136 | return size; | ||
137 | } | ||
138 | |||
139 | static void tegra_channel_fmt_align(struct tegra_channel *chan, | ||
140 | const struct tegra_video_format *vfmt, | ||
141 | u32 *width, u32 *height, u32 *bytesperline) | ||
142 | { | ||
143 | unsigned int min_width; | ||
144 | unsigned int max_width; | ||
145 | unsigned int min_bpl; | ||
146 | unsigned int max_bpl; | ||
147 | unsigned int temp_width; | ||
148 | unsigned int align, fmt_align; | ||
149 | unsigned int temp_bpl; | ||
150 | unsigned int bpl; | ||
151 | unsigned int numerator, denominator; | ||
152 | const struct tegra_frac *bpp = &vfmt->bpp; | ||
153 | |||
154 | /* Init, if un-init */ | ||
155 | if (!*width || !*height) { | ||
156 | *width = chan->format.width; | ||
157 | *height = chan->format.height; | ||
158 | } | ||
159 | |||
160 | denominator = (!bpp->denominator) ? 1 : bpp->denominator; | ||
161 | numerator = (!bpp->numerator) ? 1 : bpp->numerator; | ||
162 | |||
163 | bpl = (*width * numerator) / denominator; | ||
164 | if (!*bytesperline) | ||
165 | *bytesperline = bpl; | ||
166 | |||
167 | /* The transfer alignment requirements are expressed in bytes. Compute | ||
168 | * the minimum and maximum values, clamp the requested width and convert | ||
169 | * it back to pixels. | ||
170 | * use denominator for base width alignment when >1. | ||
171 | * use bytesperline to adjust width for applicaton related requriements. | ||
172 | */ | ||
173 | fmt_align = (denominator == 1) ? numerator : 1; | ||
174 | align = lcm(chan->width_align, fmt_align); | ||
175 | min_width = roundup(TEGRA_MIN_WIDTH, align); | ||
176 | max_width = rounddown(TEGRA_MAX_WIDTH, align); | ||
177 | temp_width = roundup(bpl, align); | ||
178 | |||
179 | *width = (clamp(temp_width, min_width, max_width) * denominator) / | ||
180 | numerator; | ||
181 | *height = clamp(*height, TEGRA_MIN_HEIGHT, TEGRA_MAX_HEIGHT); | ||
182 | |||
183 | /* Clamp the requested bytes per line value. If the maximum bytes per | ||
184 | * line value is zero, the module doesn't support user configurable line | ||
185 | * sizes. Override the requested value with the minimum in that case. | ||
186 | */ | ||
187 | min_bpl = bpl; | ||
188 | max_bpl = rounddown(TEGRA_MAX_WIDTH, chan->stride_align); | ||
189 | temp_bpl = roundup(*bytesperline, chan->stride_align); | ||
190 | |||
191 | *bytesperline = clamp(temp_bpl, min_bpl, max_bpl); | ||
192 | } | ||
193 | |||
194 | static void tegra_channel_update_format(struct tegra_channel *chan, | ||
195 | u32 width, u32 height, u32 fourcc, | ||
196 | const struct tegra_frac *bpp, | ||
197 | u32 preferred_stride) | ||
198 | { | ||
199 | u32 denominator = (!bpp->denominator) ? 1 : bpp->denominator; | ||
200 | u32 numerator = (!bpp->numerator) ? 1 : bpp->numerator; | ||
201 | u32 bytesperline = (width * numerator / denominator); | ||
202 | |||
203 | chan->format.width = width; | ||
204 | chan->format.height = height; | ||
205 | chan->format.pixelformat = fourcc; | ||
206 | chan->format.bytesperline = preferred_stride ?: bytesperline; | ||
207 | |||
208 | tegra_channel_fmt_align(chan, chan->fmtinfo, | ||
209 | &chan->format.width, | ||
210 | &chan->format.height, | ||
211 | &chan->format.bytesperline); | ||
212 | |||
213 | /* Calculate the sizeimage per plane */ | ||
214 | chan->format.sizeimage = get_aligned_buffer_size(chan, | ||
215 | chan->format.bytesperline, chan->format.height); | ||
216 | |||
217 | if (fourcc == V4L2_PIX_FMT_NV16) | ||
218 | chan->format.sizeimage *= 2; | ||
219 | } | ||
220 | |||
221 | static void tegra_channel_fmts_bitmap_init(struct tegra_channel *chan) | ||
222 | { | ||
223 | int ret, pixel_format_index = 0, init_code = 0; | ||
224 | struct v4l2_subdev *subdev = chan->subdev_on_csi; | ||
225 | struct v4l2_subdev_format fmt = {}; | ||
226 | struct v4l2_subdev_mbus_code_enum code = { | ||
227 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | ||
228 | }; | ||
229 | |||
230 | bitmap_zero(chan->fmts_bitmap, MAX_FORMAT_NUM); | ||
231 | |||
232 | /* | ||
233 | * Initialize all the formats available from | ||
234 | * the sub-device and extract the corresponding | ||
235 | * index from the pre-defined video formats and initialize | ||
236 | * the channel default format with the active code | ||
237 | * Index zero as the only sub-device is sensor | ||
238 | */ | ||
239 | while (1) { | ||
240 | ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, | ||
241 | NULL, &code); | ||
242 | if (ret < 0) | ||
243 | /* no more formats */ | ||
244 | break; | ||
245 | |||
246 | pixel_format_index = | ||
247 | tegra_core_get_idx_by_code(chan, code.code, 0); | ||
248 | while (pixel_format_index >= 0) { | ||
249 | bitmap_set(chan->fmts_bitmap, pixel_format_index, 1); | ||
250 | /* Set init_code to the first matched format */ | ||
251 | if (!init_code) | ||
252 | init_code = code.code; | ||
253 | /* Look for other formats with the same mbus code */ | ||
254 | pixel_format_index = tegra_core_get_idx_by_code(chan, | ||
255 | code.code, pixel_format_index + 1); | ||
256 | } | ||
257 | |||
258 | code.index++; | ||
259 | } | ||
260 | |||
261 | if (!init_code) { | ||
262 | pixel_format_index = | ||
263 | tegra_core_get_idx_by_code(chan, TEGRA_VF_DEF, 0); | ||
264 | if (pixel_format_index >= 0) { | ||
265 | bitmap_set(chan->fmts_bitmap, pixel_format_index, 1); | ||
266 | init_code = TEGRA_VF_DEF; | ||
267 | } | ||
268 | } | ||
269 | /* Get the format based on active code of the sub-device */ | ||
270 | ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); | ||
271 | if (ret) | ||
272 | return; | ||
273 | |||
274 | /* Initiate the channel format to the first matched format */ | ||
275 | chan->fmtinfo = | ||
276 | tegra_core_get_format_by_code(chan, fmt.format.code, 0); | ||
277 | v4l2_fill_pix_format(&chan->format, &fmt.format); | ||
278 | tegra_channel_update_format(chan, chan->format.width, | ||
279 | chan->format.height, | ||
280 | chan->fmtinfo->fourcc, | ||
281 | &chan->fmtinfo->bpp, 0); | ||
282 | |||
283 | if (chan->total_ports > 1) | ||
284 | update_gang_mode(chan); | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * ----------------------------------------------------------------------------- | ||
289 | * Tegra channel frame setup and capture operations | ||
290 | * ----------------------------------------------------------------------------- | ||
291 | */ | ||
292 | |||
293 | void tegra_channel_init_ring_buffer(struct tegra_channel *chan) | ||
294 | { | ||
295 | chan->released_bufs = 0; | ||
296 | chan->num_buffers = 0; | ||
297 | chan->save_index = 0; | ||
298 | chan->free_index = 0; | ||
299 | chan->bfirst_fstart = false; | ||
300 | } | ||
301 | |||
302 | void free_ring_buffers(struct tegra_channel *chan, int frames) | ||
303 | { | ||
304 | struct vb2_v4l2_buffer *vbuf; | ||
305 | |||
306 | while (frames) { | ||
307 | vbuf = chan->buffers[chan->free_index]; | ||
308 | |||
309 | /* release one frame */ | ||
310 | vbuf->sequence = chan->sequence++; | ||
311 | vbuf->field = V4L2_FIELD_NONE; | ||
312 | vb2_set_plane_payload(&vbuf->vb2_buf, | ||
313 | 0, chan->format.sizeimage); | ||
314 | |||
315 | /* | ||
316 | * WAR to force buffer state if capture state is not good | ||
317 | * WAR - After sync point timeout or error frame capture | ||
318 | * the second buffer is intermittently frame of zeros | ||
319 | * with no error status or padding. | ||
320 | */ | ||
321 | #if 0 | ||
322 | /* This will drop the first two frames. Disable for now. */ | ||
323 | if (chan->capture_state != CAPTURE_GOOD || | ||
324 | chan->released_bufs < 2) | ||
325 | chan->buffer_state[chan->free_index] = | ||
326 | VB2_BUF_STATE_ERROR; | ||
327 | #endif | ||
328 | vb2_buffer_done(&vbuf->vb2_buf, | ||
329 | chan->buffer_state[chan->free_index++]); | ||
330 | |||
331 | if (chan->free_index >= QUEUED_BUFFERS) | ||
332 | chan->free_index = 0; | ||
333 | chan->num_buffers--; | ||
334 | chan->released_bufs++; | ||
335 | frames--; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | static void add_buffer_to_ring(struct tegra_channel *chan, | ||
340 | struct vb2_v4l2_buffer *vb) | ||
341 | { | ||
342 | /* save the buffer to the ring first */ | ||
343 | /* Mark buffer state as error before start */ | ||
344 | chan->buffer_state[chan->save_index] = VB2_BUF_STATE_ERROR; | ||
345 | chan->buffers[chan->save_index++] = vb; | ||
346 | if (chan->save_index >= QUEUED_BUFFERS) | ||
347 | chan->save_index = 0; | ||
348 | chan->num_buffers++; | ||
349 | } | ||
350 | |||
351 | static void update_state_to_buffer(struct tegra_channel *chan, int state) | ||
352 | { | ||
353 | int save_index = (chan->save_index - PREVIOUS_BUFFER_DEC_INDEX); | ||
354 | |||
355 | /* save index decrements by 2 as 3 bufs are added in ring buffer */ | ||
356 | if (save_index < 0) | ||
357 | save_index += QUEUED_BUFFERS; | ||
358 | /* update state for the previous buffer */ | ||
359 | chan->buffer_state[save_index] = state; | ||
360 | |||
361 | /* for timeout/error case update the current buffer state as well */ | ||
362 | if (chan->capture_state != CAPTURE_GOOD) | ||
363 | chan->buffer_state[chan->save_index] = state; | ||
364 | } | ||
365 | |||
366 | void tegra_channel_ring_buffer(struct tegra_channel *chan, | ||
367 | struct vb2_v4l2_buffer *vb, | ||
368 | struct timespec *ts, int state) | ||
369 | |||
370 | { | ||
371 | if (!chan->bfirst_fstart) | ||
372 | chan->bfirst_fstart = true; | ||
373 | else | ||
374 | update_state_to_buffer(chan, state); | ||
375 | |||
376 | /* Capture state is not GOOD, release all buffers and re-init state */ | ||
377 | if (chan->capture_state != CAPTURE_GOOD) { | ||
378 | free_ring_buffers(chan, chan->num_buffers); | ||
379 | tegra_channel_init_ring_buffer(chan); | ||
380 | return; | ||
381 | } else { | ||
382 | /* update time stamp of the buffer */ | ||
383 | vb->timestamp.tv_sec = ts->tv_sec; | ||
384 | vb->timestamp.tv_usec = ts->tv_nsec / NSEC_PER_USEC; | ||
385 | } | ||
386 | |||
387 | /* release buffer N at N+2 frame start event */ | ||
388 | if (chan->num_buffers >= (QUEUED_BUFFERS - 1)) | ||
389 | free_ring_buffers(chan, 1); | ||
390 | } | ||
391 | |||
392 | void tegra_channel_ec_close(struct tegra_mc_vi *vi) | ||
393 | { | ||
394 | struct tegra_channel *chan; | ||
395 | |||
396 | /* clear all channles sync point fifo context */ | ||
397 | list_for_each_entry(chan, &vi->vi_chans, list) { | ||
398 | memset(&chan->syncpoint_fifo[0], 0, TEGRA_CSI_BLOCKS); | ||
399 | } | ||
400 | } | ||
401 | |||
402 | struct tegra_channel_buffer *dequeue_buffer(struct tegra_channel *chan) | ||
403 | { | ||
404 | struct tegra_channel_buffer *buf = NULL; | ||
405 | |||
406 | spin_lock(&chan->start_lock); | ||
407 | if (list_empty(&chan->capture)) | ||
408 | goto done; | ||
409 | |||
410 | buf = list_entry(chan->capture.next, | ||
411 | struct tegra_channel_buffer, queue); | ||
412 | list_del_init(&buf->queue); | ||
413 | |||
414 | /* add dequeued buffer to the ring buffer */ | ||
415 | add_buffer_to_ring(chan, &buf->buf); | ||
416 | done: | ||
417 | spin_unlock(&chan->start_lock); | ||
418 | return buf; | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * ----------------------------------------------------------------------------- | ||
423 | * videobuf2 queue operations | ||
424 | * ----------------------------------------------------------------------------- | ||
425 | */ | ||
426 | static int | ||
427 | tegra_channel_queue_setup(struct vb2_queue *vq, const void *parg, | ||
428 | unsigned int *nbuffers, unsigned int *nplanes, | ||
429 | unsigned int sizes[], void *alloc_ctxs[]) | ||
430 | { | ||
431 | const struct v4l2_format *fmt = parg; | ||
432 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
433 | /* Make sure the image size is large enough. */ | ||
434 | if (fmt && fmt->fmt.pix.sizeimage < chan->format.sizeimage) | ||
435 | return -EINVAL; | ||
436 | |||
437 | *nplanes = 1; | ||
438 | |||
439 | sizes[0] = fmt ? fmt->fmt.pix.sizeimage : chan->format.sizeimage; | ||
440 | alloc_ctxs[0] = chan->alloc_ctx; | ||
441 | |||
442 | /* Make sure minimum number of buffers are passed */ | ||
443 | if (*nbuffers < (QUEUED_BUFFERS - 1)) | ||
444 | *nbuffers = QUEUED_BUFFERS - 1; | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int tegra_channel_buffer_prepare(struct vb2_buffer *vb) | ||
450 | { | ||
451 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | ||
452 | struct tegra_channel *chan = vb2_get_drv_priv(vb->vb2_queue); | ||
453 | struct tegra_channel_buffer *buf = to_tegra_channel_buffer(vbuf); | ||
454 | |||
455 | buf->chan = chan; | ||
456 | vb2_set_plane_payload(&vbuf->vb2_buf, 0, chan->format.sizeimage); | ||
457 | #if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) | ||
458 | buf->addr = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
459 | #endif | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static void tegra_channel_buffer_queue(struct vb2_buffer *vb) | ||
465 | { | ||
466 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | ||
467 | struct tegra_channel *chan = vb2_get_drv_priv(vb->vb2_queue); | ||
468 | struct tegra_channel_buffer *buf = to_tegra_channel_buffer(vbuf); | ||
469 | |||
470 | /* for bypass mode - do nothing */ | ||
471 | if (chan->bypass) | ||
472 | return; | ||
473 | |||
474 | /* Put buffer into the capture queue */ | ||
475 | spin_lock(&chan->start_lock); | ||
476 | list_add_tail(&buf->queue, &chan->capture); | ||
477 | spin_unlock(&chan->start_lock); | ||
478 | |||
479 | /* Wait up kthread for capture */ | ||
480 | wake_up_interruptible(&chan->start_wait); | ||
481 | } | ||
482 | |||
483 | /* Return all queued buffers back to videobuf2 */ | ||
484 | void tegra_channel_queued_buf_done(struct tegra_channel *chan, | ||
485 | enum vb2_buffer_state state) | ||
486 | { | ||
487 | struct tegra_channel_buffer *buf, *nbuf; | ||
488 | spinlock_t *lock = &chan->start_lock; | ||
489 | struct list_head *q = &chan->capture; | ||
490 | |||
491 | spin_lock(lock); | ||
492 | list_for_each_entry_safe(buf, nbuf, q, queue) { | ||
493 | vb2_buffer_done(&buf->buf.vb2_buf, state); | ||
494 | list_del(&buf->queue); | ||
495 | } | ||
496 | spin_unlock(lock); | ||
497 | } | ||
498 | |||
499 | #define __tegra_channel_device_call_subdevs_all_p(v4l2_dev, sd, cond, o,\ | ||
500 | f, args...) \ | ||
501 | ({ \ | ||
502 | long __err = 0; \ | ||
503 | long e = 0; \ | ||
504 | \ | ||
505 | list_for_each_entry((sd), &(v4l2_dev)->subdevs, list) { \ | ||
506 | if ((cond) && (sd)->ops->o && (sd)->ops->o->f) \ | ||
507 | e = (sd)->ops->o->f((sd), ##args); \ | ||
508 | if (!__err && e && e != -ENOIOCTLCMD) \ | ||
509 | __err = e; \ | ||
510 | e = 0; \ | ||
511 | } \ | ||
512 | __err; \ | ||
513 | }) | ||
514 | |||
515 | /* | ||
516 | * Call the specified callback for all subdevs matching grp_id (if 0, then | ||
517 | * match them all), errors are ignored until the end, and the first error | ||
518 | * encountered is returned. If the callback returns an error other than 0 or | ||
519 | * -ENOIOCTLCMD, then return with that error code. Note that you cannot | ||
520 | * add or delete a subdev while walking the subdevs list. | ||
521 | */ | ||
522 | #define tegra_channel_device_call_all(v4l2_dev, grpid, o, f, args...) \ | ||
523 | ({ \ | ||
524 | struct v4l2_subdev *__sd; \ | ||
525 | __tegra_channel_device_call_subdevs_all_p(v4l2_dev, __sd, \ | ||
526 | !(grpid) || __sd->grp_id == (grpid), o, f, \ | ||
527 | ##args); \ | ||
528 | }) | ||
529 | |||
530 | /* | ||
531 | * ----------------------------------------------------------------------------- | ||
532 | * subdevice set/unset operations | ||
533 | * ----------------------------------------------------------------------------- | ||
534 | */ | ||
535 | int tegra_channel_set_stream(struct tegra_channel *chan, bool on) | ||
536 | { | ||
537 | int num_sd; | ||
538 | int ret = 0; | ||
539 | |||
540 | if (atomic_read(&chan->is_streaming) == on) | ||
541 | return 0; | ||
542 | |||
543 | for (num_sd = chan->num_subdevs - 1; num_sd >= 0; num_sd--) { | ||
544 | struct v4l2_subdev *sd = chan->subdev[num_sd]; | ||
545 | int err = 0; | ||
546 | |||
547 | err = v4l2_subdev_call(sd, video, s_stream, on); | ||
548 | if (!ret && err < 0 && err != -ENOIOCTLCMD) | ||
549 | ret = err; | ||
550 | } | ||
551 | |||
552 | atomic_set(&chan->is_streaming, on); | ||
553 | |||
554 | return ret; | ||
555 | } | ||
556 | |||
557 | int tegra_channel_set_power(struct tegra_channel *chan, bool on) | ||
558 | { | ||
559 | int num_sd; | ||
560 | int ret = 0; | ||
561 | |||
562 | for (num_sd = 0; num_sd < chan->num_subdevs; num_sd++) { | ||
563 | struct v4l2_subdev *sd = chan->subdev[num_sd]; | ||
564 | int err = 0; | ||
565 | |||
566 | err = v4l2_subdev_call(sd, core, s_power, on); | ||
567 | if (!ret && err < 0 && err != -ENOIOCTLCMD) | ||
568 | ret = err; | ||
569 | } | ||
570 | |||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | static int tegra_channel_start_streaming(struct vb2_queue *vq, u32 count) | ||
575 | { | ||
576 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
577 | struct tegra_mc_vi *vi = chan->vi; | ||
578 | |||
579 | if (vi->fops) | ||
580 | return vi->fops->vi_start_streaming(vq, count); | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static void tegra_channel_stop_streaming(struct vb2_queue *vq) | ||
585 | { | ||
586 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
587 | struct tegra_mc_vi *vi = chan->vi; | ||
588 | |||
589 | if (vi->fops) | ||
590 | vi->fops->vi_stop_streaming(vq); | ||
591 | } | ||
592 | |||
593 | static const struct vb2_ops tegra_channel_queue_qops = { | ||
594 | .queue_setup = tegra_channel_queue_setup, | ||
595 | .buf_prepare = tegra_channel_buffer_prepare, | ||
596 | .buf_queue = tegra_channel_buffer_queue, | ||
597 | .wait_prepare = vb2_ops_wait_prepare, | ||
598 | .wait_finish = vb2_ops_wait_finish, | ||
599 | .start_streaming = tegra_channel_start_streaming, | ||
600 | .stop_streaming = tegra_channel_stop_streaming, | ||
601 | }; | ||
602 | |||
603 | /* ----------------------------------------------------------------------------- | ||
604 | * V4L2 ioctls | ||
605 | */ | ||
606 | |||
607 | static int | ||
608 | tegra_channel_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | ||
609 | { | ||
610 | struct v4l2_fh *vfh = file->private_data; | ||
611 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
612 | |||
613 | cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; | ||
614 | cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; | ||
615 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; | ||
616 | |||
617 | strlcpy(cap->driver, "tegra-video", sizeof(cap->driver)); | ||
618 | strlcpy(cap->card, chan->video.name, sizeof(cap->card)); | ||
619 | snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u", | ||
620 | dev_name(chan->vi->dev), chan->port[0]); | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int | ||
626 | tegra_channel_enum_framesizes(struct file *file, void *fh, | ||
627 | struct v4l2_frmsizeenum *sizes) | ||
628 | { | ||
629 | struct v4l2_fh *vfh = file->private_data; | ||
630 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
631 | struct v4l2_subdev_frame_size_enum fse = { | ||
632 | .index = sizes->index, | ||
633 | .code = sizes->pixel_format, | ||
634 | }; | ||
635 | int ret = 0; | ||
636 | |||
637 | ret = v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
638 | chan->grp_id, pad, enum_frame_size, NULL, &fse); | ||
639 | if (!ret) { | ||
640 | sizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; | ||
641 | sizes->discrete.width = fse.max_width; | ||
642 | sizes->discrete.height = fse.max_height; | ||
643 | } | ||
644 | |||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | static int | ||
649 | tegra_channel_enum_frameintervals(struct file *file, void *fh, | ||
650 | struct v4l2_frmivalenum *intervals) | ||
651 | { | ||
652 | struct v4l2_fh *vfh = file->private_data; | ||
653 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
654 | struct v4l2_subdev_frame_interval_enum fie = { | ||
655 | .index = intervals->index, | ||
656 | .code = intervals->pixel_format, | ||
657 | .width = intervals->width, | ||
658 | .height = intervals->height, | ||
659 | }; | ||
660 | int ret = 0; | ||
661 | |||
662 | ret = v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
663 | chan->grp_id, pad, enum_frame_interval, NULL, &fie); | ||
664 | |||
665 | if (!ret) { | ||
666 | intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; | ||
667 | intervals->discrete.numerator = fie.interval.numerator; | ||
668 | intervals->discrete.denominator = fie.interval.denominator; | ||
669 | } | ||
670 | |||
671 | return ret; | ||
672 | } | ||
673 | |||
674 | static int | ||
675 | tegra_channel_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) | ||
676 | { | ||
677 | struct v4l2_fh *vfh = file->private_data; | ||
678 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
679 | unsigned int index = 0, i; | ||
680 | unsigned long *fmts_bitmap = chan->fmts_bitmap; | ||
681 | |||
682 | if (f->index >= bitmap_weight(fmts_bitmap, MAX_FORMAT_NUM)) | ||
683 | return -EINVAL; | ||
684 | |||
685 | for (i = 0; i < f->index + 1; i++, index++) | ||
686 | index = find_next_bit(fmts_bitmap, MAX_FORMAT_NUM, index); | ||
687 | |||
688 | index -= 1; | ||
689 | f->pixelformat = tegra_core_get_fourcc_by_idx(chan, index); | ||
690 | tegra_core_get_description_by_idx(chan, index, f->description); | ||
691 | |||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int | ||
696 | tegra_channel_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) | ||
697 | { | ||
698 | struct v4l2_fh *vfh = file->private_data; | ||
699 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
700 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
701 | |||
702 | if (!v4l2_subdev_has_op(sd, pad, get_edid)) | ||
703 | return -ENOTTY; | ||
704 | |||
705 | return v4l2_subdev_call(sd, pad, get_edid, edid); | ||
706 | } | ||
707 | |||
708 | static int | ||
709 | tegra_channel_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) | ||
710 | { | ||
711 | struct v4l2_fh *vfh = file->private_data; | ||
712 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
713 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
714 | |||
715 | if (!v4l2_subdev_has_op(sd, pad, set_edid)) | ||
716 | return -ENOTTY; | ||
717 | |||
718 | return v4l2_subdev_call(sd, pad, set_edid, edid); | ||
719 | } | ||
720 | |||
721 | static int | ||
722 | tegra_channel_g_dv_timings(struct file *file, void *fh, | ||
723 | struct v4l2_dv_timings *timings) | ||
724 | { | ||
725 | struct v4l2_fh *vfh = file->private_data; | ||
726 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
727 | |||
728 | if (!v4l2_subdev_has_op(chan->subdev_on_csi, video, g_dv_timings)) | ||
729 | return -ENOTTY; | ||
730 | |||
731 | return v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
732 | chan->grp_id, video, g_dv_timings, timings); | ||
733 | } | ||
734 | |||
735 | static int | ||
736 | tegra_channel_s_dv_timings(struct file *file, void *fh, | ||
737 | struct v4l2_dv_timings *timings) | ||
738 | { | ||
739 | struct v4l2_fh *vfh = file->private_data; | ||
740 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
741 | struct v4l2_bt_timings *bt = &timings->bt; | ||
742 | struct v4l2_dv_timings curr_timings; | ||
743 | int ret; | ||
744 | |||
745 | if (!v4l2_subdev_has_op(chan->subdev_on_csi, video, s_dv_timings)) | ||
746 | return -ENOTTY; | ||
747 | |||
748 | ret = tegra_channel_g_dv_timings(file, fh, &curr_timings); | ||
749 | if (ret) | ||
750 | return ret; | ||
751 | |||
752 | if (v4l2_match_dv_timings(timings, &curr_timings, 0)) | ||
753 | return 0; | ||
754 | |||
755 | if (vb2_is_busy(&chan->queue)) | ||
756 | return -EBUSY; | ||
757 | |||
758 | ret = v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
759 | chan->grp_id, video, s_dv_timings, timings); | ||
760 | |||
761 | if (!ret) | ||
762 | tegra_channel_update_format(chan, bt->width, bt->height, | ||
763 | chan->fmtinfo->fourcc, &chan->fmtinfo->bpp, 0); | ||
764 | |||
765 | if (chan->total_ports > 1) | ||
766 | update_gang_mode(chan); | ||
767 | |||
768 | return ret; | ||
769 | } | ||
770 | |||
771 | static int | ||
772 | tegra_channel_query_dv_timings(struct file *file, void *fh, | ||
773 | struct v4l2_dv_timings *timings) | ||
774 | { | ||
775 | struct v4l2_fh *vfh = file->private_data; | ||
776 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
777 | |||
778 | if (!v4l2_subdev_has_op(chan->subdev_on_csi, video, query_dv_timings)) | ||
779 | return -ENOTTY; | ||
780 | |||
781 | return v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
782 | chan->grp_id, video, query_dv_timings, timings); | ||
783 | } | ||
784 | |||
785 | static int | ||
786 | tegra_channel_enum_dv_timings(struct file *file, void *fh, | ||
787 | struct v4l2_enum_dv_timings *timings) | ||
788 | { | ||
789 | struct v4l2_fh *vfh = file->private_data; | ||
790 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
791 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
792 | |||
793 | if (!v4l2_subdev_has_op(sd, pad, enum_dv_timings)) | ||
794 | return -ENOTTY; | ||
795 | |||
796 | return v4l2_subdev_call(sd, pad, enum_dv_timings, timings); | ||
797 | } | ||
798 | |||
799 | static int | ||
800 | tegra_channel_dv_timings_cap(struct file *file, void *fh, | ||
801 | struct v4l2_dv_timings_cap *cap) | ||
802 | { | ||
803 | struct v4l2_fh *vfh = file->private_data; | ||
804 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
805 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
806 | |||
807 | if (!v4l2_subdev_has_op(sd, pad, dv_timings_cap)) | ||
808 | return -ENOTTY; | ||
809 | |||
810 | return v4l2_subdev_call(sd, pad, dv_timings_cap, cap); | ||
811 | } | ||
812 | |||
813 | int tegra_channel_s_ctrl(struct v4l2_ctrl *ctrl) | ||
814 | { | ||
815 | struct tegra_channel *chan = container_of(ctrl->handler, | ||
816 | struct tegra_channel, ctrl_handler); | ||
817 | |||
818 | switch (ctrl->id) { | ||
819 | case V4L2_CID_VI_BYPASS_MODE: | ||
820 | if (switch_ctrl_qmenu[ctrl->val] == SWITCH_ON) | ||
821 | chan->bypass = true; | ||
822 | else if (chan->vi->bypass) { | ||
823 | dev_dbg(&chan->video.dev, | ||
824 | "can't disable bypass mode\n"); | ||
825 | dev_dbg(&chan->video.dev, | ||
826 | "because the VI/CSI is in bypass mode\n"); | ||
827 | chan->bypass = true; | ||
828 | } else | ||
829 | chan->bypass = false; | ||
830 | break; | ||
831 | case V4L2_CID_OVERRIDE_ENABLE: | ||
832 | { | ||
833 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
834 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
835 | struct camera_common_data *s_data = | ||
836 | to_camera_common_data(client); | ||
837 | |||
838 | if (!i2c_get_clientdata(client)) { | ||
839 | dev_info(&chan->video.dev, | ||
840 | "i2c_client drvdata is NULL\n"); | ||
841 | break; | ||
842 | } | ||
843 | if (!s_data) | ||
844 | break; | ||
845 | if (switch_ctrl_qmenu[ctrl->val] == SWITCH_ON) { | ||
846 | s_data->override_enable = true; | ||
847 | dev_dbg(&chan->video.dev, | ||
848 | "enable override control\n"); | ||
849 | } else { | ||
850 | s_data->override_enable = false; | ||
851 | dev_dbg(&chan->video.dev, | ||
852 | "disable override control\n"); | ||
853 | } | ||
854 | } | ||
855 | break; | ||
856 | case V4L2_CID_VI_HEIGHT_ALIGN: | ||
857 | chan->height_align = ctrl->val; | ||
858 | tegra_channel_update_format(chan, chan->format.width, | ||
859 | chan->format.height, | ||
860 | chan->format.pixelformat, | ||
861 | &chan->fmtinfo->bpp, 0); | ||
862 | break; | ||
863 | case V4L2_CID_VI_SIZE_ALIGN: | ||
864 | chan->size_align = size_align_ctrl_qmenu[ctrl->val]; | ||
865 | tegra_channel_update_format(chan, chan->format.width, | ||
866 | chan->format.height, | ||
867 | chan->format.pixelformat, | ||
868 | &chan->fmtinfo->bpp, 0); | ||
869 | break; | ||
870 | default: | ||
871 | dev_err(&chan->video.dev, "%s:Not valid ctrl\n", __func__); | ||
872 | return -EINVAL; | ||
873 | } | ||
874 | |||
875 | return 0; | ||
876 | } | ||
877 | |||
878 | static const struct v4l2_ctrl_ops channel_ctrl_ops = { | ||
879 | .s_ctrl = tegra_channel_s_ctrl, | ||
880 | }; | ||
881 | |||
882 | static const struct v4l2_ctrl_config common_custom_ctrls[] = { | ||
883 | { | ||
884 | .ops = &channel_ctrl_ops, | ||
885 | .id = V4L2_CID_VI_BYPASS_MODE, | ||
886 | .name = "Bypass Mode", | ||
887 | .type = V4L2_CTRL_TYPE_INTEGER_MENU, | ||
888 | .def = 0, | ||
889 | .min = 0, | ||
890 | .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, | ||
891 | .menu_skip_mask = 0, | ||
892 | .qmenu_int = switch_ctrl_qmenu, | ||
893 | }, | ||
894 | { | ||
895 | .ops = &channel_ctrl_ops, | ||
896 | .id = V4L2_CID_OVERRIDE_ENABLE, | ||
897 | .name = "Override Enable", | ||
898 | .type = V4L2_CTRL_TYPE_INTEGER_MENU, | ||
899 | .def = 0, | ||
900 | .min = 0, | ||
901 | .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, | ||
902 | .menu_skip_mask = 0, | ||
903 | .qmenu_int = switch_ctrl_qmenu, | ||
904 | }, | ||
905 | { | ||
906 | .ops = &channel_ctrl_ops, | ||
907 | .id = V4L2_CID_VI_HEIGHT_ALIGN, | ||
908 | .name = "Height Align", | ||
909 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
910 | .min = 1, | ||
911 | .max = 16, | ||
912 | .step = 1, | ||
913 | .def = 1, | ||
914 | }, | ||
915 | { | ||
916 | .ops = &channel_ctrl_ops, | ||
917 | .id = V4L2_CID_VI_SIZE_ALIGN, | ||
918 | .name = "Size Align", | ||
919 | .type = V4L2_CTRL_TYPE_INTEGER_MENU, | ||
920 | .def = 0, | ||
921 | .min = 0, | ||
922 | .max = ARRAY_SIZE(size_align_ctrl_qmenu) - 1, | ||
923 | .menu_skip_mask = 0, | ||
924 | .qmenu_int = size_align_ctrl_qmenu, | ||
925 | }, | ||
926 | }; | ||
927 | |||
928 | static int tegra_channel_setup_controls(struct tegra_channel *chan) | ||
929 | { | ||
930 | int num_sd = 0; | ||
931 | struct v4l2_subdev *sd = NULL; | ||
932 | struct tegra_mc_vi *vi = chan->vi; | ||
933 | int i; | ||
934 | |||
935 | /* Initialize the subdev and controls here at first open */ | ||
936 | sd = chan->subdev[num_sd]; | ||
937 | while ((sd = chan->subdev[num_sd++]) && | ||
938 | (num_sd <= chan->num_subdevs)) { | ||
939 | /* Add control handler for the subdevice */ | ||
940 | v4l2_ctrl_add_handler(&chan->ctrl_handler, | ||
941 | sd->ctrl_handler, NULL); | ||
942 | if (chan->ctrl_handler.error) | ||
943 | dev_err(chan->vi->dev, | ||
944 | "Failed to add sub-device controls\n"); | ||
945 | } | ||
946 | |||
947 | /* Add new custom controls */ | ||
948 | for (i = 0; i < ARRAY_SIZE(common_custom_ctrls); i++) { | ||
949 | /* don't create override control for pg mode */ | ||
950 | if (common_custom_ctrls[i].id == V4L2_CID_OVERRIDE_ENABLE && | ||
951 | chan->pg_mode) | ||
952 | continue; | ||
953 | v4l2_ctrl_new_custom(&chan->ctrl_handler, | ||
954 | &common_custom_ctrls[i], NULL); | ||
955 | if (chan->ctrl_handler.error) { | ||
956 | dev_err(chan->vi->dev, | ||
957 | "Failed to add %s ctrl\n", | ||
958 | common_custom_ctrls[i].name); | ||
959 | return chan->ctrl_handler.error; | ||
960 | } | ||
961 | } | ||
962 | |||
963 | vi->fops->vi_add_ctrls(chan); | ||
964 | |||
965 | if (chan->pg_mode) { | ||
966 | v4l2_ctrl_add_handler(&chan->ctrl_handler, | ||
967 | &chan->vi->ctrl_handler, NULL); | ||
968 | if (chan->ctrl_handler.error) | ||
969 | dev_err(chan->vi->dev, | ||
970 | "Failed to add VI controls\n"); | ||
971 | } | ||
972 | |||
973 | /* setup the controls */ | ||
974 | return v4l2_ctrl_handler_setup(&chan->ctrl_handler); | ||
975 | } | ||
976 | |||
977 | int tegra_channel_init_subdevices(struct tegra_channel *chan) | ||
978 | { | ||
979 | struct media_entity *entity; | ||
980 | struct media_pad *pad; | ||
981 | struct v4l2_subdev *sd; | ||
982 | int index = 0; | ||
983 | int num_sd = 0; | ||
984 | int grp_id = chan->pg_mode ? (TPG_CSI_GROUP_ID + chan->port[0] + 1) | ||
985 | : chan->port[0] + 1; | ||
986 | |||
987 | /* set_stream of CSI */ | ||
988 | pad = media_entity_remote_pad(&chan->pad); | ||
989 | if (!pad) | ||
990 | return -ENODEV; | ||
991 | |||
992 | entity = pad->entity; | ||
993 | sd = media_entity_to_v4l2_subdev(entity); | ||
994 | v4l2_set_subdev_hostdata(sd, chan); | ||
995 | chan->subdev[num_sd++] = sd; | ||
996 | /* Add subdev name to this video dev name with vi-output tag*/ | ||
997 | snprintf(chan->video.name, sizeof(chan->video.name), "%s, %s", | ||
998 | "vi-output", sd->name); | ||
999 | sd->grp_id = grp_id; | ||
1000 | chan->grp_id = grp_id; | ||
1001 | index = pad->index - 1; | ||
1002 | while (index >= 0) { | ||
1003 | pad = &entity->pads[index]; | ||
1004 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
1005 | break; | ||
1006 | |||
1007 | pad = media_entity_remote_pad(pad); | ||
1008 | if (pad == NULL || | ||
1009 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
1010 | break; | ||
1011 | |||
1012 | if (num_sd >= MAX_SUBDEVICES) | ||
1013 | break; | ||
1014 | |||
1015 | entity = pad->entity; | ||
1016 | sd = media_entity_to_v4l2_subdev(entity); | ||
1017 | v4l2_set_subdev_hostdata(sd, chan); | ||
1018 | sd->grp_id = grp_id; | ||
1019 | chan->subdev[num_sd++] = sd; | ||
1020 | /* Add subdev name to this video dev name with vi-output tag*/ | ||
1021 | snprintf(chan->video.name, sizeof(chan->video.name), "%s, %s", | ||
1022 | "vi-output", sd->name); | ||
1023 | |||
1024 | index = pad->index - 1; | ||
1025 | } | ||
1026 | chan->num_subdevs = num_sd; | ||
1027 | /* | ||
1028 | * Each CSI channel has only one final remote source, | ||
1029 | * Mark that subdev as subdev_on_csi | ||
1030 | */ | ||
1031 | chan->subdev_on_csi = sd; | ||
1032 | |||
1033 | /* initialize the available formats */ | ||
1034 | if (chan->num_subdevs) | ||
1035 | tegra_channel_fmts_bitmap_init(chan); | ||
1036 | |||
1037 | return tegra_channel_setup_controls(chan); | ||
1038 | } | ||
1039 | |||
1040 | static int | ||
1041 | tegra_channel_get_format(struct file *file, void *fh, | ||
1042 | struct v4l2_format *format) | ||
1043 | { | ||
1044 | struct v4l2_fh *vfh = file->private_data; | ||
1045 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1046 | struct v4l2_pix_format *pix = &format->fmt.pix; | ||
1047 | |||
1048 | *pix = chan->format; | ||
1049 | |||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | static int | ||
1054 | __tegra_channel_try_format(struct tegra_channel *chan, | ||
1055 | struct v4l2_pix_format *pix) | ||
1056 | { | ||
1057 | const struct tegra_video_format *vfmt; | ||
1058 | struct v4l2_subdev_format fmt; | ||
1059 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
1060 | int ret = 0; | ||
1061 | |||
1062 | /* Use the channel format if pixformat is not supported */ | ||
1063 | vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); | ||
1064 | if (!vfmt) { | ||
1065 | pix->pixelformat = chan->format.pixelformat; | ||
1066 | vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); | ||
1067 | } | ||
1068 | |||
1069 | fmt.which = V4L2_SUBDEV_FORMAT_TRY; | ||
1070 | fmt.pad = 0; | ||
1071 | v4l2_fill_mbus_format(&fmt.format, pix, vfmt->code); | ||
1072 | |||
1073 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); | ||
1074 | if (ret == -ENOIOCTLCMD) | ||
1075 | return -ENOTTY; | ||
1076 | |||
1077 | v4l2_fill_pix_format(pix, &fmt.format); | ||
1078 | |||
1079 | tegra_channel_fmt_align(chan, vfmt, | ||
1080 | &pix->width, &pix->height, &pix->bytesperline); | ||
1081 | pix->sizeimage = get_aligned_buffer_size(chan, | ||
1082 | pix->bytesperline, pix->height); | ||
1083 | if (chan->fmtinfo->fourcc == V4L2_PIX_FMT_NV16) | ||
1084 | pix->sizeimage *= 2; | ||
1085 | |||
1086 | return ret; | ||
1087 | } | ||
1088 | |||
1089 | static int | ||
1090 | tegra_channel_try_format(struct file *file, void *fh, | ||
1091 | struct v4l2_format *format) | ||
1092 | { | ||
1093 | struct v4l2_fh *vfh = file->private_data; | ||
1094 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1095 | |||
1096 | return __tegra_channel_try_format(chan, &format->fmt.pix); | ||
1097 | } | ||
1098 | |||
1099 | static int | ||
1100 | __tegra_channel_set_format(struct tegra_channel *chan, | ||
1101 | struct v4l2_pix_format *pix) | ||
1102 | { | ||
1103 | const struct tegra_video_format *vfmt; | ||
1104 | struct v4l2_subdev_format fmt; | ||
1105 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
1106 | int ret = 0; | ||
1107 | |||
1108 | vfmt = tegra_core_get_format_by_fourcc(chan, pix->pixelformat); | ||
1109 | |||
1110 | fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1111 | fmt.pad = 0; | ||
1112 | v4l2_fill_mbus_format(&fmt.format, pix, vfmt->code); | ||
1113 | |||
1114 | ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); | ||
1115 | if (ret == -ENOIOCTLCMD) | ||
1116 | return -ENOTTY; | ||
1117 | |||
1118 | v4l2_fill_pix_format(pix, &fmt.format); | ||
1119 | |||
1120 | if (!ret) { | ||
1121 | chan->format = *pix; | ||
1122 | chan->fmtinfo = vfmt; | ||
1123 | tegra_channel_update_format(chan, pix->width, | ||
1124 | pix->height, vfmt->fourcc, &vfmt->bpp, | ||
1125 | pix->bytesperline); | ||
1126 | |||
1127 | *pix = chan->format; | ||
1128 | |||
1129 | if (chan->total_ports > 1) | ||
1130 | update_gang_mode(chan); | ||
1131 | } | ||
1132 | |||
1133 | return ret; | ||
1134 | } | ||
1135 | |||
1136 | static int | ||
1137 | tegra_channel_set_format(struct file *file, void *fh, | ||
1138 | struct v4l2_format *format) | ||
1139 | { | ||
1140 | struct v4l2_fh *vfh = file->private_data; | ||
1141 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1142 | int ret = 0; | ||
1143 | |||
1144 | /* get the suppod format by try_fmt */ | ||
1145 | ret = __tegra_channel_try_format(chan, &format->fmt.pix); | ||
1146 | if (ret) | ||
1147 | return ret; | ||
1148 | |||
1149 | if (vb2_is_busy(&chan->queue)) | ||
1150 | return -EBUSY; | ||
1151 | |||
1152 | return __tegra_channel_set_format(chan, &format->fmt.pix); | ||
1153 | } | ||
1154 | |||
1155 | static int tegra_channel_subscribe_event(struct v4l2_fh *fh, | ||
1156 | const struct v4l2_event_subscription *sub) | ||
1157 | { | ||
1158 | switch (sub->type) { | ||
1159 | case V4L2_EVENT_SOURCE_CHANGE: | ||
1160 | return v4l2_event_subscribe(fh, sub, 4, NULL); | ||
1161 | } | ||
1162 | return v4l2_ctrl_subscribe_event(fh, sub); | ||
1163 | } | ||
1164 | |||
1165 | static int | ||
1166 | tegra_channel_enum_input(struct file *file, void *fh, struct v4l2_input *inp) | ||
1167 | { | ||
1168 | struct v4l2_fh *vfh = file->private_data; | ||
1169 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1170 | struct v4l2_subdev *sd_on_csi = chan->subdev_on_csi; | ||
1171 | int ret; | ||
1172 | |||
1173 | if (inp->index) | ||
1174 | return -EINVAL; | ||
1175 | |||
1176 | ret = v4l2_device_call_until_err(chan->video.v4l2_dev, | ||
1177 | chan->grp_id, video, g_input_status, &inp->status); | ||
1178 | |||
1179 | if (ret == -ENODEV || sd_on_csi == NULL) | ||
1180 | return -ENODEV; | ||
1181 | |||
1182 | inp->type = V4L2_INPUT_TYPE_CAMERA; | ||
1183 | if (v4l2_subdev_has_op(sd_on_csi, video, s_dv_timings)) { | ||
1184 | inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; | ||
1185 | snprintf(inp->name, | ||
1186 | sizeof(inp->name), "HDMI %u", | ||
1187 | chan->port[0]); | ||
1188 | } else | ||
1189 | snprintf(inp->name, | ||
1190 | sizeof(inp->name), "Camera %u", | ||
1191 | chan->port[0]); | ||
1192 | |||
1193 | return ret; | ||
1194 | } | ||
1195 | |||
1196 | static int tegra_channel_g_input(struct file *file, void *priv, unsigned int *i) | ||
1197 | { | ||
1198 | *i = 0; | ||
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1202 | static int tegra_channel_s_input(struct file *file, void *priv, unsigned int i) | ||
1203 | { | ||
1204 | if (i > 0) | ||
1205 | return -EINVAL; | ||
1206 | return 0; | ||
1207 | } | ||
1208 | |||
1209 | static int tegra_channel_log_status(struct file *file, void *priv) | ||
1210 | { | ||
1211 | struct v4l2_fh *vfh = file->private_data; | ||
1212 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1213 | |||
1214 | v4l2_device_call_all(chan->video.v4l2_dev, | ||
1215 | chan->grp_id, core, log_status); | ||
1216 | return 0; | ||
1217 | } | ||
1218 | |||
1219 | static long tegra_channel_default_ioctl(struct file *file, void *fh, | ||
1220 | bool use_prio, unsigned int cmd, void *arg) | ||
1221 | { | ||
1222 | struct v4l2_fh *vfh = file->private_data; | ||
1223 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
1224 | struct tegra_mc_vi *vi = chan->vi; | ||
1225 | long ret = 0; | ||
1226 | |||
1227 | if (vi->fops && vi->fops->vi_default_ioctl) | ||
1228 | ret = vi->fops->vi_default_ioctl(file, fh, use_prio, cmd, arg); | ||
1229 | |||
1230 | return ret; | ||
1231 | } | ||
1232 | |||
1233 | #ifdef CONFIG_COMPAT | ||
1234 | static long tegra_channel_compat_ioctl(struct file *filp, | ||
1235 | unsigned int cmd, unsigned long arg) | ||
1236 | { | ||
1237 | struct video_device *vdev = video_devdata(filp); | ||
1238 | int ret = -ENODEV; | ||
1239 | |||
1240 | if (vdev->fops->unlocked_ioctl) { | ||
1241 | struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); | ||
1242 | |||
1243 | if (lock && mutex_lock_interruptible(lock)) | ||
1244 | return -ERESTARTSYS; | ||
1245 | if (video_is_registered(vdev)) | ||
1246 | ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); | ||
1247 | if (lock) | ||
1248 | mutex_unlock(lock); | ||
1249 | } else | ||
1250 | ret = -ENOTTY; | ||
1251 | |||
1252 | return ret; | ||
1253 | } | ||
1254 | #endif | ||
1255 | |||
1256 | static const struct v4l2_ioctl_ops tegra_channel_ioctl_ops = { | ||
1257 | .vidioc_querycap = tegra_channel_querycap, | ||
1258 | .vidioc_enum_framesizes = tegra_channel_enum_framesizes, | ||
1259 | .vidioc_enum_frameintervals = tegra_channel_enum_frameintervals, | ||
1260 | .vidioc_enum_fmt_vid_cap = tegra_channel_enum_format, | ||
1261 | .vidioc_g_fmt_vid_cap = tegra_channel_get_format, | ||
1262 | .vidioc_s_fmt_vid_cap = tegra_channel_set_format, | ||
1263 | .vidioc_try_fmt_vid_cap = tegra_channel_try_format, | ||
1264 | .vidioc_reqbufs = vb2_ioctl_reqbufs, | ||
1265 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
1266 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
1267 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
1268 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
1269 | .vidioc_expbuf = vb2_ioctl_expbuf, | ||
1270 | .vidioc_streamon = vb2_ioctl_streamon, | ||
1271 | .vidioc_streamoff = vb2_ioctl_streamoff, | ||
1272 | .vidioc_g_edid = tegra_channel_g_edid, | ||
1273 | .vidioc_s_edid = tegra_channel_s_edid, | ||
1274 | .vidioc_s_dv_timings = tegra_channel_s_dv_timings, | ||
1275 | .vidioc_g_dv_timings = tegra_channel_g_dv_timings, | ||
1276 | .vidioc_query_dv_timings = tegra_channel_query_dv_timings, | ||
1277 | .vidioc_enum_dv_timings = tegra_channel_enum_dv_timings, | ||
1278 | .vidioc_dv_timings_cap = tegra_channel_dv_timings_cap, | ||
1279 | .vidioc_subscribe_event = tegra_channel_subscribe_event, | ||
1280 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | ||
1281 | .vidioc_enum_input = tegra_channel_enum_input, | ||
1282 | .vidioc_g_input = tegra_channel_g_input, | ||
1283 | .vidioc_s_input = tegra_channel_s_input, | ||
1284 | .vidioc_log_status = tegra_channel_log_status, | ||
1285 | .vidioc_default = tegra_channel_default_ioctl, | ||
1286 | }; | ||
1287 | |||
1288 | static int tegra_channel_close(struct file *fp); | ||
1289 | static int tegra_channel_open(struct file *fp) | ||
1290 | { | ||
1291 | int ret; | ||
1292 | struct video_device *vdev = video_devdata(fp); | ||
1293 | struct tegra_channel *chan = video_get_drvdata(vdev); | ||
1294 | #ifdef T210 | ||
1295 | struct vi *tegra_vi; | ||
1296 | #endif | ||
1297 | struct tegra_mc_vi *vi; | ||
1298 | struct tegra_csi_device *csi; | ||
1299 | |||
1300 | mutex_lock(&chan->video_lock); | ||
1301 | ret = v4l2_fh_open(fp); | ||
1302 | if (ret || !v4l2_fh_is_singular_file(fp)) { | ||
1303 | mutex_unlock(&chan->video_lock); | ||
1304 | return ret; | ||
1305 | } | ||
1306 | |||
1307 | if (chan->subdev[0] == NULL) { | ||
1308 | ret = -ENODEV; | ||
1309 | goto fail; | ||
1310 | } | ||
1311 | |||
1312 | vi = chan->vi; | ||
1313 | #ifdef T210 | ||
1314 | tegra_vi = vi->vi; | ||
1315 | #endif | ||
1316 | csi = vi->csi; | ||
1317 | |||
1318 | /* The first open then turn on power */ | ||
1319 | if (vi->fops) | ||
1320 | ret = vi->fops->vi_power_on(chan); | ||
1321 | if (ret < 0) | ||
1322 | goto fail; | ||
1323 | |||
1324 | chan->fh = (struct v4l2_fh *)fp->private_data; | ||
1325 | |||
1326 | mutex_unlock(&chan->video_lock); | ||
1327 | return 0; | ||
1328 | |||
1329 | fail: | ||
1330 | tegra_channel_close(fp); | ||
1331 | mutex_unlock(&chan->video_lock); | ||
1332 | return ret; | ||
1333 | } | ||
1334 | |||
1335 | static int tegra_channel_close(struct file *fp) | ||
1336 | { | ||
1337 | int ret = 0; | ||
1338 | struct video_device *vdev = video_devdata(fp); | ||
1339 | struct tegra_channel *chan = video_get_drvdata(vdev); | ||
1340 | struct tegra_mc_vi *vi = chan->vi; | ||
1341 | bool is_singular; | ||
1342 | |||
1343 | mutex_lock(&chan->video_lock); | ||
1344 | is_singular = v4l2_fh_is_singular_file(fp); | ||
1345 | ret = _vb2_fop_release(fp, NULL); | ||
1346 | |||
1347 | if (!is_singular) { | ||
1348 | mutex_unlock(&chan->video_lock); | ||
1349 | return ret; | ||
1350 | } | ||
1351 | vi->fops->vi_power_off(chan); | ||
1352 | |||
1353 | mutex_unlock(&chan->video_lock); | ||
1354 | return ret; | ||
1355 | } | ||
1356 | |||
1357 | /* ----------------------------------------------------------------------------- | ||
1358 | * V4L2 file operations | ||
1359 | */ | ||
1360 | static const struct v4l2_file_operations tegra_channel_fops = { | ||
1361 | .owner = THIS_MODULE, | ||
1362 | .unlocked_ioctl = video_ioctl2, | ||
1363 | #ifdef CONFIG_COMPAT | ||
1364 | .compat_ioctl32 = tegra_channel_compat_ioctl, | ||
1365 | #endif | ||
1366 | .open = tegra_channel_open, | ||
1367 | .release = tegra_channel_close, | ||
1368 | .read = vb2_fop_read, | ||
1369 | .poll = vb2_fop_poll, | ||
1370 | .mmap = vb2_fop_mmap, | ||
1371 | }; | ||
1372 | |||
1373 | static int tegra_channel_csi_init(struct tegra_channel *chan) | ||
1374 | { | ||
1375 | int numlanes = 0; | ||
1376 | int idx = 0; | ||
1377 | struct tegra_mc_vi *vi = chan->vi; | ||
1378 | int ret = 0; | ||
1379 | |||
1380 | chan->gang_mode = CAMERA_NO_GANG_MODE; | ||
1381 | chan->total_ports = 0; | ||
1382 | memset(&chan->port[0], INVALID_CSI_PORT, TEGRA_CSI_BLOCKS); | ||
1383 | memset(&chan->syncpoint_fifo[0], 0, TEGRA_CSI_BLOCKS); | ||
1384 | if (chan->pg_mode) { | ||
1385 | /* If VI has 4 existing channels, chan->id will start | ||
1386 | * from 4 for the first TPG channel, which uses PORT_A(0). | ||
1387 | * To get the correct PORT number, subtract existing number of | ||
1388 | * channels from chan->id. | ||
1389 | */ | ||
1390 | chan->port[0] = chan->id - vi->num_channels; | ||
1391 | WARN_ON(chan->port[0] > TPG_CHANNELS); | ||
1392 | chan->numlanes = 2; | ||
1393 | } else { | ||
1394 | ret = tegra_vi_get_port_info(chan, vi->dev->of_node, chan->id); | ||
1395 | if (ret) { | ||
1396 | dev_err(vi->dev, "%s:Fail to parse port info\n", | ||
1397 | __func__); | ||
1398 | return ret; | ||
1399 | } | ||
1400 | } | ||
1401 | |||
1402 | for (idx = 0; csi_port_is_valid(chan->port[idx]); idx++) { | ||
1403 | chan->total_ports++; | ||
1404 | numlanes = chan->numlanes - (idx * MAX_CSI_BLOCK_LANES); | ||
1405 | numlanes = numlanes > MAX_CSI_BLOCK_LANES ? | ||
1406 | MAX_CSI_BLOCK_LANES : numlanes; | ||
1407 | /* maximum of 4 lanes are present per CSI block */ | ||
1408 | chan->csibase[idx] = vi->iomem + | ||
1409 | TEGRA_VI_CSI_BASE(chan->port[idx]); | ||
1410 | #ifdef T210 | ||
1411 | set_csi_portinfo(vi->csi, chan->port[idx], numlanes); | ||
1412 | #endif | ||
1413 | } | ||
1414 | /* based on gang mode valid ports will be updated - set default to 1 */ | ||
1415 | chan->valid_ports = chan->total_ports ? 1 : 0; | ||
1416 | return ret; | ||
1417 | } | ||
1418 | |||
1419 | int tegra_channel_init(struct tegra_channel *chan) | ||
1420 | { | ||
1421 | int ret; | ||
1422 | struct tegra_mc_vi *vi = chan->vi; | ||
1423 | |||
1424 | #ifdef T210 | ||
1425 | chan->fops = vi->vi->data->channel_fops; | ||
1426 | #endif | ||
1427 | ret = tegra_channel_csi_init(chan); | ||
1428 | if (ret) | ||
1429 | return ret; | ||
1430 | |||
1431 | chan->width_align = TEGRA_WIDTH_ALIGNMENT; | ||
1432 | chan->stride_align = TEGRA_STRIDE_ALIGNMENT; | ||
1433 | chan->num_subdevs = 0; | ||
1434 | mutex_init(&chan->video_lock); | ||
1435 | INIT_LIST_HEAD(&chan->capture); | ||
1436 | init_waitqueue_head(&chan->start_wait); | ||
1437 | spin_lock_init(&chan->start_lock); | ||
1438 | mutex_init(&chan->stop_kthread_lock); | ||
1439 | atomic_set(&chan->is_streaming, DISABLE); | ||
1440 | spin_lock_init(&chan->capture_state_lock); | ||
1441 | |||
1442 | /* Init video format */ | ||
1443 | vi->fops->vi_init_video_formats(chan); | ||
1444 | chan->fmtinfo = tegra_core_get_default_format(); | ||
1445 | tegra_channel_update_format(chan, TEGRA_DEF_WIDTH, | ||
1446 | TEGRA_DEF_HEIGHT, | ||
1447 | chan->fmtinfo->fourcc, | ||
1448 | &chan->fmtinfo->bpp, 0); | ||
1449 | |||
1450 | chan->buffer_offset[0] = 0; | ||
1451 | |||
1452 | /* Initialize the media entity... */ | ||
1453 | chan->pad.flags = MEDIA_PAD_FL_SINK; | ||
1454 | |||
1455 | ret = media_entity_init(&chan->video.entity, 1, &chan->pad, 0); | ||
1456 | if (ret < 0) { | ||
1457 | dev_err(&chan->video.dev, "failed to init video entity\n"); | ||
1458 | return ret; | ||
1459 | } | ||
1460 | |||
1461 | /* init control handler */ | ||
1462 | ret = v4l2_ctrl_handler_init(&chan->ctrl_handler, MAX_CID_CONTROLS); | ||
1463 | if (chan->ctrl_handler.error) { | ||
1464 | dev_err(&chan->video.dev, "failed to init control handler\n"); | ||
1465 | goto ctrl_init_error; | ||
1466 | } | ||
1467 | |||
1468 | /* init video node... */ | ||
1469 | chan->video.fops = &tegra_channel_fops; | ||
1470 | chan->video.v4l2_dev = &vi->v4l2_dev; | ||
1471 | chan->video.queue = &chan->queue; | ||
1472 | snprintf(chan->video.name, sizeof(chan->video.name), "%s-%s-%u", | ||
1473 | dev_name(vi->dev), chan->pg_mode ? "tpg" : "output", | ||
1474 | chan->port[0]); | ||
1475 | chan->video.vfl_type = VFL_TYPE_GRABBER; | ||
1476 | chan->video.vfl_dir = VFL_DIR_RX; | ||
1477 | chan->video.release = video_device_release_empty; | ||
1478 | chan->video.ioctl_ops = &tegra_channel_ioctl_ops; | ||
1479 | chan->video.ctrl_handler = &chan->ctrl_handler; | ||
1480 | chan->video.lock = &chan->video_lock; | ||
1481 | |||
1482 | set_bit(_IOC_NR(VIDIOC_G_PRIORITY), chan->video.valid_ioctls); | ||
1483 | set_bit(_IOC_NR(VIDIOC_S_PRIORITY), chan->video.valid_ioctls); | ||
1484 | |||
1485 | video_set_drvdata(&chan->video, chan); | ||
1486 | |||
1487 | #if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) | ||
1488 | /* get the buffers queue... */ | ||
1489 | chan->alloc_ctx = vb2_dma_contig_init_ctx(chan->vi->dev); | ||
1490 | if (IS_ERR(chan->alloc_ctx)) { | ||
1491 | dev_err(chan->vi->dev, "failed to init vb2 buffer\n"); | ||
1492 | ret = -ENOMEM; | ||
1493 | goto vb2_init_error; | ||
1494 | } | ||
1495 | #endif | ||
1496 | |||
1497 | chan->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1498 | chan->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ | VB2_USERPTR; | ||
1499 | chan->queue.lock = &chan->video_lock; | ||
1500 | chan->queue.drv_priv = chan; | ||
1501 | chan->queue.buf_struct_size = sizeof(struct tegra_channel_buffer); | ||
1502 | chan->queue.ops = &tegra_channel_queue_qops; | ||
1503 | #if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) | ||
1504 | chan->queue.mem_ops = &vb2_dma_contig_memops; | ||
1505 | #endif | ||
1506 | chan->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | ||
1507 | | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; | ||
1508 | ret = vb2_queue_init(&chan->queue); | ||
1509 | if (ret < 0) { | ||
1510 | dev_err(chan->vi->dev, "failed to initialize VB2 queue\n"); | ||
1511 | goto vb2_queue_error; | ||
1512 | } | ||
1513 | |||
1514 | chan->init_done = true; | ||
1515 | return 0; | ||
1516 | |||
1517 | vb2_queue_error: | ||
1518 | #if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) | ||
1519 | vb2_dma_contig_cleanup_ctx(chan->alloc_ctx); | ||
1520 | vb2_init_error: | ||
1521 | #endif | ||
1522 | v4l2_ctrl_handler_free(&chan->ctrl_handler); | ||
1523 | ctrl_init_error: | ||
1524 | media_entity_cleanup(&chan->video.entity); | ||
1525 | return ret; | ||
1526 | } | ||
1527 | |||
1528 | int tegra_channel_cleanup(struct tegra_channel *chan) | ||
1529 | { | ||
1530 | v4l2_ctrl_handler_free(&chan->ctrl_handler); | ||
1531 | vb2_queue_release(&chan->queue); | ||
1532 | #if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) | ||
1533 | vb2_dma_contig_cleanup_ctx(chan->alloc_ctx); | ||
1534 | #endif | ||
1535 | |||
1536 | media_entity_cleanup(&chan->video.entity); | ||
1537 | |||
1538 | return 0; | ||
1539 | } | ||
1540 | |||
1541 | int tegra_vi_channels_register(struct tegra_mc_vi *vi) | ||
1542 | { | ||
1543 | int ret = 0; | ||
1544 | struct tegra_channel *it; | ||
1545 | int count = 0; | ||
1546 | |||
1547 | list_for_each_entry(it, &vi->vi_chans, list) { | ||
1548 | if (!it->init_done) | ||
1549 | continue; | ||
1550 | ret = video_register_device(&it->video, VFL_TYPE_GRABBER, -1); | ||
1551 | if (ret < 0) { | ||
1552 | dev_err(&it->video.dev, "failed to register %s\n", | ||
1553 | it->video.name); | ||
1554 | continue; | ||
1555 | } | ||
1556 | count++; | ||
1557 | } | ||
1558 | |||
1559 | if (count == 0) { | ||
1560 | dev_err(vi->dev, "all channel register failed\n"); | ||
1561 | return ret; | ||
1562 | } | ||
1563 | |||
1564 | return 0; | ||
1565 | } | ||
1566 | |||
1567 | void tegra_vi_channels_unregister(struct tegra_mc_vi *vi) | ||
1568 | { | ||
1569 | struct tegra_channel *it; | ||
1570 | |||
1571 | list_for_each_entry(it, &vi->vi_chans, list) { | ||
1572 | if (it->video.cdev != NULL) | ||
1573 | video_unregister_device(&it->video); | ||
1574 | } | ||
1575 | } | ||
1576 | |||
1577 | int tegra_vi_channels_init(struct tegra_mc_vi *vi) | ||
1578 | { | ||
1579 | int ret = 0; | ||
1580 | struct tegra_channel *it; | ||
1581 | int count = 0; | ||
1582 | |||
1583 | list_for_each_entry(it, &vi->vi_chans, list) { | ||
1584 | it->vi = vi; | ||
1585 | ret = tegra_channel_init(it); | ||
1586 | if (ret < 0) { | ||
1587 | dev_err(vi->dev, "channel init failed\n"); | ||
1588 | continue; | ||
1589 | } | ||
1590 | count++; | ||
1591 | } | ||
1592 | |||
1593 | if (count == 0) { | ||
1594 | dev_err(vi->dev, "all channel init failed\n"); | ||
1595 | return ret; | ||
1596 | } | ||
1597 | |||
1598 | return 0; | ||
1599 | } | ||
1600 | EXPORT_SYMBOL(tegra_vi_channels_init); | ||
1601 | |||
1602 | int tegra_vi_channels_cleanup(struct tegra_mc_vi *vi) | ||
1603 | { | ||
1604 | int ret = 0, err = 0; | ||
1605 | struct tegra_channel *it; | ||
1606 | |||
1607 | list_for_each_entry(it, &vi->vi_chans, list) { | ||
1608 | if (!it->init_done) | ||
1609 | continue; | ||
1610 | err = tegra_channel_cleanup(it); | ||
1611 | if (err < 0) { | ||
1612 | ret = err; | ||
1613 | dev_err(vi->dev, "channel cleanup failed, err %d\n", | ||
1614 | err); | ||
1615 | } | ||
1616 | } | ||
1617 | return ret; | ||
1618 | } | ||
1619 | EXPORT_SYMBOL(tegra_vi_channels_cleanup); | ||
1620 | |||
1621 | int tegra_clean_unlinked_channels(struct tegra_mc_vi *vi) | ||
1622 | { | ||
1623 | int ret, err = 0; | ||
1624 | struct tegra_channel *chan; | ||
1625 | |||
1626 | list_for_each_entry(chan, &vi->vi_chans, list) { | ||
1627 | struct v4l2_subdev *sd = chan->subdev_on_csi; | ||
1628 | bool is_csi = false; | ||
1629 | |||
1630 | /* | ||
1631 | * If subdevice on csi is csi itself, | ||
1632 | * then sensor subdevice is not connected | ||
1633 | */ | ||
1634 | if (sd) | ||
1635 | is_csi = strstr(sd->name, "nvcsi") != NULL; | ||
1636 | |||
1637 | if (chan->num_subdevs && !is_csi) | ||
1638 | continue; | ||
1639 | |||
1640 | ret = tegra_channel_cleanup(chan); | ||
1641 | if (ret < 0) { | ||
1642 | err = ret; | ||
1643 | dev_err(vi->dev, "channel cleanup failed, err %d\n", | ||
1644 | err); | ||
1645 | } | ||
1646 | } | ||
1647 | |||
1648 | return err; | ||
1649 | } | ||
1650 | EXPORT_SYMBOL(tegra_clean_unlinked_channels); | ||
diff --git a/drivers/media/platform/tegra/camera/vi/core.c b/drivers/media/platform/tegra/camera/vi/core.c new file mode 100644 index 000000000..97de67085 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/core.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra Video Input Device Driver Core Helpers | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/export.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | |||
18 | #include "camera/vi/mc_common.h" | ||
19 | |||
20 | static const struct tegra_video_format tegra_default_format[] = { | ||
21 | { | ||
22 | TEGRA_VF_DEF, | ||
23 | 10, | ||
24 | MEDIA_BUS_FMT_SRGGB10_1X10, | ||
25 | {2, 1}, | ||
26 | TEGRA_IMAGE_FORMAT_DEF, | ||
27 | TEGRA_IMAGE_DT_RAW10, | ||
28 | V4L2_PIX_FMT_SRGGB10, | ||
29 | "RGRG.. GBGB..", | ||
30 | }, | ||
31 | }; | ||
32 | |||
33 | /* ----------------------------------------------------------------------------- | ||
34 | * Helper functions | ||
35 | */ | ||
36 | |||
37 | /** | ||
38 | * tegra_core_get_fourcc_by_idx - get fourcc of a tegra_video format | ||
39 | * @index: array index of the tegra_video_formats | ||
40 | * | ||
41 | * Return: fourcc code | ||
42 | */ | ||
43 | u32 tegra_core_get_fourcc_by_idx(struct tegra_channel *chan, | ||
44 | unsigned int index) | ||
45 | { | ||
46 | /* return default fourcc format if the index out of bounds */ | ||
47 | if (index > (chan->num_video_formats - 1)) | ||
48 | return V4L2_PIX_FMT_SGRBG10; | ||
49 | |||
50 | return chan->video_formats[index]->fourcc; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * tegra_core_get_description_by_idx - get description of a tegra_video format | ||
55 | * @index: array index of the tegra_video_formats | ||
56 | */ | ||
57 | void tegra_core_get_description_by_idx(struct tegra_channel *chan, | ||
58 | unsigned int index, __u8 *description) | ||
59 | { | ||
60 | if (index > (chan->num_video_formats - 1)) | ||
61 | return; | ||
62 | |||
63 | if (description) | ||
64 | strlcpy(description, | ||
65 | chan->video_formats[index]->description, | ||
66 | sizeof(chan->video_formats[index]->description)); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * tegra_core_get_word_count - Calculate word count | ||
71 | * @frame_width: number of pixels per line | ||
72 | * @fmt: Tegra Video format struct which has BPP information | ||
73 | * | ||
74 | * Return: word count number | ||
75 | */ | ||
76 | u32 tegra_core_get_word_count(unsigned int frame_width, | ||
77 | const struct tegra_video_format *fmt) | ||
78 | { | ||
79 | return frame_width * fmt->width / 8; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * tegra_core_get_idx_by_code - Retrieve index for a media bus code | ||
84 | * @code: the format media bus code | ||
85 | * | ||
86 | * Return: a index to the format information structure corresponding to the | ||
87 | * given V4L2 media bus format @code, or -1 if no corresponding format can | ||
88 | * be found. | ||
89 | */ | ||
90 | int tegra_core_get_idx_by_code(struct tegra_channel *chan, | ||
91 | unsigned int code, unsigned offset) | ||
92 | { | ||
93 | unsigned int i; | ||
94 | |||
95 | for (i = offset; i < chan->num_video_formats; ++i) { | ||
96 | if (chan->video_formats[i]->code == code) | ||
97 | return i; | ||
98 | } | ||
99 | |||
100 | return -1; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * tegra_core_get_default_format - Get default format | ||
105 | * | ||
106 | * Return: pointer to the format where the default format needs | ||
107 | * to be filled in. | ||
108 | */ | ||
109 | const struct tegra_video_format *tegra_core_get_default_format(void) | ||
110 | { | ||
111 | return &tegra_default_format[0]; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * tegra_core_get_format_by_code - Retrieve format information for a media | ||
116 | * bus code | ||
117 | * @code: the format media bus code | ||
118 | * | ||
119 | * Return: a pointer to the format information structure corresponding to the | ||
120 | * given V4L2 media bus format @code, or NULL if no corresponding format can | ||
121 | * be found. | ||
122 | */ | ||
123 | const struct tegra_video_format * | ||
124 | tegra_core_get_format_by_code(struct tegra_channel *chan, | ||
125 | unsigned int code, unsigned offset) | ||
126 | { | ||
127 | unsigned int i; | ||
128 | |||
129 | for (i = offset; i < chan->num_video_formats; ++i) { | ||
130 | if (chan->video_formats[i]->code == code) | ||
131 | return chan->video_formats[i]; | ||
132 | } | ||
133 | |||
134 | return NULL; | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * tegra_core_get_format_by_fourcc - Retrieve format information for a 4CC | ||
139 | * @fourcc: the format 4CC | ||
140 | * | ||
141 | * Return: a pointer to the format information structure corresponding to the | ||
142 | * given V4L2 format @fourcc, or NULL if no corresponding format can be | ||
143 | * found. | ||
144 | */ | ||
145 | const struct tegra_video_format * | ||
146 | tegra_core_get_format_by_fourcc(struct tegra_channel *chan, u32 fourcc) | ||
147 | { | ||
148 | unsigned int i; | ||
149 | |||
150 | for (i = 0; i < chan->num_video_formats; ++i) { | ||
151 | if (chan->video_formats[i]->fourcc == fourcc) | ||
152 | return chan->video_formats[i]; | ||
153 | } | ||
154 | |||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * tegra_core_bytes_per_line - Calculate bytes per line in one frame | ||
160 | * @width: frame width | ||
161 | * @align: number of alignment bytes | ||
162 | * @fmt: Tegra Video format | ||
163 | * | ||
164 | * Simply calcualte the bytes_per_line and if it's not aligned it | ||
165 | * will be padded to alignment boundary. | ||
166 | */ | ||
167 | u32 tegra_core_bytes_per_line(unsigned int width, unsigned int align, | ||
168 | const struct tegra_video_format *fmt) | ||
169 | { | ||
170 | u32 value = ((width * fmt->bpp.numerator) / fmt->bpp.denominator); | ||
171 | |||
172 | return roundup(value, align); | ||
173 | } | ||
diff --git a/drivers/media/platform/tegra/camera/vi/core.h b/drivers/media/platform/tegra/camera/vi/core.h new file mode 100644 index 000000000..56b24fd59 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/core.h | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra Video Input Device Driver Core Helpers | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __TEGRA_CORE_H__ | ||
14 | #define __TEGRA_CORE_H__ | ||
15 | |||
16 | #include <media/v4l2-subdev.h> | ||
17 | |||
18 | /* Minimum and maximum width and height common to Tegra video input device. */ | ||
19 | #define TEGRA_MIN_WIDTH 32U | ||
20 | #define TEGRA_MAX_WIDTH 32768U | ||
21 | #define TEGRA_MIN_HEIGHT 32U | ||
22 | #define TEGRA_MAX_HEIGHT 32768U | ||
23 | /* Width alignment */ | ||
24 | #define TEGRA_WIDTH_ALIGNMENT 1 | ||
25 | /* Stride alignment is 256, for VIC worse case */ | ||
26 | #define TEGRA_STRIDE_ALIGNMENT 256 | ||
27 | |||
28 | /* 1080p resolution as default resolution for test pattern generator */ | ||
29 | #define TEGRA_DEF_WIDTH 1920 | ||
30 | #define TEGRA_DEF_HEIGHT 1080 | ||
31 | |||
32 | #define TEGRA_VF_DEF MEDIA_BUS_FMT_SRGGB10_1X10 | ||
33 | #define TEGRA_IMAGE_FORMAT_DEF 32 | ||
34 | |||
35 | enum tegra_image_dt { | ||
36 | TEGRA_IMAGE_DT_YUV420_8 = 24, | ||
37 | TEGRA_IMAGE_DT_YUV420_10, | ||
38 | |||
39 | TEGRA_IMAGE_DT_YUV420CSPS_8 = 28, | ||
40 | TEGRA_IMAGE_DT_YUV420CSPS_10, | ||
41 | TEGRA_IMAGE_DT_YUV422_8, | ||
42 | TEGRA_IMAGE_DT_YUV422_10, | ||
43 | TEGRA_IMAGE_DT_RGB444, | ||
44 | TEGRA_IMAGE_DT_RGB555, | ||
45 | TEGRA_IMAGE_DT_RGB565, | ||
46 | TEGRA_IMAGE_DT_RGB666, | ||
47 | TEGRA_IMAGE_DT_RGB888, | ||
48 | |||
49 | TEGRA_IMAGE_DT_RAW6 = 40, | ||
50 | TEGRA_IMAGE_DT_RAW7, | ||
51 | TEGRA_IMAGE_DT_RAW8, | ||
52 | TEGRA_IMAGE_DT_RAW10, | ||
53 | TEGRA_IMAGE_DT_RAW12, | ||
54 | TEGRA_IMAGE_DT_RAW14, | ||
55 | }; | ||
56 | |||
57 | /* Supported CSI to VI Data Formats */ | ||
58 | enum tegra_vf_code { | ||
59 | TEGRA_VF_RAW6 = 0, | ||
60 | TEGRA_VF_RAW7, | ||
61 | TEGRA_VF_RAW8, | ||
62 | TEGRA_VF_RAW10, | ||
63 | TEGRA_VF_RAW12, | ||
64 | TEGRA_VF_RAW14, | ||
65 | TEGRA_VF_EMBEDDED8, | ||
66 | TEGRA_VF_RGB565, | ||
67 | TEGRA_VF_RGB555, | ||
68 | TEGRA_VF_RGB888, | ||
69 | TEGRA_VF_RGB444, | ||
70 | TEGRA_VF_RGB666, | ||
71 | TEGRA_VF_YUV422, | ||
72 | TEGRA_VF_YUV420, | ||
73 | TEGRA_VF_YUV420_CSPS, | ||
74 | }; | ||
75 | |||
76 | /** | ||
77 | * struct tegra_frac | ||
78 | * @numerator: numerator of the fraction | ||
79 | * @denominator: denominator of the fraction | ||
80 | */ | ||
81 | struct tegra_frac { | ||
82 | unsigned int numerator; | ||
83 | unsigned int denominator; | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * struct tegra_video_format - Tegra video format description | ||
88 | * @vf_code: video format code | ||
89 | * @width: format width in bits per component | ||
90 | * @code: media bus format code | ||
91 | * @bpp: bytes per pixel fraction (when stored in memory) | ||
92 | * @img_fmt: image format | ||
93 | * @img_dt: image data type | ||
94 | * @fourcc: V4L2 pixel format FCC identifier | ||
95 | * @description: format description, suitable for userspace | ||
96 | */ | ||
97 | struct tegra_video_format { | ||
98 | enum tegra_vf_code vf_code; | ||
99 | unsigned int width; | ||
100 | unsigned int code; | ||
101 | struct tegra_frac bpp; | ||
102 | u32 img_fmt; | ||
103 | enum tegra_image_dt img_dt; | ||
104 | u32 fourcc; | ||
105 | __u8 description[32]; | ||
106 | }; | ||
107 | |||
108 | #define TEGRA_VIDEO_FORMAT(VF_CODE, BPP, MBUS_CODE, FRAC_BPP_NUM, \ | ||
109 | FRAC_BPP_DEN, FORMAT, DATA_TYPE, FOURCC, DESCRIPTION) \ | ||
110 | { \ | ||
111 | TEGRA_VF_##VF_CODE, \ | ||
112 | BPP, \ | ||
113 | MEDIA_BUS_FMT_##MBUS_CODE, \ | ||
114 | {FRAC_BPP_NUM, FRAC_BPP_DEN}, \ | ||
115 | TEGRA_IMAGE_FORMAT_##FORMAT, \ | ||
116 | TEGRA_IMAGE_DT_##DATA_TYPE, \ | ||
117 | V4L2_PIX_FMT_##FOURCC, \ | ||
118 | DESCRIPTION, \ | ||
119 | } | ||
120 | |||
121 | u32 tegra_core_get_word_count(unsigned int frame_width, | ||
122 | const struct tegra_video_format *fmt); | ||
123 | u32 tegra_core_bytes_per_line(unsigned int width, unsigned int align, | ||
124 | const struct tegra_video_format *fmt); | ||
125 | const struct tegra_video_format *tegra_core_get_default_format(void); | ||
126 | |||
127 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/graph.c b/drivers/media/platform/tegra/camera/vi/graph.c new file mode 100644 index 000000000..c2dd058a1 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/graph.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * NVIDIA Media controller graph management | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/clk.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_graph.h> | ||
17 | #include <linux/of_device.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/regulator/consumer.h> | ||
20 | #include <linux/reset.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/tegra-soc.h> | ||
23 | #include <linux/tegra_pm_domains.h> | ||
24 | |||
25 | #include <media/media-device.h> | ||
26 | #include <media/v4l2-async.h> | ||
27 | #include <media/v4l2-common.h> | ||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-of.h> | ||
30 | #include <media/tegra_v4l2_camera.h> | ||
31 | |||
32 | #include "camera/vi/mc_common.h" | ||
33 | #include "camera/csi/csi.h" | ||
34 | #include "vi/vi4.h" | ||
35 | #include "nvcsi/nvcsi.h" | ||
36 | |||
37 | /* ----------------------------------------------------------------------------- | ||
38 | * Graph Management | ||
39 | */ | ||
40 | |||
41 | static struct tegra_vi_graph_entity * | ||
42 | tegra_vi_graph_find_entity(struct tegra_mc_vi *vi, | ||
43 | const struct device_node *node) | ||
44 | { | ||
45 | struct tegra_vi_graph_entity *entity; | ||
46 | |||
47 | list_for_each_entry(entity, &vi->entities, list) { | ||
48 | if (entity->node == node) | ||
49 | return entity; | ||
50 | } | ||
51 | |||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | static int tegra_vi_graph_build_one(struct tegra_mc_vi *vi, | ||
56 | struct tegra_vi_graph_entity *entity) | ||
57 | { | ||
58 | u32 link_flags = MEDIA_LNK_FL_ENABLED; | ||
59 | struct media_entity *local; | ||
60 | struct media_entity *remote; | ||
61 | struct media_pad *local_pad; | ||
62 | struct media_pad *remote_pad; | ||
63 | struct tegra_vi_graph_entity *ent; | ||
64 | struct v4l2_of_link link; | ||
65 | struct device_node *ep = NULL; | ||
66 | struct device_node *next; | ||
67 | int ret = 0; | ||
68 | |||
69 | if (!entity->subdev) { | ||
70 | dev_err(vi->dev, "%s:No subdev under entity, skip linking\n", | ||
71 | __func__); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | local = entity->entity; | ||
76 | dev_dbg(vi->dev, "creating links for entity %s\n", local->name); | ||
77 | |||
78 | do { | ||
79 | /* Get the next endpoint and parse its link. */ | ||
80 | next = of_graph_get_next_endpoint(entity->node, ep); | ||
81 | if (next == NULL) | ||
82 | break; | ||
83 | |||
84 | ep = next; | ||
85 | |||
86 | dev_dbg(vi->dev, "processing endpoint %s\n", ep->full_name); | ||
87 | |||
88 | ret = v4l2_of_parse_link(ep, &link); | ||
89 | if (ret < 0) { | ||
90 | dev_err(vi->dev, "failed to parse link for %s\n", | ||
91 | ep->full_name); | ||
92 | continue; | ||
93 | } | ||
94 | |||
95 | /* Skip sink ports, they will be processed from the other end of | ||
96 | * the link. | ||
97 | */ | ||
98 | if (link.local_port >= local->num_pads) { | ||
99 | dev_err(vi->dev, "invalid port number %u on %s\n", | ||
100 | link.local_port, link.local_node->full_name); | ||
101 | v4l2_of_put_link(&link); | ||
102 | ret = -EINVAL; | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | local_pad = &local->pads[link.local_port]; | ||
107 | |||
108 | if (local_pad->flags & MEDIA_PAD_FL_SINK) { | ||
109 | dev_dbg(vi->dev, "skipping sink port %s:%u\n", | ||
110 | link.local_node->full_name, link.local_port); | ||
111 | v4l2_of_put_link(&link); | ||
112 | continue; | ||
113 | } | ||
114 | |||
115 | /* Skip channel entity , they will be processed separately. */ | ||
116 | if (link.remote_node == vi->dev->of_node) { | ||
117 | dev_dbg(vi->dev, "skipping channel port %s:%u\n", | ||
118 | link.local_node->full_name, link.local_port); | ||
119 | v4l2_of_put_link(&link); | ||
120 | continue; | ||
121 | } | ||
122 | |||
123 | /* Find the remote entity. */ | ||
124 | ent = tegra_vi_graph_find_entity(vi, link.remote_node); | ||
125 | if (ent == NULL) { | ||
126 | dev_err(vi->dev, "no entity found for %s\n", | ||
127 | link.remote_node->full_name); | ||
128 | v4l2_of_put_link(&link); | ||
129 | ret = -EINVAL; | ||
130 | break; | ||
131 | } | ||
132 | |||
133 | remote = ent->entity; | ||
134 | |||
135 | if (link.remote_port >= remote->num_pads) { | ||
136 | dev_err(vi->dev, "invalid port number %u on %s\n", | ||
137 | link.remote_port, link.remote_node->full_name); | ||
138 | v4l2_of_put_link(&link); | ||
139 | ret = -EINVAL; | ||
140 | break; | ||
141 | } | ||
142 | |||
143 | remote_pad = &remote->pads[link.remote_port]; | ||
144 | |||
145 | v4l2_of_put_link(&link); | ||
146 | |||
147 | /* Create the media link. */ | ||
148 | dev_dbg(vi->dev, "creating %s:%u -> %s:%u link\n", | ||
149 | local->name, local_pad->index, | ||
150 | remote->name, remote_pad->index); | ||
151 | |||
152 | ret = media_entity_create_link(local, local_pad->index, | ||
153 | remote, remote_pad->index, | ||
154 | link_flags); | ||
155 | if (ret < 0) { | ||
156 | dev_err(vi->dev, | ||
157 | "failed to create %s:%u -> %s:%u link\n", | ||
158 | local->name, local_pad->index, | ||
159 | remote->name, remote_pad->index); | ||
160 | break; | ||
161 | } | ||
162 | } while (next); | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int tegra_vi_graph_build_links(struct tegra_mc_vi *vi) | ||
168 | { | ||
169 | u32 link_flags = MEDIA_LNK_FL_ENABLED; | ||
170 | struct device_node *node = vi->dev->of_node; | ||
171 | struct media_entity *source; | ||
172 | struct media_entity *sink; | ||
173 | struct media_pad *source_pad; | ||
174 | struct media_pad *sink_pad; | ||
175 | struct tegra_vi_graph_entity *ent; | ||
176 | struct v4l2_of_link link; | ||
177 | struct device_node *ep = NULL; | ||
178 | struct device_node *next; | ||
179 | struct tegra_channel *chan; | ||
180 | int ret = 0; | ||
181 | |||
182 | dev_dbg(vi->dev, "creating links for channels\n"); | ||
183 | |||
184 | chan = list_first_entry(&vi->vi_chans, struct tegra_channel, list); | ||
185 | do { | ||
186 | /* Get the next endpoint and parse its link. */ | ||
187 | next = of_graph_get_next_endpoint(node, ep); | ||
188 | if (next == NULL || !of_device_is_available(next)) | ||
189 | break; | ||
190 | |||
191 | /* Device not registered */ | ||
192 | if (!chan->init_done) { | ||
193 | chan = list_next_entry(chan, list); | ||
194 | continue; | ||
195 | } | ||
196 | |||
197 | ep = next; | ||
198 | |||
199 | dev_dbg(vi->dev, "processing endpoint %s\n", ep->full_name); | ||
200 | |||
201 | ret = v4l2_of_parse_link(ep, &link); | ||
202 | if (ret < 0) { | ||
203 | dev_err(vi->dev, "failed to parse link for %s\n", | ||
204 | ep->full_name); | ||
205 | continue; | ||
206 | } | ||
207 | |||
208 | if (link.local_port >= vi->num_channels) { | ||
209 | dev_err(vi->dev, "wrong channel number for port %u\n", | ||
210 | link.local_port); | ||
211 | v4l2_of_put_link(&link); | ||
212 | ret = -EINVAL; | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | dev_dbg(vi->dev, "creating link for channel %s\n", | ||
217 | chan->video.name); | ||
218 | |||
219 | /* Find the remote entity. */ | ||
220 | ent = tegra_vi_graph_find_entity(vi, link.remote_node); | ||
221 | if (ent == NULL) { | ||
222 | dev_err(vi->dev, "no entity found for %s\n", | ||
223 | link.remote_node->full_name); | ||
224 | v4l2_of_put_link(&link); | ||
225 | ret = -EINVAL; | ||
226 | break; | ||
227 | } | ||
228 | |||
229 | if (ent->entity == NULL) { | ||
230 | dev_err(vi->dev, "entity not bounded %s\n", | ||
231 | link.remote_node->full_name); | ||
232 | continue; | ||
233 | } | ||
234 | |||
235 | source = ent->entity; | ||
236 | source_pad = &source->pads[link.remote_port]; | ||
237 | sink = &chan->video.entity; | ||
238 | sink_pad = &chan->pad; | ||
239 | |||
240 | v4l2_of_put_link(&link); | ||
241 | |||
242 | /* Create the media link. */ | ||
243 | dev_dbg(vi->dev, "creating %s:%u -> %s:%u link\n", | ||
244 | source->name, source_pad->index, | ||
245 | sink->name, sink_pad->index); | ||
246 | |||
247 | ret = media_entity_create_link(source, source_pad->index, | ||
248 | sink, sink_pad->index, | ||
249 | link_flags); | ||
250 | if (ret < 0) { | ||
251 | dev_err(vi->dev, | ||
252 | "failed to create %s:%u -> %s:%u link\n", | ||
253 | source->name, source_pad->index, | ||
254 | sink->name, sink_pad->index); | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | tegra_channel_init_subdevices(chan); | ||
259 | chan = list_next_entry(chan, list); | ||
260 | } while (next != NULL); | ||
261 | |||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier) | ||
266 | { | ||
267 | struct tegra_mc_vi *vi = | ||
268 | container_of(notifier, struct tegra_mc_vi, notifier); | ||
269 | struct tegra_vi_graph_entity *entity; | ||
270 | int ret; | ||
271 | |||
272 | dev_dbg(vi->dev, "notify complete, all subdevs registered\n"); | ||
273 | |||
274 | /* Create links for every entity. */ | ||
275 | list_for_each_entry(entity, &vi->entities, list) { | ||
276 | if (entity->entity != NULL) { | ||
277 | ret = tegra_vi_graph_build_one(vi, entity); | ||
278 | if (ret < 0) | ||
279 | return ret; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* Create links for channels */ | ||
284 | ret = tegra_vi_graph_build_links(vi); | ||
285 | if (ret < 0) | ||
286 | return ret; | ||
287 | |||
288 | ret = v4l2_device_register_subdev_nodes(&vi->v4l2_dev); | ||
289 | if (ret < 0) | ||
290 | dev_err(vi->dev, "failed to register subdev nodes\n"); | ||
291 | |||
292 | vi->link_status++; | ||
293 | |||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | static int tegra_vi_graph_notify_bound(struct v4l2_async_notifier *notifier, | ||
298 | struct v4l2_subdev *subdev, | ||
299 | struct v4l2_async_subdev *asd) | ||
300 | { | ||
301 | struct tegra_mc_vi *vi = | ||
302 | container_of(notifier, struct tegra_mc_vi, notifier); | ||
303 | struct tegra_vi_graph_entity *entity; | ||
304 | |||
305 | /* Locate the entity corresponding to the bound subdev and store the | ||
306 | * subdev pointer. | ||
307 | */ | ||
308 | list_for_each_entry(entity, &vi->entities, list) { | ||
309 | if (entity->node != subdev->dev->of_node && | ||
310 | entity->node != subdev->of_node) | ||
311 | continue; | ||
312 | |||
313 | if (entity->subdev) { | ||
314 | dev_err(vi->dev, "duplicate subdev for node %s\n", | ||
315 | entity->node->full_name); | ||
316 | return -EINVAL; | ||
317 | } | ||
318 | |||
319 | dev_dbg(vi->dev, "subdev %s bound\n", subdev->name); | ||
320 | entity->entity = &subdev->entity; | ||
321 | entity->subdev = subdev; | ||
322 | vi->subdevs_bound++; | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | dev_err(vi->dev, "no entity for subdev %s\n", subdev->name); | ||
327 | return -EINVAL; | ||
328 | } | ||
329 | |||
330 | void tegra_vi_graph_cleanup(struct tegra_mc_vi *vi) | ||
331 | { | ||
332 | struct tegra_vi_graph_entity *entityp; | ||
333 | struct tegra_vi_graph_entity *entity; | ||
334 | |||
335 | v4l2_async_notifier_unregister(&vi->notifier); | ||
336 | |||
337 | list_for_each_entry_safe(entity, entityp, &vi->entities, list) { | ||
338 | of_node_put(entity->node); | ||
339 | list_del(&entity->list); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | int tegra_vi_get_port_info(struct tegra_channel *chan, | ||
344 | struct device_node *node, unsigned int index) | ||
345 | { | ||
346 | struct device_node *ep = NULL; | ||
347 | struct device_node *ports; | ||
348 | struct device_node *port; | ||
349 | int value = 0xFFFF; | ||
350 | int ret = 0, i; | ||
351 | |||
352 | ports = of_get_child_by_name(node, "ports"); | ||
353 | if (ports == NULL) | ||
354 | ports = node; | ||
355 | |||
356 | for_each_child_of_node(ports, port) { | ||
357 | if (!port->name || of_node_cmp(port->name, "port")) | ||
358 | continue; | ||
359 | |||
360 | ret = of_property_read_u32(port, "reg", &value); | ||
361 | if (ret < 0) | ||
362 | continue; | ||
363 | |||
364 | if (value != index) | ||
365 | continue; | ||
366 | |||
367 | for_each_child_of_node(port, ep) { | ||
368 | if (!ep->name || of_node_cmp(ep->name, "endpoint")) | ||
369 | continue; | ||
370 | |||
371 | /* Get CSI port */ | ||
372 | ret = of_property_read_u32(ep, "csi-port", &value); | ||
373 | if (ret < 0) | ||
374 | dev_err(&chan->video.dev, "csi port error\n"); | ||
375 | chan->port[0] = value; | ||
376 | |||
377 | /* Get number of data lanes for the endpoint */ | ||
378 | ret = of_property_read_u32(ep, "bus-width", &value); | ||
379 | if (ret < 0) | ||
380 | dev_err(&chan->video.dev, "num lanes error\n"); | ||
381 | chan->numlanes = value; | ||
382 | |||
383 | if (value > 12) { | ||
384 | dev_err(&chan->video.dev, "num lanes >12!\n"); | ||
385 | return -EINVAL; | ||
386 | } | ||
387 | /* | ||
388 | * for numlanes greater than 4 multiple CSI bricks | ||
389 | * are needed to capture the image, the logic below | ||
390 | * checks for numlanes > 4 and add a new CSI brick | ||
391 | * as a valid port. Loops around the three CSI | ||
392 | * bricks to add as many ports necessary. | ||
393 | */ | ||
394 | value -= 4; | ||
395 | for (i = 1; value > 0; i++, value -= 4) { | ||
396 | int next_port = chan->port[i-1] + 2; | ||
397 | |||
398 | next_port = (next_port % (PORT_F + 1)); | ||
399 | chan->port[i] = next_port; | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | |||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | static int tegra_vi_graph_parse_one(struct tegra_mc_vi *vi, | ||
408 | struct device_node *node) | ||
409 | { | ||
410 | struct device_node *ep = NULL; | ||
411 | struct device_node *next; | ||
412 | struct device_node *remote = NULL; | ||
413 | struct tegra_vi_graph_entity *entity; | ||
414 | int ret = 0; | ||
415 | |||
416 | dev_dbg(vi->dev, "parsing node %s\n", node->full_name); | ||
417 | |||
418 | do { | ||
419 | /* Parse all the remote entities and put them into the list */ | ||
420 | next = of_graph_get_next_endpoint(node, ep); | ||
421 | if (next == NULL || !of_device_is_available(next)) | ||
422 | break; | ||
423 | ep = next; | ||
424 | |||
425 | dev_dbg(vi->dev, "handling endpoint %s\n", ep->full_name); | ||
426 | |||
427 | remote = of_graph_get_remote_port_parent(ep); | ||
428 | if (!remote) { | ||
429 | ret = -EINVAL; | ||
430 | break; | ||
431 | } | ||
432 | |||
433 | /* Skip entities that we have already processed. */ | ||
434 | if (remote == vi->dev->of_node || | ||
435 | tegra_vi_graph_find_entity(vi, remote) || | ||
436 | !of_device_is_available(remote)) | ||
437 | continue; | ||
438 | |||
439 | entity = devm_kzalloc(vi->dev, sizeof(*entity), | ||
440 | GFP_KERNEL); | ||
441 | if (entity == NULL) { | ||
442 | ret = -ENOMEM; | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | entity->node = remote; | ||
447 | entity->asd.match_type = V4L2_ASYNC_MATCH_OF; | ||
448 | entity->asd.match.of.node = remote; | ||
449 | list_add_tail(&entity->list, &vi->entities); | ||
450 | vi->num_subdevs++; | ||
451 | } while (next); | ||
452 | |||
453 | return ret; | ||
454 | } | ||
455 | |||
456 | int tegra_vi_tpg_graph_init(struct tegra_mc_vi *mc_vi) | ||
457 | { | ||
458 | int err = 0; | ||
459 | u32 link_flags = MEDIA_LNK_FL_ENABLED; | ||
460 | struct tegra_csi_device *csi = mc_vi->csi; | ||
461 | struct tegra_channel *vi_it; | ||
462 | struct tegra_csi_channel *csi_it; | ||
463 | |||
464 | if (!csi) { | ||
465 | dev_err(mc_vi->dev, "CSI is NULL\n"); | ||
466 | return -EINVAL; | ||
467 | } | ||
468 | mc_vi->num_subdevs = mc_vi->num_channels; | ||
469 | vi_it = mc_vi->tpg_start; | ||
470 | csi_it = csi->tpg_start; | ||
471 | |||
472 | list_for_each_entry_from(vi_it, &mc_vi->vi_chans, list) { | ||
473 | /* Device not registered */ | ||
474 | if (!vi_it->init_done) | ||
475 | continue; | ||
476 | |||
477 | list_for_each_entry_from(csi_it, &csi->csi_chans, list) { | ||
478 | struct media_entity *source = &csi_it->subdev.entity; | ||
479 | struct media_entity *sink = &vi_it->video.entity; | ||
480 | struct media_pad *source_pad = csi_it->pads; | ||
481 | struct media_pad *sink_pad = &vi_it->pad; | ||
482 | |||
483 | vi_it->bypass = 0; | ||
484 | err = v4l2_device_register_subdev(&mc_vi->v4l2_dev, | ||
485 | &csi_it->subdev); | ||
486 | if (err) { | ||
487 | dev_err(mc_vi->dev, | ||
488 | "%s:Fail to register subdev\n", | ||
489 | __func__); | ||
490 | goto register_fail; | ||
491 | } | ||
492 | dev_dbg(mc_vi->dev, "creating %s:%u -> %s:%u link\n", | ||
493 | source->name, source_pad->index, | ||
494 | sink->name, sink_pad->index); | ||
495 | |||
496 | err = media_entity_create_link(source, | ||
497 | source_pad->index, | ||
498 | sink, sink_pad->index, | ||
499 | link_flags); | ||
500 | if (err < 0) { | ||
501 | dev_err(mc_vi->dev, | ||
502 | "failed to create %s:%u -> %s:%u link\n", | ||
503 | source->name, source_pad->index, | ||
504 | sink->name, sink_pad->index); | ||
505 | goto register_fail; | ||
506 | } | ||
507 | err = tegra_channel_init_subdevices(vi_it); | ||
508 | if (err) { | ||
509 | dev_err(mc_vi->dev, | ||
510 | "%s:Init subdevice error\n", __func__); | ||
511 | goto register_fail; | ||
512 | } | ||
513 | csi_it = list_next_entry(csi_it, list); | ||
514 | break; | ||
515 | } | ||
516 | } | ||
517 | |||
518 | return 0; | ||
519 | register_fail: | ||
520 | csi_it = csi->tpg_start; | ||
521 | list_for_each_entry_from(csi_it, &csi->csi_chans, list) | ||
522 | v4l2_device_unregister_subdev(&csi_it->subdev); | ||
523 | return err; | ||
524 | } | ||
525 | |||
526 | int tegra_vi_graph_init(struct tegra_mc_vi *vi) | ||
527 | { | ||
528 | struct tegra_vi_graph_entity *entity; | ||
529 | struct v4l2_async_subdev **subdevs = NULL; | ||
530 | unsigned int num_subdevs = 0; | ||
531 | int ret = 0, i; | ||
532 | |||
533 | /* | ||
534 | * Walk the links to parse the full graph. Start by parsing the | ||
535 | * composite node and then parse entities in turn. The list_for_each | ||
536 | * loop will handle entities added at the end of the list while walking | ||
537 | * the links. | ||
538 | */ | ||
539 | ret = tegra_vi_graph_parse_one(vi, vi->dev->of_node); | ||
540 | if (ret < 0) | ||
541 | return 0; | ||
542 | |||
543 | list_for_each_entry(entity, &vi->entities, list) { | ||
544 | ret = tegra_vi_graph_parse_one(vi, entity->node); | ||
545 | if (ret < 0) | ||
546 | break; | ||
547 | } | ||
548 | |||
549 | if (!vi->num_subdevs) { | ||
550 | dev_dbg(vi->dev, "warning: no subdev found in graph\n"); | ||
551 | goto done; | ||
552 | } | ||
553 | |||
554 | /* Register the subdevices notifier. */ | ||
555 | num_subdevs = vi->num_subdevs; | ||
556 | subdevs = devm_kzalloc(vi->dev, sizeof(*subdevs) * num_subdevs, | ||
557 | GFP_KERNEL); | ||
558 | if (subdevs == NULL) { | ||
559 | ret = -ENOMEM; | ||
560 | goto done; | ||
561 | } | ||
562 | |||
563 | /* | ||
564 | * Add code to check for sensors and | ||
565 | * set TPG mode for VI if no sensors found | ||
566 | * logic varies for different platforms | ||
567 | */ | ||
568 | i = 0; | ||
569 | list_for_each_entry(entity, &vi->entities, list) | ||
570 | subdevs[i++] = &entity->asd; | ||
571 | |||
572 | vi->notifier.subdevs = subdevs; | ||
573 | vi->notifier.num_subdevs = num_subdevs; | ||
574 | vi->notifier.bound = tegra_vi_graph_notify_bound; | ||
575 | vi->notifier.complete = tegra_vi_graph_notify_complete; | ||
576 | vi->link_status = 0; | ||
577 | vi->subdevs_bound = 0; | ||
578 | |||
579 | ret = v4l2_async_notifier_register(&vi->v4l2_dev, &vi->notifier); | ||
580 | if (ret < 0) { | ||
581 | dev_err(vi->dev, "notifier registration failed\n"); | ||
582 | goto done; | ||
583 | } | ||
584 | |||
585 | if (!vi->link_status) { | ||
586 | if (vi->subdevs_bound) { | ||
587 | ret = tegra_vi_graph_notify_complete(&vi->notifier); | ||
588 | if (ret < 0) | ||
589 | goto done; | ||
590 | } | ||
591 | tegra_clean_unlinked_channels(vi); | ||
592 | } | ||
593 | |||
594 | return 0; | ||
595 | |||
596 | done: | ||
597 | tegra_clean_unlinked_channels(vi); | ||
598 | if (ret < 0) | ||
599 | tegra_vi_graph_cleanup(vi); | ||
600 | |||
601 | return ret; | ||
602 | } | ||
diff --git a/drivers/media/platform/tegra/camera/vi/mc_common.c b/drivers/media/platform/tegra/camera/vi/mc_common.c new file mode 100644 index 000000000..11acb7cfe --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/mc_common.c | |||
@@ -0,0 +1,346 @@ | |||
1 | /* | ||
2 | * Tegra Video Input device common APIs | ||
3 | * | ||
4 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/of_device.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | |||
20 | #include <media/tegra_v4l2_camera.h> | ||
21 | #include <media/camera_common.h> | ||
22 | #include <media/v4l2-event.h> | ||
23 | #include <media/tegra_camera_platform.h> | ||
24 | |||
25 | #include "dev.h" | ||
26 | #include "camera/vi/mc_common.h" | ||
27 | #include "vi/vi.h" | ||
28 | #include "vi/vi4.h" | ||
29 | #include "host1x/host1x.h" | ||
30 | #include "camera/vi/registers.h" | ||
31 | |||
32 | /* In TPG mode, VI only support 2 formats */ | ||
33 | static void vi_tpg_fmts_bitmap_init(struct tegra_channel *chan) | ||
34 | { | ||
35 | int index; | ||
36 | |||
37 | bitmap_zero(chan->fmts_bitmap, MAX_FORMAT_NUM); | ||
38 | |||
39 | index = tegra_core_get_idx_by_code(chan, | ||
40 | MEDIA_BUS_FMT_SRGGB10_1X10, 0); | ||
41 | bitmap_set(chan->fmts_bitmap, index, 1); | ||
42 | |||
43 | index = tegra_core_get_idx_by_code(chan, | ||
44 | MEDIA_BUS_FMT_RGB888_1X32_PADHI, 0); | ||
45 | bitmap_set(chan->fmts_bitmap, index, 1); | ||
46 | } | ||
47 | |||
48 | /* ----------------------------------------------------------------------------- | ||
49 | * Media Controller and V4L2 | ||
50 | */ | ||
51 | |||
52 | static const char *const vi_pattern_strings[] = { | ||
53 | "Disabled", | ||
54 | "Black/White Direct Mode", | ||
55 | "Color Patch Mode", | ||
56 | }; | ||
57 | |||
58 | static int vi_s_ctrl(struct v4l2_ctrl *ctrl) | ||
59 | { | ||
60 | struct tegra_mc_vi *vi = container_of(ctrl->handler, struct tegra_mc_vi, | ||
61 | ctrl_handler); | ||
62 | |||
63 | switch (ctrl->id) { | ||
64 | case V4L2_CID_TEST_PATTERN: | ||
65 | /* | ||
66 | * TPG control is only avaiable to TPG driver, | ||
67 | * it can't be changed to 0 to disable TPG mode. | ||
68 | */ | ||
69 | if (ctrl->val) { | ||
70 | dev_info(&vi->ndev->dev, "Set TPG mode to %d\n", | ||
71 | ctrl->val); | ||
72 | vi->pg_mode = ctrl->val; | ||
73 | } | ||
74 | break; | ||
75 | default: | ||
76 | dev_err(vi->dev, "%s:Not valid ctrl\n", __func__); | ||
77 | return -EINVAL; | ||
78 | } | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static const struct v4l2_ctrl_ops vi_ctrl_ops = { | ||
84 | .s_ctrl = vi_s_ctrl, | ||
85 | }; | ||
86 | |||
87 | void tegra_vi_v4l2_cleanup(struct tegra_mc_vi *vi) | ||
88 | { | ||
89 | v4l2_ctrl_handler_free(&vi->ctrl_handler); | ||
90 | v4l2_device_unregister(&vi->v4l2_dev); | ||
91 | if (!vi->pg_mode) | ||
92 | media_device_unregister(&vi->media_dev); | ||
93 | } | ||
94 | EXPORT_SYMBOL(tegra_vi_v4l2_cleanup); | ||
95 | |||
96 | static void tegra_vi_notify(struct v4l2_subdev *sd, | ||
97 | unsigned int notification, void *arg) | ||
98 | { | ||
99 | struct tegra_mc_vi *vi = container_of(sd->v4l2_dev, | ||
100 | struct tegra_mc_vi, v4l2_dev); | ||
101 | unsigned i; | ||
102 | struct tegra_channel *chan; | ||
103 | |||
104 | if (notification != V4L2_DEVICE_NOTIFY_EVENT) | ||
105 | return; | ||
106 | |||
107 | list_for_each_entry(chan, &vi->vi_chans, list) { | ||
108 | for (i = 0; i < chan->num_subdevs; i++) | ||
109 | if (sd == chan->subdev[i]) | ||
110 | v4l2_event_queue(&chan->video, arg); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | int tegra_vi_v4l2_init(struct tegra_mc_vi *vi) | ||
115 | { | ||
116 | int ret; | ||
117 | |||
118 | vi->media_dev.dev = vi->dev; | ||
119 | strlcpy(vi->media_dev.model, "NVIDIA Tegra Video Input Device", | ||
120 | sizeof(vi->media_dev.model)); | ||
121 | vi->media_dev.hw_revision = 3; | ||
122 | |||
123 | ret = media_device_register(&vi->media_dev); | ||
124 | if (ret < 0) { | ||
125 | dev_err(vi->dev, | ||
126 | "media device registration failed (%d)\n", | ||
127 | ret); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | mutex_init(&vi->bw_update_lock); | ||
132 | vi->v4l2_dev.mdev = &vi->media_dev; | ||
133 | vi->v4l2_dev.notify = tegra_vi_notify; | ||
134 | ret = v4l2_device_register(vi->dev, &vi->v4l2_dev); | ||
135 | if (ret < 0) { | ||
136 | dev_err(vi->dev, "V4L2 device registration failed (%d)\n", | ||
137 | ret); | ||
138 | goto register_error; | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | |||
143 | register_error: | ||
144 | media_device_unregister(&vi->media_dev); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | static int vi_parse_dt(struct tegra_mc_vi *vi, struct platform_device *dev) | ||
149 | { | ||
150 | int err = 0; | ||
151 | int num_channels = 0; | ||
152 | int i; | ||
153 | struct tegra_channel *item; | ||
154 | struct device_node *node = dev->dev.of_node; | ||
155 | |||
156 | err = of_property_read_u32(node, "num-channels", &num_channels); | ||
157 | if (err) { | ||
158 | dev_dbg(&dev->dev, | ||
159 | "Failed to find num of channels, set to 0\n"); | ||
160 | num_channels = 0; | ||
161 | } | ||
162 | vi->num_channels = num_channels; | ||
163 | for (i = 0; i < num_channels; i++) { | ||
164 | item = devm_kzalloc(vi->dev, sizeof(*item), GFP_KERNEL); | ||
165 | if (!item) | ||
166 | return -ENOMEM; | ||
167 | item->id = i; | ||
168 | list_add_tail(&item->list, &vi->vi_chans); | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void set_vi_register_base(struct tegra_mc_vi *mc_vi, | ||
175 | void __iomem *regbase) | ||
176 | { | ||
177 | mc_vi->iomem = regbase; | ||
178 | } | ||
179 | int tpg_vi_media_controller_init(struct tegra_mc_vi *mc_vi, int pg_mode) | ||
180 | { | ||
181 | int err = 0, i; | ||
182 | struct tegra_channel *item; | ||
183 | int count = 0; | ||
184 | |||
185 | /* Allocate TPG channel */ | ||
186 | v4l2_ctrl_handler_init(&mc_vi->ctrl_handler, 1); | ||
187 | mc_vi->pattern = v4l2_ctrl_new_std_menu_items(&mc_vi->ctrl_handler, | ||
188 | &vi_ctrl_ops, V4L2_CID_TEST_PATTERN, | ||
189 | ARRAY_SIZE(vi_pattern_strings) - 1, | ||
190 | 0, mc_vi->pg_mode, vi_pattern_strings); | ||
191 | |||
192 | if (mc_vi->ctrl_handler.error) { | ||
193 | dev_err(mc_vi->dev, "failed to add controls\n"); | ||
194 | err = mc_vi->ctrl_handler.error; | ||
195 | goto ctrl_error; | ||
196 | } | ||
197 | |||
198 | mc_vi->tpg_start = NULL; | ||
199 | for (i = 0; i < TPG_CHANNELS; i++) { | ||
200 | item = devm_kzalloc(mc_vi->dev, sizeof(*item), GFP_KERNEL); | ||
201 | if (!item) | ||
202 | continue; | ||
203 | item->id = mc_vi->num_channels + i; | ||
204 | item->pg_mode = pg_mode; | ||
205 | item->vi = mc_vi; | ||
206 | |||
207 | err = tegra_channel_init(item); | ||
208 | if (err) { | ||
209 | devm_kfree(mc_vi->dev, item); | ||
210 | continue; | ||
211 | } | ||
212 | vi_tpg_fmts_bitmap_init(item); | ||
213 | /* only inited tpg channels are added */ | ||
214 | list_add_tail(&item->list, &mc_vi->vi_chans); | ||
215 | mc_vi->num_channels++; | ||
216 | if (mc_vi->tpg_start == NULL) | ||
217 | mc_vi->tpg_start = item; | ||
218 | } | ||
219 | |||
220 | err = tegra_vi_tpg_graph_init(mc_vi); | ||
221 | if (err) | ||
222 | goto channel_init_error; | ||
223 | |||
224 | list_for_each_entry(item, &mc_vi->vi_chans, list) { | ||
225 | if (!item->pg_mode) | ||
226 | continue; | ||
227 | err = video_register_device(&item->video, VFL_TYPE_GRABBER, -1); | ||
228 | if (err < 0) { | ||
229 | dev_err(&item->video.dev, "failed to register %s\n", | ||
230 | item->video.name); | ||
231 | continue; | ||
232 | } | ||
233 | count++; | ||
234 | } | ||
235 | |||
236 | if (count == 0) { | ||
237 | dev_err(mc_vi->dev, "all tpg register failed\n"); | ||
238 | goto channel_init_error; | ||
239 | } | ||
240 | |||
241 | return err; | ||
242 | |||
243 | channel_init_error: | ||
244 | dev_err(mc_vi->dev, "%s: channel init failed\n", __func__); | ||
245 | if (!mc_vi->tpg_start) | ||
246 | tpg_vi_media_controller_cleanup(mc_vi); | ||
247 | return err; | ||
248 | ctrl_error: | ||
249 | v4l2_ctrl_handler_free(&mc_vi->ctrl_handler); | ||
250 | dev_err(mc_vi->dev, "%s: v2l4_ctl error\n", __func__); | ||
251 | return err; | ||
252 | } | ||
253 | EXPORT_SYMBOL(tpg_vi_media_controller_init); | ||
254 | |||
255 | void tpg_vi_media_controller_cleanup(struct tegra_mc_vi *mc_vi) | ||
256 | { | ||
257 | struct tegra_channel *item; | ||
258 | struct tegra_channel *itemn; | ||
259 | |||
260 | list_for_each_entry_safe(item, itemn, &mc_vi->vi_chans, list) { | ||
261 | if (!item->pg_mode) | ||
262 | continue; | ||
263 | if (item->video.cdev != NULL) | ||
264 | video_unregister_device(&item->video); | ||
265 | tegra_channel_cleanup(item); | ||
266 | list_del(&item->list); | ||
267 | devm_kfree(mc_vi->dev, item); | ||
268 | /* decrement media device entity count */ | ||
269 | mc_vi->media_dev.entity_id--; | ||
270 | mc_vi->num_channels--; | ||
271 | } | ||
272 | mc_vi->tpg_start = NULL; | ||
273 | v4l2_ctrl_handler_free(&mc_vi->ctrl_handler); | ||
274 | } | ||
275 | EXPORT_SYMBOL(tpg_vi_media_controller_cleanup); | ||
276 | |||
277 | int tegra_vi_media_controller_init(struct tegra_mc_vi *mc_vi, | ||
278 | struct platform_device *pdev) | ||
279 | { | ||
280 | int err = 0; | ||
281 | struct nvhost_device_data *pdata = (struct nvhost_device_data *) | ||
282 | platform_get_drvdata(pdev); | ||
283 | |||
284 | if (!pdata) | ||
285 | return -EINVAL; | ||
286 | set_vi_register_base(mc_vi, pdata->aperture[0]); | ||
287 | |||
288 | mc_vi->ndev = pdev; | ||
289 | mc_vi->dev = &pdev->dev; | ||
290 | INIT_LIST_HEAD(&mc_vi->vi_chans); | ||
291 | INIT_LIST_HEAD(&mc_vi->entities); | ||
292 | mutex_init(&mc_vi->mipical_lock); | ||
293 | |||
294 | err = vi_parse_dt(mc_vi, pdev); | ||
295 | if (err) | ||
296 | goto mc_init_fail; | ||
297 | |||
298 | /* | ||
299 | * if there is no vi channels listed in DT, | ||
300 | * no need to init the channel and graph | ||
301 | */ | ||
302 | if (mc_vi->num_channels == 0) | ||
303 | return 0; | ||
304 | |||
305 | err = tegra_vi_v4l2_init(mc_vi); | ||
306 | if (err < 0) | ||
307 | goto mc_init_fail; | ||
308 | |||
309 | /* Init Tegra VI channels */ | ||
310 | err = tegra_vi_channels_init(mc_vi); | ||
311 | if (err < 0) { | ||
312 | dev_err(&pdev->dev, "Init channel failed\n"); | ||
313 | goto channels_error; | ||
314 | } | ||
315 | |||
316 | /* Setup media links between VI and external sensor subdev. */ | ||
317 | err = tegra_vi_graph_init(mc_vi); | ||
318 | if (err < 0) | ||
319 | goto graph_error; | ||
320 | |||
321 | err = tegra_vi_channels_register(mc_vi); | ||
322 | if (err < 0) | ||
323 | goto register_error; | ||
324 | |||
325 | return 0; | ||
326 | |||
327 | register_error: | ||
328 | tegra_vi_graph_cleanup(mc_vi); | ||
329 | graph_error: | ||
330 | tegra_vi_channels_cleanup(mc_vi); | ||
331 | channels_error: | ||
332 | tegra_vi_v4l2_cleanup(mc_vi); | ||
333 | mc_init_fail: | ||
334 | dev_err(&pdev->dev, "%s: failed\n", __func__); | ||
335 | return err; | ||
336 | } | ||
337 | EXPORT_SYMBOL(tegra_vi_media_controller_init); | ||
338 | |||
339 | void tegra_vi_media_controller_cleanup(struct tegra_mc_vi *mc_vi) | ||
340 | { | ||
341 | tegra_vi_channels_unregister(mc_vi); | ||
342 | tegra_vi_graph_cleanup(mc_vi); | ||
343 | tegra_vi_channels_cleanup(mc_vi); | ||
344 | tegra_vi_v4l2_cleanup(mc_vi); | ||
345 | } | ||
346 | EXPORT_SYMBOL(tegra_vi_media_controller_cleanup); | ||
diff --git a/drivers/media/platform/tegra/camera/vi/mc_common.h b/drivers/media/platform/tegra/camera/vi/mc_common.h new file mode 100644 index 000000000..42982bef8 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/mc_common.h | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * drivers/media/platform/tegra/camera/mc_common.h | ||
3 | * | ||
4 | * Tegra Media controller common APIs | ||
5 | * | ||
6 | * Copyright (c) 2012-2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #ifndef __CAMERA_MC_COMMON_H__ | ||
22 | #define __CAMERA_MC_COMMON_H__ | ||
23 | |||
24 | #include <media/media-device.h> | ||
25 | #include <media/media-entity.h> | ||
26 | #include <media/v4l2-async.h> | ||
27 | #include <media/v4l2-ctrls.h> | ||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-dev.h> | ||
30 | #include <media/videobuf2-core.h> | ||
31 | #include <linux/workqueue.h> | ||
32 | |||
33 | #include "core.h" | ||
34 | #include "../csi/csi.h" | ||
35 | |||
36 | #define MAX_FORMAT_NUM 64 | ||
37 | #define MAX_SUBDEVICES 4 | ||
38 | #define QUEUED_BUFFERS 4 | ||
39 | #define ENABLE 1 | ||
40 | #define DISABLE 0 | ||
41 | #define MAX_SYNCPT_PER_CHANNEL 3 | ||
42 | #define TPG_CHANNELS 6 | ||
43 | |||
44 | #define TEGRA_MEM_FORMAT 0 | ||
45 | #define TEGRA_ISP_FORMAT 1 | ||
46 | |||
47 | enum channel_capture_state { | ||
48 | CAPTURE_IDLE = 0, | ||
49 | CAPTURE_GOOD, | ||
50 | CAPTURE_TIMEOUT, | ||
51 | CAPTURE_ERROR, | ||
52 | }; | ||
53 | |||
54 | enum tegra_vi_pg_mode { | ||
55 | TEGRA_VI_PG_DISABLED = 0, | ||
56 | TEGRA_VI_PG_DIRECT, | ||
57 | TEGRA_VI_PG_PATCH, | ||
58 | }; | ||
59 | |||
60 | /** | ||
61 | * struct tegra_channel_buffer - video channel buffer | ||
62 | * @buf: vb2 buffer base object | ||
63 | * @queue: buffer list entry in the channel queued buffers list | ||
64 | * @chan: channel that uses the buffer | ||
65 | * @addr: Tegra IOVA buffer address for VI output | ||
66 | */ | ||
67 | struct tegra_channel_buffer { | ||
68 | struct vb2_v4l2_buffer buf; | ||
69 | struct list_head queue; | ||
70 | struct tegra_channel *chan; | ||
71 | |||
72 | dma_addr_t addr; | ||
73 | }; | ||
74 | |||
75 | #define to_tegra_channel_buffer(vb) \ | ||
76 | container_of(vb, struct tegra_channel_buffer, buf) | ||
77 | |||
78 | /** | ||
79 | * struct tegra_vi_graph_entity - Entity in the video graph | ||
80 | * @list: list entry in a graph entities list | ||
81 | * @node: the entity's DT node | ||
82 | * @entity: media entity, from the corresponding V4L2 subdev | ||
83 | * @asd: subdev asynchronous registration information | ||
84 | * @subdev: V4L2 subdev | ||
85 | */ | ||
86 | struct tegra_vi_graph_entity { | ||
87 | struct list_head list; | ||
88 | struct device_node *node; | ||
89 | struct media_entity *entity; | ||
90 | |||
91 | struct v4l2_async_subdev asd; | ||
92 | struct v4l2_subdev *subdev; | ||
93 | }; | ||
94 | |||
95 | /** | ||
96 | * struct tegra_channel - Tegra video channel | ||
97 | * @list: list entry in a composite device dmas list | ||
98 | * @video: V4L2 video device associated with the video channel | ||
99 | * @video_lock: | ||
100 | * @pad: media pad for the video device entity | ||
101 | * @pipe: pipeline belonging to the channel | ||
102 | * | ||
103 | * @vi: composite device DT node port number for the channel | ||
104 | * | ||
105 | * @kthread_capture: kernel thread task structure of this video channel | ||
106 | * @wait: wait queue structure for kernel thread | ||
107 | * | ||
108 | * @format: active V4L2 pixel format | ||
109 | * @fmtinfo: format information corresponding to the active @format | ||
110 | * | ||
111 | * @queue: vb2 buffers queue | ||
112 | * @alloc_ctx: allocation context for the vb2 @queue | ||
113 | * @sequence: V4L2 buffers sequence number | ||
114 | * | ||
115 | * @capture: list of queued buffers for capture | ||
116 | * @queued_lock: protects the buf_queued list | ||
117 | * | ||
118 | * @csi: CSI register bases | ||
119 | * @stride_align: channel buffer stride alignment, default is 64 | ||
120 | * @width_align: image width alignment, default is 4 | ||
121 | * @height_align: channel buffer height alignment, default is 1 | ||
122 | * @size_align: channel buffer size alignment, default is 128K bytes | ||
123 | * @port: CSI port of this video channel | ||
124 | * @io_id: Tegra IO rail ID of this video channel | ||
125 | * | ||
126 | * @fmts_bitmap: a bitmap for formats supported | ||
127 | * @bypass: bypass flag for VI bypass mode | ||
128 | */ | ||
129 | struct tegra_channel { | ||
130 | int id; | ||
131 | struct list_head list; | ||
132 | struct video_device video; | ||
133 | struct media_pad pad; | ||
134 | struct media_pipeline pipe; | ||
135 | struct mutex video_lock; | ||
136 | |||
137 | struct tegra_mc_vi *vi; | ||
138 | struct v4l2_subdev *subdev[MAX_SUBDEVICES]; | ||
139 | struct v4l2_subdev *subdev_on_csi; | ||
140 | |||
141 | struct v4l2_ctrl_handler ctrl_handler; | ||
142 | struct v4l2_pix_format format; | ||
143 | const struct tegra_video_format *fmtinfo; | ||
144 | const struct tegra_video_format *video_formats[MAX_FORMAT_NUM]; | ||
145 | unsigned int num_video_formats; | ||
146 | struct mutex stop_kthread_lock; | ||
147 | |||
148 | unsigned char port[TEGRA_CSI_BLOCKS]; | ||
149 | unsigned int syncpt[TEGRA_CSI_BLOCKS][MAX_SYNCPT_PER_CHANNEL]; | ||
150 | unsigned int syncpoint_fifo[TEGRA_CSI_BLOCKS]; | ||
151 | unsigned int buffer_offset[TEGRA_CSI_BLOCKS]; | ||
152 | unsigned int buffer_state[QUEUED_BUFFERS]; | ||
153 | struct vb2_v4l2_buffer *buffers[QUEUED_BUFFERS]; | ||
154 | unsigned int timeout; | ||
155 | unsigned int save_index; | ||
156 | unsigned int free_index; | ||
157 | unsigned int num_buffers; | ||
158 | unsigned int released_bufs; | ||
159 | |||
160 | struct task_struct *kthread_capture_start; | ||
161 | wait_queue_head_t start_wait; | ||
162 | struct vb2_queue queue; | ||
163 | void *alloc_ctx; | ||
164 | bool init_done; | ||
165 | struct list_head capture; | ||
166 | spinlock_t start_lock; | ||
167 | struct work_struct status_work; | ||
168 | struct work_struct error_work; | ||
169 | |||
170 | void __iomem *csibase[TEGRA_CSI_BLOCKS]; | ||
171 | unsigned int stride_align; | ||
172 | unsigned int width_align; | ||
173 | unsigned int height_align; | ||
174 | unsigned int size_align; | ||
175 | unsigned int valid_ports; | ||
176 | unsigned int total_ports; | ||
177 | unsigned int numlanes; | ||
178 | unsigned int io_id; | ||
179 | unsigned int num_subdevs; | ||
180 | unsigned int sequence; | ||
181 | unsigned int saved_ctx_bypass; | ||
182 | unsigned int saved_ctx_pgmode; | ||
183 | unsigned int gang_mode; | ||
184 | unsigned int gang_width; | ||
185 | unsigned int gang_height; | ||
186 | unsigned int gang_bytesperline; | ||
187 | unsigned int gang_sizeimage; | ||
188 | |||
189 | DECLARE_BITMAP(fmts_bitmap, MAX_FORMAT_NUM); | ||
190 | atomic_t power_on_refcnt; | ||
191 | struct v4l2_fh *fh; | ||
192 | bool bypass; | ||
193 | bool write_ispformat; | ||
194 | enum tegra_vi_pg_mode pg_mode; | ||
195 | bool bfirst_fstart; | ||
196 | enum channel_capture_state capture_state; | ||
197 | spinlock_t capture_state_lock; | ||
198 | atomic_t is_streaming; | ||
199 | int requested_kbyteps; | ||
200 | unsigned long requested_hz; | ||
201 | |||
202 | struct vi_notify_channel *vnc[TEGRA_CSI_BLOCKS]; | ||
203 | int vnc_id[TEGRA_CSI_BLOCKS]; | ||
204 | int grp_id; | ||
205 | |||
206 | struct vi_capture *capture_data; | ||
207 | }; | ||
208 | |||
209 | #define to_tegra_channel(vdev) \ | ||
210 | container_of(vdev, struct tegra_channel, video) | ||
211 | |||
212 | /** | ||
213 | * struct tegra_mc_vi - NVIDIA Tegra Media controller structure | ||
214 | * @v4l2_dev: V4L2 device | ||
215 | * @media_dev: media device | ||
216 | * @dev: device struct | ||
217 | * @tegra_camera: tegra camera structure | ||
218 | * @nvhost_device_data: NvHost VI device information | ||
219 | * | ||
220 | * @notifier: V4L2 asynchronous subdevs notifier | ||
221 | * @entities: entities in the graph as a list of tegra_vi_graph_entity | ||
222 | * @num_subdevs: number of subdevs in the pipeline | ||
223 | * | ||
224 | * @channels: list of channels at the pipeline output and input | ||
225 | * | ||
226 | * @ctrl_handler: V4L2 control handler | ||
227 | * @pattern: test pattern generator V4L2 control | ||
228 | * @pg_mode: test pattern generator mode (disabled/direct/patch) | ||
229 | * | ||
230 | * @has_sensors: a flag to indicate whether is a real sensor connecting | ||
231 | */ | ||
232 | struct tegra_mc_vi { | ||
233 | struct vi *vi; | ||
234 | struct platform_device *ndev; | ||
235 | struct v4l2_device v4l2_dev; | ||
236 | struct media_device media_dev; | ||
237 | struct device *dev; | ||
238 | struct nvhost_device_data *ndata; | ||
239 | |||
240 | struct regulator *reg; | ||
241 | struct clk *clk; | ||
242 | struct clk *parent_clk; | ||
243 | |||
244 | struct v4l2_async_notifier notifier; | ||
245 | struct list_head entities; | ||
246 | unsigned int num_channels; | ||
247 | unsigned int num_subdevs; | ||
248 | |||
249 | struct tegra_csi_device *csi; | ||
250 | struct list_head vi_chans; | ||
251 | struct tegra_channel *tpg_start; | ||
252 | void __iomem *iomem; | ||
253 | |||
254 | struct v4l2_ctrl_handler ctrl_handler; | ||
255 | struct v4l2_ctrl *pattern; | ||
256 | enum tegra_vi_pg_mode pg_mode; | ||
257 | |||
258 | bool has_sensors; | ||
259 | atomic_t power_on_refcnt; | ||
260 | unsigned int link_status; | ||
261 | unsigned int subdevs_bound; | ||
262 | struct mutex bw_update_lock; | ||
263 | unsigned long aggregated_kbyteps; | ||
264 | unsigned long max_requested_hz; | ||
265 | struct mutex mipical_lock; | ||
266 | |||
267 | bool bypass; | ||
268 | |||
269 | struct tegra_vi_fops *fops; | ||
270 | }; | ||
271 | |||
272 | int tegra_vi_get_port_info(struct tegra_channel *chan, | ||
273 | struct device_node *node, unsigned int index); | ||
274 | void tegra_vi_v4l2_cleanup(struct tegra_mc_vi *vi); | ||
275 | int tegra_vi_v4l2_init(struct tegra_mc_vi *vi); | ||
276 | int tegra_vi_tpg_graph_init(struct tegra_mc_vi *vi); | ||
277 | int tegra_vi_graph_init(struct tegra_mc_vi *vi); | ||
278 | void tegra_vi_graph_cleanup(struct tegra_mc_vi *vi); | ||
279 | int tegra_channel_init(struct tegra_channel *chan); | ||
280 | int tegra_vi_channels_register(struct tegra_mc_vi *vi); | ||
281 | void tegra_vi_channels_unregister(struct tegra_mc_vi *vi); | ||
282 | int tegra_vi_channels_init(struct tegra_mc_vi *vi); | ||
283 | int tegra_channel_cleanup(struct tegra_channel *chan); | ||
284 | int tegra_vi_channels_cleanup(struct tegra_mc_vi *vi); | ||
285 | int tegra_channel_init_subdevices(struct tegra_channel *chan); | ||
286 | int tegra_vi2_power_on(struct tegra_mc_vi *vi); | ||
287 | void tegra_vi2_power_off(struct tegra_mc_vi *vi); | ||
288 | int tegra_vi4_power_on(struct tegra_mc_vi *vi); | ||
289 | void tegra_vi4_power_off(struct tegra_mc_vi *vi); | ||
290 | int tegra_clean_unlinked_channels(struct tegra_mc_vi *vi); | ||
291 | int tegra_channel_s_ctrl(struct v4l2_ctrl *ctrl); | ||
292 | int tegra_vi_media_controller_init(struct tegra_mc_vi *mc_vi, | ||
293 | struct platform_device *pdev); | ||
294 | void tegra_vi_media_controller_cleanup(struct tegra_mc_vi *mc_vi); | ||
295 | void tegra_channel_ec_close(struct tegra_mc_vi *mc_vi); | ||
296 | void tegra_channel_query_hdmiin_unplug(struct tegra_channel *chan, | ||
297 | struct v4l2_event *event); | ||
298 | int tpg_vi_media_controller_init(struct tegra_mc_vi *mc_vi, int pg_mode); | ||
299 | void tpg_vi_media_controller_cleanup(struct tegra_mc_vi *mc_vi); | ||
300 | |||
301 | u32 tegra_core_get_fourcc_by_idx(struct tegra_channel *chan, | ||
302 | unsigned int index); | ||
303 | int tegra_core_get_idx_by_code(struct tegra_channel *chan, | ||
304 | unsigned int code, unsigned offset); | ||
305 | const struct tegra_video_format *tegra_core_get_format_by_code( | ||
306 | struct tegra_channel *chan, | ||
307 | unsigned int code, unsigned offset); | ||
308 | const struct tegra_video_format *tegra_core_get_format_by_fourcc( | ||
309 | struct tegra_channel *chan, u32 fourcc); | ||
310 | void tegra_core_get_description_by_idx(struct tegra_channel *chan, | ||
311 | unsigned int index, __u8 *description); | ||
312 | |||
313 | struct tegra_vi_fops { | ||
314 | int (*vi_power_on)(struct tegra_channel *chan); | ||
315 | void (*vi_power_off)(struct tegra_channel *chan); | ||
316 | int (*vi_start_streaming)(struct vb2_queue *vq, u32 count); | ||
317 | int (*vi_stop_streaming)(struct vb2_queue *vq); | ||
318 | int (*vi_add_ctrls)(struct tegra_channel *chan); | ||
319 | void (*vi_init_video_formats)(struct tegra_channel *chan); | ||
320 | long (*vi_default_ioctl)(struct file *file, void *fh, | ||
321 | bool use_prio, unsigned int cmd, void *arg); | ||
322 | }; | ||
323 | |||
324 | struct tegra_csi_fops { | ||
325 | int (*csi_power_on)(struct tegra_csi_device *csi); | ||
326 | int (*csi_power_off)(struct tegra_csi_device *csi); | ||
327 | int (*csi_start_streaming)(struct tegra_csi_channel *chan, | ||
328 | enum tegra_csi_port_num port_num); | ||
329 | void (*csi_stop_streaming)(struct tegra_csi_channel *chan, | ||
330 | enum tegra_csi_port_num port_num); | ||
331 | void (*csi_override_format)(struct tegra_csi_channel *chan, | ||
332 | enum tegra_csi_port_num port_num); | ||
333 | int (*mipical)(struct tegra_csi_channel *chan); | ||
334 | int (*hw_init)(struct tegra_csi_device *csi); | ||
335 | }; | ||
336 | |||
337 | struct tegra_t210_vi_data { | ||
338 | struct nvhost_device_data *info; | ||
339 | struct tegra_vi_fops *vi_fops; | ||
340 | struct tegra_csi_fops *csi_fops; | ||
341 | }; | ||
342 | |||
343 | struct tegra_vi_data { | ||
344 | struct nvhost_device_data *info; | ||
345 | struct tegra_vi_fops *vi_fops; | ||
346 | }; | ||
347 | |||
348 | struct tegra_csi_data { | ||
349 | struct nvhost_device_data *info; | ||
350 | struct tegra_csi_fops *csi_fops; | ||
351 | }; | ||
352 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/registers.h b/drivers/media/platform/tegra/camera/vi/registers.h new file mode 100644 index 000000000..176575dff --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/registers.h | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * drivers/media/platform/tegra/camera/registers.h | ||
3 | * | ||
4 | * Tegra VI/CSI register offsets | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #ifndef __REGISTERS_H__ | ||
22 | #define __REGISTERS_H__ | ||
23 | |||
24 | /* VI registers */ | ||
25 | #define TEGRA_VI_SYNCPT_WAIT_TIMEOUT 200 | ||
26 | #define TEGRA_VI_CFG_VI_INCR_SYNCPT 0x000 | ||
27 | #define VI_CFG_VI_INCR_SYNCPT_COND(x) (x << 8) | ||
28 | #define VI_CSI_PP_LINE_START(port) (4 + (port) * 4) | ||
29 | #define VI_CSI_PP_FRAME_START(port) (5 + (port) * 4) | ||
30 | #define VI_CSI_MW_REQ_DONE(port) (6 + (port) * 4) | ||
31 | #define VI_CSI_MW_ACK_DONE(port) (7 + (port) * 4) | ||
32 | |||
33 | #define TEGRA_VI_CFG_VI_INCR_SYNCPT_CNTRL 0x004 | ||
34 | #define TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR 0x008 | ||
35 | #define TEGRA_VI_CFG_CTXSW 0x020 | ||
36 | #define TEGRA_VI_CFG_INTSTATUS 0x024 | ||
37 | #define TEGRA_VI_CFG_PWM_CONTROL 0x038 | ||
38 | #define TEGRA_VI_CFG_PWM_HIGH_PULSE 0x03c | ||
39 | #define TEGRA_VI_CFG_PWM_LOW_PULSE 0x040 | ||
40 | #define TEGRA_VI_CFG_PWM_SELECT_PULSE_A 0x044 | ||
41 | #define TEGRA_VI_CFG_PWM_SELECT_PULSE_B 0x048 | ||
42 | #define TEGRA_VI_CFG_PWM_SELECT_PULSE_C 0x04c | ||
43 | #define TEGRA_VI_CFG_PWM_SELECT_PULSE_D 0x050 | ||
44 | #define TEGRA_VI_CFG_VGP1 0x064 | ||
45 | #define TEGRA_VI_CFG_VGP2 0x068 | ||
46 | #define TEGRA_VI_CFG_VGP3 0x06c | ||
47 | #define TEGRA_VI_CFG_VGP4 0x070 | ||
48 | #define TEGRA_VI_CFG_VGP5 0x074 | ||
49 | #define TEGRA_VI_CFG_VGP6 0x078 | ||
50 | #define TEGRA_VI_CFG_INTERRUPT_MASK 0x08c | ||
51 | #define TEGRA_VI_CFG_INTERRUPT_TYPE_SELECT 0x090 | ||
52 | #define TEGRA_VI_CFG_INTERRUPT_POLARITY_SELECT 0x094 | ||
53 | #define TEGRA_VI_CFG_INTERRUPT_STATUS 0x098 | ||
54 | #define TEGRA_VI_CFG_VGP_SYNCPT_CONFIG 0x0ac | ||
55 | #define TEGRA_VI_CFG_VI_SW_RESET 0x0b4 | ||
56 | #define TEGRA_VI_CFG_CG_CTRL 0x0b8 | ||
57 | #define VI_CG_2ND_LEVEL_EN 0x1 | ||
58 | #define TEGRA_VI_CFG_VI_MCCIF_FIFOCTRL 0x0e4 | ||
59 | #define TEGRA_VI_CFG_TIMEOUT_WCOAL_VI 0x0e8 | ||
60 | #define TEGRA_VI_CFG_DVFS 0x0f0 | ||
61 | #define TEGRA_VI_CFG_RESERVE 0x0f4 | ||
62 | #define TEGRA_VI_CFG_RESERVE_1 0x0f8 | ||
63 | |||
64 | /* CSI registers */ | ||
65 | #define TEGRA_VI_CSI_BASE(x) (0x100 + (x) * 0x100) | ||
66 | |||
67 | #define TEGRA_VI_CSI_SW_RESET 0x000 | ||
68 | #define TEGRA_VI_CSI_SINGLE_SHOT 0x004 | ||
69 | #define SINGLE_SHOT_CAPTURE 0x1 | ||
70 | #define CAPTURE_GOOD_FRAME 0x1 | ||
71 | #define TEGRA_VI_CSI_SINGLE_SHOT_STATE_UPDATE 0x008 | ||
72 | #define TEGRA_VI_CSI_IMAGE_DEF 0x00c | ||
73 | #define BYPASS_PXL_TRANSFORM_OFFSET 24 | ||
74 | #define IMAGE_DEF_FORMAT_OFFSET 16 | ||
75 | #define IMAGE_DEF_DEST_MEM 0x1 | ||
76 | #define TEGRA_VI_CSI_RGB2Y_CTRL 0x010 | ||
77 | #define TEGRA_VI_CSI_MEM_TILING 0x014 | ||
78 | #define TEGRA_VI_CSI_IMAGE_SIZE 0x018 | ||
79 | #define IMAGE_SIZE_HEIGHT_OFFSET 16 | ||
80 | #define TEGRA_VI_CSI_IMAGE_SIZE_WC 0x01c | ||
81 | #define TEGRA_VI_CSI_IMAGE_DT 0x020 | ||
82 | #define TEGRA_VI_CSI_SURFACE0_OFFSET_MSB 0x024 | ||
83 | #define TEGRA_VI_CSI_SURFACE0_OFFSET_LSB 0x028 | ||
84 | #define TEGRA_VI_CSI_SURFACE1_OFFSET_MSB 0x02c | ||
85 | #define TEGRA_VI_CSI_SURFACE1_OFFSET_LSB 0x030 | ||
86 | #define TEGRA_VI_CSI_SURFACE2_OFFSET_MSB 0x034 | ||
87 | #define TEGRA_VI_CSI_SURFACE2_OFFSET_LSB 0x038 | ||
88 | #define TEGRA_VI_CSI_SURFACE0_BF_OFFSET_MSB 0x03c | ||
89 | #define TEGRA_VI_CSI_SURFACE0_BF_OFFSET_LSB 0x040 | ||
90 | #define TEGRA_VI_CSI_SURFACE1_BF_OFFSET_MSB 0x044 | ||
91 | #define TEGRA_VI_CSI_SURFACE1_BF_OFFSET_LSB 0x048 | ||
92 | #define TEGRA_VI_CSI_SURFACE2_BF_OFFSET_MSB 0x04c | ||
93 | #define TEGRA_VI_CSI_SURFACE2_BF_OFFSET_LSB 0x050 | ||
94 | #define TEGRA_VI_CSI_SURFACE0_STRIDE 0x054 | ||
95 | #define TEGRA_VI_CSI_SURFACE1_STRIDE 0x058 | ||
96 | #define TEGRA_VI_CSI_SURFACE2_STRIDE 0x05c | ||
97 | #define TEGRA_VI_CSI_SURFACE_HEIGHT0 0x060 | ||
98 | #define TEGRA_VI_CSI_ISPINTF_CONFIG 0x064 | ||
99 | #define TEGRA_VI_CSI_ERROR_STATUS 0x084 | ||
100 | #define TEGRA_VI_CSI_ERROR_INT_MASK 0x088 | ||
101 | #define TEGRA_VI_CSI_WD_CTRL 0x08c | ||
102 | #define TEGRA_VI_CSI_WD_PERIOD 0x090 | ||
103 | |||
104 | /* CSI Pixel Parser registers: Starts from 0x838, offset 0x0 */ | ||
105 | #define TEGRA_CSI_INPUT_STREAM_CONTROL 0x000 | ||
106 | #define CSI_SKIP_PACKET_THRESHOLD_OFFSET 16 | ||
107 | |||
108 | #define TEGRA_CSI_PIXEL_STREAM_CONTROL0 0x004 | ||
109 | #define CSI_PP_PACKET_HEADER_SENT (0x1 << 4) | ||
110 | #define CSI_PP_DATA_IDENTIFIER_ENABLE (0x1 << 5) | ||
111 | #define CSI_PP_WORD_COUNT_SELECT_HEADER (0x1 << 6) | ||
112 | #define CSI_PP_CRC_CHECK_ENABLE (0x1 << 7) | ||
113 | #define CSI_PP_WC_CHECK (0x1 << 8) | ||
114 | #define CSI_PP_OUTPUT_FORMAT_STORE (0x3 << 16) | ||
115 | #define CSI_PPA_PAD_LINE_NOPAD (0x2 << 24) | ||
116 | #define CSI_PP_HEADER_EC_DISABLE (0x1 << 27) | ||
117 | #define CSI_PPA_PAD_FRAME_NOPAD (0x2 << 28) | ||
118 | |||
119 | #define TEGRA_CSI_PIXEL_STREAM_CONTROL1 0x008 | ||
120 | #define CSI_PP_TOP_FIELD_FRAME_OFFSET 0 | ||
121 | #define CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET 4 | ||
122 | |||
123 | #define TEGRA_CSI_PIXEL_STREAM_GAP 0x00c | ||
124 | #define PP_FRAME_MIN_GAP_OFFSET 16 | ||
125 | |||
126 | #define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND 0x010 | ||
127 | #define CSI_PP_ENABLE 0x1 | ||
128 | #define CSI_PP_DISABLE 0x2 | ||
129 | #define CSI_PP_RST 0x3 | ||
130 | #define CSI_PP_SINGLE_SHOT_ENABLE (0x1 << 2) | ||
131 | #define CSI_PP_START_MARKER_FRAME_MAX_OFFSET 12 | ||
132 | |||
133 | #define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME 0x014 | ||
134 | #define TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x018 | ||
135 | #define TEGRA_CSI_PIXEL_PARSER_STATUS 0x01c | ||
136 | #define TEGRA_CSI_CSI_SW_SENSOR_RESET 0x020 | ||
137 | |||
138 | /* CSI PHY registers */ | ||
139 | /* CSI_PHY_CIL_COMMAND_0 offset 0x0d0 from TEGRA_CSI_PIXEL_PARSER_0_BASE */ | ||
140 | #define TEGRA_CSI_PHY_CIL_COMMAND 0x0d0 | ||
141 | #define CSI_A_PHY_CIL_ENABLE 0x1 | ||
142 | #define CSI_B_PHY_CIL_ENABLE (0x1 << 8) | ||
143 | |||
144 | /* CSI CIL registers: Starts from 0x92c, offset 0xF4 */ | ||
145 | #define TEGRA_CSI_CIL_OFFSET 0x0f4 | ||
146 | |||
147 | #define TEGRA_CSI_CIL_PAD_CONFIG0 0x000 | ||
148 | #define BRICK_CLOCK_A_4X (0x1 << 16) | ||
149 | #define BRICK_CLOCK_B_4X (0x2 << 16) | ||
150 | #define TEGRA_CSI_CIL_PAD_CONFIG1 0x004 | ||
151 | #define TEGRA_CSI_CIL_PHY_CONTROL 0x008 | ||
152 | #define BYPASS_LP_SEQ (0x1 << 6) | ||
153 | #define TEGRA_CSI_CIL_INTERRUPT_MASK 0x00c | ||
154 | #define TEGRA_CSI_CIL_STATUS 0x010 | ||
155 | #define TEGRA_CSI_CILX_STATUS 0x014 | ||
156 | #define TEGRA_CSI_CIL_ESCAPE_MODE_COMMAND 0x018 | ||
157 | #define TEGRA_CSI_CIL_ESCAPE_MODE_DATA 0x01c | ||
158 | #define TEGRA_CSI_CIL_SW_SENSOR_RESET 0x020 | ||
159 | |||
160 | /* CSI Pattern Generator registers: Starts from 0x9c4, offset 0x18c */ | ||
161 | #define TEGRA_CSI_TPG_OFFSET 0x18c | ||
162 | |||
163 | #define TEGRA_CSI_PATTERN_GENERATOR_CTRL 0x000 | ||
164 | #define PG_MODE_OFFSET 2 | ||
165 | #define PG_ENABLE 0x1 | ||
166 | #define PG_DISABLE 0x0 | ||
167 | |||
168 | #define PG_VBLANK_OFFSET 16 | ||
169 | #define TEGRA_CSI_PG_BLANK 0x004 | ||
170 | #define TEGRA_CSI_PG_PHASE 0x008 | ||
171 | #define TEGRA_CSI_PG_RED_FREQ 0x00c | ||
172 | #define PG_RED_VERT_INIT_FREQ_OFFSET 16 | ||
173 | #define PG_RED_HOR_INIT_FREQ_OFFSET 0 | ||
174 | |||
175 | #define TEGRA_CSI_PG_RED_FREQ_RATE 0x010 | ||
176 | #define TEGRA_CSI_PG_GREEN_FREQ 0x014 | ||
177 | #define PG_GREEN_VERT_INIT_FREQ_OFFSET 16 | ||
178 | #define PG_GREEN_HOR_INIT_FREQ_OFFSET 0 | ||
179 | |||
180 | #define TEGRA_CSI_PG_GREEN_FREQ_RATE 0x018 | ||
181 | #define TEGRA_CSI_PG_BLUE_FREQ 0x01c | ||
182 | #define PG_BLUE_VERT_INIT_FREQ_OFFSET 16 | ||
183 | #define PG_BLUE_HOR_INIT_FREQ_OFFSET 0 | ||
184 | |||
185 | #define TEGRA_CSI_PG_BLUE_FREQ_RATE 0x020 | ||
186 | #define TEGRA_CSI_PG_AOHDR 0x024 | ||
187 | |||
188 | #define TEGRA_CSI_DPCM_CTRL_A 0xa2c | ||
189 | #define TEGRA_CSI_DPCM_CTRL_B 0xa30 | ||
190 | |||
191 | /* Other CSI registers: Starts from 0xa44, offset 0x20c */ | ||
192 | #define TEGRA_CSI_STALL_COUNTER 0x20c | ||
193 | #define TEGRA_CSI_CSI_READONLY_STATUS 0x210 | ||
194 | #define TEGRA_CSI_CSI_SW_STATUS_RESET 0x214 | ||
195 | #define TEGRA_CSI_CLKEN_OVERRIDE 0x218 | ||
196 | #define TEGRA_CSI_DEBUG_CONTROL 0x21c | ||
197 | #define TEGRA_CSI_DEBUG_COUNTER_0 0x220 | ||
198 | #define TEGRA_CSI_DEBUG_COUNTER_1 0x224 | ||
199 | #define TEGRA_CSI_DEBUG_COUNTER_2 0x228 | ||
200 | |||
201 | |||
202 | /* CSI Pixel Parser registers */ | ||
203 | #define TEGRA_CSI_PIXEL_PARSER_0_BASE 0x0838 | ||
204 | #define TEGRA_CSI_PIXEL_PARSER_1_BASE 0x086c | ||
205 | #define TEGRA_CSI_PIXEL_PARSER_2_BASE 0x1038 | ||
206 | #define TEGRA_CSI_PIXEL_PARSER_3_BASE 0x106c | ||
207 | #define TEGRA_CSI_PIXEL_PARSER_4_BASE 0x1838 | ||
208 | #define TEGRA_CSI_PIXEL_PARSER_5_BASE 0x186c | ||
209 | |||
210 | /* CSIA to CSIB register offset */ | ||
211 | #define TEGRA_CSI_PORT_OFFSET 0x34 | ||
212 | |||
213 | #define INVALID_CSI_PORT 0xFF | ||
214 | #define TEGRA_CSI_BLOCKS 3 | ||
215 | #define SYNCPT_FIFO_DEPTH 2 | ||
216 | #define PREVIOUS_BUFFER_DEC_INDEX 2 | ||
217 | |||
218 | #define TEGRA_CLOCK_VI_MAX 793600000 | ||
219 | #define TEGRA_CLOCK_TPG 927000000 | ||
220 | #define TEGRA_CLOCK_CSI_PORT_MAX 102000000 | ||
221 | |||
222 | #define TEGRA_SURFACE_ALIGNMENT 64 | ||
223 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi2_fops.c b/drivers/media/platform/tegra/camera/vi/vi2_fops.c new file mode 100644 index 000000000..fa081b9e0 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi2_fops.c | |||
@@ -0,0 +1,825 @@ | |||
1 | /* | ||
2 | * Tegra Video Input 2 device common APIs | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bryan Wu <pengw@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/nvhost.h> | ||
14 | #include <linux/tegra-powergate.h> | ||
15 | #include <linux/kthread.h> | ||
16 | #include <linux/freezer.h> | ||
17 | #include <media/tegra_camera_platform.h> | ||
18 | #include "nvhost_acm.h" | ||
19 | #include "camera/vi/mc_common.h" | ||
20 | #include "camera/csi/csi2_fops.h" | ||
21 | #include "vi2_formats.h" | ||
22 | #include "vi/vi.h" | ||
23 | |||
24 | #define DEFAULT_FRAMERATE 30 | ||
25 | #define DEFAULT_CSI_FREQ 204000000 | ||
26 | #define BPP_MEM 2 | ||
27 | #define NUM_PPC 2 | ||
28 | #define VI_CSI_CLK_SCALE 110 | ||
29 | |||
30 | extern void tegra_channel_queued_buf_done(struct tegra_channel *chan, | ||
31 | enum vb2_buffer_state state); | ||
32 | static void tegra_channel_stop_kthreads(struct tegra_channel *chan); | ||
33 | extern int tegra_channel_set_stream(struct tegra_channel *chan, bool on); | ||
34 | extern void tegra_channel_ring_buffer(struct tegra_channel *chan, | ||
35 | struct vb2_v4l2_buffer *vb, | ||
36 | struct timespec *ts, int state); | ||
37 | extern struct tegra_channel_buffer *dequeue_buffer(struct tegra_channel *chan); | ||
38 | extern void tegra_channel_init_ring_buffer(struct tegra_channel *chan); | ||
39 | extern void free_ring_buffers(struct tegra_channel *chan, int frames); | ||
40 | extern int tegra_channel_set_power(struct tegra_channel *chan, bool on); | ||
41 | |||
42 | static void vi_write(struct tegra_mc_vi *vi, unsigned int addr, u32 val) | ||
43 | { | ||
44 | writel(val, vi->iomem + addr); | ||
45 | } | ||
46 | |||
47 | static u32 tegra_channel_read(struct tegra_channel *chan, | ||
48 | unsigned int addr) | ||
49 | { | ||
50 | return readl(chan->vi->iomem + addr); | ||
51 | } | ||
52 | |||
53 | static void tegra_channel_write(struct tegra_channel *chan, | ||
54 | unsigned int addr, u32 val) | ||
55 | { | ||
56 | writel(val, chan->vi->iomem + addr); | ||
57 | } | ||
58 | |||
59 | /* CSI registers */ | ||
60 | static void csi_write(struct tegra_channel *chan, unsigned int index, | ||
61 | unsigned int addr, u32 val) | ||
62 | { | ||
63 | writel(val, chan->csibase[index] + addr); | ||
64 | } | ||
65 | |||
66 | static u32 csi_read(struct tegra_channel *chan, unsigned int index, | ||
67 | unsigned int addr) | ||
68 | { | ||
69 | return readl(chan->csibase[index] + addr); | ||
70 | } | ||
71 | |||
72 | static void vi_channel_syncpt_init(struct tegra_channel *chan) | ||
73 | { | ||
74 | int i; | ||
75 | |||
76 | for (i = 0; i < chan->total_ports; i++) | ||
77 | chan->syncpt[i][0] = | ||
78 | nvhost_get_syncpt_client_managed(chan->vi->ndev, "vi"); | ||
79 | } | ||
80 | |||
81 | static void vi_channel_syncpt_free(struct tegra_channel *chan) | ||
82 | { | ||
83 | int i; | ||
84 | |||
85 | for (i = 0; i < chan->total_ports; i++) | ||
86 | nvhost_syncpt_put_ref_ext(chan->vi->ndev, chan->syncpt[i][0]); | ||
87 | } | ||
88 | |||
89 | void vi2_init_video_formats(struct tegra_channel *chan) | ||
90 | { | ||
91 | int i; | ||
92 | |||
93 | chan->num_video_formats = ARRAY_SIZE(vi2_video_formats); | ||
94 | for (i = 0; i < chan->num_video_formats; i++) | ||
95 | chan->video_formats[i] = &vi2_video_formats[i]; | ||
96 | } | ||
97 | |||
98 | int tegra_vi2_s_ctrl(struct v4l2_ctrl *ctrl) | ||
99 | { | ||
100 | struct tegra_channel *chan = container_of(ctrl->handler, | ||
101 | struct tegra_channel, ctrl_handler); | ||
102 | int err = 0; | ||
103 | |||
104 | switch (ctrl->id) { | ||
105 | case V4L2_CID_WRITE_ISPFORMAT: | ||
106 | chan->write_ispformat = ctrl->val; | ||
107 | break; | ||
108 | default: | ||
109 | dev_err(&chan->video.dev, "%s:Not valid ctrl\n", __func__); | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | |||
113 | return err; | ||
114 | } | ||
115 | |||
116 | static const struct v4l2_ctrl_ops vi2_ctrl_ops = { | ||
117 | .s_ctrl = tegra_vi2_s_ctrl, | ||
118 | }; | ||
119 | |||
120 | static const struct v4l2_ctrl_config vi2_custom_ctrls[] = { | ||
121 | { | ||
122 | .ops = &vi2_ctrl_ops, | ||
123 | .id = V4L2_CID_WRITE_ISPFORMAT, | ||
124 | .name = "Write ISP format", | ||
125 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
126 | .def = 0, | ||
127 | .min = 0, | ||
128 | .step = 1, | ||
129 | .max = 1, | ||
130 | }, | ||
131 | }; | ||
132 | |||
133 | int vi2_add_ctrls(struct tegra_channel *chan) | ||
134 | { | ||
135 | int i; | ||
136 | |||
137 | /* Add vi2 custom controls */ | ||
138 | for (i = 0; i < ARRAY_SIZE(vi2_custom_ctrls); i++) { | ||
139 | v4l2_ctrl_new_custom(&chan->ctrl_handler, | ||
140 | &vi2_custom_ctrls[i], NULL); | ||
141 | if (chan->ctrl_handler.error) { | ||
142 | dev_err(chan->vi->dev, | ||
143 | "Failed to add %s ctrl\n", | ||
144 | vi2_custom_ctrls[i].name); | ||
145 | return chan->ctrl_handler.error; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static struct tegra_csi_channel *find_linked_csi_channel( | ||
153 | struct tegra_channel *chan, struct tegra_csi_device *csi) | ||
154 | { | ||
155 | struct tegra_csi_channel *csi_it; | ||
156 | struct tegra_csi_channel *csi_chan = NULL; | ||
157 | int i; | ||
158 | /* Find connected csi_channel */ | ||
159 | list_for_each_entry(csi_it, &csi->csi_chans, list) { | ||
160 | for (i = 0; i < chan->num_subdevs; i++) { | ||
161 | if (chan->subdev[i] == &csi_it->subdev) { | ||
162 | csi_chan = csi_it; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | return csi_chan; | ||
168 | } | ||
169 | static int tegra_channel_capture_setup(struct tegra_channel *chan) | ||
170 | { | ||
171 | u32 height = chan->format.height; | ||
172 | u32 width = chan->format.width; | ||
173 | u32 format = chan->fmtinfo->img_fmt; | ||
174 | u32 data_type = chan->fmtinfo->img_dt; | ||
175 | u32 word_count = tegra_core_get_word_count(width, chan->fmtinfo); | ||
176 | u32 bypass_pixel_transform = 1; | ||
177 | int index; | ||
178 | |||
179 | if (chan->valid_ports > 1) { | ||
180 | height = chan->gang_height; | ||
181 | width = chan->gang_width; | ||
182 | word_count = tegra_core_get_word_count(width, chan->fmtinfo); | ||
183 | } | ||
184 | |||
185 | if (chan->pg_mode || | ||
186 | (chan->write_ispformat == TEGRA_ISP_FORMAT) || | ||
187 | (chan->fmtinfo->vf_code == TEGRA_VF_YUV422) || | ||
188 | (chan->fmtinfo->vf_code == TEGRA_VF_RGB888)) | ||
189 | bypass_pixel_transform = 0; | ||
190 | |||
191 | for (index = 0; index < chan->valid_ports; index++) { | ||
192 | csi_write(chan, index, TEGRA_VI_CSI_ERROR_STATUS, 0xFFFFFFFF); | ||
193 | csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DEF, | ||
194 | (bypass_pixel_transform << BYPASS_PXL_TRANSFORM_OFFSET) | | ||
195 | (format << IMAGE_DEF_FORMAT_OFFSET)); | ||
196 | csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DT, data_type); | ||
197 | csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count); | ||
198 | csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE, | ||
199 | (height << IMAGE_SIZE_HEIGHT_OFFSET) | width); | ||
200 | } | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int tegra_channel_enable_stream(struct tegra_channel *chan) | ||
206 | { | ||
207 | int ret = 0; | ||
208 | struct tegra_csi_channel *csi_chan = NULL; | ||
209 | struct tegra_csi_device *csi = chan->vi->csi; | ||
210 | /* | ||
211 | * enable pad power and perform calibration before arming | ||
212 | * single shot for first frame after the HW setup is complete | ||
213 | */ | ||
214 | /* Find connected csi_channel */ | ||
215 | csi_chan = find_linked_csi_channel(chan, csi); | ||
216 | |||
217 | /* start streaming */ | ||
218 | ret = tegra_channel_set_stream(chan, true); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static void tegra_channel_ec_init(struct tegra_channel *chan) | ||
223 | { | ||
224 | /* | ||
225 | * error recover initialization sequence | ||
226 | * set timeout as 200 ms, use default if fps not available | ||
227 | * Time limit allow CSI to capture good frames and drop error frames | ||
228 | * Timeout units is jiffies, 1 jiffy = 10ms | ||
229 | * TODO: Get frame rate from sub-device and adopt timeout | ||
230 | */ | ||
231 | chan->timeout = 20; | ||
232 | |||
233 | /* | ||
234 | * Sync point FIFO full blocks host interface | ||
235 | * Below setting enables SW to process error recovery | ||
236 | */ | ||
237 | tegra_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_CNTRL, 0x100); | ||
238 | } | ||
239 | |||
240 | |||
241 | static void tegra_channel_clear_singleshot(struct tegra_channel *chan, | ||
242 | int index) | ||
243 | { | ||
244 | /* clear single shot */ | ||
245 | csi_write(chan, index, TEGRA_VI_CSI_SW_RESET, 0xF); | ||
246 | csi_write(chan, index, TEGRA_VI_CSI_SW_RESET, 0x0); | ||
247 | } | ||
248 | |||
249 | static void tegra_channel_vi_csi_recover(struct tegra_channel *chan) | ||
250 | { | ||
251 | u32 error_val = tegra_channel_read(chan, | ||
252 | TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR); | ||
253 | u32 frame_start; | ||
254 | int index, valid_ports = chan->valid_ports; | ||
255 | struct tegra_csi_channel *csi_chan; | ||
256 | struct tegra_csi_device *csi = chan->vi->csi; | ||
257 | |||
258 | /* Disable clock gating to enable continuous clock */ | ||
259 | tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE); | ||
260 | /* Find connected csi_channel */ | ||
261 | csi_chan = find_linked_csi_channel(chan, csi); | ||
262 | |||
263 | /* clear CSI state */ | ||
264 | for (index = 0; index < valid_ports; index++) { | ||
265 | tegra_csi_error_recover(csi_chan, index); | ||
266 | csi_write(chan, index, | ||
267 | TEGRA_VI_CSI_IMAGE_DEF, 0); | ||
268 | tegra_channel_clear_singleshot(chan, index); | ||
269 | } | ||
270 | |||
271 | /* clear VI errors */ | ||
272 | for (index = 0; index < valid_ports; index++) { | ||
273 | frame_start = VI_CSI_PP_FRAME_START(chan->port[index]); | ||
274 | if (error_val & frame_start) | ||
275 | chan->syncpoint_fifo[index] = SYNCPT_FIFO_DEPTH; | ||
276 | } | ||
277 | /* clear FIFO error status */ | ||
278 | tegra_channel_write(chan, | ||
279 | TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, error_val); | ||
280 | |||
281 | /* Enable clock gating so VI can be clock gated if necessary */ | ||
282 | tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE); | ||
283 | |||
284 | /* re-init VI and CSI */ | ||
285 | tegra_channel_capture_setup(chan); | ||
286 | for (index = 0; index < valid_ports; index++) { | ||
287 | csi2_stop_streaming(csi_chan, index); | ||
288 | csi2_start_streaming(csi_chan, index); | ||
289 | nvhost_syncpt_set_min_eq_max_ext(chan->vi->ndev, | ||
290 | chan->syncpt[index][0]); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | static void tegra_channel_capture_error(struct tegra_channel *chan) | ||
295 | { | ||
296 | u32 val; | ||
297 | int index = 0; | ||
298 | struct tegra_csi_channel *csi_chan; | ||
299 | struct tegra_csi_device *csi = chan->vi->csi; | ||
300 | |||
301 | /* Find connected csi_channel */ | ||
302 | csi_chan = find_linked_csi_channel(chan, csi); | ||
303 | |||
304 | for (index = 0; index < chan->valid_ports; index++) { | ||
305 | val = csi_read(chan, index, TEGRA_VI_CSI_ERROR_STATUS); | ||
306 | dev_dbg(&chan->video.dev, | ||
307 | "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val); | ||
308 | tegra_csi_status(csi_chan, index); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | static void tegra_channel_ec_recover(struct tegra_channel *chan) | ||
313 | { | ||
314 | tegra_channel_capture_error(chan); | ||
315 | tegra_channel_vi_csi_recover(chan); | ||
316 | } | ||
317 | |||
318 | static int tegra_channel_error_status(struct tegra_channel *chan) | ||
319 | { | ||
320 | u32 val; | ||
321 | int err = 0; | ||
322 | int index = 0; | ||
323 | struct tegra_csi_channel *csi_chan; | ||
324 | struct tegra_csi_device *csi = chan->vi->csi; | ||
325 | |||
326 | /* Find connected csi_channel */ | ||
327 | csi_chan = find_linked_csi_channel(chan, csi); | ||
328 | |||
329 | for (index = 0; index < chan->valid_ports; index++) { | ||
330 | /* Ignore error based on resolution but reset status */ | ||
331 | val = csi_read(chan, index, TEGRA_VI_CSI_ERROR_STATUS); | ||
332 | csi_write(chan, index, TEGRA_VI_CSI_ERROR_STATUS, val); | ||
333 | err = tegra_csi_error(csi_chan, index); | ||
334 | } | ||
335 | |||
336 | if (err) | ||
337 | dev_err(chan->vi->dev, "%s:error %x frame %d\n", | ||
338 | __func__, err, chan->sequence); | ||
339 | return err; | ||
340 | } | ||
341 | |||
342 | static int tegra_channel_capture_frame(struct tegra_channel *chan, | ||
343 | struct tegra_channel_buffer *buf) | ||
344 | { | ||
345 | struct vb2_v4l2_buffer *vb = &buf->buf; | ||
346 | struct timespec ts; | ||
347 | int err = 0; | ||
348 | u32 val, frame_start; | ||
349 | int bytes_per_line = chan->format.bytesperline; | ||
350 | int index = 0; | ||
351 | u32 thresh[TEGRA_CSI_BLOCKS] = { 0 }; | ||
352 | int valid_ports = chan->valid_ports; | ||
353 | int state = VB2_BUF_STATE_DONE; | ||
354 | |||
355 | /* Init registers related to each frames */ | ||
356 | for (index = 0; index < valid_ports; index++) { | ||
357 | /* Program buffer address by using surface 0 */ | ||
358 | csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0); | ||
359 | csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, | ||
360 | (buf->addr + chan->buffer_offset[index])); | ||
361 | csi_write(chan, index, | ||
362 | TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line); | ||
363 | |||
364 | /* Program syncpoints */ | ||
365 | thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev, | ||
366 | chan->syncpt[index][0], 1); | ||
367 | /* Do not arm sync points if FIFO had entries before */ | ||
368 | if (!chan->syncpoint_fifo[index]) { | ||
369 | frame_start = VI_CSI_PP_FRAME_START(chan->port[index]); | ||
370 | val = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) | | ||
371 | chan->syncpt[index][0]; | ||
372 | tegra_channel_write(chan, | ||
373 | TEGRA_VI_CFG_VI_INCR_SYNCPT, val); | ||
374 | } else | ||
375 | chan->syncpoint_fifo[index]--; | ||
376 | } | ||
377 | |||
378 | /* enable input stream once the VI registers are configured */ | ||
379 | if (!chan->bfirst_fstart) { | ||
380 | err = tegra_channel_enable_stream(chan); | ||
381 | if (err) { | ||
382 | state = VB2_BUF_STATE_ERROR; | ||
383 | chan->capture_state = CAPTURE_ERROR; | ||
384 | tegra_channel_ring_buffer(chan, vb, &ts, state); | ||
385 | return err; | ||
386 | } | ||
387 | /* Bit controls VI memory write, enable after all regs */ | ||
388 | for (index = 0; index < valid_ports; index++) { | ||
389 | val = csi_read(chan, index, | ||
390 | TEGRA_VI_CSI_IMAGE_DEF); | ||
391 | csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DEF, | ||
392 | val | IMAGE_DEF_DEST_MEM); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | /* Ensure all CSI ports are ready with setup to avoid timing issue */ | ||
397 | for (index = 0; index < valid_ports; index++) | ||
398 | csi_write(chan, index, | ||
399 | TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE); | ||
400 | |||
401 | chan->capture_state = CAPTURE_GOOD; | ||
402 | for (index = 0; index < valid_ports; index++) { | ||
403 | err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev, | ||
404 | chan->syncpt[index][0], thresh[index], | ||
405 | chan->timeout, NULL, &ts); | ||
406 | if (err) { | ||
407 | dev_err(&chan->video.dev, | ||
408 | "frame start syncpt timeout!%d\n", index); | ||
409 | state = VB2_BUF_STATE_ERROR; | ||
410 | /* perform error recovery for timeout */ | ||
411 | tegra_channel_ec_recover(chan); | ||
412 | chan->capture_state = CAPTURE_TIMEOUT; | ||
413 | break; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | if (!err && !chan->pg_mode) { | ||
418 | /* Marking error frames and resume capture */ | ||
419 | /* TODO: TPG has frame height short error always set */ | ||
420 | err = tegra_channel_error_status(chan); | ||
421 | if (err) { | ||
422 | state = VB2_BUF_STATE_ERROR; | ||
423 | chan->capture_state = CAPTURE_ERROR; | ||
424 | /* do we have to run recover here ?? */ | ||
425 | /* tegra_channel_ec_recover(chan); */ | ||
426 | } | ||
427 | } | ||
428 | |||
429 | tegra_channel_ring_buffer(chan, vb, &ts, state); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static void tegra_channel_capture_done(struct tegra_channel *chan) | ||
435 | { | ||
436 | struct timespec ts; | ||
437 | int index, err; | ||
438 | int bytes_per_line = chan->format.bytesperline; | ||
439 | u32 val, mw_ack_done; | ||
440 | u32 thresh[TEGRA_CSI_BLOCKS] = { 0 }; | ||
441 | struct tegra_channel_buffer *buf; | ||
442 | int state = VB2_BUF_STATE_DONE; | ||
443 | |||
444 | /* dequeue buffer and return if no buffer exists */ | ||
445 | buf = dequeue_buffer(chan); | ||
446 | if (!buf) | ||
447 | return; | ||
448 | |||
449 | for (index = 0; index < chan->valid_ports; index++) { | ||
450 | /* Program buffer address by using surface 0 */ | ||
451 | csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0); | ||
452 | csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, | ||
453 | (buf->addr + chan->buffer_offset[index])); | ||
454 | csi_write(chan, index, | ||
455 | TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line); | ||
456 | |||
457 | /* Program syncpoints */ | ||
458 | thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev, | ||
459 | chan->syncpt[index][0], 1); | ||
460 | mw_ack_done = VI_CSI_MW_ACK_DONE(chan->port[index]); | ||
461 | val = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) | | ||
462 | chan->syncpt[index][0]; | ||
463 | tegra_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, val); | ||
464 | csi_write(chan, index, | ||
465 | TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE); | ||
466 | } | ||
467 | |||
468 | for (index = 0; index < chan->valid_ports; index++) { | ||
469 | err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev, | ||
470 | chan->syncpt[index][0], thresh[index], | ||
471 | chan->timeout, NULL, &ts); | ||
472 | if (err) { | ||
473 | dev_err(&chan->video.dev, | ||
474 | "MW_ACK_DONE syncpoint time out!%d\n", index); | ||
475 | state = VB2_BUF_STATE_ERROR; | ||
476 | /* perform error recovery for timeout */ | ||
477 | tegra_channel_ec_recover(chan); | ||
478 | chan->capture_state = CAPTURE_TIMEOUT; | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | /* Mark capture state to IDLE as capture is finished */ | ||
484 | chan->capture_state = CAPTURE_IDLE; | ||
485 | |||
486 | tegra_channel_ring_buffer(chan, &buf->buf, &ts, state); | ||
487 | } | ||
488 | |||
489 | static int tegra_channel_kthread_capture_start(void *data) | ||
490 | { | ||
491 | struct tegra_channel *chan = data; | ||
492 | struct tegra_channel_buffer *buf; | ||
493 | int err = 0; | ||
494 | |||
495 | set_freezable(); | ||
496 | |||
497 | while (1) { | ||
498 | |||
499 | try_to_freeze(); | ||
500 | |||
501 | wait_event_interruptible(chan->start_wait, | ||
502 | !list_empty(&chan->capture) || | ||
503 | kthread_should_stop()); | ||
504 | |||
505 | if (kthread_should_stop()) | ||
506 | break; | ||
507 | |||
508 | /* source is not streaming if error is non-zero */ | ||
509 | /* wait till kthread stop and dont DeQ buffers */ | ||
510 | if (err) | ||
511 | continue; | ||
512 | |||
513 | buf = dequeue_buffer(chan); | ||
514 | if (!buf) | ||
515 | continue; | ||
516 | |||
517 | err = tegra_channel_capture_frame(chan, buf); | ||
518 | } | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | static void tegra_channel_stop_kthreads(struct tegra_channel *chan) | ||
524 | { | ||
525 | mutex_lock(&chan->stop_kthread_lock); | ||
526 | /* Stop the kthread for capture */ | ||
527 | if (chan->kthread_capture_start) { | ||
528 | kthread_stop(chan->kthread_capture_start); | ||
529 | chan->kthread_capture_start = NULL; | ||
530 | } | ||
531 | mutex_unlock(&chan->stop_kthread_lock); | ||
532 | } | ||
533 | |||
534 | static int tegra_channel_update_clknbw( | ||
535 | struct tegra_channel *chan, u8 on) __maybe_unused; | ||
536 | static int tegra_channel_update_clknbw(struct tegra_channel *chan, u8 on) | ||
537 | { | ||
538 | int ret = 0; | ||
539 | unsigned long request_pixelrate; | ||
540 | struct v4l2_subdev_frame_interval fie; | ||
541 | unsigned long csi_freq = 0; | ||
542 | |||
543 | fie.interval.denominator = DEFAULT_FRAMERATE; | ||
544 | fie.interval.numerator = 1; | ||
545 | |||
546 | if (v4l2_subdev_has_op(chan->subdev_on_csi, | ||
547 | video, g_frame_interval)) | ||
548 | v4l2_subdev_call(chan->subdev_on_csi, video, | ||
549 | g_frame_interval, &fie); | ||
550 | if (on) { | ||
551 | /** | ||
552 | * TODO: use real sensor pixelrate | ||
553 | * See PowerService code | ||
554 | */ | ||
555 | request_pixelrate = (long long)(chan->format.width | ||
556 | * chan->format.height | ||
557 | * fie.interval.denominator / 100) | ||
558 | * VI_CSI_CLK_SCALE; | ||
559 | /* for PG, get csi frequency from nvhost */ | ||
560 | if (chan->pg_mode) { | ||
561 | ret = nvhost_module_get_rate( | ||
562 | chan->vi->csi->pdev, &csi_freq, 0); | ||
563 | csi_freq = ret ? DEFAULT_CSI_FREQ : csi_freq; | ||
564 | } else | ||
565 | /* Use default csi4 frequency for t186 for now | ||
566 | * We can't get the frequency from nvhost because | ||
567 | * vi4 does not has access to csi4 | ||
568 | */ | ||
569 | csi_freq = DEFAULT_CSI_FREQ; | ||
570 | |||
571 | /* VI clk should be slightly faster than CSI clk*/ | ||
572 | ret = nvhost_module_set_rate(chan->vi->ndev, &chan->video, | ||
573 | max(request_pixelrate, | ||
574 | csi_freq * VI_CSI_CLK_SCALE * NUM_PPC / 100), | ||
575 | 0, NVHOST_PIXELRATE); | ||
576 | if (ret) { | ||
577 | dev_err(chan->vi->dev, "Fail to update vi clk\n"); | ||
578 | return ret; | ||
579 | } | ||
580 | } else { | ||
581 | ret = nvhost_module_set_rate(chan->vi->ndev, &chan->video, 0, 0, | ||
582 | NVHOST_PIXELRATE); | ||
583 | if (ret) { | ||
584 | dev_err(chan->vi->dev, "Fail to update vi clk\n"); | ||
585 | return ret; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | chan->requested_kbyteps = (on > 0 ? 1 : -1) * | ||
590 | ((long long)(chan->format.width * chan->format.height | ||
591 | * fie.interval.denominator * BPP_MEM) * 115 / 100) / 1000; | ||
592 | |||
593 | mutex_lock(&chan->vi->bw_update_lock); | ||
594 | chan->vi->aggregated_kbyteps += chan->requested_kbyteps; | ||
595 | ret = vi_v4l2_update_isobw(chan->vi->aggregated_kbyteps, 0); | ||
596 | mutex_unlock(&chan->vi->bw_update_lock); | ||
597 | if (ret) | ||
598 | dev_info(chan->vi->dev, | ||
599 | "WAR:Calculation not precise.Ignore BW request failure\n"); | ||
600 | #if 0 | ||
601 | ret = vi4_v4l2_set_la(chan->vi->ndev, 0, 0); | ||
602 | if (ret) | ||
603 | dev_info(chan->vi->dev, | ||
604 | "WAR:Calculation not precise.Ignore LA failure\n"); | ||
605 | #endif | ||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | int vi2_channel_start_streaming(struct vb2_queue *vq, u32 count) | ||
610 | { | ||
611 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
612 | struct media_pipeline *pipe = chan->video.entity.pipe; | ||
613 | int ret = 0, i; | ||
614 | struct tegra_csi_channel *csi_chan = NULL; | ||
615 | struct tegra_csi_device *csi = chan->vi->csi; | ||
616 | struct v4l2_ctrl *override_ctrl; | ||
617 | |||
618 | vi_channel_syncpt_init(chan); | ||
619 | |||
620 | tegra_channel_ec_init(chan); | ||
621 | |||
622 | /* Start the pipeline. */ | ||
623 | ret = media_entity_pipeline_start(&chan->video.entity, pipe); | ||
624 | if (ret < 0) | ||
625 | goto error_pipeline_start; | ||
626 | |||
627 | if (chan->bypass) { | ||
628 | ret = tegra_channel_set_stream(chan, true); | ||
629 | if (ret < 0) | ||
630 | goto error_set_stream; | ||
631 | return ret; | ||
632 | } | ||
633 | chan->capture_state = CAPTURE_IDLE; | ||
634 | /* Find connected csi_channel */ | ||
635 | csi_chan = find_linked_csi_channel(chan, csi); | ||
636 | |||
637 | if (!csi_chan) | ||
638 | goto error_set_stream; | ||
639 | for (i = 0; i < chan->valid_ports; i++) { | ||
640 | /* csi2_start_streaming(csi_chan, i); */ | ||
641 | /* ensure sync point state is clean */ | ||
642 | nvhost_syncpt_set_min_eq_max_ext(chan->vi->ndev, | ||
643 | chan->syncpt[i][0]); | ||
644 | } | ||
645 | |||
646 | /* Note: Program VI registers after TPG, sensors and CSI streaming */ | ||
647 | ret = tegra_channel_capture_setup(chan); | ||
648 | if (ret < 0) | ||
649 | goto error_capture_setup; | ||
650 | |||
651 | chan->sequence = 0; | ||
652 | tegra_channel_init_ring_buffer(chan); | ||
653 | |||
654 | /* disable override for vi mode */ | ||
655 | override_ctrl = v4l2_ctrl_find( | ||
656 | &chan->ctrl_handler, V4L2_CID_OVERRIDE_ENABLE); | ||
657 | if (!chan->pg_mode) { | ||
658 | if (override_ctrl) { | ||
659 | ret = v4l2_ctrl_s_ctrl(override_ctrl, false); | ||
660 | if (ret < 0) | ||
661 | dev_err(&chan->video.dev, | ||
662 | "failed to disable override control\n"); | ||
663 | } else | ||
664 | dev_err(&chan->video.dev, | ||
665 | "No override control\n"); | ||
666 | } | ||
667 | /* Update clock and bandwidth based on the format */ | ||
668 | tegra_channel_update_clknbw(chan, 1); | ||
669 | |||
670 | /* Start kthread to capture data to buffer */ | ||
671 | chan->kthread_capture_start = kthread_run( | ||
672 | tegra_channel_kthread_capture_start, | ||
673 | chan, chan->video.name); | ||
674 | if (IS_ERR(chan->kthread_capture_start)) { | ||
675 | dev_err(&chan->video.dev, | ||
676 | "failed to run kthread for capture start\n"); | ||
677 | ret = PTR_ERR(chan->kthread_capture_start); | ||
678 | goto error_capture_setup; | ||
679 | } | ||
680 | |||
681 | return 0; | ||
682 | |||
683 | error_capture_setup: | ||
684 | if (!chan->pg_mode) | ||
685 | tegra_channel_set_stream(chan, false); | ||
686 | error_set_stream: | ||
687 | if (!chan->pg_mode) | ||
688 | media_entity_pipeline_stop(&chan->video.entity); | ||
689 | error_pipeline_start: | ||
690 | vq->start_streaming_called = 0; | ||
691 | tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_QUEUED); | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | void vi2_channel_stop_streaming(struct vb2_queue *vq) | ||
697 | { | ||
698 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
699 | int index; | ||
700 | bool is_streaming = atomic_read(&chan->is_streaming); | ||
701 | struct tegra_csi_channel *csi_chan = NULL; | ||
702 | struct tegra_csi_device *csi = chan->vi->csi; | ||
703 | |||
704 | if (!chan->bypass) { | ||
705 | tegra_channel_stop_kthreads(chan); | ||
706 | /* wait for last frame memory write ack */ | ||
707 | if (is_streaming && chan->capture_state == CAPTURE_GOOD) | ||
708 | tegra_channel_capture_done(chan); | ||
709 | /* free all the ring buffers */ | ||
710 | free_ring_buffers(chan, chan->num_buffers); | ||
711 | /* dequeue buffers back to app which are in capture queue */ | ||
712 | tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR); | ||
713 | |||
714 | /* Disable clock gating to enable continuous clock */ | ||
715 | tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE); | ||
716 | /* Find connected csi_channel */ | ||
717 | csi_chan = find_linked_csi_channel(chan, csi); | ||
718 | if (!csi_chan) | ||
719 | pr_err("%s, no csi_chan found\n", __func__); | ||
720 | for (index = 0; index < chan->valid_ports; index++) { | ||
721 | /* csi2_stop_streaming(csi_chan, index); */ | ||
722 | /* Always clear single shot if armed at close */ | ||
723 | if (csi_read(chan, index, TEGRA_VI_CSI_SINGLE_SHOT)) | ||
724 | tegra_channel_clear_singleshot(chan, index); | ||
725 | } | ||
726 | /* Enable clock gating so VI can be clock gated if necessary */ | ||
727 | tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE); | ||
728 | } | ||
729 | |||
730 | tegra_channel_set_stream(chan, false); | ||
731 | media_entity_pipeline_stop(&chan->video.entity); | ||
732 | |||
733 | if (!chan->bypass) | ||
734 | tegra_channel_update_clknbw(chan, 0); | ||
735 | |||
736 | vi_channel_syncpt_free(chan); | ||
737 | } | ||
738 | |||
739 | int tegra_vi2_power_on(struct tegra_mc_vi *vi) | ||
740 | { | ||
741 | int ret; | ||
742 | |||
743 | ret = nvhost_module_busy(vi->ndev); | ||
744 | if (ret) { | ||
745 | dev_err(vi->dev, "%s:nvhost module is busy\n", __func__); | ||
746 | return ret; | ||
747 | } | ||
748 | |||
749 | vi_write(vi, TEGRA_VI_CFG_CG_CTRL, 1); | ||
750 | |||
751 | ret = tegra_camera_emc_clk_enable(); | ||
752 | if (ret) | ||
753 | goto err_emc_enable; | ||
754 | |||
755 | return 0; | ||
756 | |||
757 | err_emc_enable: | ||
758 | nvhost_module_idle(vi->ndev); | ||
759 | |||
760 | return ret; | ||
761 | } | ||
762 | |||
763 | void tegra_vi2_power_off(struct tegra_mc_vi *vi) | ||
764 | { | ||
765 | tegra_channel_ec_close(vi); | ||
766 | tegra_camera_emc_clk_disable(); | ||
767 | nvhost_module_idle(vi->ndev); | ||
768 | } | ||
769 | |||
770 | int vi2_power_on(struct tegra_channel *chan) | ||
771 | { | ||
772 | int ret = 0; | ||
773 | struct tegra_mc_vi *vi; | ||
774 | struct vi *tegra_vi; | ||
775 | struct tegra_csi_device *csi; | ||
776 | |||
777 | vi = chan->vi; | ||
778 | tegra_vi = vi->vi; | ||
779 | csi = vi->csi; | ||
780 | |||
781 | ret = nvhost_module_add_client(vi->ndev, &chan->video); | ||
782 | if (ret) | ||
783 | return ret; | ||
784 | |||
785 | if (atomic_add_return(1, &vi->power_on_refcnt) == 1) { | ||
786 | tegra_vi2_power_on(vi); | ||
787 | if (chan->pg_mode) | ||
788 | tegra_vi->tpg_opened = true; | ||
789 | else | ||
790 | tegra_vi->sensor_opened = true; | ||
791 | } | ||
792 | |||
793 | if ((atomic_add_return(1, &chan->power_on_refcnt) == 1)) | ||
794 | ret = tegra_channel_set_power(chan, 1); | ||
795 | |||
796 | return ret; | ||
797 | } | ||
798 | |||
799 | void vi2_power_off(struct tegra_channel *chan) | ||
800 | { | ||
801 | int ret = 0; | ||
802 | struct tegra_mc_vi *vi; | ||
803 | struct vi *tegra_vi; | ||
804 | struct tegra_csi_device *csi; | ||
805 | |||
806 | vi = chan->vi; | ||
807 | tegra_vi = vi->vi; | ||
808 | csi = vi->csi; | ||
809 | |||
810 | if (atomic_dec_and_test(&chan->power_on_refcnt)) { | ||
811 | ret = tegra_channel_set_power(chan, 0); | ||
812 | if (ret < 0) | ||
813 | dev_err(vi->dev, "Failed to power off subdevices\n"); | ||
814 | } | ||
815 | |||
816 | /* The last release then turn off power */ | ||
817 | if (atomic_dec_and_test(&vi->power_on_refcnt)) { | ||
818 | tegra_vi2_power_off(vi); | ||
819 | if (vi->pg_mode) | ||
820 | tegra_vi->tpg_opened = false; | ||
821 | else | ||
822 | tegra_vi->sensor_opened = false; | ||
823 | } | ||
824 | nvhost_module_remove_client(vi->ndev, &chan->video); | ||
825 | } | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi2_fops.h b/drivers/media/platform/tegra/camera/vi/vi2_fops.h new file mode 100644 index 000000000..66c59879c --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi2_fops.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Tegra Video Input 2 device common APIs | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * Author: Bryan Wu <pengw@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef __T210_VI_H__ | ||
16 | #define __T210_VI_H__ | ||
17 | |||
18 | int vi2_power_on(struct tegra_channel *chan); | ||
19 | void vi2_power_off(struct tegra_channel *chan); | ||
20 | int vi2_channel_start_streaming(struct vb2_queue *vq, u32 count); | ||
21 | int vi2_channel_stop_streaming(struct vb2_queue *vq); | ||
22 | int vi2_add_ctrls(struct tegra_channel *chan); | ||
23 | void vi2_init_video_formats(struct tegra_channel *chan); | ||
24 | |||
25 | struct tegra_vi_fops vi2_fops = { | ||
26 | .vi_power_on = vi2_power_on, | ||
27 | .vi_power_off = vi2_power_off, | ||
28 | .vi_start_streaming = vi2_channel_start_streaming, | ||
29 | .vi_stop_streaming = vi2_channel_stop_streaming, | ||
30 | .vi_add_ctrls = vi2_add_ctrls, | ||
31 | .vi_init_video_formats = vi2_init_video_formats, | ||
32 | }; | ||
33 | |||
34 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi2_formats.h b/drivers/media/platform/tegra/camera/vi/vi2_formats.h new file mode 100644 index 000000000..c272eac3e --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi2_formats.h | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra Video Input Device Driver VI2 formats | ||
3 | * | ||
4 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bhanu Murthy V <bmurthyv@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __VI2_FORMATS_H_ | ||
14 | #define __VI2_FORMATS_H_ | ||
15 | |||
16 | #include "camera/vi/core.h" | ||
17 | |||
18 | /* | ||
19 | * These go into the TEGRA_VI_CSI_n_IMAGE_DEF registers bits 23:16 | ||
20 | * Output pixel memory format for the VI channel. | ||
21 | */ | ||
22 | enum tegra_image_format { | ||
23 | TEGRA_IMAGE_FORMAT_T_L8 = 16, | ||
24 | |||
25 | TEGRA_IMAGE_FORMAT_T_R16_I = 32, | ||
26 | TEGRA_IMAGE_FORMAT_T_B5G6R5, | ||
27 | TEGRA_IMAGE_FORMAT_T_R5G6B5, | ||
28 | TEGRA_IMAGE_FORMAT_T_A1B5G5R5, | ||
29 | TEGRA_IMAGE_FORMAT_T_A1R5G5B5, | ||
30 | TEGRA_IMAGE_FORMAT_T_B5G5R5A1, | ||
31 | TEGRA_IMAGE_FORMAT_T_R5G5B5A1, | ||
32 | TEGRA_IMAGE_FORMAT_T_A4B4G4R4, | ||
33 | TEGRA_IMAGE_FORMAT_T_A4R4G4B4, | ||
34 | TEGRA_IMAGE_FORMAT_T_B4G4R4A4, | ||
35 | TEGRA_IMAGE_FORMAT_T_R4G4B4A4, | ||
36 | |||
37 | TEGRA_IMAGE_FORMAT_T_A8B8G8R8 = 64, | ||
38 | TEGRA_IMAGE_FORMAT_T_A8R8G8B8, | ||
39 | TEGRA_IMAGE_FORMAT_T_B8G8R8A8, | ||
40 | TEGRA_IMAGE_FORMAT_T_R8G8B8A8, | ||
41 | TEGRA_IMAGE_FORMAT_T_A2B10G10R10, | ||
42 | TEGRA_IMAGE_FORMAT_T_A2R10G10B10, | ||
43 | TEGRA_IMAGE_FORMAT_T_B10G10R10A2, | ||
44 | TEGRA_IMAGE_FORMAT_T_R10G10B10A2, | ||
45 | |||
46 | TEGRA_IMAGE_FORMAT_T_A8Y8U8V8 = 193, | ||
47 | TEGRA_IMAGE_FORMAT_T_V8U8Y8A8, | ||
48 | |||
49 | TEGRA_IMAGE_FORMAT_T_A2Y10U10V10 = 197, | ||
50 | TEGRA_IMAGE_FORMAT_T_V10U10Y10A2, | ||
51 | |||
52 | TEGRA_IMAGE_FORMAT_T_Y8_U8__Y8_V8 = 200, | ||
53 | TEGRA_IMAGE_FORMAT_T_Y8_V8__Y8_U8, | ||
54 | TEGRA_IMAGE_FORMAT_T_U8_Y8__V8_Y8, | ||
55 | TEGRA_IMAGE_FORMAT_T_T_V8_Y8__U8_Y8, | ||
56 | |||
57 | TEGRA_IMAGE_FORMAT_T_T_Y8__U8__V8_N444 = 224, | ||
58 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N444, | ||
59 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N444, | ||
60 | TEGRA_IMAGE_FORMAT_T_Y8__U8__V8_N422, | ||
61 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N422, | ||
62 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N422, | ||
63 | TEGRA_IMAGE_FORMAT_T_Y8__U8__V8_N420, | ||
64 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N420, | ||
65 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N420, | ||
66 | TEGRA_IMAGE_FORMAT_T_X2Lc10Lb10La10, | ||
67 | TEGRA_IMAGE_FORMAT_T_A2R6R6R6R6R6, | ||
68 | }; | ||
69 | |||
70 | static const struct tegra_video_format vi2_video_formats[] = { | ||
71 | /* RAW 6: TODO */ | ||
72 | |||
73 | /* RAW 7: TODO */ | ||
74 | |||
75 | /* RAW 8 */ | ||
76 | TEGRA_VIDEO_FORMAT(RAW8, 8, SRGGB8_1X8, 1, 1, T_L8, | ||
77 | RAW8, SRGGB8, "RGRG.. GBGB.."), | ||
78 | TEGRA_VIDEO_FORMAT(RAW8, 8, SGRBG8_1X8, 1, 1, T_L8, | ||
79 | RAW8, SGRBG8, "GRGR.. BGBG.."), | ||
80 | TEGRA_VIDEO_FORMAT(RAW8, 8, SGBRG8_1X8, 1, 1, T_L8, | ||
81 | RAW8, SGBRG8, "GBGB.. RGRG.."), | ||
82 | TEGRA_VIDEO_FORMAT(RAW8, 8, SBGGR8_1X8, 1, 1, T_L8, | ||
83 | RAW8, SBGGR8, "BGBG.. GRGR.."), | ||
84 | |||
85 | /* RAW 10 */ | ||
86 | TEGRA_VIDEO_FORMAT(RAW10, 10, SRGGB10_1X10, 2, 1, T_R16_I, | ||
87 | RAW10, SRGGB10, "RGRG.. GBGB.."), | ||
88 | TEGRA_VIDEO_FORMAT(RAW10, 10, SGRBG10_1X10, 2, 1, T_R16_I, | ||
89 | RAW10, SGRBG10, "GRGR.. BGBG.."), | ||
90 | TEGRA_VIDEO_FORMAT(RAW10, 10, SGBRG10_1X10, 2, 1, T_R16_I, | ||
91 | RAW10, SGBRG10, "GBGB.. RGRG.."), | ||
92 | TEGRA_VIDEO_FORMAT(RAW10, 10, SBGGR10_1X10, 2, 1, T_R16_I, | ||
93 | RAW10, SBGGR10, "BGBG.. GRGR.."), | ||
94 | |||
95 | /* RAW 10 Packed format */ | ||
96 | TEGRA_VIDEO_FORMAT(RAW10, 10, XBGGR10P_3X10, 4, 3, T_X2Lc10Lb10La10, | ||
97 | RAW10, XBGGR10P, "BGBG.. GRGR.."), | ||
98 | TEGRA_VIDEO_FORMAT(RAW10, 10, XRGGB10P_3X10, 4, 3, T_X2Lc10Lb10La10, | ||
99 | RAW10, XRGGB10P, "RGRG.. GBGB.."), | ||
100 | |||
101 | /* RAW 12 */ | ||
102 | TEGRA_VIDEO_FORMAT(RAW12, 12, SRGGB12_1X12, 2, 1, T_R16_I, | ||
103 | RAW12, SRGGB12, "RGRG.. GBGB.."), | ||
104 | TEGRA_VIDEO_FORMAT(RAW12, 12, SGRBG12_1X12, 2, 1, T_R16_I, | ||
105 | RAW12, SGRBG12, "GRGR.. BGBG.."), | ||
106 | TEGRA_VIDEO_FORMAT(RAW12, 12, SGBRG12_1X12, 2, 1, T_R16_I, | ||
107 | RAW12, SGBRG12, "GBGB.. RGRG.."), | ||
108 | TEGRA_VIDEO_FORMAT(RAW12, 12, SBGGR12_1X12, 2, 1, T_R16_I, | ||
109 | RAW12, SBGGR12, "BGBG.. GRGR.."), | ||
110 | |||
111 | /* RGB888 */ | ||
112 | TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X24, 4, 1, T_A8R8G8B8, | ||
113 | RGB888, ABGR32, "BGRA-8-8-8-8"), | ||
114 | TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X32_PADHI, 4, 1, T_A8B8G8R8, | ||
115 | RGB888, RGB32, "RGB-8-8-8-8"), | ||
116 | |||
117 | /* YUV422 */ | ||
118 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 2, 1, T_U8_Y8__V8_Y8, | ||
119 | YUV422_8, UYVY, "YUV 4:2:2"), | ||
120 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 1, 1, T_Y8__V8U8_N422, | ||
121 | YUV422_8, NV16, "NV16"), | ||
122 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8, | ||
123 | YUV422_8, UYVY, "YUV 4:2:2 UYVY"), | ||
124 | TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_2X8, 2, 1, T_T_V8_Y8__U8_Y8, | ||
125 | YUV422_8, VYUY, "YUV 4:2:2 VYUY"), | ||
126 | TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_2X8, 2, 1, T_Y8_U8__Y8_V8, | ||
127 | YUV422_8, YUYV, "YUV 4:2:2 YUYV"), | ||
128 | TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_2X8, 2, 1, T_Y8_V8__Y8_U8, | ||
129 | YUV422_8, YVYU, "YUV 4:2:2 YVYU"), | ||
130 | }; | ||
131 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi4_fops.c b/drivers/media/platform/tegra/camera/vi/vi4_fops.c new file mode 100644 index 000000000..d78ed3421 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi4_fops.c | |||
@@ -0,0 +1,1056 @@ | |||
1 | /* | ||
2 | * Tegra Video Input 4 device common APIs | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Frank Chen <frank@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/freezer.h> | ||
13 | #include <linux/kthread.h> | ||
14 | #include <linux/nvhost.h> | ||
15 | #include <linux/tegra-powergate.h> | ||
16 | #include <media/capture.h> | ||
17 | #include <media/tegra_camera_platform.h> | ||
18 | #include "linux/nvhost_ioctl.h" | ||
19 | #include "mc_common.h" | ||
20 | #include "nvhost_acm.h" | ||
21 | #include "vi4_formats.h" | ||
22 | #include "vi4_registers.h" | ||
23 | #include "vi/vi4.h" | ||
24 | #include "vi/vi_notify.h" | ||
25 | |||
26 | #define DEFAULT_FRAMERATE 30 | ||
27 | #define DEFAULT_CSI_FREQ 204000000 | ||
28 | #define BPP_MEM 2 | ||
29 | #define MAX_VI_CHANNEL 12 | ||
30 | #define NUM_PPC 8 | ||
31 | #define VI_CSI_CLK_SCALE 110 | ||
32 | #define SOF_SYNCPT_IDX 0 | ||
33 | #define FE_SYNCPT_IDX 1 | ||
34 | |||
35 | void tegra_channel_queued_buf_done(struct tegra_channel *chan, | ||
36 | enum vb2_buffer_state state); | ||
37 | int tegra_channel_set_stream(struct tegra_channel *chan, bool on); | ||
38 | void tegra_channel_ring_buffer(struct tegra_channel *chan, | ||
39 | struct vb2_v4l2_buffer *vb, | ||
40 | struct timespec *ts, int state); | ||
41 | struct tegra_channel_buffer *dequeue_buffer(struct tegra_channel *chan); | ||
42 | void tegra_channel_init_ring_buffer(struct tegra_channel *chan); | ||
43 | void free_ring_buffers(struct tegra_channel *chan, int frames); | ||
44 | int tegra_channel_set_power(struct tegra_channel *chan, bool on); | ||
45 | static void tegra_channel_stop_kthreads(struct tegra_channel *chan); | ||
46 | static int tegra_channel_stop_increments(struct tegra_channel *chan); | ||
47 | static void tegra_channel_notify_status_callback( | ||
48 | struct vi_notify_channel *, | ||
49 | const struct vi_capture_status *, | ||
50 | void *); | ||
51 | static void tegra_channel_error_worker(struct work_struct *status_work); | ||
52 | static void tegra_channel_notify_error_callback(void *); | ||
53 | |||
54 | u32 csimux_config_stream[] = { | ||
55 | CSIMUX_CONFIG_STREAM_0, | ||
56 | CSIMUX_CONFIG_STREAM_1, | ||
57 | CSIMUX_CONFIG_STREAM_2, | ||
58 | CSIMUX_CONFIG_STREAM_3, | ||
59 | CSIMUX_CONFIG_STREAM_4, | ||
60 | CSIMUX_CONFIG_STREAM_5 | ||
61 | }; | ||
62 | |||
63 | static void vi4_write(struct tegra_channel *chan, unsigned int addr, u32 val) | ||
64 | { | ||
65 | writel(val, chan->vi->iomem + addr); | ||
66 | } | ||
67 | |||
68 | static u32 vi4_read(struct tegra_channel *chan, unsigned int addr) | ||
69 | { | ||
70 | return readl(chan->vi->iomem + addr); | ||
71 | } | ||
72 | |||
73 | static void vi4_channel_write(struct tegra_channel *chan, | ||
74 | unsigned int index, unsigned int addr, u32 val) | ||
75 | { | ||
76 | writel(val, | ||
77 | chan->vi->iomem + VI4_CHANNEL_OFFSET * (index + 1) + addr); | ||
78 | } | ||
79 | |||
80 | void vi4_init_video_formats(struct tegra_channel *chan) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | chan->num_video_formats = ARRAY_SIZE(vi4_video_formats); | ||
85 | for (i = 0; i < chan->num_video_formats; i++) | ||
86 | chan->video_formats[i] = &vi4_video_formats[i]; | ||
87 | } | ||
88 | |||
89 | long vi4_default_ioctl(struct file *file, void *fh, | ||
90 | bool use_prio, unsigned int cmd, void *arg) | ||
91 | { | ||
92 | struct v4l2_fh *vfh = file->private_data; | ||
93 | struct tegra_channel *chan = to_tegra_channel(vfh->vdev); | ||
94 | long err = 0; | ||
95 | |||
96 | switch (_IOC_NR(cmd)) { | ||
97 | #if defined(CONFIG_TEGRA_CAMERA_RTCPU) | ||
98 | case _IOC_NR(VIDIOC_CAPTURE_SETUP): | ||
99 | if (chan->bypass) | ||
100 | err = vi_capture_setup(chan, | ||
101 | (struct vi_capture_setup *)arg); | ||
102 | else { | ||
103 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
104 | err = -ENODEV; | ||
105 | } | ||
106 | if (err) | ||
107 | dev_err(&chan->video.dev, "capture setup failed\n"); | ||
108 | break; | ||
109 | case _IOC_NR(VIDIOC_CAPTURE_RESET): | ||
110 | if (chan->bypass) | ||
111 | err = vi_capture_reset(chan, *(uint32_t *)arg); | ||
112 | else { | ||
113 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
114 | err = -ENODEV; | ||
115 | } | ||
116 | if (err) | ||
117 | dev_err(&chan->video.dev, "capture reset failed\n"); | ||
118 | break; | ||
119 | case _IOC_NR(VIDIOC_CAPTURE_RELEASE): | ||
120 | if (chan->bypass) | ||
121 | err = vi_capture_release(chan, *(uint32_t *)arg); | ||
122 | else { | ||
123 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
124 | err = -ENODEV; | ||
125 | } | ||
126 | if (err) | ||
127 | dev_err(&chan->video.dev, "capture release failed\n"); | ||
128 | break; | ||
129 | case _IOC_NR(VIDIOC_CAPTURE_GET_INFO): | ||
130 | if (chan->bypass) | ||
131 | err = vi_capture_get_info(chan, | ||
132 | (struct vi_capture_info *)arg); | ||
133 | else { | ||
134 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
135 | err = -ENODEV; | ||
136 | } | ||
137 | if (err) | ||
138 | dev_err(&chan->video.dev, "capture get info failed\n"); | ||
139 | break; | ||
140 | case _IOC_NR(VIDIOC_CAPTURE_SET_CONFIG): | ||
141 | if (chan->bypass) | ||
142 | err = vi_capture_control_message(chan, | ||
143 | (struct vi_capture_control_msg *)arg); | ||
144 | else { | ||
145 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
146 | err = -ENODEV; | ||
147 | } | ||
148 | if (err) | ||
149 | dev_err(&chan->video.dev, "capture config failed\n"); | ||
150 | break; | ||
151 | case _IOC_NR(VIDIOC_CAPTURE_REQUEST): | ||
152 | if (chan->bypass) | ||
153 | err = vi_capture_request(chan, | ||
154 | (struct vi_capture_req *)arg); | ||
155 | else { | ||
156 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
157 | err = -ENODEV; | ||
158 | } | ||
159 | if (err) | ||
160 | dev_err(&chan->video.dev, | ||
161 | "capture request submit failed\n"); | ||
162 | break; | ||
163 | case _IOC_NR(VIDIOC_CAPTURE_STATUS): | ||
164 | if (chan->bypass) | ||
165 | err = vi_capture_status(chan, *(uint32_t *)arg); | ||
166 | else { | ||
167 | dev_err(&chan->video.dev, "not in bypass mode\n"); | ||
168 | err = -ENODEV; | ||
169 | } | ||
170 | if (err) | ||
171 | dev_err(&chan->video.dev, | ||
172 | "capture get status failed\n"); | ||
173 | break; | ||
174 | #endif | ||
175 | default: | ||
176 | dev_err(&chan->video.dev, "%s:Unknown ioctl\n", __func__); | ||
177 | return -ENOIOCTLCMD; | ||
178 | } | ||
179 | |||
180 | return err; | ||
181 | } | ||
182 | |||
183 | |||
184 | int tegra_vi4_s_ctrl(struct v4l2_ctrl *ctrl) | ||
185 | { | ||
186 | struct tegra_channel *chan = container_of(ctrl->handler, | ||
187 | struct tegra_channel, ctrl_handler); | ||
188 | int err = 0; | ||
189 | |||
190 | switch (ctrl->id) { | ||
191 | case V4L2_CID_WRITE_ISPFORMAT: | ||
192 | chan->write_ispformat = ctrl->val; | ||
193 | break; | ||
194 | default: | ||
195 | dev_err(&chan->video.dev, "%s:Not valid ctrl\n", __func__); | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
199 | return err; | ||
200 | } | ||
201 | |||
202 | static const struct v4l2_ctrl_ops vi4_ctrl_ops = { | ||
203 | .s_ctrl = tegra_vi4_s_ctrl, | ||
204 | }; | ||
205 | |||
206 | static const struct v4l2_ctrl_config vi4_custom_ctrls[] = { | ||
207 | { | ||
208 | .ops = &vi4_ctrl_ops, | ||
209 | .id = V4L2_CID_WRITE_ISPFORMAT, | ||
210 | .name = "Write ISP format", | ||
211 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
212 | .def = 1, | ||
213 | .min = 1, | ||
214 | .max = 1, | ||
215 | .step = 1, | ||
216 | }, | ||
217 | }; | ||
218 | |||
219 | int vi4_add_ctrls(struct tegra_channel *chan) | ||
220 | { | ||
221 | int i; | ||
222 | |||
223 | /* Add vi4 custom controls */ | ||
224 | for (i = 0; i < ARRAY_SIZE(vi4_custom_ctrls); i++) { | ||
225 | v4l2_ctrl_new_custom(&chan->ctrl_handler, | ||
226 | &vi4_custom_ctrls[i], NULL); | ||
227 | if (chan->ctrl_handler.error) { | ||
228 | dev_err(chan->vi->dev, | ||
229 | "Failed to add %s ctrl\n", | ||
230 | vi4_custom_ctrls[i].name); | ||
231 | return chan->ctrl_handler.error; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static bool vi4_init(struct tegra_channel *chan) | ||
239 | { | ||
240 | vi4_write(chan, CFG_INTERRUPT_MASK, 0x3f0000f9); | ||
241 | vi4_write(chan, CFG_INTERRUPT_STATUS, 0x3f000001); | ||
242 | vi4_write(chan, NOTIFY_ERROR, 0x1); | ||
243 | vi4_write(chan, NOTIFY_TAG_CLASSIFY_0, 0xe39c08e3); | ||
244 | return true; | ||
245 | } | ||
246 | |||
247 | static bool vi4_check_status(struct tegra_channel *chan) | ||
248 | { | ||
249 | int status; | ||
250 | |||
251 | /* check interrupt status error */ | ||
252 | status = vi4_read(chan, CFG_INTERRUPT_STATUS); | ||
253 | if (status & 0x1) | ||
254 | dev_err(chan->vi->dev, | ||
255 | "VI_CFG_INTERRUPT_STATUS_0: MASTER_ERR_STATUS error!\n"); | ||
256 | |||
257 | /* Check VI NOTIFY input FIFO error */ | ||
258 | status = vi4_read(chan, NOTIFY_ERROR); | ||
259 | if (status & 0x1) | ||
260 | dev_err(chan->vi->dev, | ||
261 | "VI_NOTIFY_ERROR_0: NOTIFY_FIFO_OVERFLOW error!\n"); | ||
262 | |||
263 | return true; | ||
264 | } | ||
265 | |||
266 | static bool vi_notify_wait(struct tegra_channel *chan, | ||
267 | struct timespec *ts) | ||
268 | { | ||
269 | int i, err; | ||
270 | u32 thresh[TEGRA_CSI_BLOCKS], temp; | ||
271 | |||
272 | /* | ||
273 | * Increment syncpt for ATOMP_FE | ||
274 | * | ||
275 | * This is needed in order to keep the syncpt max up to date, | ||
276 | * even if we are not waiting for ATOMP_FE here | ||
277 | */ | ||
278 | for (i = 0; i < chan->valid_ports; i++) | ||
279 | temp = nvhost_syncpt_incr_max_ext(chan->vi->ndev, | ||
280 | chan->syncpt[i][FE_SYNCPT_IDX], 1); | ||
281 | |||
282 | /* | ||
283 | * Increment syncpt for PXL_SOF | ||
284 | * | ||
285 | * Increment and retrieve PXL_SOF syncpt max value. | ||
286 | * This value will be used to wait for next syncpt | ||
287 | */ | ||
288 | for (i = 0; i < chan->valid_ports; i++) | ||
289 | thresh[i] = nvhost_syncpt_incr_max_ext(chan->vi->ndev, | ||
290 | chan->syncpt[i][SOF_SYNCPT_IDX], 1); | ||
291 | |||
292 | /* | ||
293 | * Wait for PXL_SOF syncpt | ||
294 | * | ||
295 | * Use the syncpt max value we just set as threshold | ||
296 | */ | ||
297 | for (i = 0; i < chan->valid_ports; i++) { | ||
298 | err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev, | ||
299 | chan->syncpt[i][SOF_SYNCPT_IDX], thresh[i], | ||
300 | 250, NULL, NULL); | ||
301 | if (unlikely(err)) | ||
302 | dev_err(chan->vi->dev, | ||
303 | "PXL_SOF syncpt timeout! err = %d\n", err); | ||
304 | else { | ||
305 | struct vi_capture_status status; | ||
306 | |||
307 | err = vi_notify_get_capture_status(chan->vnc[i], | ||
308 | chan->vnc_id[i], | ||
309 | thresh[i], &status); | ||
310 | if (unlikely(err)) | ||
311 | dev_err(chan->vi->dev, | ||
312 | "no capture status! err = %d\n", err); | ||
313 | else | ||
314 | *ts = ns_to_timespec((s64)status.sof_ts); | ||
315 | } | ||
316 | } | ||
317 | return true; | ||
318 | } | ||
319 | |||
320 | static void tegra_channel_surface_setup( | ||
321 | struct tegra_channel *chan, struct tegra_channel_buffer *buf, int index) | ||
322 | { | ||
323 | int vnc_id = chan->vnc_id[index]; | ||
324 | unsigned int offset = chan->buffer_offset[index]; | ||
325 | |||
326 | vi4_channel_write(chan, vnc_id, ATOMP_EMB_SURFACE_OFFSET0, 0x0); | ||
327 | vi4_channel_write(chan, vnc_id, ATOMP_EMB_SURFACE_OFFSET0_H, 0x0); | ||
328 | vi4_channel_write(chan, vnc_id, ATOMP_EMB_SURFACE_STRIDE0, 0x0); | ||
329 | vi4_channel_write(chan, vnc_id, | ||
330 | ATOMP_SURFACE_OFFSET0, buf->addr + offset); | ||
331 | vi4_channel_write(chan, vnc_id, | ||
332 | ATOMP_SURFACE_STRIDE0, chan->format.bytesperline); | ||
333 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_OFFSET0_H, 0x0); | ||
334 | |||
335 | if (chan->fmtinfo->fourcc == V4L2_PIX_FMT_NV16) { | ||
336 | vi4_channel_write(chan, vnc_id, | ||
337 | ATOMP_SURFACE_OFFSET1, buf->addr + offset + | ||
338 | chan->format.sizeimage / 2); | ||
339 | vi4_channel_write(chan, vnc_id, | ||
340 | ATOMP_SURFACE_OFFSET1_H, 0x0); | ||
341 | vi4_channel_write(chan, vnc_id, | ||
342 | ATOMP_SURFACE_STRIDE1, chan->format.bytesperline); | ||
343 | |||
344 | } else { | ||
345 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_OFFSET1, 0x0); | ||
346 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_OFFSET1_H, 0x0); | ||
347 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_STRIDE1, 0x0); | ||
348 | } | ||
349 | |||
350 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_OFFSET2, 0x0); | ||
351 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_OFFSET2_H, 0x0); | ||
352 | vi4_channel_write(chan, vnc_id, ATOMP_SURFACE_STRIDE2, 0x0); | ||
353 | } | ||
354 | |||
355 | static void tegra_channel_handle_error(struct tegra_channel *chan) | ||
356 | { | ||
357 | struct v4l2_subdev *sd_on_csi = chan->subdev_on_csi; | ||
358 | static const struct v4l2_event source_ev_fmt = { | ||
359 | .type = V4L2_EVENT_SOURCE_CHANGE, | ||
360 | .u.src_change.changes = V4L2_EVENT_SRC_ERROR, | ||
361 | }; | ||
362 | |||
363 | tegra_channel_stop_increments(chan); | ||
364 | vb2_queue_error(&chan->queue); | ||
365 | |||
366 | /* Application gets notified after CSI Tx's are reset */ | ||
367 | if (sd_on_csi->devnode) | ||
368 | v4l2_subdev_notify_event(sd_on_csi, &source_ev_fmt); | ||
369 | } | ||
370 | |||
371 | static void tegra_channel_status_worker(struct work_struct *status_work) | ||
372 | { | ||
373 | struct tegra_channel *chan; | ||
374 | |||
375 | chan = container_of(status_work, struct tegra_channel, status_work); | ||
376 | |||
377 | tegra_channel_handle_error(chan); | ||
378 | } | ||
379 | |||
380 | static void tegra_channel_notify_status_callback( | ||
381 | struct vi_notify_channel *vnc, | ||
382 | const struct vi_capture_status *status, | ||
383 | void *client_data) | ||
384 | { | ||
385 | struct tegra_channel *chan = (struct tegra_channel *)client_data; | ||
386 | int i; | ||
387 | |||
388 | spin_lock(&chan->capture_state_lock); | ||
389 | if (chan->capture_state == CAPTURE_GOOD) | ||
390 | chan->capture_state = CAPTURE_ERROR; | ||
391 | else { | ||
392 | spin_unlock(&chan->capture_state_lock); | ||
393 | return; | ||
394 | } | ||
395 | spin_unlock(&chan->capture_state_lock); | ||
396 | |||
397 | for (i = 0; i < chan->valid_ports; i++) | ||
398 | dev_err(chan->vi->dev, "Status: %2u channel:%02X frame:%04X\n", | ||
399 | status->status, chan->vnc_id[i], status->frame); | ||
400 | dev_err(chan->vi->dev, " timestamp sof %llu eof %llu data 0x%08x\n", | ||
401 | status->sof_ts, status->eof_ts, status->data); | ||
402 | dev_err(chan->vi->dev, " capture_id %u stream %2u vchan %2u\n", | ||
403 | status->capture_id, status->st, status->vc); | ||
404 | |||
405 | schedule_work(&chan->status_work); | ||
406 | } | ||
407 | |||
408 | static int tegra_channel_notify_enable( | ||
409 | struct tegra_channel *chan, unsigned int index) | ||
410 | { | ||
411 | struct tegra_vi4_syncpts_req req; | ||
412 | int i, err; | ||
413 | |||
414 | chan->vnc_id[index] = -1; | ||
415 | for (i = 0; i < MAX_VI_CHANNEL; i++) { | ||
416 | chan->vnc[index] = vi_notify_channel_open(i); | ||
417 | if (!IS_ERR(chan->vnc[index])) { | ||
418 | chan->vnc_id[index] = i; | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | if (chan->vnc_id[index] < 0) { | ||
423 | dev_err(chan->vi->dev, "No VI channel available!\n"); | ||
424 | return -EFAULT; | ||
425 | } | ||
426 | |||
427 | vi_notify_channel_set_notify_funcs(chan->vnc[index], | ||
428 | &tegra_channel_notify_status_callback, | ||
429 | &tegra_channel_notify_error_callback, | ||
430 | (void *)chan); | ||
431 | |||
432 | /* get PXL_SOF syncpt id */ | ||
433 | chan->syncpt[index][SOF_SYNCPT_IDX] = | ||
434 | nvhost_get_syncpt_client_managed(chan->vi->ndev, "tegra-vi4"); | ||
435 | if (chan->syncpt[index][SOF_SYNCPT_IDX] == 0) { | ||
436 | dev_err(chan->vi->dev, "Failed to get PXL_SOF syncpt!\n"); | ||
437 | return -EFAULT; | ||
438 | } | ||
439 | |||
440 | /* get ATOMP_FE syncpt id */ | ||
441 | chan->syncpt[index][FE_SYNCPT_IDX] = | ||
442 | nvhost_get_syncpt_client_managed(chan->vi->ndev, "tegra-vi4"); | ||
443 | if (chan->syncpt[index][FE_SYNCPT_IDX] == 0) { | ||
444 | dev_err(chan->vi->dev, "Failed to get ATOMP_FE syncpt!\n"); | ||
445 | nvhost_syncpt_put_ref_ext( | ||
446 | chan->vi->ndev, chan->syncpt[index][SOF_SYNCPT_IDX]); | ||
447 | return -EFAULT; | ||
448 | } | ||
449 | |||
450 | nvhost_syncpt_set_min_eq_max_ext( | ||
451 | chan->vi->ndev, chan->syncpt[index][SOF_SYNCPT_IDX]); | ||
452 | nvhost_syncpt_set_min_eq_max_ext( | ||
453 | chan->vi->ndev, chan->syncpt[index][FE_SYNCPT_IDX]); | ||
454 | |||
455 | /* enable VI Notify report */ | ||
456 | req.syncpt_ids[0] = chan->syncpt[index][SOF_SYNCPT_IDX]; /* PXL_SOF */ | ||
457 | req.syncpt_ids[1] = chan->syncpt[index][FE_SYNCPT_IDX]; /* ATOMP_FE */ | ||
458 | req.syncpt_ids[2] = 0xffffffff; | ||
459 | req.stream = chan->port[index]; | ||
460 | req.vc = 0; | ||
461 | req.pad = 0; | ||
462 | |||
463 | err = vi_notify_channel_enable_reports( | ||
464 | chan->vnc_id[index], chan->vnc[index], &req); | ||
465 | if (err < 0) | ||
466 | dev_err(chan->vi->dev, | ||
467 | "Failed to enable report for VI Notify, err = %d\n", | ||
468 | err); | ||
469 | |||
470 | return err; | ||
471 | } | ||
472 | |||
473 | static int tegra_channel_notify_disable( | ||
474 | struct tegra_channel *chan, unsigned int index) | ||
475 | { | ||
476 | int err; | ||
477 | int ret = 0; | ||
478 | struct tegra_vi4_syncpts_req req; | ||
479 | |||
480 | /* free syncpts */ | ||
481 | nvhost_syncpt_put_ref_ext( | ||
482 | chan->vi->ndev, chan->syncpt[index][SOF_SYNCPT_IDX]); | ||
483 | nvhost_syncpt_put_ref_ext( | ||
484 | chan->vi->ndev, chan->syncpt[index][FE_SYNCPT_IDX]); | ||
485 | |||
486 | /* close vi-notifier */ | ||
487 | req.syncpt_ids[0] = 0xffffffff; | ||
488 | req.syncpt_ids[1] = 0xffffffff; | ||
489 | req.syncpt_ids[2] = 0xffffffff; | ||
490 | req.stream = chan->port[index]; | ||
491 | req.vc = 0; | ||
492 | req.pad = 0; | ||
493 | |||
494 | err = vi_notify_channel_reset( | ||
495 | chan->vnc_id[index], chan->vnc[index], &req); | ||
496 | if (err < 0) { | ||
497 | dev_err(chan->vi->dev, | ||
498 | "VI Notify channel reset failed, err = %d\n", err); | ||
499 | if (!ret) | ||
500 | ret = err; | ||
501 | } | ||
502 | |||
503 | err = vi_notify_channel_close(chan->vnc_id[index], chan->vnc[index]); | ||
504 | if (err < 0) { | ||
505 | dev_err(chan->vi->dev, | ||
506 | "VI Notify channel close failed, err = %d\n", err); | ||
507 | if (!ret) | ||
508 | ret = err; | ||
509 | } | ||
510 | |||
511 | return ret; | ||
512 | } | ||
513 | |||
514 | static int tegra_channel_capture_setup(struct tegra_channel *chan, | ||
515 | unsigned int index) | ||
516 | { | ||
517 | u32 height = chan->format.height; | ||
518 | u32 width = chan->format.width; | ||
519 | u32 format = chan->fmtinfo->img_fmt; | ||
520 | u32 data_type = chan->fmtinfo->img_dt; | ||
521 | u32 csi_port = chan->port[index]; | ||
522 | u32 stream = 1U << csi_port; | ||
523 | u32 virtual_ch = 1U << 0; | ||
524 | u32 vnc_id; | ||
525 | int err; | ||
526 | |||
527 | if (chan->valid_ports > 1) { | ||
528 | height = chan->gang_height; | ||
529 | width = chan->gang_width; | ||
530 | } | ||
531 | |||
532 | err = tegra_channel_notify_enable(chan, index); | ||
533 | if (err < 0) { | ||
534 | dev_err(chan->vi->dev, | ||
535 | "Failed to setup VI Notifier, err = %d\n", err); | ||
536 | return err; | ||
537 | } | ||
538 | |||
539 | vnc_id = chan->vnc_id[index]; | ||
540 | |||
541 | vi4_write(chan, csimux_config_stream[csi_port], 0x1); | ||
542 | |||
543 | vi4_channel_write(chan, vnc_id, MATCH, | ||
544 | ((stream << STREAM_SHIFT) & STREAM) | | ||
545 | STREAM_MASK | | ||
546 | ((virtual_ch << VIRTUAL_CHANNEL_SHIFT) & | ||
547 | VIRTUAL_CHANNEL) | | ||
548 | VIRTUAL_CHANNEL_MASK); | ||
549 | |||
550 | vi4_channel_write(chan, vnc_id, MATCH_DATATYPE, | ||
551 | ((data_type << DATATYPE_SHIFT) & DATATYPE) | | ||
552 | DATATYPE_MASK); | ||
553 | |||
554 | vi4_channel_write(chan, vnc_id, DT_OVERRIDE, 0x0); | ||
555 | |||
556 | vi4_channel_write(chan, vnc_id, MATCH_FRAMEID, | ||
557 | ((0 << FRAMEID_SHIFT) & FRAMEID) | 0); | ||
558 | |||
559 | vi4_channel_write(chan, vnc_id, FRAME_X, width); | ||
560 | vi4_channel_write(chan, vnc_id, FRAME_Y, height); | ||
561 | vi4_channel_write(chan, vnc_id, SKIP_X, 0x0); | ||
562 | vi4_channel_write(chan, vnc_id, CROP_X, width); | ||
563 | vi4_channel_write(chan, vnc_id, OUT_X, width); | ||
564 | vi4_channel_write(chan, vnc_id, SKIP_Y, 0x0); | ||
565 | vi4_channel_write(chan, vnc_id, CROP_Y, height); | ||
566 | vi4_channel_write(chan, vnc_id, OUT_Y, height); | ||
567 | vi4_channel_write(chan, vnc_id, PIXFMT_ENABLE, PIXFMT_EN); | ||
568 | vi4_channel_write(chan, vnc_id, PIXFMT_WIDE, 0x0); | ||
569 | vi4_channel_write(chan, vnc_id, PIXFMT_FORMAT, format); | ||
570 | vi4_channel_write(chan, vnc_id, DPCM_STRIP, 0x0); | ||
571 | vi4_channel_write(chan, vnc_id, ATOMP_DPCM_CHUNK, 0x0); | ||
572 | vi4_channel_write(chan, vnc_id, ISPBUFA, 0x0); | ||
573 | vi4_channel_write(chan, vnc_id, LINE_TIMER, 0x1000000); | ||
574 | vi4_channel_write(chan, vnc_id, EMBED_X, 0x0); | ||
575 | vi4_channel_write(chan, vnc_id, EMBED_Y, 0x0); | ||
576 | /* | ||
577 | * Set ATOMP_RESERVE to 0 so rctpu won't increment syncpt | ||
578 | * for captureInfo. This is copied from nvvi driver. | ||
579 | * | ||
580 | * If we don't set this register to 0, ATOMP_FE syncpt | ||
581 | * will be increment by 2 for each frame | ||
582 | */ | ||
583 | vi4_channel_write(chan, vnc_id, ATOMP_RESERVE, 0x0); | ||
584 | dev_dbg(chan->vi->dev, | ||
585 | "Create Surface with imgW=%d, imgH=%d, memFmt=%d\n", | ||
586 | width, height, format); | ||
587 | |||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static int tegra_channel_capture_frame(struct tegra_channel *chan, | ||
592 | struct tegra_channel_buffer *buf) | ||
593 | { | ||
594 | struct vb2_v4l2_buffer *vb = &buf->buf; | ||
595 | struct timespec ts; | ||
596 | int state = VB2_BUF_STATE_DONE; | ||
597 | unsigned long flags; | ||
598 | int err = false; | ||
599 | int i; | ||
600 | |||
601 | for (i = 0; i < chan->valid_ports; i++) | ||
602 | tegra_channel_surface_setup(chan, buf, i); | ||
603 | |||
604 | if (!chan->bfirst_fstart) { | ||
605 | err = tegra_channel_set_stream(chan, true); | ||
606 | if (err < 0) | ||
607 | return err; | ||
608 | } | ||
609 | |||
610 | for (i = 0; i < chan->valid_ports; i++) { | ||
611 | vi4_channel_write(chan, chan->vnc_id[i], CHANNEL_COMMAND, LOAD); | ||
612 | vi4_channel_write(chan, chan->vnc_id[i], | ||
613 | CONTROL, SINGLESHOT | MATCH_STATE_EN); | ||
614 | } | ||
615 | |||
616 | /* wait for vi notifier events */ | ||
617 | vi_notify_wait(chan, &ts); | ||
618 | |||
619 | vi4_check_status(chan); | ||
620 | |||
621 | spin_lock_irqsave(&chan->capture_state_lock, flags); | ||
622 | if (chan->capture_state != CAPTURE_ERROR) | ||
623 | chan->capture_state = CAPTURE_GOOD; | ||
624 | spin_unlock_irqrestore(&chan->capture_state_lock, flags); | ||
625 | |||
626 | tegra_channel_ring_buffer(chan, vb, &ts, state); | ||
627 | |||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static int tegra_channel_stop_increments(struct tegra_channel *chan) | ||
632 | { | ||
633 | int i; | ||
634 | struct tegra_vi4_syncpts_req req = { | ||
635 | .syncpt_ids = { | ||
636 | 0xffffffff, | ||
637 | 0xffffffff, | ||
638 | 0xffffffff, | ||
639 | }, | ||
640 | .stream = chan->port[0], | ||
641 | .vc = 0, | ||
642 | }; | ||
643 | |||
644 | /* No need to check errors. There's nothing we could do. */ | ||
645 | for (i = 0; i < chan->valid_ports; i++) | ||
646 | vi_notify_channel_reset(chan->vnc_id[i], chan->vnc[i], &req); | ||
647 | |||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | static void tegra_channel_capture_done(struct tegra_channel *chan) | ||
652 | { | ||
653 | struct timespec ts; | ||
654 | struct tegra_channel_buffer *buf; | ||
655 | int state = VB2_BUF_STATE_DONE; | ||
656 | u32 thresh[TEGRA_CSI_BLOCKS]; | ||
657 | int i, err; | ||
658 | |||
659 | /* dequeue buffer and return if no buffer exists */ | ||
660 | buf = dequeue_buffer(chan); | ||
661 | if (!buf) | ||
662 | return; | ||
663 | |||
664 | /* make sure to read the last frame out before exit */ | ||
665 | for (i = 0; i < chan->valid_ports; i++) { | ||
666 | tegra_channel_surface_setup(chan, buf, i); | ||
667 | vi4_channel_write(chan, chan->vnc_id[i], CHANNEL_COMMAND, LOAD); | ||
668 | vi4_channel_write(chan, chan->vnc_id[i], | ||
669 | CONTROL, SINGLESHOT | MATCH_STATE_EN); | ||
670 | } | ||
671 | |||
672 | for (i = 0; i < chan->valid_ports; i++) { | ||
673 | err = nvhost_syncpt_read_ext_check(chan->vi->ndev, | ||
674 | chan->syncpt[i][FE_SYNCPT_IDX], &thresh[i]); | ||
675 | /* Get current ATOMP_FE syncpt min value */ | ||
676 | if (!err) { | ||
677 | struct vi_capture_status status; | ||
678 | u32 index = thresh[i] + 1; | ||
679 | /* Wait for ATOMP_FE syncpt | ||
680 | * | ||
681 | * This is to make sure we don't exit the capture thread | ||
682 | * before the last frame is done writing to memory | ||
683 | */ | ||
684 | err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev, | ||
685 | chan->syncpt[i][FE_SYNCPT_IDX], | ||
686 | index, | ||
687 | 250, NULL, NULL); | ||
688 | if (unlikely(err)) | ||
689 | dev_err(chan->vi->dev, | ||
690 | "ATOMP_FE syncpt timeout!\n"); | ||
691 | else { | ||
692 | err = vi_notify_get_capture_status(chan->vnc[i], | ||
693 | chan->vnc_id[i], | ||
694 | index, &status); | ||
695 | if (unlikely(err)) | ||
696 | dev_err(chan->vi->dev, | ||
697 | "no capture status! err = %d\n", | ||
698 | err); | ||
699 | else | ||
700 | ts = ns_to_timespec((s64)status.eof_ts); | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | |||
705 | /* Mark capture state to IDLE as capture is finished */ | ||
706 | chan->capture_state = CAPTURE_IDLE; | ||
707 | |||
708 | tegra_channel_ring_buffer(chan, &buf->buf, &ts, state); | ||
709 | } | ||
710 | |||
711 | static int tegra_channel_kthread_capture_start(void *data) | ||
712 | { | ||
713 | struct tegra_channel *chan = data; | ||
714 | struct tegra_channel_buffer *buf; | ||
715 | int err = 0; | ||
716 | |||
717 | set_freezable(); | ||
718 | |||
719 | while (1) { | ||
720 | |||
721 | try_to_freeze(); | ||
722 | |||
723 | wait_event_interruptible(chan->start_wait, | ||
724 | !list_empty(&chan->capture) || | ||
725 | kthread_should_stop()); | ||
726 | |||
727 | if (kthread_should_stop()) | ||
728 | break; | ||
729 | |||
730 | /* source is not streaming if error is non-zero */ | ||
731 | /* wait till kthread stop and dont DeQ buffers */ | ||
732 | if (err) | ||
733 | continue; | ||
734 | |||
735 | buf = dequeue_buffer(chan); | ||
736 | if (!buf) | ||
737 | continue; | ||
738 | |||
739 | err = tegra_channel_capture_frame(chan, buf); | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static void tegra_channel_stop_kthreads(struct tegra_channel *chan) | ||
746 | { | ||
747 | mutex_lock(&chan->stop_kthread_lock); | ||
748 | /* Stop the kthread for capture */ | ||
749 | if (chan->kthread_capture_start) { | ||
750 | kthread_stop(chan->kthread_capture_start); | ||
751 | chan->kthread_capture_start = NULL; | ||
752 | } | ||
753 | mutex_unlock(&chan->stop_kthread_lock); | ||
754 | } | ||
755 | |||
756 | static int tegra_channel_update_clknbw(struct tegra_channel *chan, u8 on) | ||
757 | { | ||
758 | int ret = 0; | ||
759 | unsigned long request_pixelrate; | ||
760 | struct v4l2_subdev_frame_interval fie; | ||
761 | unsigned long csi_freq = 0; | ||
762 | |||
763 | fie.interval.denominator = DEFAULT_FRAMERATE; | ||
764 | fie.interval.numerator = 1; | ||
765 | |||
766 | if (v4l2_subdev_has_op(chan->subdev_on_csi, | ||
767 | video, g_frame_interval)) | ||
768 | v4l2_subdev_call(chan->subdev_on_csi, video, | ||
769 | g_frame_interval, &fie); | ||
770 | if (on) { | ||
771 | /* for PG, using default frequence */ | ||
772 | if (chan->pg_mode) { | ||
773 | csi_freq = DEFAULT_CSI_FREQ; | ||
774 | request_pixelrate = csi_freq * NUM_PPC; | ||
775 | } else { | ||
776 | /** | ||
777 | * TODO: use real sensor pixelrate | ||
778 | * See PowerService code | ||
779 | */ | ||
780 | request_pixelrate = (long long)(chan->format.width | ||
781 | * chan->format.height | ||
782 | * fie.interval.denominator / 100) | ||
783 | * VI_CSI_CLK_SCALE; | ||
784 | csi_freq = ((long long)chan->format.width | ||
785 | * chan->format.height | ||
786 | * fie.interval.denominator) / NUM_PPC; | ||
787 | } | ||
788 | |||
789 | /* VI clk should be slightly faster than CSI clk*/ | ||
790 | ret = nvhost_module_set_rate(chan->vi->ndev, &chan->video, | ||
791 | request_pixelrate, 0, NVHOST_PIXELRATE); | ||
792 | if (ret) { | ||
793 | dev_err(chan->vi->dev, "Fail to update vi clk\n"); | ||
794 | return ret; | ||
795 | } | ||
796 | } else { | ||
797 | csi_freq = DEFAULT_CSI_FREQ; | ||
798 | ret = nvhost_module_set_rate(chan->vi->ndev, &chan->video, 0, 0, | ||
799 | NVHOST_PIXELRATE); | ||
800 | if (ret) { | ||
801 | dev_err(chan->vi->dev, "Fail to update vi clk\n"); | ||
802 | return ret; | ||
803 | } | ||
804 | } | ||
805 | if (chan->pg_mode) | ||
806 | chan->requested_kbyteps = (on > 0 ? 1 : -1) * | ||
807 | ((long long)csi_freq * BPP_MEM * 110 / 100) / 1000; | ||
808 | else | ||
809 | chan->requested_kbyteps = (on > 0 ? 1 : -1) * | ||
810 | (((long long) chan->format.width * chan->format.height | ||
811 | * fie.interval.denominator * BPP_MEM) * 115 / 100) / 1000; | ||
812 | |||
813 | mutex_lock(&chan->vi->bw_update_lock); | ||
814 | chan->vi->aggregated_kbyteps += chan->requested_kbyteps; | ||
815 | ret = vi_v4l2_update_isobw(chan->vi->aggregated_kbyteps, 0); | ||
816 | mutex_unlock(&chan->vi->bw_update_lock); | ||
817 | if (ret) | ||
818 | dev_info(chan->vi->dev, | ||
819 | "WAR:Calculation not precise.Ignore BW request failure\n"); | ||
820 | ret = vi4_v4l2_set_la(chan->vi->ndev, 0, 0); | ||
821 | if (ret) | ||
822 | dev_info(chan->vi->dev, | ||
823 | "WAR:Calculation not precise.Ignore LA failure\n"); | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | int vi4_channel_start_streaming(struct vb2_queue *vq, u32 count) | ||
828 | { | ||
829 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
830 | struct media_pipeline *pipe = chan->video.entity.pipe; | ||
831 | int ret = 0, i; | ||
832 | unsigned long flags; | ||
833 | struct v4l2_ctrl *override_ctrl; | ||
834 | |||
835 | vi4_init(chan); | ||
836 | ret = media_entity_pipeline_start(&chan->video.entity, pipe); | ||
837 | if (ret < 0) | ||
838 | goto error_pipeline_start; | ||
839 | |||
840 | if (chan->bypass) { | ||
841 | ret = tegra_channel_set_stream(chan, true); | ||
842 | if (ret < 0) | ||
843 | goto error_set_stream; | ||
844 | return ret; | ||
845 | } | ||
846 | |||
847 | spin_lock_irqsave(&chan->capture_state_lock, flags); | ||
848 | chan->capture_state = CAPTURE_IDLE; | ||
849 | spin_unlock_irqrestore(&chan->capture_state_lock, flags); | ||
850 | |||
851 | for (i = 0; i < chan->valid_ports; i++) { | ||
852 | ret = tegra_channel_capture_setup(chan, i); | ||
853 | if (ret < 0) | ||
854 | goto error_capture_setup; | ||
855 | } | ||
856 | |||
857 | chan->sequence = 0; | ||
858 | tegra_channel_init_ring_buffer(chan); | ||
859 | |||
860 | /* disable override for vi mode */ | ||
861 | override_ctrl = v4l2_ctrl_find( | ||
862 | &chan->ctrl_handler, V4L2_CID_OVERRIDE_ENABLE); | ||
863 | if (!chan->pg_mode) { | ||
864 | if (override_ctrl) { | ||
865 | ret = v4l2_ctrl_s_ctrl(override_ctrl, false); | ||
866 | if (ret < 0) | ||
867 | dev_err(&chan->video.dev, | ||
868 | "failed to disable override control\n"); | ||
869 | } else | ||
870 | dev_err(&chan->video.dev, | ||
871 | "No override control\n"); | ||
872 | } | ||
873 | |||
874 | /* Update clock and bandwidth based on the format */ | ||
875 | ret = tegra_channel_update_clknbw(chan, 1); | ||
876 | if (ret) | ||
877 | goto error_capture_setup; | ||
878 | |||
879 | INIT_WORK(&chan->error_work, tegra_channel_error_worker); | ||
880 | INIT_WORK(&chan->status_work, tegra_channel_status_worker); | ||
881 | |||
882 | /* Start kthread to capture data to buffer */ | ||
883 | chan->kthread_capture_start = kthread_run( | ||
884 | tegra_channel_kthread_capture_start, | ||
885 | chan, chan->video.name); | ||
886 | if (IS_ERR(chan->kthread_capture_start)) { | ||
887 | dev_err(&chan->video.dev, | ||
888 | "failed to run kthread for capture start\n"); | ||
889 | ret = PTR_ERR(chan->kthread_capture_start); | ||
890 | goto error_capture_setup; | ||
891 | } | ||
892 | |||
893 | return 0; | ||
894 | |||
895 | error_capture_setup: | ||
896 | if (!chan->pg_mode) | ||
897 | tegra_channel_set_stream(chan, false); | ||
898 | error_set_stream: | ||
899 | media_entity_pipeline_stop(&chan->video.entity); | ||
900 | error_pipeline_start: | ||
901 | vq->start_streaming_called = 0; | ||
902 | tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_QUEUED); | ||
903 | |||
904 | return ret; | ||
905 | } | ||
906 | |||
907 | int vi4_channel_stop_streaming(struct vb2_queue *vq) | ||
908 | { | ||
909 | struct tegra_channel *chan = vb2_get_drv_priv(vq); | ||
910 | bool is_streaming = atomic_read(&chan->is_streaming); | ||
911 | int i; | ||
912 | |||
913 | for (i = 0; i < chan->valid_ports; i++) { | ||
914 | if (chan->vnc_id[i] == -1) | ||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | cancel_work_sync(&chan->status_work); | ||
919 | cancel_work_sync(&chan->error_work); | ||
920 | |||
921 | if (!chan->bypass) { | ||
922 | tegra_channel_stop_kthreads(chan); | ||
923 | /* wait for last frame memory write ack */ | ||
924 | if (is_streaming) | ||
925 | tegra_channel_capture_done(chan); | ||
926 | for (i = 0; i < chan->valid_ports; i++) | ||
927 | tegra_channel_notify_disable(chan, i); | ||
928 | /* free all the ring buffers */ | ||
929 | free_ring_buffers(chan, chan->num_buffers); | ||
930 | /* dequeue buffers back to app which are in capture queue */ | ||
931 | tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR); | ||
932 | } | ||
933 | |||
934 | tegra_channel_set_stream(chan, false); | ||
935 | media_entity_pipeline_stop(&chan->video.entity); | ||
936 | |||
937 | if (!chan->bypass) | ||
938 | tegra_channel_update_clknbw(chan, 0); | ||
939 | |||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | int tegra_vi4_power_on(struct tegra_mc_vi *vi) | ||
944 | { | ||
945 | int ret; | ||
946 | |||
947 | ret = nvhost_module_busy(vi->ndev); | ||
948 | if (ret) { | ||
949 | dev_err(vi->dev, "%s:nvhost module is busy\n", __func__); | ||
950 | return ret; | ||
951 | } | ||
952 | |||
953 | ret = tegra_camera_emc_clk_enable(); | ||
954 | if (ret) | ||
955 | goto err_emc_enable; | ||
956 | |||
957 | return 0; | ||
958 | |||
959 | err_emc_enable: | ||
960 | nvhost_module_idle(vi->ndev); | ||
961 | |||
962 | return ret; | ||
963 | } | ||
964 | |||
965 | void tegra_vi4_power_off(struct tegra_mc_vi *vi) | ||
966 | { | ||
967 | tegra_channel_ec_close(vi); | ||
968 | tegra_camera_emc_clk_disable(); | ||
969 | nvhost_module_idle(vi->ndev); | ||
970 | } | ||
971 | |||
972 | int vi4_power_on(struct tegra_channel *chan) | ||
973 | { | ||
974 | int ret = 0; | ||
975 | struct tegra_mc_vi *vi; | ||
976 | struct tegra_csi_device *csi; | ||
977 | |||
978 | vi = chan->vi; | ||
979 | csi = vi->csi; | ||
980 | |||
981 | /* Use chan->video as identifier of vi4 nvhost_module client | ||
982 | * since they are unique per channel | ||
983 | */ | ||
984 | ret = nvhost_module_add_client(vi->ndev, &chan->video); | ||
985 | if (ret < 0) | ||
986 | return ret; | ||
987 | |||
988 | ret = tegra_vi4_power_on(vi); | ||
989 | if (ret < 0) | ||
990 | return ret; | ||
991 | |||
992 | if (atomic_add_return(1, &chan->power_on_refcnt) == 1) { | ||
993 | ret = tegra_channel_set_power(chan, 1); | ||
994 | if (ret < 0) { | ||
995 | dev_err(vi->dev, "Failed to power on subdevices\n"); | ||
996 | return ret; | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | #if defined(CONFIG_TEGRA_CAMERA_RTCPU) | ||
1001 | ret = vi_capture_init(chan); | ||
1002 | if (ret < 0) | ||
1003 | return ret; | ||
1004 | #endif | ||
1005 | |||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | void vi4_power_off(struct tegra_channel *chan) | ||
1010 | { | ||
1011 | int ret = 0; | ||
1012 | struct tegra_mc_vi *vi; | ||
1013 | struct tegra_csi_device *csi; | ||
1014 | |||
1015 | vi = chan->vi; | ||
1016 | csi = vi->csi; | ||
1017 | |||
1018 | #if defined(CONFIG_TEGRA_CAMERA_RTCPU) | ||
1019 | vi_capture_shutdown(chan); | ||
1020 | #endif | ||
1021 | |||
1022 | if (atomic_dec_and_test(&chan->power_on_refcnt)) { | ||
1023 | ret = tegra_channel_set_power(chan, 0); | ||
1024 | if (ret < 0) | ||
1025 | dev_err(vi->dev, "Failed to power off subdevices\n"); | ||
1026 | } | ||
1027 | |||
1028 | tegra_vi4_power_off(vi); | ||
1029 | nvhost_module_remove_client(vi->ndev, &chan->video); | ||
1030 | } | ||
1031 | |||
1032 | static void tegra_channel_error_worker(struct work_struct *error_work) | ||
1033 | { | ||
1034 | struct tegra_channel *chan; | ||
1035 | |||
1036 | chan = container_of(error_work, struct tegra_channel, error_work); | ||
1037 | |||
1038 | vi4_power_off(chan); | ||
1039 | tegra_channel_handle_error(chan); | ||
1040 | } | ||
1041 | |||
1042 | static void tegra_channel_notify_error_callback(void *client_data) | ||
1043 | { | ||
1044 | struct tegra_channel *chan = (struct tegra_channel *)client_data; | ||
1045 | |||
1046 | spin_lock(&chan->capture_state_lock); | ||
1047 | if (chan->capture_state == CAPTURE_GOOD) | ||
1048 | chan->capture_state = CAPTURE_ERROR; | ||
1049 | else { | ||
1050 | spin_unlock(&chan->capture_state_lock); | ||
1051 | return; | ||
1052 | } | ||
1053 | spin_unlock(&chan->capture_state_lock); | ||
1054 | |||
1055 | schedule_work(&chan->error_work); | ||
1056 | } | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi4_fops.h b/drivers/media/platform/tegra/camera/vi/vi4_fops.h new file mode 100644 index 000000000..4f48aca8d --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi4_fops.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Tegra Video Input 4 device common APIs | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * Author: Frank Chen <frankc@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef __T186_VI_H__ | ||
16 | #define __T186_VI_H__ | ||
17 | |||
18 | int vi4_power_on(struct tegra_channel *chan); | ||
19 | void vi4_power_off(struct tegra_channel *chan); | ||
20 | int vi4_channel_start_streaming(struct vb2_queue *vq, u32 count); | ||
21 | int vi4_channel_stop_streaming(struct vb2_queue *vq); | ||
22 | int vi4_add_ctrls(struct tegra_channel *chan); | ||
23 | void vi4_init_video_formats(struct tegra_channel *chan); | ||
24 | long vi4_default_ioctl(struct file *file, void *fh, | ||
25 | bool use_prio, unsigned int cmd, void *arg); | ||
26 | |||
27 | struct tegra_vi_fops vi4_fops = { | ||
28 | .vi_power_on = vi4_power_on, | ||
29 | .vi_power_off = vi4_power_off, | ||
30 | .vi_start_streaming = vi4_channel_start_streaming, | ||
31 | .vi_stop_streaming = vi4_channel_stop_streaming, | ||
32 | .vi_add_ctrls = vi4_add_ctrls, | ||
33 | .vi_init_video_formats = vi4_init_video_formats, | ||
34 | .vi_default_ioctl = vi4_default_ioctl, | ||
35 | }; | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi4_formats.h b/drivers/media/platform/tegra/camera/vi/vi4_formats.h new file mode 100644 index 000000000..bbdbbab71 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi4_formats.h | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra Video Input Device Driver VI4 formats | ||
3 | * | ||
4 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Author: Bhanu Murthy V <bmurthyv@nvidia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __VI4_FORMATS_H_ | ||
14 | #define __VI4_FORMATS_H_ | ||
15 | |||
16 | #include "camera/vi/core.h" | ||
17 | |||
18 | /* | ||
19 | * These go into the VI_CHn_PIXFMT_FORMAT register bits 7:0 | ||
20 | * Output pixel memory format for the VI channel. | ||
21 | */ | ||
22 | enum tegra_image_format { | ||
23 | TEGRA_IMAGE_FORMAT_T_L8 = 16, | ||
24 | |||
25 | TEGRA_IMAGE_FORMAT_T_R16_I = 32, | ||
26 | TEGRA_IMAGE_FORMAT_T_B5G6R5, | ||
27 | TEGRA_IMAGE_FORMAT_T_R5G6B5, | ||
28 | TEGRA_IMAGE_FORMAT_T_A1B5G5R5, | ||
29 | TEGRA_IMAGE_FORMAT_T_A1R5G5B5, | ||
30 | TEGRA_IMAGE_FORMAT_T_B5G5R5A1, | ||
31 | TEGRA_IMAGE_FORMAT_T_R5G5B5A1, | ||
32 | TEGRA_IMAGE_FORMAT_T_A4B4G4R4, | ||
33 | TEGRA_IMAGE_FORMAT_T_A4R4G4B4, | ||
34 | TEGRA_IMAGE_FORMAT_T_B4G4R4A4, | ||
35 | TEGRA_IMAGE_FORMAT_T_R4G4B4A4, | ||
36 | |||
37 | TEGRA_IMAGE_FORMAT_T_A8B8G8R8 = 64, | ||
38 | TEGRA_IMAGE_FORMAT_T_A8R8G8B8, | ||
39 | TEGRA_IMAGE_FORMAT_T_B8G8R8A8, | ||
40 | TEGRA_IMAGE_FORMAT_T_R8G8B8A8, | ||
41 | TEGRA_IMAGE_FORMAT_T_A2B10G10R10, | ||
42 | TEGRA_IMAGE_FORMAT_T_A2R10G10B10, | ||
43 | TEGRA_IMAGE_FORMAT_T_B10G10R10A2, | ||
44 | TEGRA_IMAGE_FORMAT_T_R10G10B10A2, | ||
45 | |||
46 | TEGRA_IMAGE_FORMAT_T_A8Y8U8V8 = 193, | ||
47 | TEGRA_IMAGE_FORMAT_T_V8U8Y8A8, | ||
48 | |||
49 | TEGRA_IMAGE_FORMAT_T_A2Y10U10V10 = 197, | ||
50 | TEGRA_IMAGE_FORMAT_T_V10U10Y10A2, | ||
51 | |||
52 | TEGRA_IMAGE_FORMAT_T_Y8_U8__Y8_V8 = 200, | ||
53 | TEGRA_IMAGE_FORMAT_T_Y8_V8__Y8_U8, | ||
54 | |||
55 | TEGRA_IMAGE_FORMAT_T_U8_Y8__V8_Y8 = 203, | ||
56 | |||
57 | TEGRA_IMAGE_FORMAT_T_T_V8_Y8__U8_Y8 = 223, | ||
58 | TEGRA_IMAGE_FORMAT_T_T_Y8__U8__V8_N444, | ||
59 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N444, | ||
60 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N444, | ||
61 | TEGRA_IMAGE_FORMAT_T_Y8__U8__V8_N422, | ||
62 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N422, | ||
63 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N422, | ||
64 | TEGRA_IMAGE_FORMAT_T_Y8__U8__V8_N420, | ||
65 | TEGRA_IMAGE_FORMAT_T_Y8__U8V8_N420, | ||
66 | TEGRA_IMAGE_FORMAT_T_Y8__V8U8_N420, | ||
67 | |||
68 | TEGRA_IMAGE_FORMAT_T_Y10__U10__V10_N422 = 240, | ||
69 | TEGRA_IMAGE_FORMAT_T_Y10__U10V10_N422, | ||
70 | TEGRA_IMAGE_FORMAT_T_Y10__V10U10_N422, | ||
71 | TEGRA_IMAGE_FORMAT_T_Y10__U10__V10_N420, | ||
72 | TEGRA_IMAGE_FORMAT_T_Y10__U10V10_N420, | ||
73 | TEGRA_IMAGE_FORMAT_T_Y10__V10U10_N420, | ||
74 | |||
75 | TEGRA_IMAGE_FORMAT_T_R16 = 248, | ||
76 | TEGRA_IMAGE_FORMAT_T_R32, | ||
77 | TEGRA_IMAGE_FORMAT_T_L16_F, | ||
78 | TEGRA_IMAGE_FORMAT_T_L32_F, | ||
79 | |||
80 | TEGRA_IMAGE_FORMAT_T_DPCM_RAW10 = 254, | ||
81 | TEGRA_IMAGE_FORMAT_T_DPCM_RAW12, | ||
82 | }; | ||
83 | |||
84 | static const struct tegra_video_format vi4_video_formats[] = { | ||
85 | /* RAW 6: TODO */ | ||
86 | |||
87 | /* RAW 7: TODO */ | ||
88 | |||
89 | /* RAW 8 */ | ||
90 | TEGRA_VIDEO_FORMAT(RAW8, 8, SRGGB8_1X8, 1, 1, T_L8, | ||
91 | RAW8, SRGGB8, "RGRG.. GBGB.."), | ||
92 | TEGRA_VIDEO_FORMAT(RAW8, 8, SGRBG8_1X8, 1, 1, T_L8, | ||
93 | RAW8, SGRBG8, "GRGR.. BGBG.."), | ||
94 | TEGRA_VIDEO_FORMAT(RAW8, 8, SGBRG8_1X8, 1, 1, T_L8, | ||
95 | RAW8, SGBRG8, "GBGB.. RGRG.."), | ||
96 | TEGRA_VIDEO_FORMAT(RAW8, 8, SBGGR8_1X8, 1, 1, T_L8, | ||
97 | RAW8, SBGGR8, "BGBG.. GRGR.."), | ||
98 | |||
99 | /* RAW 10 */ | ||
100 | TEGRA_VIDEO_FORMAT(RAW10, 10, SRGGB10_1X10, 2, 1, T_R16_I, | ||
101 | RAW10, SRGGB10, "RGRG.. GBGB.."), | ||
102 | TEGRA_VIDEO_FORMAT(RAW10, 10, SGRBG10_1X10, 2, 1, T_R16_I, | ||
103 | RAW10, SGRBG10, "GRGR.. BGBG.."), | ||
104 | TEGRA_VIDEO_FORMAT(RAW10, 10, SGBRG10_1X10, 2, 1, T_R16_I, | ||
105 | RAW10, SGBRG10, "GBGB.. RGRG.."), | ||
106 | TEGRA_VIDEO_FORMAT(RAW10, 10, SBGGR10_1X10, 2, 1, T_R16_I, | ||
107 | RAW10, SBGGR10, "BGBG.. GRGR.."), | ||
108 | |||
109 | /* RAW 12 */ | ||
110 | TEGRA_VIDEO_FORMAT(RAW12, 12, SRGGB12_1X12, 2, 1, T_R16_I, | ||
111 | RAW12, SRGGB12, "RGRG.. GBGB.."), | ||
112 | TEGRA_VIDEO_FORMAT(RAW12, 12, SGRBG12_1X12, 2, 1, T_R16_I, | ||
113 | RAW12, SGRBG12, "GRGR.. BGBG.."), | ||
114 | TEGRA_VIDEO_FORMAT(RAW12, 12, SGBRG12_1X12, 2, 1, T_R16_I, | ||
115 | RAW12, SGBRG12, "GBGB.. RGRG.."), | ||
116 | TEGRA_VIDEO_FORMAT(RAW12, 12, SBGGR12_1X12, 2, 1, T_R16_I, | ||
117 | RAW12, SBGGR12, "BGBG.. GRGR.."), | ||
118 | |||
119 | /* RGB888 */ | ||
120 | TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X24, 4, 1, T_A8R8G8B8, | ||
121 | RGB888, ABGR32, "BGRA-8-8-8-8"), | ||
122 | TEGRA_VIDEO_FORMAT(RGB888, 24, RGB888_1X32_PADHI, 4, 1, T_A8B8G8R8, | ||
123 | RGB888, RGB32, "RGB-8-8-8-8"), | ||
124 | |||
125 | /* YUV422 */ | ||
126 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 2, 1, T_U8_Y8__V8_Y8, | ||
127 | YUV422_8, UYVY, "YUV 4:2:2"), | ||
128 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 1, 1, T_Y8__V8U8_N422, | ||
129 | YUV422_8, NV16, "NV16"), | ||
130 | TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8, | ||
131 | YUV422_8, UYVY, "YUV 4:2:2 UYVY"), | ||
132 | TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_2X8, 2, 1, T_T_V8_Y8__U8_Y8, | ||
133 | YUV422_8, VYUY, "YUV 4:2:2 VYUY"), | ||
134 | TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_2X8, 2, 1, T_Y8_U8__Y8_V8, | ||
135 | YUV422_8, YUYV, "YUV 4:2:2 YUYV"), | ||
136 | TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_2X8, 2, 1, T_Y8_V8__Y8_U8, | ||
137 | YUV422_8, YVYU, "YUV 4:2:2 YVYU"), | ||
138 | }; | ||
139 | |||
140 | #endif | ||
diff --git a/drivers/media/platform/tegra/camera/vi/vi4_registers.h b/drivers/media/platform/tegra/camera/vi/vi4_registers.h new file mode 100644 index 000000000..588ebb324 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi/vi4_registers.h | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * drivers/media/platform/tegra/camera/vi/vi4_registers.h | ||
3 | * | ||
4 | * Tegra 18x VI register offsets | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #ifndef __VI4_REGISTERS_H__ | ||
22 | #define __VI4_REGISTERS_H__ | ||
23 | |||
24 | /* VI registers. Start from 0x0 */ | ||
25 | #define VI_STREAMS (6) | ||
26 | #define VIRTUAL_CHANNELS (4) | ||
27 | #define VI4_CHANNEL_OFFSET 0x10000 | ||
28 | |||
29 | #define CFG_INTERRUPT_STATUS 0x44 | ||
30 | #define CFG_INTERRUPT_MASK 0x48 | ||
31 | #define VGP6_INT_MASK (0x1 << 29) | ||
32 | #define VGP5_INT_MASK (0x1 << 28) | ||
33 | #define VGP4_INT_MASK (0x1 << 27) | ||
34 | #define VGP3_INT_MASK (0x1 << 26) | ||
35 | #define VGP2_INT_MASK (0x1 << 25) | ||
36 | #define VGP1_INT_MASK (0x1 << 24) | ||
37 | #define HOST_PKTINJECT_STALL_ERR_MASK (0x1 << 7) | ||
38 | #define CSIMUX_FIFO_OVFL_ERR_MASK (0x1 << 6) | ||
39 | #define ATOMP_PACKER_OVFL_ERR_MASK (0x1 << 5) | ||
40 | #define FMLITE_BUF_OVFL_ERR_MASK (0x1 << 4) | ||
41 | #define NOTIFY_FIFO_OVFL_ERR_MASK (0x1 << 3) | ||
42 | #define ISPBUFA_ERR_MASK (0x1 << 0) | ||
43 | |||
44 | #define CFG_PWM_HIGH_PULSE 0x50 | ||
45 | #define PWM_HIGH_PULSE (0xffffffff << 0) | ||
46 | |||
47 | #define CSIMUX_CONFIG_STREAM_0 0x424 | ||
48 | #define CSIMUX_CONFIG_STREAM_1 0x428 | ||
49 | #define CSIMUX_CONFIG_STREAM_2 0x42C | ||
50 | #define CSIMUX_CONFIG_STREAM_3 0x430 | ||
51 | #define CSIMUX_CONFIG_STREAM_4 0x434 | ||
52 | #define CSIMUX_CONFIG_STREAM_5 0x438 | ||
53 | #define FRAMEIDGEN (0xf << 26) | ||
54 | #define STICKYFAULT (0x1 << 25) | ||
55 | #define VPR (0x1 << 24) | ||
56 | #define SRESET (0x1 << 23) | ||
57 | #define QBLOCK (0x1 << 22) | ||
58 | #define FEINJECT (0x1 << 21) | ||
59 | #define FESHORTTIMER (0x1 << 20) | ||
60 | #define FEMAXTIME (0xffff << 4) | ||
61 | #define WT (0xf << 0) | ||
62 | |||
63 | #define NOTIFY_FIFO_TAG_0 0x4000 | ||
64 | #define NOTIFY_FRAME_ID (0xffff << 16) | ||
65 | #define NOTIFY_CHANNEL (0xff << 8) | ||
66 | #define NOTIFY_CHANNEL_SHIFT (8) | ||
67 | #define NOTIFY_TAG (0x1f << 1) | ||
68 | #define NOTIFY_TAG_SHIFT (1) | ||
69 | #define NOTIFY_VALID (0x1 << 0) | ||
70 | |||
71 | #define TAG_FS 0 | ||
72 | #define TAG_FE 1 | ||
73 | #define TAG_CSIMUX_FRAME 2 | ||
74 | #define TAG_CSIMUX_STREAM 3 | ||
75 | #define TAG_CHANSEL_PXL_SOF 4 | ||
76 | #define TAG_CHANSEL_PXL_EOF 5 | ||
77 | #define TAG_CHANSEL_EMBED_SOF 6 | ||
78 | #define TAG_CHANSEL_EMBED_EOF 7 | ||
79 | #define TAG_CHANSEL_NLINES 8 | ||
80 | #define TAG_CHANSEL_FAULT 9 | ||
81 | #define TAG_CHANSEL_FAULT_FE 10 | ||
82 | #define TAG_CHANSEL_NOMATCH 11 | ||
83 | #define TAG_CHANSEL_COLLISION 12 | ||
84 | #define TAG_CHANSEL_SHORT_FRAME 13 | ||
85 | #define TAG_CHANSEL_LOAD_FRAMED 14 | ||
86 | #define TAG_ATOMP_PACKER_OVERFLOW 15 | ||
87 | #define TAG_ATOMP_FS 16 | ||
88 | #define TAG_ATOMP_FE 17 | ||
89 | #define TAG_ATOMP_FRAME_DONE 18 | ||
90 | #define TAG_ATOMP_EMB_DATA_DONE 19 | ||
91 | #define TAG_ATOMP_FRAME_NLINES_DONE 20 | ||
92 | #define TAG_ATOMP_FRAME_TRUNCATED 21 | ||
93 | #define TAG_ATOMP_FRAME_TOSSED 22 | ||
94 | #define TAG_ATOMP_PDAF_DATA_DONE 23 | ||
95 | #define TAG_ISPBUF_FIFO_OVERFLOW 26 | ||
96 | #define TAG_ISPBUF_FS 27 | ||
97 | #define TAG_ISPBUF_FE 28 | ||
98 | #define TAG_VGP0_DONE 29 | ||
99 | #define TAG_VGP1_DONE 30 | ||
100 | #define TAG_FMLITE_DONE 31 | ||
101 | |||
102 | #define NOTIFY_FIFO_TIMESTAMP_0 0x4004 | ||
103 | #define NOTIFY_TIMESTAMP (0xffffffff << 0) | ||
104 | |||
105 | #define NOTIFY_FIFO_DATA_0 0x4008 | ||
106 | #define NOTIFY_DATA (0xffffffff << 0) | ||
107 | |||
108 | #define NOTIFY_TAG_CLASSIFY_0 0x6000 | ||
109 | #define NOTIFY_TAG_CLASSIFY_1 0x6004 | ||
110 | #define NOTIFY_TAG_CLASSIFY_2 0x6008 | ||
111 | #define NOTIFY_TAG_CLASSIFY_3 0x600c | ||
112 | #define NOTIFY_TAG_CLASSIFY_4 0x6010 | ||
113 | #define STREAM5_FEINJECT_VC (0xf << 20) | ||
114 | #define STREAM4_FEINJECT_VC (0xf << 16) | ||
115 | #define STREAM3_FEINJECT_VC (0xf << 12) | ||
116 | #define STREAM2_FEINJECT_VC (0xf << 8) | ||
117 | #define STREAM1_FEINJECT_VC (0xf << 4) | ||
118 | #define STREAM0_FEINJECT_VC (0xf << 0) | ||
119 | |||
120 | #define NOTIFY_FIFO_OCCUPANCY_0 0x6014 | ||
121 | #define NOTIFY_MAX (0x3ff << 20) | ||
122 | #define NOTIFY_CURRENT (0x3ff << 10) | ||
123 | #define NOTIFY_CURRENT_SHIFT 10 | ||
124 | #define NOTIFY_SIZE (0x3ff << 0) | ||
125 | |||
126 | /* VI_CH registers. Start from 0x10000, offset 0x10000 */ | ||
127 | #define CHANNEL_COMMAND 0x004 | ||
128 | #define WR_ACT_SEL (0x1 << 5) | ||
129 | #define RD_MUX_SEL (0x1 << 4) | ||
130 | #define AUTOLOAD (0x1 << 1) | ||
131 | #define LOAD (0x1 << 0) | ||
132 | |||
133 | #define CONTROL 0x01c | ||
134 | #define SPARE (0xffff << 16) | ||
135 | #define POST_RUNAWAY_EMBED (0x1 << 4) | ||
136 | #define POST_RUNAWAY_PIXEL (0x1 << 3) | ||
137 | #define EARLY_ABORT (0x1 << 2) | ||
138 | #define SINGLESHOT (0x1 << 1) | ||
139 | #define MATCH_STATE_EN (0x1 << 0) | ||
140 | |||
141 | #define MATCH 0x020 | ||
142 | #define STREAM (0x3f << 14) | ||
143 | #define STREAM_SHIFT (14) | ||
144 | #define STREAM_MASK (0x3f << 8) | ||
145 | #define VIRTUAL_CHANNEL (0xf << 4) | ||
146 | #define VIRTUAL_CHANNEL_SHIFT (4) | ||
147 | #define VIRTUAL_CHANNEL_MASK (0xf << 0) | ||
148 | |||
149 | #define MATCH_DATATYPE 0x024 | ||
150 | #define DATATYPE (0x3f << 6) | ||
151 | #define DATATYPE_SHIFT (6) | ||
152 | #define DATATYPE_MASK (0x3f << 0) | ||
153 | #define DATATYPE_MASK_SHIFT (0) | ||
154 | |||
155 | #define MATCH_FRAMEID 0x028 | ||
156 | #define FRAMEID (0xffff << 16) | ||
157 | #define FRAMEID_SHIFT (16) | ||
158 | #define FRAMEID_MASK (0xffff << 0) | ||
159 | |||
160 | #define DT_OVERRIDE 0x02c | ||
161 | #define OVRD_DT (0x3f << 1) | ||
162 | #define DT_OVRD_EN (0x1 << 0) | ||
163 | |||
164 | #define FRAME_X 0x030 | ||
165 | #define CROP_X 0x04c | ||
166 | #define OUT_X 0x058 | ||
167 | #define WIDTH (0xffff < 0) | ||
168 | |||
169 | #define FRAME_Y 0x034 | ||
170 | #define CROP_Y 0x054 | ||
171 | #define OUT_Y 0x05c | ||
172 | #define HEIGHT (0xffff < 0) | ||
173 | |||
174 | #define EMBED_X 0x038 | ||
175 | #define MAX_BYTES (0x3ffff < 0) | ||
176 | |||
177 | #define EMBED_Y 0x03c | ||
178 | #define SKIP_Y 0x050 | ||
179 | #define LINES (0xffff < 0) | ||
180 | /* for EMBED_Y only */ | ||
181 | #define EXPECT (0x1 << 24) | ||
182 | |||
183 | #define LINE_TIMER 0x044 | ||
184 | #define LINE_TIMER_EN (0x1 << 25) | ||
185 | #define PERIODIC (0x1 << 24) | ||
186 | #define TRIPLINE (0xffff << 0) | ||
187 | |||
188 | #define SKIP_X 0x048 | ||
189 | #define PACKETS (0x1fff << 0) | ||
190 | |||
191 | #define NOTIFY_MASK 0x060 | ||
192 | #define MASK_DTYPE_MISMATCH (0x1 << 31) | ||
193 | #define MASK_EMBED_INFRINGE (0x1 << 22) | ||
194 | #define MASK_EMBED_LONG_LINE (0x1 << 21) | ||
195 | #define MASK_EMBED_SPURIOUS (0x1 << 20) | ||
196 | #define MASK_EMBED_RUNAWAY (0x1 << 19) | ||
197 | #define MASK_EMBED_MISSING_LE (0x1 << 18) | ||
198 | #define MASK_EMBED_EOF (0x1 << 17) | ||
199 | #define MASK_EMBED_SOF (0x1 << 16) | ||
200 | #define MASK_PIXEL_LINE_TIMER (0x1 << 7) | ||
201 | #define MASK_PIXEL_SHORT_LINE (0x1 << 6) | ||
202 | #define MASK_PIXEL_LONG_LINE (0x1 << 5) | ||
203 | #define MASK_PIXEL_SPURIOUS (0x1 << 4) | ||
204 | #define MASK_PIXEL_RUNAWAY (0x1 << 3) | ||
205 | #define MASK_PIXEL_MISSING_LE (0x1 << 2) | ||
206 | #define MASK_PIXEL_EOF (0x1 << 1) | ||
207 | #define MASK_PIXEL_SOF (0x1 << 0) | ||
208 | |||
209 | #define NOTIFY_MASK_XCPT 0x064 | ||
210 | #define MASK_NOMATCH (0x1 << 9) | ||
211 | #define MASK_EMBED_OPEN_LINE (0x1 << 8) | ||
212 | #define MASK_PIXEL_OPEN_LINE (0x1 << 7) | ||
213 | #define MASK_FORCE_FE (0x1 << 6) | ||
214 | #define MASK_STALE_FRAME (0x1 << 5) | ||
215 | #define MASK_COLLISION (0x1 << 4) | ||
216 | #define MASK_EMPTY_FRAME (0x1 << 3) | ||
217 | #define MASK_EMBED_SHORT_FRAME (0x1 << 2) | ||
218 | #define MASK_PIXEL_SHORT_FRAME (0x1 << 1) | ||
219 | #define MASK_LOAD_FRAMED (0x1 << 0) | ||
220 | |||
221 | #define FRAME_COUNT 0x06c | ||
222 | |||
223 | #define PIXFMT_ENABLE 0x080 | ||
224 | #define PDAF_EN (0x1 << 2) | ||
225 | #define COMPAND_EN (0x1 << 1) | ||
226 | #define PIXFMT_EN (0x1 << 0) | ||
227 | |||
228 | #define PIXFMT_FORMAT 0x084 | ||
229 | #define FORMAT (0xff << 0) | ||
230 | /* refer to enum tegra_image_format in core.h */ | ||
231 | |||
232 | #define PIXFMT_WIDE 0x088 | ||
233 | #define ENDIAN_BIG (0x0 << 1) | ||
234 | #define ENDIAN_LITTLE (0x1 << 1) | ||
235 | #define PIXFMT_WIDE_EN (0x1 << 0) | ||
236 | |||
237 | #define DPCM_STRIP 0x0b8 | ||
238 | #define OVERFETCH (0x1fff < 16) | ||
239 | #define STRIP_WIDTH (0x1fff < 0) | ||
240 | |||
241 | #define ATOMP_DPCM_CHUNK 0x0ec | ||
242 | #define CHUNK_OFFSET (0x3ffff << 0) | ||
243 | |||
244 | #define ATOMP_SURFACE_OFFSET0 0x0e0 | ||
245 | #define ATOMP_SURFACE_OFFSET1 0x0f0 | ||
246 | #define ATOMP_SURFACE_OFFSET2 0x0fc | ||
247 | #define ATOMP_EMB_SURFACE_OFFSET0 0x108 | ||
248 | #define SURFACE_OFFSET (0xffffffff << 0) | ||
249 | |||
250 | #define ATOMP_SURFACE_OFFSET0_H 0x0e4 | ||
251 | #define ATOMP_SURFACE_OFFSET1_H 0x0f4 | ||
252 | #define ATOMP_SURFACE_OFFSET2_H 0x100 | ||
253 | #define ATOMP_EMB_SURFACE_OFFSET0_H 0x10c | ||
254 | #define SURFACE_OFFSET_HI (0xff << 0) | ||
255 | |||
256 | #define ATOMP_SURFACE_STRIDE0 0x0e8 | ||
257 | #define ATOMP_SURFACE_STRIDE1 0x0f8 | ||
258 | #define ATOMP_SURFACE_STRIDE2 0x104 | ||
259 | #define ATOMP_EMB_SURFACE_STRIDE0 0x110 | ||
260 | #define SURFACE_STRIDE (0x3ffff << 0) | ||
261 | #define ATOMP_RESERVE 0x120 | ||
262 | |||
263 | #define ISPBUFA 0x134 | ||
264 | #define ISPBUFA_EN (0x1 << 0) | ||
265 | |||
266 | #define ISPBUFA_ERROR 0x1000 | ||
267 | #define FIFO_OVERFLOW (0x1 << 0) | ||
268 | |||
269 | #define FMLITE_ERROR 0x313c | ||
270 | #define NOTIFY_ERROR 0x6020 | ||
271 | |||
272 | #endif /* __VI4_REGISTERS_H__ */ | ||
diff --git a/drivers/media/platform/tegra/mipical/Kconfig b/drivers/media/platform/tegra/mipical/Kconfig new file mode 100644 index 000000000..e00659b2b --- /dev/null +++ b/drivers/media/platform/tegra/mipical/Kconfig | |||
@@ -0,0 +1,11 @@ | |||
1 | config TEGRA_MIPI_CAL | ||
2 | depends on TEGRA_DC || TEGRA_CAMERA_PLATFORM | ||
3 | bool "Enable MIPI Cal driver" | ||
4 | default y | ||
5 | select REGMAP | ||
6 | select REGMAP_MMIO | ||
7 | help | ||
8 | Provides an interfaces to do MIPI calibration for both DSI and CSI blocks. | ||
9 | You will always want this enabled if you are using either Camera or Display. | ||
10 | Disabling this can cause issues for drivers that depend on it being present. | ||
11 | Leaving this on is harmless even if no drivers that need it are enabled. | ||
diff --git a/drivers/media/platform/tegra/mipical/Makefile b/drivers/media/platform/tegra/mipical/Makefile new file mode 100644 index 000000000..ffd996e09 --- /dev/null +++ b/drivers/media/platform/tegra/mipical/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | # | ||
3 | # Makefile for Mipical driver | ||
4 | # | ||
5 | obj-$(CONFIG_TEGRA_MIPI_CAL) += mipi_cal.o | ||
6 | obj-$(CONFIG_TEGRA_MIPI_CAL) += vmipi/vmipi.o | ||
diff --git a/drivers/media/platform/tegra/mipical/mipi_cal.c b/drivers/media/platform/tegra/mipical/mipi_cal.c new file mode 100644 index 000000000..d53af8ae3 --- /dev/null +++ b/drivers/media/platform/tegra/mipical/mipi_cal.c | |||
@@ -0,0 +1,1096 @@ | |||
1 | /* | ||
2 | * mipi_cal.c | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION, All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/regmap.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/export.h> | ||
26 | #include <linux/reset.h> | ||
27 | #include <linux/string.h> | ||
28 | #include <linux/debugfs.h> | ||
29 | #include <linux/clk/tegra.h> | ||
30 | #include <linux/pm.h> | ||
31 | #include <linux/miscdevice.h> | ||
32 | #include <linux/fs.h> | ||
33 | #include <linux/uaccess.h> | ||
34 | |||
35 | #include <soc/tegra/fuse.h> | ||
36 | #include <soc/tegra/tegra_powergate.h> | ||
37 | #include <linux/tegra_prod.h> | ||
38 | #include <uapi/misc/tegra_mipi_ioctl.h> | ||
39 | |||
40 | #include "registers.h" | ||
41 | #include "mipi_cal.h" | ||
42 | #include "vmipi/vmipi.h" | ||
43 | |||
44 | #define DRV_NAME "tegra_mipi_cal" | ||
45 | #define MIPI_CAL_TIMEOUT_MSEC 500 | ||
46 | |||
47 | struct tegra_mipi_bias { | ||
48 | /* BIAS_PAD_CFG0 */ | ||
49 | u8 pad_pdvclamp; | ||
50 | u8 e_vclamp_ref; | ||
51 | /* BIAS_PAD_CFG1 */ | ||
52 | u8 pad_driv_up_ref; | ||
53 | u8 pad_driv_dn_ref; | ||
54 | /* BIAS_PAD_CFG2 */ | ||
55 | u8 pad_vclamp_level; | ||
56 | u8 pad_vauxp_level; | ||
57 | }; | ||
58 | |||
59 | struct tegra_mipi_prod_csi { | ||
60 | struct tegra_mipi_bias bias_csi; | ||
61 | u8 overide_x; | ||
62 | u8 termos_x; | ||
63 | u8 termos_x_clk; | ||
64 | }; | ||
65 | |||
66 | struct tegra_mipi_prod_dsi { | ||
67 | struct tegra_mipi_bias bias_dsi; | ||
68 | u8 overide_x; | ||
69 | u8 hspdos_x; | ||
70 | u8 hspuos_x; | ||
71 | u8 termos_x; | ||
72 | u8 clk_overide_x; | ||
73 | u8 clk_hspdos_x; | ||
74 | u8 clk_hspuos_x; | ||
75 | u8 clk_hstermos_x; | ||
76 | }; | ||
77 | |||
78 | struct tegra_mipi; | ||
79 | struct tegra_mipi { | ||
80 | struct device *dev; | ||
81 | struct clk *mipi_cal_clk; | ||
82 | struct clk *mipi_cal_fixed; | ||
83 | struct reset_control *rst; | ||
84 | struct regmap *regmap; | ||
85 | struct mutex lock; | ||
86 | /* Legacy way of storing mipical reg config */ | ||
87 | struct tegra_mipi_prod_csi *prod_csi; | ||
88 | struct tegra_mipi_prod_dsi *prod_dsi; | ||
89 | /* If use tegra_prod framework */ | ||
90 | struct tegra_prod *prod_gr_csi; | ||
91 | struct tegra_prod *prod_gr_dsi; | ||
92 | const struct tegra_mipi_soc *soc; | ||
93 | void __iomem *io; | ||
94 | atomic_t refcount; | ||
95 | struct miscdevice misc_dev; | ||
96 | }; | ||
97 | |||
98 | static struct tegra_mipi *mipi; | ||
99 | static const struct regmap_config t210_mipi_cal_regmap_config = { | ||
100 | .reg_bits = 32, | ||
101 | .reg_stride = 4, | ||
102 | .val_bits = 32, | ||
103 | .cache_type = REGCACHE_NONE, | ||
104 | .fast_io = 1, | ||
105 | }; | ||
106 | |||
107 | #define ADDR(x) (x + mipi->soc->addr_offset) | ||
108 | #define dump_register(nm) \ | ||
109 | { \ | ||
110 | .name = #nm, \ | ||
111 | .offset = nm, \ | ||
112 | } | ||
113 | struct tegra_mipi_soc { | ||
114 | int powergate_id; | ||
115 | unsigned int total_dsilanes; | ||
116 | unsigned int total_cillanes; | ||
117 | char addr_offset; | ||
118 | char ppsb_war; | ||
119 | int (*pad_enable)(struct tegra_mipi *mipi); | ||
120 | int (*pad_disable)(struct tegra_mipi *mipi); | ||
121 | int (*calibrate)(struct tegra_mipi *mipi, int lanes); | ||
122 | int (*parse_cfg)(struct platform_device *pdev, struct tegra_mipi *mipi); | ||
123 | u8 virtual_dev; | ||
124 | }; | ||
125 | |||
126 | static int mipical_write(struct regmap *map, | ||
127 | unsigned int reg, unsigned int val) | ||
128 | { | ||
129 | int ret, rb_val; | ||
130 | |||
131 | ret = regmap_write(map, reg, val); | ||
132 | if (mipi->soc->ppsb_war) | ||
133 | /* Read back register to make sure that register writes completed */ | ||
134 | regmap_read(map, reg, &rb_val); | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | static int mipical_update_bits(struct regmap *map, unsigned int reg, | ||
140 | unsigned int mask, unsigned int val) | ||
141 | { | ||
142 | int ret, rb_val; | ||
143 | |||
144 | ret = regmap_update_bits(map, reg, mask, val); | ||
145 | if (mipi->soc->ppsb_war) | ||
146 | /* Read back register to make sure that register writes completed */ | ||
147 | regmap_read(map, reg, &rb_val); | ||
148 | |||
149 | return ret; | ||
150 | } | ||
151 | static int tegra_mipi_clk_enable(struct tegra_mipi *mipi) | ||
152 | { | ||
153 | int err; | ||
154 | |||
155 | err = tegra_unpowergate_partition(mipi->soc->powergate_id); | ||
156 | if (err) { | ||
157 | dev_err(mipi->dev, "Fail to unpowergate SOR\n"); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | err = clk_prepare_enable(mipi->mipi_cal_fixed); | ||
162 | if (err) { | ||
163 | dev_err(mipi->dev, "Fail to enable uart_mipi_cal clk\n"); | ||
164 | goto err_fixed_clk; | ||
165 | } | ||
166 | mdelay(1); | ||
167 | err = clk_prepare_enable(mipi->mipi_cal_clk); | ||
168 | if (err) { | ||
169 | dev_err(mipi->dev, "Fail to enable mipi_cal clk\n"); | ||
170 | goto err_mipi_cal_clk; | ||
171 | } | ||
172 | return 0; | ||
173 | |||
174 | err_mipi_cal_clk: | ||
175 | clk_disable_unprepare(mipi->mipi_cal_fixed); | ||
176 | err_fixed_clk: | ||
177 | tegra_powergate_partition(mipi->soc->powergate_id); | ||
178 | |||
179 | return err; | ||
180 | } | ||
181 | |||
182 | static void tegra_mipi_clk_disable(struct tegra_mipi *mipi) | ||
183 | { | ||
184 | clk_disable_unprepare(mipi->mipi_cal_clk); | ||
185 | clk_disable_unprepare(mipi->mipi_cal_fixed); | ||
186 | tegra_powergate_partition(mipi->soc->powergate_id); | ||
187 | } | ||
188 | |||
189 | static void tegra_mipi_print(struct tegra_mipi *mipi) __maybe_unused; | ||
190 | static void tegra_mipi_print(struct tegra_mipi *mipi) | ||
191 | { | ||
192 | int val; | ||
193 | unsigned long rate; | ||
194 | #define pr_reg(a) \ | ||
195 | do { \ | ||
196 | regmap_read(mipi->regmap, ADDR(a), &val); \ | ||
197 | dev_info(mipi->dev, "%-30s %#04x %#010x\n", \ | ||
198 | #a, a + mipi->soc->addr_offset, val); \ | ||
199 | } while (0) | ||
200 | |||
201 | rate = clk_get_rate(mipi->mipi_cal_fixed); | ||
202 | dev_dbg(mipi->dev, "Fixed clk %luMHz\n", rate/1000000); | ||
203 | |||
204 | pr_reg(MIPI_CAL_CTRL); | ||
205 | pr_reg(CIL_MIPI_CAL_STATUS); | ||
206 | pr_reg(CIL_MIPI_CAL_STATUS_2); | ||
207 | pr_reg(CILA_MIPI_CAL_CONFIG); | ||
208 | pr_reg(CILB_MIPI_CAL_CONFIG); | ||
209 | pr_reg(CILC_MIPI_CAL_CONFIG); | ||
210 | pr_reg(CILD_MIPI_CAL_CONFIG); | ||
211 | pr_reg(CILE_MIPI_CAL_CONFIG); | ||
212 | pr_reg(CILF_MIPI_CAL_CONFIG); | ||
213 | pr_reg(DSIA_MIPI_CAL_CONFIG); | ||
214 | pr_reg(DSIB_MIPI_CAL_CONFIG); | ||
215 | pr_reg(DSIC_MIPI_CAL_CONFIG); | ||
216 | pr_reg(DSID_MIPI_CAL_CONFIG); | ||
217 | pr_reg(MIPI_BIAS_PAD_CFG0); | ||
218 | pr_reg(MIPI_BIAS_PAD_CFG1); | ||
219 | pr_reg(MIPI_BIAS_PAD_CFG2); | ||
220 | pr_reg(DSIA_MIPI_CAL_CONFIG_2); | ||
221 | pr_reg(DSIB_MIPI_CAL_CONFIG_2); | ||
222 | pr_reg(DSIC_MIPI_CAL_CONFIG_2); | ||
223 | pr_reg(DSID_MIPI_CAL_CONFIG_2); | ||
224 | #undef pr_reg | ||
225 | } | ||
226 | static int tegra_mipi_wait(struct tegra_mipi *mipi, int lanes) | ||
227 | { | ||
228 | unsigned long timeout; | ||
229 | int val; | ||
230 | |||
231 | mipical_write(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS), 0xffffffff); | ||
232 | mipical_write(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS_2), 0xffffffff); | ||
233 | mipical_update_bits(mipi->regmap, ADDR(MIPI_CAL_CTRL), STARTCAL, 0x1); | ||
234 | |||
235 | timeout = jiffies + msecs_to_jiffies(MIPI_CAL_TIMEOUT_MSEC); | ||
236 | while (time_before(jiffies, timeout)) { | ||
237 | regmap_read(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS), &val); | ||
238 | if (((val & lanes) == lanes) && ((val & CAL_ACTIVE) == 0)) | ||
239 | return 0; | ||
240 | usleep_range(10, 50); | ||
241 | } | ||
242 | /* Sometimes there is false timeout. Sleep past the timeout and did | ||
243 | * not check the status again. | ||
244 | * Later status register dump shows no timeout. | ||
245 | * Add another check here in case sleep past the timeout. | ||
246 | */ | ||
247 | regmap_read(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS), &val); | ||
248 | if (((val & lanes) == lanes) && ((val & CAL_ACTIVE) == 0)) | ||
249 | return 0; | ||
250 | dev_err(mipi->dev, "Mipi cal timeout,val:%x, lanes:%x\n", val, lanes); | ||
251 | tegra_mipi_print(mipi); | ||
252 | return -ETIMEDOUT; | ||
253 | |||
254 | } | ||
255 | |||
256 | static int tegra_mipi_apply_bias_prod(struct regmap *reg, | ||
257 | struct tegra_mipi_bias *bias) | ||
258 | { | ||
259 | int ret; | ||
260 | unsigned int val; | ||
261 | |||
262 | val = (bias->pad_pdvclamp << PDVCLAMP_SHIFT) | | ||
263 | (bias->e_vclamp_ref << E_VCLAMP_REF_SHIFT); | ||
264 | ret = mipical_write(reg, ADDR(MIPI_BIAS_PAD_CFG0), val); | ||
265 | if (ret) | ||
266 | return ret; | ||
267 | val = (bias->pad_driv_up_ref << PAD_DRIV_UP_REF_SHIFT) | | ||
268 | (bias->pad_driv_dn_ref << PAD_DRIV_DN_REF_SHIFT); | ||
269 | ret = mipical_write(reg, ADDR(MIPI_BIAS_PAD_CFG1), val); | ||
270 | if (ret) | ||
271 | return ret; | ||
272 | val = (bias->pad_vclamp_level << PAD_VCLAMP_LEVEL_SHIFT) | | ||
273 | (bias->pad_vauxp_level << PAD_VAUXP_LEVEL_SHIFT); | ||
274 | ret = mipical_write(reg, ADDR(MIPI_BIAS_PAD_CFG2), val); | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | static void tegra_mipi_apply_csi_prod(struct regmap *reg, | ||
279 | struct tegra_mipi_prod_csi *prod_csi, | ||
280 | int lanes) | ||
281 | { | ||
282 | int val; | ||
283 | |||
284 | tegra_mipi_apply_bias_prod(reg, &prod_csi->bias_csi); | ||
285 | val = (prod_csi->termos_x << TERMOSA_SHIFT) | | ||
286 | (prod_csi->termos_x_clk << TERMOSA_CLK_SHIFT); | ||
287 | |||
288 | if (lanes & CSIA) | ||
289 | mipical_write(reg, ADDR(CILA_MIPI_CAL_CONFIG), val); | ||
290 | if (lanes & CSIB) | ||
291 | mipical_write(reg, ADDR(CILB_MIPI_CAL_CONFIG), val); | ||
292 | if (lanes & CSIC) | ||
293 | mipical_write(reg, ADDR(CILC_MIPI_CAL_CONFIG), val); | ||
294 | if (lanes & CSID) | ||
295 | mipical_write(reg, ADDR(CILD_MIPI_CAL_CONFIG), val); | ||
296 | if (lanes & CSIE) | ||
297 | mipical_write(reg, ADDR(CILE_MIPI_CAL_CONFIG), val); | ||
298 | if (lanes & CSIF) | ||
299 | mipical_write(reg, ADDR(CILF_MIPI_CAL_CONFIG), val); | ||
300 | } | ||
301 | |||
302 | static void tegra_mipi_apply_dsi_prod(struct regmap *reg, | ||
303 | struct tegra_mipi_prod_dsi *prod_dsi, | ||
304 | int lanes) | ||
305 | { | ||
306 | int val, clk_val; | ||
307 | |||
308 | tegra_mipi_apply_bias_prod(reg, &prod_dsi->bias_dsi); | ||
309 | val = (prod_dsi->hspuos_x << HSPUOSDSIA_SHIFT) | | ||
310 | (prod_dsi->termos_x << TERMOSDSIA_SHIFT); | ||
311 | clk_val = (prod_dsi->clk_hspuos_x << HSCLKPUOSDSIA_SHIFT) | | ||
312 | (prod_dsi->clk_hstermos_x << HSCLKTERMOSDSIA_SHIFT); | ||
313 | if (lanes & DSIA) { | ||
314 | mipical_write(reg, ADDR(DSIA_MIPI_CAL_CONFIG), val); | ||
315 | mipical_write(reg, ADDR(DSIA_MIPI_CAL_CONFIG_2), clk_val); | ||
316 | } | ||
317 | if (lanes & DSIB) { | ||
318 | mipical_write(reg, ADDR(DSIB_MIPI_CAL_CONFIG), val); | ||
319 | mipical_write(reg, ADDR(DSIB_MIPI_CAL_CONFIG_2), clk_val); | ||
320 | } | ||
321 | if (lanes & DSIC) { | ||
322 | mipical_write(reg, ADDR(DSIC_MIPI_CAL_CONFIG), val); | ||
323 | mipical_write(reg, ADDR(DSIC_MIPI_CAL_CONFIG_2), clk_val); | ||
324 | } | ||
325 | if (lanes & DSID) { | ||
326 | mipical_write(reg, ADDR(DSID_MIPI_CAL_CONFIG), val); | ||
327 | mipical_write(reg, ADDR(DSID_MIPI_CAL_CONFIG_2), clk_val); | ||
328 | } | ||
329 | |||
330 | } | ||
331 | |||
332 | static int _t18x_tegra_mipi_bias_pad_enable(struct tegra_mipi *mipi) | ||
333 | { | ||
334 | if (atomic_read(&mipi->refcount) < 0) { | ||
335 | WARN_ON(1); | ||
336 | return -EINVAL; | ||
337 | } | ||
338 | if (atomic_inc_return(&mipi->refcount) == 1) { | ||
339 | tegra_mipi_clk_enable(mipi); | ||
340 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG0), | ||
341 | PDVCLAMP, 0 << PDVCLAMP_SHIFT); | ||
342 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG2), | ||
343 | PDVREG, 0 << PDVREG_SHIFT); | ||
344 | } | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int _t18x_tegra_mipi_bias_pad_disable(struct tegra_mipi *mipi) | ||
349 | { | ||
350 | if (atomic_read(&mipi->refcount) < 1) { | ||
351 | WARN_ON(1); | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | if (atomic_dec_return(&mipi->refcount) == 0) { | ||
355 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG0), | ||
356 | PDVCLAMP, 1 << PDVCLAMP_SHIFT); | ||
357 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG2), | ||
358 | PDVREG, 1 << PDVREG_SHIFT); | ||
359 | tegra_mipi_clk_disable(mipi); | ||
360 | } | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int _t21x_tegra_mipi_bias_pad_enable(struct tegra_mipi *mipi) | ||
365 | { | ||
366 | if (atomic_read(&mipi->refcount) < 0) { | ||
367 | WARN_ON(1); | ||
368 | return -EINVAL; | ||
369 | } | ||
370 | if (atomic_inc_return(&mipi->refcount) == 1) { | ||
371 | tegra_mipi_clk_enable(mipi); | ||
372 | return mipical_update_bits(mipi->regmap, | ||
373 | ADDR(MIPI_BIAS_PAD_CFG2), PDVREG, 0); | ||
374 | } | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | int tegra_mipi_bias_pad_enable(void) | ||
379 | { | ||
380 | if (!mipi) | ||
381 | return -EPROBE_DEFER; | ||
382 | dev_dbg(mipi->dev, "%s", __func__); | ||
383 | |||
384 | if (mipi->soc->pad_enable) | ||
385 | return mipi->soc->pad_enable(mipi); | ||
386 | else | ||
387 | return 0; | ||
388 | } | ||
389 | EXPORT_SYMBOL(tegra_mipi_bias_pad_enable); | ||
390 | |||
391 | static int _t21x_tegra_mipi_bias_pad_disable(struct tegra_mipi *mipi) | ||
392 | { | ||
393 | if (atomic_read(&mipi->refcount) < 1) { | ||
394 | WARN_ON(1); | ||
395 | return -EINVAL; | ||
396 | } | ||
397 | if (atomic_dec_return(&mipi->refcount) == 0) { | ||
398 | mipical_update_bits(mipi->regmap, | ||
399 | ADDR(MIPI_BIAS_PAD_CFG2), PDVREG, PDVREG); | ||
400 | tegra_mipi_clk_disable(mipi); | ||
401 | } | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | int tegra_mipi_bias_pad_disable(void) | ||
406 | { | ||
407 | if (!mipi) | ||
408 | return -ENODEV; | ||
409 | dev_dbg(mipi->dev, "%s", __func__); | ||
410 | |||
411 | if (mipi->soc->pad_disable) | ||
412 | return mipi->soc->pad_disable(mipi); | ||
413 | else | ||
414 | return 0; | ||
415 | } | ||
416 | EXPORT_SYMBOL(tegra_mipi_bias_pad_disable); | ||
417 | |||
418 | static void select_lanes(struct tegra_mipi *mipi, int lanes) | ||
419 | { | ||
420 | mipical_update_bits(mipi->regmap, ADDR(CILA_MIPI_CAL_CONFIG), SELA, | ||
421 | ((lanes & CSIA) != 0 ? SELA : 0)); | ||
422 | mipical_update_bits(mipi->regmap, ADDR(CILB_MIPI_CAL_CONFIG), SELA, | ||
423 | ((lanes & CSIB) != 0 ? SELA : 0)); | ||
424 | mipical_update_bits(mipi->regmap, ADDR(CILC_MIPI_CAL_CONFIG), SELA, | ||
425 | ((lanes & CSIC) != 0 ? SELA : 0)); | ||
426 | mipical_update_bits(mipi->regmap, ADDR(CILD_MIPI_CAL_CONFIG), SELA, | ||
427 | ((lanes & CSID) != 0 ? SELA : 0)); | ||
428 | mipical_update_bits(mipi->regmap, ADDR(CILE_MIPI_CAL_CONFIG), SELA, | ||
429 | ((lanes & CSIE) != 0 ? SELA : 0)); | ||
430 | mipical_update_bits(mipi->regmap, ADDR(CILF_MIPI_CAL_CONFIG), SELA, | ||
431 | ((lanes & CSIF) != 0 ? SELA : 0)); | ||
432 | |||
433 | mipical_update_bits(mipi->regmap, ADDR(DSIA_MIPI_CAL_CONFIG), SELDSIA, | ||
434 | ((lanes & DSIA) != 0 ? SELDSIA : 0)); | ||
435 | mipical_update_bits(mipi->regmap, ADDR(DSIB_MIPI_CAL_CONFIG), SELDSIA, | ||
436 | ((lanes & DSIB) != 0 ? SELDSIA : 0)); | ||
437 | mipical_update_bits(mipi->regmap, ADDR(DSIC_MIPI_CAL_CONFIG), SELDSIA, | ||
438 | ((lanes & DSIC) != 0 ? SELDSIA : 0)); | ||
439 | mipical_update_bits(mipi->regmap, ADDR(DSID_MIPI_CAL_CONFIG), SELDSIA, | ||
440 | ((lanes & DSID) != 0 ? SELDSIA : 0)); | ||
441 | mipical_update_bits(mipi->regmap, ADDR(DSIA_MIPI_CAL_CONFIG_2), | ||
442 | CLKSELDSIA, ((lanes & DSIA) != 0 ? CLKSELDSIA : 0)); | ||
443 | mipical_update_bits(mipi->regmap, ADDR(DSIB_MIPI_CAL_CONFIG_2), | ||
444 | CLKSELDSIA, ((lanes & DSIB) != 0 ? CLKSELDSIA : 0)); | ||
445 | mipical_update_bits(mipi->regmap, ADDR(DSIC_MIPI_CAL_CONFIG_2), | ||
446 | CLKSELDSIA, ((lanes & DSIC) != 0 ? CLKSELDSIA : 0)); | ||
447 | mipical_update_bits(mipi->regmap, ADDR(DSID_MIPI_CAL_CONFIG_2), | ||
448 | CLKSELDSIA, ((lanes & DSID) != 0 ? CLKSELDSIA : 0)); | ||
449 | } | ||
450 | |||
451 | static void clear_all(struct tegra_mipi *mipi) | ||
452 | { | ||
453 | mipical_write(mipi->regmap, ADDR(CILA_MIPI_CAL_CONFIG), 0); | ||
454 | mipical_write(mipi->regmap, ADDR(CILB_MIPI_CAL_CONFIG), 0); | ||
455 | mipical_write(mipi->regmap, ADDR(CILC_MIPI_CAL_CONFIG), 0); | ||
456 | mipical_write(mipi->regmap, ADDR(CILD_MIPI_CAL_CONFIG), 0); | ||
457 | mipical_write(mipi->regmap, ADDR(CILE_MIPI_CAL_CONFIG), 0); | ||
458 | mipical_write(mipi->regmap, ADDR(CILF_MIPI_CAL_CONFIG), 0); | ||
459 | |||
460 | mipical_write(mipi->regmap, ADDR(DSIA_MIPI_CAL_CONFIG), 0); | ||
461 | mipical_write(mipi->regmap, ADDR(DSIB_MIPI_CAL_CONFIG), 0); | ||
462 | mipical_write(mipi->regmap, ADDR(DSIC_MIPI_CAL_CONFIG), 0); | ||
463 | mipical_write(mipi->regmap, ADDR(DSID_MIPI_CAL_CONFIG), 0); | ||
464 | mipical_write(mipi->regmap, ADDR(DSIA_MIPI_CAL_CONFIG_2), 0); | ||
465 | mipical_write(mipi->regmap, ADDR(DSIB_MIPI_CAL_CONFIG_2), 0); | ||
466 | mipical_write(mipi->regmap, ADDR(DSIC_MIPI_CAL_CONFIG_2), 0); | ||
467 | mipical_write(mipi->regmap, ADDR(DSID_MIPI_CAL_CONFIG_2), 0); | ||
468 | } | ||
469 | |||
470 | #ifdef CONFIG_DEBUG_FS | ||
471 | static struct debugfs_reg32 mipical_regs[] = { | ||
472 | dump_register(MIPI_CAL_CTRL), | ||
473 | dump_register(MIPI_CAL_AUTOCAL_CTRL0), | ||
474 | dump_register(CIL_MIPI_CAL_STATUS), | ||
475 | dump_register(CIL_MIPI_CAL_STATUS_2), | ||
476 | dump_register(CILA_MIPI_CAL_CONFIG), | ||
477 | dump_register(CILB_MIPI_CAL_CONFIG), | ||
478 | dump_register(CILC_MIPI_CAL_CONFIG), | ||
479 | dump_register(CILD_MIPI_CAL_CONFIG), | ||
480 | dump_register(CILE_MIPI_CAL_CONFIG), | ||
481 | dump_register(CILF_MIPI_CAL_CONFIG), | ||
482 | dump_register(DSIA_MIPI_CAL_CONFIG), | ||
483 | dump_register(DSIB_MIPI_CAL_CONFIG), | ||
484 | dump_register(DSIC_MIPI_CAL_CONFIG), | ||
485 | dump_register(DSID_MIPI_CAL_CONFIG), | ||
486 | dump_register(MIPI_BIAS_PAD_CFG0), | ||
487 | dump_register(MIPI_BIAS_PAD_CFG1), | ||
488 | dump_register(MIPI_BIAS_PAD_CFG2), | ||
489 | dump_register(DSIA_MIPI_CAL_CONFIG_2), | ||
490 | dump_register(DSIB_MIPI_CAL_CONFIG_2), | ||
491 | dump_register(DSIC_MIPI_CAL_CONFIG_2), | ||
492 | dump_register(DSID_MIPI_CAL_CONFIG_2), | ||
493 | }; | ||
494 | |||
495 | static u32 mipical_status; | ||
496 | static u32 timeout_ct; | ||
497 | static u32 counts; | ||
498 | |||
499 | static int dbgfs_show_regs(struct seq_file *s, void *data) | ||
500 | { | ||
501 | struct tegra_mipi *mipi = s->private; | ||
502 | int err; | ||
503 | |||
504 | err = tegra_unpowergate_partition(mipi->soc->powergate_id); | ||
505 | if (err) { | ||
506 | dev_err(mipi->dev, "Fail to unpowergate SOR\n"); | ||
507 | return err; | ||
508 | } | ||
509 | |||
510 | err = tegra_mipi_clk_enable(mipi); | ||
511 | if (err) { | ||
512 | dev_err(mipi->dev, "Fail to enable mipi clk\n"); | ||
513 | goto clk_err; | ||
514 | } | ||
515 | debugfs_print_regs32(s, mipical_regs, ARRAY_SIZE(mipical_regs), | ||
516 | mipi->io, ""); | ||
517 | tegra_mipi_clk_disable(mipi); | ||
518 | err = 0; | ||
519 | |||
520 | clk_err: | ||
521 | tegra_powergate_partition(mipi->soc->powergate_id); | ||
522 | return err; | ||
523 | } | ||
524 | |||
525 | static int dbgfs_open(struct inode *inode, struct file *file) | ||
526 | { | ||
527 | return single_open(file, dbgfs_show_regs, inode->i_private); | ||
528 | } | ||
529 | |||
530 | static const struct file_operations dbgfs_ops = { | ||
531 | .open = dbgfs_open, | ||
532 | .read = seq_read, | ||
533 | .llseek = seq_lseek, | ||
534 | .release = single_release | ||
535 | }; | ||
536 | |||
537 | static int dbgfs_mipi_init(struct tegra_mipi *mipi) | ||
538 | { | ||
539 | struct dentry *dir; | ||
540 | struct dentry *val; | ||
541 | int i; | ||
542 | |||
543 | dir = debugfs_create_dir(DRV_NAME, NULL); | ||
544 | if (!dir) | ||
545 | return -ENOMEM; | ||
546 | |||
547 | val = debugfs_create_x32("LAST_STATUS", S_IRUGO, dir, &mipical_status); | ||
548 | if (!val) | ||
549 | goto err; | ||
550 | val = debugfs_create_u32("COUNT", S_IRUGO | S_IWUGO, dir, &counts); | ||
551 | if (!val) | ||
552 | goto err; | ||
553 | val = debugfs_create_u32("TIMEOUTS", S_IRUGO | S_IWUGO, dir, | ||
554 | &timeout_ct); | ||
555 | if (!val) | ||
556 | goto err; | ||
557 | |||
558 | val = debugfs_create_file("regs", S_IRUGO, dir, mipi, &dbgfs_ops); | ||
559 | if (!val) | ||
560 | goto err; | ||
561 | |||
562 | /* Assign register address with adjusted offset */ | ||
563 | for (i = 0; i < ARRAY_SIZE(mipical_regs); ++i) | ||
564 | mipical_regs[i].offset += mipi->soc->addr_offset; | ||
565 | return 0; | ||
566 | err: | ||
567 | dev_err(mipi->dev, "%s:Fail to create debugfs\n", __func__); | ||
568 | debugfs_remove_recursive(dir); | ||
569 | return -ENODEV; | ||
570 | } | ||
571 | #endif | ||
572 | static int _tegra_mipi_calibration(struct tegra_mipi *mipi, int lanes) | ||
573 | { | ||
574 | int err; | ||
575 | |||
576 | mutex_lock(&mipi->lock); | ||
577 | /* clean up lanes */ | ||
578 | clear_all(mipi); | ||
579 | |||
580 | /* Apply MIPI_CAL PROD_Set */ | ||
581 | if (lanes & (CSIA|CSIB|CSIC|CSID|CSIE|CSIF)) | ||
582 | tegra_mipi_apply_csi_prod(mipi->regmap, mipi->prod_csi, | ||
583 | lanes); | ||
584 | else | ||
585 | tegra_mipi_apply_dsi_prod(mipi->regmap, mipi->prod_dsi, | ||
586 | lanes); | ||
587 | |||
588 | /*Select lanes */ | ||
589 | select_lanes(mipi, lanes); | ||
590 | /* Start calibration */ | ||
591 | err = tegra_mipi_wait(mipi, lanes); | ||
592 | |||
593 | #ifdef CONFIG_DEBUG_FS | ||
594 | regmap_read(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS), &mipical_status); | ||
595 | counts++; | ||
596 | if (err) | ||
597 | timeout_ct++; | ||
598 | #endif | ||
599 | mutex_unlock(&mipi->lock); | ||
600 | return err; | ||
601 | } | ||
602 | static int tegra_mipical_using_prod(struct tegra_mipi *mipi, int lanes) | ||
603 | { | ||
604 | int err = 0; | ||
605 | |||
606 | mutex_lock(&mipi->lock); | ||
607 | |||
608 | /* clean up lanes */ | ||
609 | clear_all(mipi); | ||
610 | |||
611 | /* Apply MIPI_CAL PROD_Set */ | ||
612 | if (lanes & (CSIA|CSIB|CSIC|CSID|CSIE|CSIF)) { | ||
613 | if (!IS_ERR(mipi->prod_gr_csi)) | ||
614 | err = tegra_prod_set_by_name(&mipi->io, "prod", | ||
615 | mipi->prod_gr_csi); | ||
616 | } else { | ||
617 | if (!IS_ERR(mipi->prod_gr_dsi)) | ||
618 | err = tegra_prod_set_by_name(&mipi->io, | ||
619 | "prod", mipi->prod_gr_dsi); | ||
620 | } | ||
621 | if (err) { | ||
622 | dev_err(mipi->dev, "tegra_prod set failed\n"); | ||
623 | goto err_unlock; | ||
624 | } | ||
625 | /*Select lanes */ | ||
626 | select_lanes(mipi, lanes); | ||
627 | /* Start calibration */ | ||
628 | err = tegra_mipi_wait(mipi, lanes); | ||
629 | |||
630 | #ifdef CONFIG_DEBUG_FS | ||
631 | regmap_read(mipi->regmap, ADDR(CIL_MIPI_CAL_STATUS), &mipical_status); | ||
632 | counts++; | ||
633 | if (err) | ||
634 | timeout_ct++; | ||
635 | #endif | ||
636 | err_unlock: | ||
637 | mutex_unlock(&mipi->lock); | ||
638 | return err; | ||
639 | |||
640 | } | ||
641 | int tegra_mipi_calibration(int lanes) | ||
642 | { | ||
643 | if (!mipi) | ||
644 | return -ENODEV; | ||
645 | dev_dbg(mipi->dev, "%s", __func__); | ||
646 | if (mipi->soc->calibrate) | ||
647 | return mipi->soc->calibrate(mipi, lanes); | ||
648 | else | ||
649 | return 0; | ||
650 | |||
651 | } | ||
652 | EXPORT_SYMBOL(tegra_mipi_calibration); | ||
653 | |||
654 | static void parse_bias_prod(struct device_node *np, | ||
655 | struct tegra_mipi_bias *bias) | ||
656 | { | ||
657 | int ret; | ||
658 | unsigned int v; | ||
659 | |||
660 | ret = of_property_read_u32(np, "bias-pad-cfg0", &v); | ||
661 | if (!ret) { | ||
662 | bias->pad_pdvclamp = (v & PDVCLAMP) >> PDVCLAMP_SHIFT; | ||
663 | bias->e_vclamp_ref = (v & E_VCLAMP_REF) >> E_VCLAMP_REF_SHIFT; | ||
664 | } | ||
665 | |||
666 | ret = of_property_read_u32(np, "bias-pad-cfg1", &v); | ||
667 | if (!ret) { | ||
668 | bias->pad_driv_up_ref = | ||
669 | (v & PAD_DRIV_UP_REF) >> PAD_DRIV_UP_REF_SHIFT; | ||
670 | bias->pad_driv_dn_ref = | ||
671 | (v & PAD_DRIV_DN_REF) >> PAD_DRIV_DN_REF_SHIFT; | ||
672 | } | ||
673 | |||
674 | ret = of_property_read_u32(np, "bias-pad-cfg2", &v); | ||
675 | if (!ret) { | ||
676 | bias->pad_vclamp_level = | ||
677 | (v & PAD_VCLAMP_LEVEL) >> PAD_VCLAMP_LEVEL_SHIFT; | ||
678 | bias->pad_vauxp_level = | ||
679 | (v & PAD_VAUXP_LEVEL) >> PAD_VAUXP_LEVEL_SHIFT; | ||
680 | } | ||
681 | } | ||
682 | |||
683 | static void parse_dsi_prod(struct device_node *np, | ||
684 | struct tegra_mipi_prod_dsi *dsi) | ||
685 | { | ||
686 | int ret; | ||
687 | unsigned int v; | ||
688 | |||
689 | if (!np) | ||
690 | return; | ||
691 | ret = of_property_read_u32(np, "dsix-cfg", &v); | ||
692 | if (!ret) { | ||
693 | dsi->overide_x = (v & OVERIDEDSIA) >> OVERIDEDSIA_SHIFT; | ||
694 | dsi->hspdos_x = (v & HSPDOSDSIA) >> HSPDOSDSIA_SHIFT; | ||
695 | dsi->hspuos_x = (v & HSPUOSDSIA) >> HSPUOSDSIA_SHIFT; | ||
696 | dsi->termos_x = (v & TERMOSDSIA) >> TERMOSDSIA_SHIFT; | ||
697 | } | ||
698 | ret = of_property_read_u32(np, "dsix-cfg2", &v); | ||
699 | if (!ret) { | ||
700 | dsi->clk_overide_x = | ||
701 | (v & CLKOVERIDEDSIA) >> CLKOVERIDEDSIA_SHIFT; | ||
702 | dsi->clk_hspdos_x = (v & HSCLKPDOSDSIA) >> HSCLKPDOSDSIA_SHIFT; | ||
703 | dsi->clk_hspuos_x = (v & HSCLKPUOSDSIA) >> HSCLKPUOSDSIA_SHIFT; | ||
704 | dsi->clk_hstermos_x = | ||
705 | (v & HSCLKTERMOSDSIA) >> HSCLKTERMOSDSIA_SHIFT; | ||
706 | } | ||
707 | parse_bias_prod(np, &dsi->bias_dsi); | ||
708 | } | ||
709 | static void parse_csi_prod(struct device_node *np, | ||
710 | struct tegra_mipi_prod_csi *csi) | ||
711 | { | ||
712 | int ret; | ||
713 | unsigned int v; | ||
714 | |||
715 | if (!np) | ||
716 | return; | ||
717 | ret = of_property_read_u32(np, "cilx-cfg", &v); | ||
718 | if (!ret) { | ||
719 | csi->overide_x = (v & OVERIDEA) >> OVERIDEA_SHIFT; | ||
720 | csi->termos_x = (v & TERMOSA) >> TERMOSA_SHIFT; | ||
721 | csi->termos_x_clk = (v & TERMOSA_CLK) >> TERMOSA_CLK_SHIFT; | ||
722 | } | ||
723 | parse_bias_prod(np, &csi->bias_csi); | ||
724 | |||
725 | } | ||
726 | static void parse_prod(struct device_node *np, struct tegra_mipi *mipi) | ||
727 | { | ||
728 | struct device_node *next; | ||
729 | |||
730 | if (!np) | ||
731 | return; | ||
732 | next = of_get_child_by_name(np, "dsi"); | ||
733 | parse_dsi_prod(next, mipi->prod_dsi); | ||
734 | next = of_get_child_by_name(np, "csi"); | ||
735 | parse_csi_prod(next, mipi->prod_csi); | ||
736 | } | ||
737 | |||
738 | static void print_config(struct tegra_mipi *mipi) | ||
739 | { | ||
740 | struct tegra_mipi_prod_csi *csi = mipi->prod_csi; | ||
741 | struct tegra_mipi_prod_dsi *dsi = mipi->prod_dsi; | ||
742 | #define pr_val(x) dev_dbg(mipi->dev, "%s:%x", #x, x) | ||
743 | |||
744 | pr_val(csi->overide_x); | ||
745 | pr_val(csi->termos_x); | ||
746 | pr_val(csi->termos_x_clk); | ||
747 | pr_val(dsi->overide_x); | ||
748 | pr_val(dsi->hspdos_x); | ||
749 | pr_val(dsi->hspuos_x); | ||
750 | pr_val(dsi->termos_x); | ||
751 | pr_val(dsi->clk_overide_x); | ||
752 | pr_val(dsi->clk_hspdos_x); | ||
753 | pr_val(dsi->clk_hspuos_x); | ||
754 | pr_val(dsi->clk_hstermos_x); | ||
755 | #undef pr_val | ||
756 | } | ||
757 | |||
758 | static int tegra_prod_get_config(struct platform_device *pdev, | ||
759 | struct tegra_mipi *mipi) | ||
760 | { | ||
761 | struct device_node *np; | ||
762 | |||
763 | if (mipi->prod_gr_dsi != NULL && mipi->prod_gr_csi != NULL) | ||
764 | return 0; | ||
765 | |||
766 | np = of_find_node_by_name(NULL, "mipical"); | ||
767 | if (!np) { | ||
768 | pr_err("%s: Can not find dsi prod node\n", __func__); | ||
769 | } else { | ||
770 | mipi->prod_gr_dsi = | ||
771 | devm_tegra_prod_get_from_node(mipi->dev, np); | ||
772 | if (IS_ERR(mipi->prod_gr_dsi)) | ||
773 | dev_err(mipi->dev, "Fail to get DSI PROD settings\n"); | ||
774 | } | ||
775 | |||
776 | np = of_find_node_by_name(NULL, "csi_mipical"); | ||
777 | if (!np) { | ||
778 | pr_err("%s: Can not find csi_mipical node\n", __func__); | ||
779 | } else { | ||
780 | mipi->prod_gr_csi = | ||
781 | devm_tegra_prod_get_from_node(mipi->dev, np); | ||
782 | if (IS_ERR(mipi->prod_gr_csi)) | ||
783 | dev_err(mipi->dev, "Fail to get CSI PROD settings\n"); | ||
784 | } | ||
785 | |||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | static int tegra_mipi_parse_config(struct platform_device *pdev, | ||
790 | struct tegra_mipi *mipi) | ||
791 | { | ||
792 | struct device_node *np = pdev->dev.of_node; | ||
793 | struct device_node *next; | ||
794 | |||
795 | if (!np) | ||
796 | return -ENODEV; | ||
797 | |||
798 | if (of_device_is_compatible(np, "nvidia,tegra210-mipical")) | ||
799 | parse_prod(np, mipi); | ||
800 | |||
801 | if (of_device_is_compatible(np, "nvidia, tegra186-mipical")) { | ||
802 | if (tegra_chip_get_revision() == TEGRA186_REVISION_A01) { | ||
803 | dev_dbg(mipi->dev, "T186-A01\n"); | ||
804 | next = of_get_child_by_name(np, "a01"); | ||
805 | parse_prod(next, mipi); | ||
806 | } else { | ||
807 | dev_dbg(mipi->dev, "T186-A02\n"); | ||
808 | next = of_get_child_by_name(np, "a02"); | ||
809 | parse_prod(next, mipi); | ||
810 | } | ||
811 | } | ||
812 | print_config(mipi); | ||
813 | return 0; | ||
814 | } | ||
815 | static const struct tegra_mipi_soc tegra21x_mipi_soc = { | ||
816 | .total_dsilanes = 4, | ||
817 | .total_cillanes = 6, | ||
818 | .addr_offset = 0, | ||
819 | .ppsb_war = 1, | ||
820 | .pad_enable = &_t21x_tegra_mipi_bias_pad_enable, | ||
821 | .pad_disable = &_t21x_tegra_mipi_bias_pad_disable, | ||
822 | .calibrate = &_tegra_mipi_calibration, | ||
823 | .parse_cfg = &tegra_mipi_parse_config, | ||
824 | .powergate_id = TEGRA210_POWER_DOMAIN_SOR, | ||
825 | }; | ||
826 | |||
827 | static const struct tegra_mipi_soc tegra18x_mipi_soc = { | ||
828 | .total_dsilanes = 4, | ||
829 | .total_cillanes = 6, | ||
830 | .addr_offset = 4, | ||
831 | .ppsb_war = 0, | ||
832 | .pad_enable = &_t18x_tegra_mipi_bias_pad_enable, | ||
833 | .pad_disable = &_t18x_tegra_mipi_bias_pad_disable, | ||
834 | .calibrate = &tegra_mipical_using_prod, | ||
835 | .parse_cfg = &tegra_prod_get_config, | ||
836 | .powergate_id = TEGRA186_POWER_DOMAIN_DISP, | ||
837 | }; | ||
838 | |||
839 | static const struct tegra_mipi_soc tegra_vmipi_soc = { | ||
840 | .pad_enable = &tegra_vmipi_bias_pad_enable, | ||
841 | .pad_disable = &tegra_vmipi_bias_pad_disable, | ||
842 | .calibrate = &tegra_vmipi_calibration, | ||
843 | .virtual_dev = 1, | ||
844 | }; | ||
845 | |||
846 | static const struct of_device_id tegra_mipi_of_match[] = { | ||
847 | { | ||
848 | .compatible = "nvidia,tegra210-mipical", | ||
849 | .data = &tegra21x_mipi_soc, | ||
850 | }, { | ||
851 | .compatible = "nvidia, tegra186-mipical", | ||
852 | .data = &tegra18x_mipi_soc, | ||
853 | }, { | ||
854 | .compatible = "nvidia, tegra186-mipical-shared-multi-os", | ||
855 | .data = &tegra18x_mipi_soc, | ||
856 | }, { | ||
857 | .compatible = "nvidia,tegra-mipical-hv", | ||
858 | .data = &tegra_vmipi_soc, | ||
859 | }, { | ||
860 | } | ||
861 | }; | ||
862 | MODULE_DEVICE_TABLE(of, tegra_mipi_of_match); | ||
863 | |||
864 | static int tegra_mipi_open(struct inode *inode, struct file *file) | ||
865 | { | ||
866 | return 0; | ||
867 | } | ||
868 | static int tegra_mipi_release(struct inode *inode, struct file *file) | ||
869 | { | ||
870 | return 0; | ||
871 | } | ||
872 | static long mipi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
873 | { | ||
874 | struct tegra_mipi *mipi = container_of(file->private_data, | ||
875 | struct tegra_mipi, misc_dev); | ||
876 | if (_IOC_TYPE(cmd) != TEGRA_MIPI_IOCTL_MAGIC) | ||
877 | return -EINVAL; | ||
878 | |||
879 | switch (_IOC_NR(cmd)) { | ||
880 | case _IOC_NR(TEGRA_MIPI_IOCTL_BIAS_PAD_CTRL): { | ||
881 | unsigned int enable = 0; | ||
882 | int err; | ||
883 | |||
884 | if (copy_from_user(&enable, (const void __user *)arg, | ||
885 | sizeof(unsigned int))) { | ||
886 | dev_err(mipi->dev, "Fail to get user data\n"); | ||
887 | return -EFAULT; | ||
888 | } | ||
889 | if (enable) | ||
890 | err = tegra_mipi_bias_pad_enable(); | ||
891 | else | ||
892 | err = tegra_mipi_bias_pad_disable(); | ||
893 | return err; | ||
894 | } | ||
895 | case _IOC_NR(TEGRA_MIPI_IOCTL_CAL): { | ||
896 | int lanes = 0; | ||
897 | |||
898 | if (copy_from_user(&lanes, (const void __user *)arg, | ||
899 | sizeof(int))) { | ||
900 | dev_err(mipi->dev, "Fail to get user data\n"); | ||
901 | return -EFAULT; | ||
902 | } | ||
903 | if (lanes) | ||
904 | return tegra_mipi_calibration(lanes); | ||
905 | |||
906 | dev_err(mipi->dev, "Selected lane %x, skip mipical\n", lanes); | ||
907 | return 0; | ||
908 | } | ||
909 | default: | ||
910 | dev_err(mipi->dev, "Unknown ioctl\n"); | ||
911 | return -EINVAL; | ||
912 | } | ||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | static const struct file_operations tegra_mipi_miscdev_fops = { | ||
917 | .owner = THIS_MODULE, | ||
918 | .open = tegra_mipi_open, | ||
919 | .release = tegra_mipi_release, | ||
920 | .unlocked_ioctl = mipi_ioctl, | ||
921 | #ifdef CONFIG_COMPAT | ||
922 | .compat_ioctl = mipi_ioctl, | ||
923 | #endif | ||
924 | }; | ||
925 | |||
926 | static int tegra_mipi_misc_register(struct tegra_mipi *mipi) | ||
927 | { | ||
928 | int err; | ||
929 | |||
930 | if (!mipi) | ||
931 | return -EINVAL; | ||
932 | |||
933 | mipi->misc_dev.minor = MISC_DYNAMIC_MINOR; | ||
934 | mipi->misc_dev.name = DRV_NAME; | ||
935 | mipi->misc_dev.fops = &tegra_mipi_miscdev_fops; | ||
936 | err = misc_register(&mipi->misc_dev); | ||
937 | if (err) | ||
938 | dev_err(mipi->dev, "Fail to register misc dev\n"); | ||
939 | |||
940 | return err; | ||
941 | } | ||
942 | |||
943 | int tegra_vmipi_probe(struct platform_device *pdev) | ||
944 | { | ||
945 | int err; | ||
946 | |||
947 | err = tegra_vmipi_init(pdev); | ||
948 | if (err) { | ||
949 | dev_err(&pdev->dev, "Mipi cal virtual dev probe failed\n"); | ||
950 | return err; | ||
951 | } | ||
952 | |||
953 | err = tegra_mipi_misc_register(mipi); | ||
954 | if (err) | ||
955 | tegra_vmipi_deinit(); | ||
956 | |||
957 | return err; | ||
958 | } | ||
959 | |||
960 | static int tegra_mipi_probe(struct platform_device *pdev) | ||
961 | { | ||
962 | const struct of_device_id *match; | ||
963 | struct resource *mem, *memregion; | ||
964 | void __iomem *regs; | ||
965 | int err = 0; | ||
966 | struct device_node *np; | ||
967 | const struct tegra_mipi_soc *cdata = NULL; | ||
968 | |||
969 | np = pdev->dev.of_node; | ||
970 | match = of_match_device(tegra_mipi_of_match, &pdev->dev); | ||
971 | if (!match) { | ||
972 | dev_err(&pdev->dev, "No device match found\n"); | ||
973 | return -ENODEV; | ||
974 | } | ||
975 | cdata = match->data; | ||
976 | mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL); | ||
977 | if (!mipi) | ||
978 | return -ENOMEM; | ||
979 | |||
980 | mipi->dev = &pdev->dev; | ||
981 | mipi->soc = cdata; | ||
982 | platform_set_drvdata(pdev, mipi); | ||
983 | |||
984 | if (mipi->soc->virtual_dev) | ||
985 | return tegra_vmipi_probe(pdev); | ||
986 | |||
987 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
988 | if (!mem) { | ||
989 | dev_err(&pdev->dev, "No memory resource\n"); | ||
990 | return -EINVAL; | ||
991 | } | ||
992 | memregion = devm_request_mem_region(&pdev->dev, mem->start, | ||
993 | resource_size(mem), pdev->name); | ||
994 | if (!memregion) { | ||
995 | dev_err(&pdev->dev, "Cannot request mem region\n"); | ||
996 | return -EBUSY; | ||
997 | } | ||
998 | regs = devm_ioremap_nocache(&pdev->dev, mem->start, resource_size(mem)); | ||
999 | if (!regs) { | ||
1000 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
1001 | return -ENOMEM; | ||
1002 | } | ||
1003 | |||
1004 | mipi->io = regs; | ||
1005 | mipi->regmap = devm_regmap_init_mmio(&pdev->dev, regs, | ||
1006 | &t210_mipi_cal_regmap_config); | ||
1007 | if (IS_ERR(mipi->regmap)) { | ||
1008 | dev_err(&pdev->dev, "Fai to initialize regmap\n"); | ||
1009 | return PTR_ERR(mipi->regmap); | ||
1010 | } | ||
1011 | |||
1012 | mipi->mipi_cal_clk = devm_clk_get(&pdev->dev, "mipi_cal"); | ||
1013 | if (IS_ERR(mipi->mipi_cal_clk)) | ||
1014 | return PTR_ERR(mipi->mipi_cal_clk); | ||
1015 | |||
1016 | if (of_device_is_compatible(np, "nvidia,tegra210-mipical")) { | ||
1017 | mipi->mipi_cal_fixed = devm_clk_get(&pdev->dev, | ||
1018 | "uart_mipi_cal"); | ||
1019 | if (IS_ERR(mipi->mipi_cal_fixed)) | ||
1020 | return PTR_ERR(mipi->mipi_cal_fixed); | ||
1021 | mipi->prod_csi = devm_kzalloc(&pdev->dev, | ||
1022 | sizeof(*mipi->prod_csi), GFP_KERNEL); | ||
1023 | mipi->prod_dsi = devm_kzalloc(&pdev->dev, | ||
1024 | sizeof(*mipi->prod_dsi), GFP_KERNEL); | ||
1025 | } else if (of_device_is_compatible(np, "nvidia, tegra186-mipical")) { | ||
1026 | mipi->mipi_cal_fixed = devm_clk_get(&pdev->dev, | ||
1027 | "uart_fs_mipi_cal"); | ||
1028 | if (IS_ERR(mipi->mipi_cal_fixed)) | ||
1029 | return PTR_ERR(mipi->mipi_cal_fixed); | ||
1030 | mipi->rst = devm_reset_control_get(mipi->dev, "mipi_cal"); | ||
1031 | reset_control_deassert(mipi->rst); | ||
1032 | /* Bug 200224083 requires both register fields set to 1 | ||
1033 | * after de-asserted | ||
1034 | */ | ||
1035 | tegra_mipi_clk_enable(mipi); | ||
1036 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG0), | ||
1037 | PDVCLAMP, 1 << PDVCLAMP_SHIFT); | ||
1038 | mipical_update_bits(mipi->regmap, ADDR(MIPI_BIAS_PAD_CFG2), | ||
1039 | PDVREG, 1 << PDVREG_SHIFT); | ||
1040 | tegra_mipi_clk_disable(mipi); | ||
1041 | } else if (of_device_is_compatible(np, "nvidia, tegra186-mipical-shared-multi-os")) { | ||
1042 | mipi->mipi_cal_fixed = devm_clk_get(&pdev->dev, | ||
1043 | "uart_fs_mipi_cal"); | ||
1044 | if (IS_ERR(mipi->mipi_cal_fixed)) | ||
1045 | return PTR_ERR(mipi->mipi_cal_fixed); | ||
1046 | mipi->rst = devm_reset_control_get(mipi->dev, "mipi_cal"); | ||
1047 | reset_control_deassert(mipi->rst); | ||
1048 | } | ||
1049 | |||
1050 | if (mipi->soc->parse_cfg) | ||
1051 | err = mipi->soc->parse_cfg(pdev, mipi); | ||
1052 | if (err) | ||
1053 | return err; | ||
1054 | |||
1055 | mutex_init(&mipi->lock); | ||
1056 | atomic_set(&mipi->refcount, 0); | ||
1057 | |||
1058 | err = tegra_mipi_misc_register(mipi); | ||
1059 | if (err) | ||
1060 | return err; | ||
1061 | |||
1062 | #ifdef CONFIG_DEBUG_FS | ||
1063 | err = dbgfs_mipi_init(mipi); | ||
1064 | if (err) | ||
1065 | dev_err(&pdev->dev, "Fail to create debugfs\n"); | ||
1066 | #endif | ||
1067 | |||
1068 | dev_dbg(&pdev->dev, "Mipi cal done probing...\n"); | ||
1069 | return err; | ||
1070 | } | ||
1071 | |||
1072 | static struct platform_driver tegra_mipi_cal_platform_driver = { | ||
1073 | .driver = { | ||
1074 | .name = DRV_NAME, | ||
1075 | .owner = THIS_MODULE, | ||
1076 | .of_match_table = tegra_mipi_of_match, | ||
1077 | }, | ||
1078 | .probe = tegra_mipi_probe, | ||
1079 | }; | ||
1080 | |||
1081 | static int __init tegra_mipi_module_init(void) | ||
1082 | { | ||
1083 | return platform_driver_register(&tegra_mipi_cal_platform_driver); | ||
1084 | } | ||
1085 | |||
1086 | static void __exit tegra_mipi_module_exit(void) | ||
1087 | { | ||
1088 | platform_driver_unregister(&tegra_mipi_cal_platform_driver); | ||
1089 | } | ||
1090 | subsys_initcall(tegra_mipi_module_init); | ||
1091 | module_exit(tegra_mipi_module_exit); | ||
1092 | |||
1093 | MODULE_AUTHOR("Wenjia Zhou <wenjiaz@nvidia.com>"); | ||
1094 | MODULE_DESCRIPTION("Common MIPI calibration driver for CSI and DSI"); | ||
1095 | MODULE_LICENSE("GPL v2"); | ||
1096 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/drivers/media/platform/tegra/mipical/mipi_cal.h b/drivers/media/platform/tegra/mipical/mipi_cal.h new file mode 100644 index 000000000..0fd5171fb --- /dev/null +++ b/drivers/media/platform/tegra/mipical/mipi_cal.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * mipi_cal.h | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION, All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef MIPI_CAL_H | ||
18 | #define MIPI_CAL_H | ||
19 | |||
20 | #define DSID (1 << 31) | ||
21 | #define DSIC (1 << 30) | ||
22 | #define DSIB (1 << 29) | ||
23 | #define DSIA (1 << 28) | ||
24 | #define CSIF (1 << 25) | ||
25 | #define CSIE (1 << 24) | ||
26 | #define CSID (1 << 23) | ||
27 | #define CSIC (1 << 22) | ||
28 | #define CSIB (1 << 21) | ||
29 | #define CSIA (1 << 20) | ||
30 | #define MIPI_CPHY 1 | ||
31 | |||
32 | #ifdef CONFIG_TEGRA_MIPI_CAL | ||
33 | int tegra_mipi_bias_pad_enable(void); | ||
34 | int tegra_mipi_bias_pad_disable(void); | ||
35 | int tegra_mipi_calibration(int lanes); | ||
36 | #else | ||
37 | static inline int tegra_mipi_bias_pad_enable(void) | ||
38 | { | ||
39 | return 0; | ||
40 | } | ||
41 | static inline int tegra_mipi_bias_pad_disable(void) | ||
42 | { | ||
43 | return 0; | ||
44 | } | ||
45 | static inline int tegra_mipi_calibration(int lanes) | ||
46 | { | ||
47 | return 0; | ||
48 | } | ||
49 | #endif | ||
50 | #endif | ||
diff --git a/drivers/media/platform/tegra/mipical/registers.h b/drivers/media/platform/tegra/mipical/registers.h new file mode 100644 index 000000000..5f209790b --- /dev/null +++ b/drivers/media/platform/tegra/mipical/registers.h | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * registers.h | ||
3 | * | ||
4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION, All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /* New register on T186 that starts | ||
18 | * at offset 0 | ||
19 | */ | ||
20 | #define MIPI_CAL_MODE 0x00 | ||
21 | |||
22 | /* T210 register offset*/ | ||
23 | #define MIPI_CAL_CTRL 0x00 | ||
24 | #define NOISE_FLT (0xf << 26) | ||
25 | #define PRESCALE (0x3 << 24) | ||
26 | #define CLKEN_OVR (1 << 4) | ||
27 | #define AUTOCAL_EN (1 << 1) | ||
28 | #define STARTCAL (1 << 0) | ||
29 | #define MIPI_CAL_AUTOCAL_CTRL0 0x04 | ||
30 | #define CIL_MIPI_CAL_STATUS 0x08 | ||
31 | #define CAL_DONE_DSID (1 << 31) | ||
32 | #define CAL_DONE_DSIC (1 << 30) | ||
33 | #define CAL_DONE_DSIB (1 << 29) | ||
34 | #define CAL_DONE_DSIA (1 << 28) | ||
35 | #define CAL_DONE_CSIF (1 << 25) | ||
36 | #define CAL_DONE_CSIE (1 << 24) | ||
37 | #define CAL_DONE_CSID (1 << 23) | ||
38 | #define CAL_DONE_CSIC (1 << 22) | ||
39 | #define CAL_DONE_CSIB (1 << 21) | ||
40 | #define CAL_DONE_CSIA (1 << 20) | ||
41 | #define CAL_DONE (1 << 16) | ||
42 | #define CAL_ACTIVE (1 << 0) | ||
43 | #define CIL_MIPI_CAL_STATUS_2 0x0c | ||
44 | #define CILA_MIPI_CAL_CONFIG 0x14 | ||
45 | #define OVERIDEA (1 << 30) | ||
46 | #define OVERIDEA_SHIFT 30 | ||
47 | #define SELA (1 << 21) | ||
48 | #define TERMOSA_CLK (0x1f << 11) | ||
49 | #define TERMOSA_CLK_SHIFT 11 | ||
50 | #define TERMOSA 0x1f | ||
51 | #define TERMOSA_SHIFT 0 | ||
52 | #define CILB_MIPI_CAL_CONFIG 0x18 | ||
53 | #define CILC_MIPI_CAL_CONFIG 0x1c | ||
54 | #define CILD_MIPI_CAL_CONFIG 0x20 | ||
55 | #define CILE_MIPI_CAL_CONFIG 0x24 | ||
56 | #define CILF_MIPI_CAL_CONFIG 0x28 | ||
57 | #define DSIA_MIPI_CAL_CONFIG 0x38 | ||
58 | #define OVERIDEDSIA (1 << 30) | ||
59 | #define OVERIDEDSIA_SHIFT 30 | ||
60 | #define SELDSIA (1 << 21) | ||
61 | #define HSPDOSDSIA (0x1f << 16) | ||
62 | #define HSPDOSDSIA_SHIFT 16 | ||
63 | #define HSPUOSDSIA (0x1f << 8) | ||
64 | #define HSPUOSDSIA_SHIFT 8 | ||
65 | #define TERMOSDSIA 0x1f | ||
66 | #define TERMOSDSIA_SHIFT 0 | ||
67 | #define DSIB_MIPI_CAL_CONFIG 0x3c | ||
68 | #define DSIC_MIPI_CAL_CONFIG 0x40 | ||
69 | #define DSID_MIPI_CAL_CONFIG 0x44 | ||
70 | #define MIPI_BIAS_PAD_CFG0 0x58 | ||
71 | #define E_VCLAMP_REF (1 << 0) | ||
72 | #define E_VCLAMP_REF_SHIFT 0 | ||
73 | #define PDVCLAMP (1 << 1) | ||
74 | #define PDVCLAMP_SHIFT 1 | ||
75 | #define MIPI_BIAS_PAD_CFG1 0x5c | ||
76 | #define PAD_DRIV_UP_REF (0x7 << 8) | ||
77 | #define PAD_DRIV_UP_REF_SHIFT 8 | ||
78 | #define PAD_DRIV_DN_REF (0x7 << 16) | ||
79 | #define PAD_DRIV_DN_REF_SHIFT 16 | ||
80 | #define MIPI_BIAS_PAD_CFG2 0x60 | ||
81 | #define PDVREG (1 << 1) | ||
82 | #define PDVREG_SHIFT 1 | ||
83 | #define PAD_VAUXP_LEVEL (0x7 << 4) | ||
84 | #define PAD_VAUXP_LEVEL_SHIFT 4 | ||
85 | #define PAD_VCLAMP_LEVEL (0x7 << 16) | ||
86 | #define PAD_VCLAMP_LEVEL_SHIFT 16 | ||
87 | #define DSIA_MIPI_CAL_CONFIG_2 0x64 | ||
88 | #define CLKOVERIDEDSIA (1 << 30) | ||
89 | #define CLKOVERIDEDSIA_SHIFT 30 | ||
90 | #define CLKSELDSIA (1 << 21) | ||
91 | #define HSCLKTERMOSDSIA (0x1f << 16) | ||
92 | #define HSCLKTERMOSDSIA_SHIFT 16 | ||
93 | #define HSCLKPDOSDSIA (0x1f << 8) | ||
94 | #define HSCLKPDOSDSIA_SHIFT 8 | ||
95 | #define HSCLKPUOSDSIA 0x1f | ||
96 | #define HSCLKPUOSDSIA_SHIFT 0 | ||
97 | #define DSIB_MIPI_CAL_CONFIG_2 0x68 | ||
98 | #define DSIC_MIPI_CAL_CONFIG_2 0x70 | ||
99 | #define DSID_MIPI_CAL_CONFIG_2 0x74 | ||
diff --git a/drivers/media/platform/tegra/mipical/vmipi/vmipi.c b/drivers/media/platform/tegra/mipical/vmipi/vmipi.c new file mode 100644 index 000000000..ea441c53e --- /dev/null +++ b/drivers/media/platform/tegra/mipical/vmipi/vmipi.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * vmipi.c | ||
3 | * | ||
4 | * Copyright (c) 2017, NVIDIA CORPORATION, All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/of_platform.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/mutex.h> | ||
23 | #include <linux/semaphore.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/tegra-ivc.h> | ||
26 | |||
27 | #include "mipi_cal.h" | ||
28 | #include "vmipi.h" | ||
29 | |||
30 | const char *tegra_vmipi_cmd[] = { | ||
31 | "TEGRA_VMIPI_CMD_CALIBRATE", | ||
32 | "TEGRA_VMIPI_CMD_BIAS_PAD_ENABLE", | ||
33 | "TEGRA_VMIPI_CMD_BIAS_PAD_DISABLE", | ||
34 | }; | ||
35 | |||
36 | struct tegra_vmipi_ivc_context { | ||
37 | struct device *dev; | ||
38 | struct tegra_hv_ivc_cookie *cookie; | ||
39 | wait_queue_head_t tx_wq; | ||
40 | wait_queue_head_t rx_wq; | ||
41 | struct mutex lock; | ||
42 | }; | ||
43 | static struct tegra_vmipi_ivc_context *ctx; | ||
44 | |||
45 | static int tegra_vmipi_ivc_send(struct tegra_vmipi_cmd_msg *msg, int size) | ||
46 | { | ||
47 | int ret = 0; | ||
48 | |||
49 | if (!tegra_hv_ivc_can_write(ctx->cookie)) { | ||
50 | ret = wait_event_timeout(ctx->tx_wq, | ||
51 | tegra_hv_ivc_can_write(ctx->cookie), | ||
52 | msecs_to_jiffies(500)); | ||
53 | if (!ret) { | ||
54 | dev_err(ctx->dev, | ||
55 | "%s timeout waiting for tx buffer for cmd %s\n", | ||
56 | __func__, tegra_vmipi_cmd[msg->cmd]); | ||
57 | return -ETIMEDOUT; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | ret = tegra_hv_ivc_write(ctx->cookie, msg, size); | ||
62 | if (ret != size) { | ||
63 | dev_err(ctx->dev, | ||
64 | "%s: ivc send incorrect msg size %d for cmd %s\n", | ||
65 | __func__, ret, tegra_vmipi_cmd[msg->cmd]); | ||
66 | return -EIO; | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int tegra_vmipi_ivc_rcv(struct tegra_vmipi_cmd_msg *msg, int size) | ||
73 | { | ||
74 | int ret = 0, len; | ||
75 | |||
76 | if (!tegra_hv_ivc_can_read(ctx->cookie)) { | ||
77 | ret = wait_event_timeout(ctx->rx_wq, | ||
78 | tegra_hv_ivc_can_read(ctx->cookie), | ||
79 | msecs_to_jiffies(500)); | ||
80 | if (!ret) { | ||
81 | dev_err(ctx->dev, | ||
82 | "%s timeout waiting for rx buffer for cmd %s\n", | ||
83 | __func__, tegra_vmipi_cmd[msg->cmd]); | ||
84 | ret = -ETIMEDOUT; | ||
85 | goto out; | ||
86 | } | ||
87 | ret = 0; | ||
88 | } | ||
89 | |||
90 | len = tegra_hv_ivc_read(ctx->cookie, msg, size); | ||
91 | if (len != size) { | ||
92 | dev_err(ctx->dev, | ||
93 | "%s: ivc read incorrect msg size %d for cmd %s\n", | ||
94 | __func__, len, tegra_vmipi_cmd[msg->cmd]); | ||
95 | ret = -EIO; | ||
96 | } | ||
97 | |||
98 | out: | ||
99 | if (tegra_hv_ivc_can_write(ctx->cookie)) | ||
100 | wake_up(&ctx->tx_wq); | ||
101 | |||
102 | return ret ? ret : msg->ret; | ||
103 | } | ||
104 | |||
105 | static irqreturn_t tegra_vmipi_ivc_isr(int irq, void *data) | ||
106 | { | ||
107 | struct tegra_vmipi_ivc_context *ictx = | ||
108 | (struct tegra_vmipi_ivc_context *)data; | ||
109 | |||
110 | if (tegra_hv_ivc_channel_notified(ictx->cookie)) | ||
111 | return IRQ_HANDLED; | ||
112 | |||
113 | wake_up(&ictx->rx_wq); | ||
114 | return IRQ_HANDLED; | ||
115 | } | ||
116 | |||
117 | static int tegra_vmipi_ivc_send_rcv(struct tegra_vmipi_cmd_msg *msg, int size) | ||
118 | { | ||
119 | int err; | ||
120 | |||
121 | if (!msg || !size) | ||
122 | return -EINVAL; | ||
123 | |||
124 | mutex_lock(&ctx->lock); | ||
125 | |||
126 | err = tegra_vmipi_ivc_send(msg, size); | ||
127 | if (err) | ||
128 | goto out; | ||
129 | |||
130 | err = tegra_vmipi_ivc_rcv(msg, size); | ||
131 | |||
132 | out: | ||
133 | mutex_unlock(&ctx->lock); | ||
134 | |||
135 | return err; | ||
136 | } | ||
137 | |||
138 | int tegra_vmipi_calibration(struct tegra_mipi *mipi, int lanes) | ||
139 | { | ||
140 | struct tegra_vmipi_cmd_msg msg; | ||
141 | int size = sizeof(msg); | ||
142 | struct tegra_vmipi_calibrate_params *p; | ||
143 | |||
144 | if (!ctx) | ||
145 | return -EINVAL; | ||
146 | |||
147 | msg.cmd = TEGRA_VMIPI_CMD_CALIBRATE; | ||
148 | p = &msg.params.calibrate; | ||
149 | p->lanes = lanes; | ||
150 | return tegra_vmipi_ivc_send_rcv(&msg, size); | ||
151 | } | ||
152 | |||
153 | int tegra_vmipi_bias_pad_enable(struct tegra_mipi *mipi) | ||
154 | { | ||
155 | struct tegra_vmipi_cmd_msg msg; | ||
156 | int size = sizeof(msg); | ||
157 | |||
158 | if (!ctx) | ||
159 | return -EINVAL; | ||
160 | |||
161 | msg.cmd = TEGRA_VMIPI_CMD_BIAS_PAD_ENABLE; | ||
162 | return tegra_vmipi_ivc_send_rcv(&msg, size); | ||
163 | } | ||
164 | |||
165 | int tegra_vmipi_bias_pad_disable(struct tegra_mipi *mipi) | ||
166 | { | ||
167 | struct tegra_vmipi_cmd_msg msg; | ||
168 | int size = sizeof(msg); | ||
169 | |||
170 | if (!ctx) | ||
171 | return -EINVAL; | ||
172 | |||
173 | msg.cmd = TEGRA_VMIPI_CMD_BIAS_PAD_DISABLE; | ||
174 | return tegra_vmipi_ivc_send_rcv(&msg, size); | ||
175 | } | ||
176 | |||
177 | int tegra_vmipi_init(struct platform_device *pdev) | ||
178 | { | ||
179 | static struct tegra_vmipi_ivc_context *context; | ||
180 | struct device_node *np, *ivcq_np; | ||
181 | struct tegra_hv_ivc_cookie *cookie; | ||
182 | int err, ivcq; | ||
183 | |||
184 | if (!pdev) | ||
185 | return -ENODEV; | ||
186 | |||
187 | context = devm_kzalloc(&pdev->dev, sizeof(*context), GFP_KERNEL); | ||
188 | if (!context) | ||
189 | return -ENOMEM; | ||
190 | |||
191 | context->dev = &pdev->dev; | ||
192 | |||
193 | np = pdev->dev.of_node; | ||
194 | ivcq_np = of_parse_phandle(np, "ivc_queue", 0); | ||
195 | if (!ivcq_np) { | ||
196 | dev_err(&pdev->dev, "Fail to find vmipi ivc queue\n"); | ||
197 | return -ENOMEM; | ||
198 | } | ||
199 | |||
200 | err = of_property_read_u32_index(np, "ivc_queue", 1, &ivcq); | ||
201 | if (err) { | ||
202 | dev_err(&pdev->dev, "Fail to read vmipi ivc queue value\n"); | ||
203 | of_node_put(ivcq_np); | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | of_node_put(ivcq_np); | ||
207 | |||
208 | cookie = tegra_hv_ivc_reserve(ivcq_np, ivcq, NULL); | ||
209 | if (IS_ERR_OR_NULL(cookie)) { | ||
210 | if (cookie == ERR_PTR(-EPROBE_DEFER)) | ||
211 | return -EPROBE_DEFER; | ||
212 | |||
213 | dev_err(&pdev->dev, | ||
214 | "Fail to reserve vmipi ivc queue %d\n", ivcq); | ||
215 | return PTR_ERR(cookie); | ||
216 | } | ||
217 | context->cookie = cookie; | ||
218 | |||
219 | init_waitqueue_head(&context->tx_wq); | ||
220 | init_waitqueue_head(&context->rx_wq); | ||
221 | |||
222 | mutex_init(&context->lock); | ||
223 | |||
224 | tegra_hv_ivc_channel_reset(context->cookie); | ||
225 | |||
226 | err = devm_request_irq(&pdev->dev, cookie->irq, tegra_vmipi_ivc_isr, 0, | ||
227 | "mipi-virt", context); | ||
228 | if (err) { | ||
229 | dev_err(&pdev->dev, "Fail to request vmipi ivc irq %d\n", | ||
230 | cookie->irq); | ||
231 | goto fail; | ||
232 | } | ||
233 | |||
234 | ctx = context; | ||
235 | return 0; | ||
236 | |||
237 | fail: | ||
238 | tegra_hv_ivc_unreserve(context->cookie); | ||
239 | return err; | ||
240 | } | ||
241 | |||
242 | void tegra_vmipi_deinit(void) | ||
243 | { | ||
244 | if (ctx) | ||
245 | tegra_hv_ivc_unreserve(ctx->cookie); | ||
246 | } | ||
diff --git a/drivers/media/platform/tegra/mipical/vmipi/vmipi.h b/drivers/media/platform/tegra/mipical/vmipi/vmipi.h new file mode 100644 index 000000000..694f1ac21 --- /dev/null +++ b/drivers/media/platform/tegra/mipical/vmipi/vmipi.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * vmipi.h | ||
3 | * | ||
4 | * Copyright (c) 2017, NVIDIA CORPORATION, All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef VMIPI_H | ||
18 | #define VMIPI_H | ||
19 | |||
20 | struct tegra_mipi; | ||
21 | |||
22 | enum { | ||
23 | TEGRA_VMIPI_CMD_CALIBRATE = 0, | ||
24 | TEGRA_VMIPI_CMD_BIAS_PAD_ENABLE = 1, | ||
25 | TEGRA_VMIPI_CMD_BIAS_PAD_DISABLE = 2, | ||
26 | }; | ||
27 | |||
28 | struct tegra_vmipi_calibrate_params { | ||
29 | u32 lanes; | ||
30 | }; | ||
31 | |||
32 | struct tegra_vmipi_cmd_msg { | ||
33 | u32 cmd; | ||
34 | int ret; | ||
35 | union { | ||
36 | struct tegra_vmipi_calibrate_params calibrate; | ||
37 | } params; | ||
38 | }; | ||
39 | |||
40 | #ifdef CONFIG_TEGRA_MIPI_CAL | ||
41 | int tegra_vmipi_init(struct platform_device *pdev); | ||
42 | void tegra_vmipi_deinit(void); | ||
43 | int tegra_vmipi_bias_pad_enable(struct tegra_mipi *mipi); | ||
44 | int tegra_vmipi_bias_pad_disable(struct tegra_mipi *mipi); | ||
45 | int tegra_vmipi_calibration(struct tegra_mipi *mipi, int lanes); | ||
46 | #else | ||
47 | static inline int tegra_vmipi_init(struct platform_device *pdev) | ||
48 | { | ||
49 | return 0; | ||
50 | } | ||
51 | static inline void tegra_vmipi_init(void) {} | ||
52 | static inline int tegra_vmipi_bias_pad_enable(struct tegra_mipi *mipi) | ||
53 | { | ||
54 | return 0; | ||
55 | } | ||
56 | static inline int tegra_vmipi_bias_pad_disable(struct tegra_mipi *mipi) | ||
57 | { | ||
58 | return 0; | ||
59 | } | ||
60 | static inline int tegra_vmipi_calibration(struct tegra_mipi *mipi, | ||
61 | int lanes) | ||
62 | { | ||
63 | return 0; | ||
64 | } | ||
65 | #endif | ||
66 | #endif | ||
diff --git a/drivers/media/platform/tegra/regmap_util.c b/drivers/media/platform/tegra/regmap_util.c new file mode 100644 index 000000000..c21dcac78 --- /dev/null +++ b/drivers/media/platform/tegra/regmap_util.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * regmap_util.c - utilities for writing regmap tables | ||
3 | * | ||
4 | * Copyright (c) 2013-2016, NVIDIA Corporation. All Rights Reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/regmap.h> | ||
20 | #include <media/camera_common.h> | ||
21 | |||
22 | int | ||
23 | regmap_util_write_table_8(struct regmap *regmap, | ||
24 | const struct reg_8 table[], | ||
25 | const struct reg_8 override_list[], | ||
26 | int num_override_regs, u16 wait_ms_addr, u16 end_addr) | ||
27 | { | ||
28 | int err; | ||
29 | const struct reg_8 *next; | ||
30 | int i; | ||
31 | u8 val; | ||
32 | |||
33 | int range_start = -1; | ||
34 | int range_count = 0; | ||
35 | /* bug 200048392 - | ||
36 | * the vi i2c cannot take a FIFO buffer bigger than 16 bytes | ||
37 | */ | ||
38 | u8 range_vals[16]; | ||
39 | int max_range_vals = ARRAY_SIZE(range_vals); | ||
40 | |||
41 | for (next = table;; next++) { | ||
42 | /* If we have a range open and */ | ||
43 | /* either the address doesn't match */ | ||
44 | /* or the temporary storage is full, flush */ | ||
45 | if ((next->addr != range_start + range_count) || | ||
46 | (next->addr == end_addr) || | ||
47 | (next->addr == wait_ms_addr) || | ||
48 | (range_count == max_range_vals)) { | ||
49 | |||
50 | if (range_count == 1) { | ||
51 | err = | ||
52 | regmap_write(regmap, range_start, | ||
53 | range_vals[0]); | ||
54 | } else if (range_count > 1) { | ||
55 | err = | ||
56 | regmap_bulk_write(regmap, range_start, | ||
57 | &range_vals[0], | ||
58 | range_count); | ||
59 | } | ||
60 | |||
61 | if (err) { | ||
62 | pr_err("%s:regmap_util_write_table:%d", | ||
63 | __func__, err); | ||
64 | return err; | ||
65 | } | ||
66 | |||
67 | range_start = -1; | ||
68 | range_count = 0; | ||
69 | |||
70 | /* Handle special address values */ | ||
71 | if (next->addr == end_addr) | ||
72 | break; | ||
73 | |||
74 | if (next->addr == wait_ms_addr) { | ||
75 | msleep_range(next->val); | ||
76 | continue; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | val = next->val; | ||
81 | |||
82 | /* When an override list is passed in, replace the reg */ | ||
83 | /* value to write if the reg is in the list */ | ||
84 | if (override_list) { | ||
85 | for (i = 0; i < num_override_regs; i++) { | ||
86 | if (next->addr == override_list[i].addr) { | ||
87 | val = override_list[i].val; | ||
88 | break; | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (range_start == -1) | ||
94 | range_start = next->addr; | ||
95 | |||
96 | range_vals[range_count++] = val; | ||
97 | } | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | int | ||
102 | regmap_util_write_table_16_as_8(struct regmap *regmap, | ||
103 | const struct reg_16 table[], | ||
104 | const struct reg_16 override_list[], | ||
105 | int num_override_regs, | ||
106 | u16 wait_ms_addr, u16 end_addr) | ||
107 | { | ||
108 | int err; | ||
109 | const struct reg_16 *next; | ||
110 | int i; | ||
111 | u16 val; | ||
112 | |||
113 | int range_start = -1; | ||
114 | int range_count = 0; | ||
115 | u8 range_vals[256]; | ||
116 | int max_range_vals = ARRAY_SIZE(range_vals); | ||
117 | |||
118 | for (next = table;; next++) { | ||
119 | /* If we have a range open and */ | ||
120 | /* either the address doesn't match */ | ||
121 | /* or the temporary storage is full, flush*/ | ||
122 | if ((next->addr != range_start + range_count) || | ||
123 | (next->addr == end_addr) || | ||
124 | (next->addr == wait_ms_addr) || | ||
125 | (range_count == max_range_vals)) { | ||
126 | |||
127 | if (range_count > 1) { | ||
128 | err = | ||
129 | regmap_bulk_write(regmap, range_start, | ||
130 | &range_vals[0], | ||
131 | range_count); | ||
132 | } | ||
133 | |||
134 | if (err) { | ||
135 | pr_err("%s:regmap_util_write_table:%d", | ||
136 | __func__, err); | ||
137 | return err; | ||
138 | } | ||
139 | |||
140 | range_start = -1; | ||
141 | range_count = 0; | ||
142 | |||
143 | /* Handle special address values */ | ||
144 | if (next->addr == end_addr) | ||
145 | break; | ||
146 | |||
147 | if (next->addr == wait_ms_addr) { | ||
148 | msleep_range(next->val); | ||
149 | continue; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | val = next->val; | ||
154 | |||
155 | /* When an override list is passed in, replace the reg */ | ||
156 | /* value to write if the reg is in the list */ | ||
157 | if (override_list) { | ||
158 | for (i = 0; i < num_override_regs; i++) { | ||
159 | if (next->addr == override_list[i].addr) { | ||
160 | val = override_list[i].val; | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | if (range_start == -1) | ||
167 | range_start = next->addr; | ||
168 | |||
169 | range_vals[range_count++] = (u8) (val >> 8); | ||
170 | range_vals[range_count++] = (u8) (val & 0xFF); | ||
171 | } | ||
172 | return 0; | ||
173 | } | ||
diff --git a/drivers/media/platform/tegra/tpg/Makefile b/drivers/media/platform/tegra/tpg/Makefile new file mode 100644 index 000000000..135473166 --- /dev/null +++ b/drivers/media/platform/tegra/tpg/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | ccflags-y += -I../nvhost/drivers/video/tegra/host | ||
3 | ccflags-y += -Idrivers/media/platform/tegra | ||
4 | ccflags-y += -Werror | ||
5 | |||
6 | ifeq ($(CONFIG_ARCH_TEGRA_18x_SOC),y) | ||
7 | ccflags-y += -I../t18x/drivers/video/tegra/host | ||
8 | nvhost-vi-tpg-objs += tpg_t18x.o | ||
9 | else | ||
10 | nvhost-vi-tpg-objs += tpg_t21x.o | ||
11 | endif | ||
12 | |||
13 | |||
14 | obj-$(CONFIG_VIDEO_TEGRA_VI_TPG) += nvhost-vi-tpg.o | ||
diff --git a/drivers/media/platform/tegra/tpg/tpg_t18x.c b/drivers/media/platform/tegra/tpg/tpg_t18x.c new file mode 100644 index 000000000..539d9b4de --- /dev/null +++ b/drivers/media/platform/tegra/tpg/tpg_t18x.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/tpg/tpg.c | ||
3 | * | ||
4 | * Tegra VI test pattern generator driver | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/export.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/debugfs.h> | ||
24 | |||
25 | #include "camera/vi/mc_common.h" | ||
26 | #include "camera/csi/csi.h" | ||
27 | #include "vi/vi4.h" | ||
28 | #include "nvcsi/nvcsi.h" | ||
29 | #include "host1x/host1x.h" | ||
30 | |||
31 | /* PG generate 1 pixel per nvcsi_clk: | ||
32 | * ((width + hblank) * height + vblank) * fps = nvcsi_clk_freq | ||
33 | * | ||
34 | */ | ||
35 | const struct tpg_frmfmt tegra18x_csi_tpg_frmfmt[] = { | ||
36 | {{1280, 720}, V4L2_PIX_FMT_SRGGB10, 120, 750, 0xffff}, | ||
37 | {{1920, 1080}, V4L2_PIX_FMT_SRGGB10, 60, 930, 0xffff}, | ||
38 | {{3840, 2160}, V4L2_PIX_FMT_SRGGB10, 20, 900, 0xffff}, | ||
39 | {{1280, 720}, V4L2_PIX_FMT_RGB32, 60, 3351, 0xffff}, | ||
40 | {{1920, 1080}, V4L2_PIX_FMT_RGB32, 30, 4315, 0xffff}, | ||
41 | {{3840, 2160}, V4L2_PIX_FMT_RGB32, 20, 851, 0xffff}, | ||
42 | }; | ||
43 | |||
44 | #define TPG_PORT_IDX 0 | ||
45 | |||
46 | static int tpg_debugfs_height_show(void *data, u64 *val) | ||
47 | { | ||
48 | struct tegra_csi_channel *chan = data; | ||
49 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
50 | |||
51 | mutex_lock(&chan->format_lock); | ||
52 | *val = port->format.height; | ||
53 | mutex_unlock(&chan->format_lock); | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int tpg_debugfs_height_write(void *data, u64 val) | ||
59 | { | ||
60 | struct tegra_csi_channel *chan = data; | ||
61 | struct tegra_csi_device *csi = chan->csi; | ||
62 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
63 | |||
64 | mutex_lock(&chan->format_lock); | ||
65 | port->format.height = val; | ||
66 | mutex_unlock(&chan->format_lock); | ||
67 | |||
68 | if (csi->fops->csi_override_format) | ||
69 | csi->fops->csi_override_format(chan, TPG_PORT_IDX); | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | DEFINE_SIMPLE_ATTRIBUTE(tpg_debugfs_height_fops, | ||
75 | tpg_debugfs_height_show, | ||
76 | tpg_debugfs_height_write, | ||
77 | "%lld\n"); | ||
78 | |||
79 | static int tpg_debugfs_width_show(void *data, u64 *val) | ||
80 | { | ||
81 | struct tegra_csi_channel *chan = data; | ||
82 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
83 | |||
84 | mutex_lock(&chan->format_lock); | ||
85 | *val = port->format.width; | ||
86 | mutex_unlock(&chan->format_lock); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int tpg_debugfs_width_write(void *data, u64 val) | ||
92 | { | ||
93 | struct tegra_csi_channel *chan = data; | ||
94 | struct tegra_csi_device *csi = chan->csi; | ||
95 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
96 | |||
97 | mutex_lock(&chan->format_lock); | ||
98 | port->format.width = val; | ||
99 | mutex_unlock(&chan->format_lock); | ||
100 | |||
101 | if (csi->fops->csi_override_format) | ||
102 | csi->fops->csi_override_format(chan, TPG_PORT_IDX); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | DEFINE_SIMPLE_ATTRIBUTE(tpg_debugfs_width_fops, | ||
108 | tpg_debugfs_width_show, | ||
109 | tpg_debugfs_width_write, | ||
110 | "%lld\n"); | ||
111 | |||
112 | |||
113 | static void tpg_remove_debugfs(struct tegra_csi_device *csi) | ||
114 | { | ||
115 | debugfs_remove_recursive(csi->debugdir); | ||
116 | csi->debugdir = NULL; | ||
117 | } | ||
118 | |||
119 | static int tpg_create_debugfs(struct tegra_csi_device *csi) | ||
120 | { | ||
121 | struct dentry *dir; | ||
122 | struct tegra_csi_channel *chan; | ||
123 | |||
124 | csi->debugdir = dir = debugfs_create_dir("tpg", NULL); | ||
125 | if (dir == NULL) | ||
126 | return -ENOMEM; | ||
127 | |||
128 | list_for_each_entry(chan, &csi->csi_chans, list) { | ||
129 | const struct tegra_channel *vi_chan = | ||
130 | v4l2_get_subdev_hostdata(&chan->subdev); | ||
131 | if (vi_chan->pg_mode) { | ||
132 | const char *name = vi_chan->video.name; | ||
133 | |||
134 | dev_dbg(csi->dev, "debugfs node installed %s\n", name); | ||
135 | dir = debugfs_create_dir(name, csi->debugdir); | ||
136 | if (!dir) | ||
137 | goto error; | ||
138 | |||
139 | if (!debugfs_create_file("height", S_IRUGO | S_IWUSR, | ||
140 | dir, chan, | ||
141 | &tpg_debugfs_height_fops)) | ||
142 | goto error; | ||
143 | if (!debugfs_create_file("width", S_IRUGO | S_IWUSR, | ||
144 | dir, chan, | ||
145 | &tpg_debugfs_width_fops)) | ||
146 | goto error; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | error: | ||
152 | tpg_remove_debugfs(csi); | ||
153 | return -ENOMEM; | ||
154 | } | ||
155 | |||
156 | static int __init tpg_probe_t18x(void) | ||
157 | { | ||
158 | struct tegra_csi_device *mc_csi = tegra_get_mc_csi(); | ||
159 | struct tegra_mc_vi *mc_vi = tegra_get_mc_vi(); | ||
160 | int err; | ||
161 | |||
162 | dev_info(mc_csi->dev, "%s\n", __func__); | ||
163 | mc_vi->csi = mc_csi; | ||
164 | /* Init CSI related media controller interface */ | ||
165 | mc_csi->tpg_frmfmt_table = tegra18x_csi_tpg_frmfmt; | ||
166 | mc_csi->tpg_frmfmt_table_size = ARRAY_SIZE(tegra18x_csi_tpg_frmfmt); | ||
167 | err = tpg_csi_media_controller_init(mc_csi, TEGRA_VI_PG_PATCH); | ||
168 | if (err) | ||
169 | return -EINVAL; | ||
170 | err = tpg_vi_media_controller_init(mc_vi, TEGRA_VI_PG_PATCH); | ||
171 | if (err) | ||
172 | goto vi_init_err; | ||
173 | |||
174 | err = tpg_create_debugfs(mc_csi); | ||
175 | if (err) | ||
176 | goto debugfs_init_err; | ||
177 | |||
178 | return err; | ||
179 | debugfs_init_err: | ||
180 | tpg_remove_debugfs(mc_csi); | ||
181 | vi_init_err: | ||
182 | tpg_csi_media_controller_cleanup(mc_csi); | ||
183 | dev_err(mc_csi->dev, "%s error\n", __func__); | ||
184 | return err; | ||
185 | } | ||
186 | static void __exit tpg_remove_t18x(void) | ||
187 | { | ||
188 | struct tegra_csi_device *mc_csi = tegra_get_mc_csi(); | ||
189 | struct tegra_mc_vi *mc_vi = tegra_get_mc_vi(); | ||
190 | |||
191 | dev_info(mc_csi->dev, "%s\n", __func__); | ||
192 | tpg_remove_debugfs(mc_csi); | ||
193 | tpg_csi_media_controller_cleanup(mc_csi); | ||
194 | tpg_vi_media_controller_cleanup(mc_vi); | ||
195 | } | ||
196 | |||
197 | module_init(tpg_probe_t18x); | ||
198 | module_exit(tpg_remove_t18x); | ||
199 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/media/platform/tegra/tpg/tpg_t21x.c b/drivers/media/platform/tegra/tpg/tpg_t21x.c new file mode 100644 index 000000000..6291d0209 --- /dev/null +++ b/drivers/media/platform/tegra/tpg/tpg_t21x.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/tpg/tpg.c | ||
3 | * | ||
4 | * Tegra VI test pattern generator driver | ||
5 | * | ||
6 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/export.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/debugfs.h> | ||
24 | |||
25 | #include "camera/vi/mc_common.h" | ||
26 | #include "camera/csi/csi.h" | ||
27 | #include "vi/vi.h" | ||
28 | #include "host1x/host1x.h" | ||
29 | |||
30 | /* | ||
31 | * total_cycles_per_lane for one second = pll_d freq / 8 | ||
32 | * width_in_bytes = ((width * bpp) / 8) | ||
33 | * cycles_per_line = width_in_bytes + hblank | ||
34 | * cycles_per_image = (cycles_per_line * height) + vblank | ||
35 | * image_cycles_per_lane = cycles_per_image / numlanes | ||
36 | * framerate = total_cycles_per_lane / image_cycles_per_lane | ||
37 | * As per IAS maximum overhead of ~15% can occur | ||
38 | * hblank and vblank are tuned to consider overhead during capture | ||
39 | * e.g. for 1920x1080, RAW 10 and two lane TPG | ||
40 | * cycles_per_lane = (((((1920 * 10)/8) + 512) * 1080) + 8) / 2 ~ 1572480 | ||
41 | * framerate = ((927000000 / 8) / 1572480) ~ 73fps | ||
42 | * Max overhead of 15% results in minimum of 62fps (max can be 73fps) | ||
43 | * Note: with changing resolution, bpp and hblank overhead % varies. | ||
44 | */ | ||
45 | |||
46 | static struct tpg_frmfmt tegra21x_csi_tpg_frmfmt[] = { | ||
47 | {{1280, 720}, V4L2_PIX_FMT_SRGGB10, 120, 512, 8}, | ||
48 | {{1920, 1080}, V4L2_PIX_FMT_SRGGB10, 60, 512, 8}, | ||
49 | {{3840, 2160}, V4L2_PIX_FMT_SRGGB10, 20, 8, 8}, | ||
50 | {{1280, 720}, V4L2_PIX_FMT_RGB32, 60, 512, 8}, | ||
51 | {{1920, 1080}, V4L2_PIX_FMT_RGB32, 30, 512, 8}, | ||
52 | {{3840, 2160}, V4L2_PIX_FMT_RGB32, 8, 8, 8}, | ||
53 | }; | ||
54 | |||
55 | #define TPG_PORT_IDX 0 | ||
56 | |||
57 | static int tpg_debugfs_height_show(void *data, u64 *val) | ||
58 | { | ||
59 | struct tegra_csi_channel *chan = data; | ||
60 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
61 | |||
62 | mutex_lock(&chan->format_lock); | ||
63 | *val = port->format.height; | ||
64 | mutex_unlock(&chan->format_lock); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int tpg_debugfs_height_write(void *data, u64 val) | ||
70 | { | ||
71 | struct tegra_csi_channel *chan = data; | ||
72 | struct tegra_csi_device *csi = chan->csi; | ||
73 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
74 | |||
75 | mutex_lock(&chan->format_lock); | ||
76 | port->format.height = val; | ||
77 | mutex_unlock(&chan->format_lock); | ||
78 | |||
79 | if (csi->fops->csi_override_format) | ||
80 | csi->fops->csi_override_format(chan, TPG_PORT_IDX); | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | DEFINE_SIMPLE_ATTRIBUTE(tpg_debugfs_height_fops, | ||
86 | tpg_debugfs_height_show, | ||
87 | tpg_debugfs_height_write, | ||
88 | "%lld\n"); | ||
89 | |||
90 | static int tpg_debugfs_width_show(void *data, u64 *val) | ||
91 | { | ||
92 | struct tegra_csi_channel *chan = data; | ||
93 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
94 | |||
95 | mutex_lock(&chan->format_lock); | ||
96 | *val = port->format.width; | ||
97 | mutex_unlock(&chan->format_lock); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int tpg_debugfs_width_write(void *data, u64 val) | ||
103 | { | ||
104 | struct tegra_csi_channel *chan = data; | ||
105 | struct tegra_csi_device *csi = chan->csi; | ||
106 | struct tegra_csi_port *port = &chan->ports[TPG_PORT_IDX]; | ||
107 | |||
108 | mutex_lock(&chan->format_lock); | ||
109 | port->format.width = val; | ||
110 | mutex_unlock(&chan->format_lock); | ||
111 | |||
112 | if (csi->fops->csi_override_format) | ||
113 | csi->fops->csi_override_format(chan, TPG_PORT_IDX); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | DEFINE_SIMPLE_ATTRIBUTE(tpg_debugfs_width_fops, | ||
119 | tpg_debugfs_width_show, | ||
120 | tpg_debugfs_width_write, | ||
121 | "%lld\n"); | ||
122 | |||
123 | |||
124 | static void tpg_remove_debugfs(struct tegra_csi_device *csi) | ||
125 | { | ||
126 | debugfs_remove_recursive(csi->debugdir); | ||
127 | csi->debugdir = NULL; | ||
128 | } | ||
129 | |||
130 | static int tpg_create_debugfs(struct tegra_csi_device *csi) | ||
131 | { | ||
132 | struct dentry *dir; | ||
133 | struct tegra_csi_channel *chan; | ||
134 | |||
135 | csi->debugdir = dir = debugfs_create_dir("tpg", NULL); | ||
136 | if (dir == NULL) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | list_for_each_entry(chan, &csi->csi_chans, list) { | ||
140 | const struct tegra_channel *vi_chan = | ||
141 | v4l2_get_subdev_hostdata(&chan->subdev); | ||
142 | if (vi_chan->pg_mode) { | ||
143 | const char *name = vi_chan->video.name; | ||
144 | |||
145 | dev_dbg(csi->dev, "debugfs node installed %s\n", name); | ||
146 | dir = debugfs_create_dir(name, csi->debugdir); | ||
147 | if (!dir) | ||
148 | goto error; | ||
149 | |||
150 | if (!debugfs_create_file("height", S_IRUGO | S_IWUSR, | ||
151 | dir, chan, | ||
152 | &tpg_debugfs_height_fops)) | ||
153 | goto error; | ||
154 | if (!debugfs_create_file("width", S_IRUGO | S_IWUSR, | ||
155 | dir, chan, | ||
156 | &tpg_debugfs_width_fops)) | ||
157 | goto error; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | error: | ||
163 | tpg_remove_debugfs(csi); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | |||
167 | static int __init tpg_probe_t21x(void) | ||
168 | { | ||
169 | struct tegra_csi_device *mc_csi = &(tegra_vi_get()->csi); | ||
170 | struct tegra_mc_vi *mc_vi = &(tegra_vi_get()->mc_vi); | ||
171 | int err; | ||
172 | |||
173 | dev_info(mc_csi->dev, "%s\n", __func__); | ||
174 | mc_vi->csi = mc_csi; | ||
175 | /* Init CSI related media controller interface */ | ||
176 | mc_csi->tpg_frmfmt_table = tegra21x_csi_tpg_frmfmt; | ||
177 | mc_csi->tpg_frmfmt_table_size = ARRAY_SIZE(tegra21x_csi_tpg_frmfmt); | ||
178 | err = tpg_csi_media_controller_init(mc_csi, TEGRA_VI_PG_PATCH); | ||
179 | if (err) | ||
180 | return -EINVAL; | ||
181 | err = tpg_vi_media_controller_init(mc_vi, TEGRA_VI_PG_PATCH); | ||
182 | if (err) | ||
183 | goto vi_init_err; | ||
184 | |||
185 | err = tpg_create_debugfs(mc_csi); | ||
186 | if (err) | ||
187 | goto debugfs_init_err; | ||
188 | |||
189 | return err; | ||
190 | debugfs_init_err: | ||
191 | tpg_remove_debugfs(mc_csi); | ||
192 | vi_init_err: | ||
193 | tpg_csi_media_controller_cleanup(mc_csi); | ||
194 | dev_err(mc_csi->dev, "%s error\n", __func__); | ||
195 | return err; | ||
196 | } | ||
197 | static void __exit tpg_remove_t21x(void) | ||
198 | { | ||
199 | struct tegra_csi_device *mc_csi = &(tegra_vi_get()->csi); | ||
200 | struct tegra_mc_vi *mc_vi = &(tegra_vi_get()->mc_vi); | ||
201 | |||
202 | dev_info(mc_csi->dev, "%s\n", __func__); | ||
203 | tpg_remove_debugfs(mc_csi); | ||
204 | tpg_csi_media_controller_cleanup(mc_csi); | ||
205 | tpg_vi_media_controller_cleanup(mc_vi); | ||
206 | } | ||
207 | |||
208 | module_init(tpg_probe_t21x); | ||
209 | module_exit(tpg_remove_t21x); | ||
210 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/media/platform/tegra/vi/Makefile b/drivers/media/platform/tegra/vi/Makefile new file mode 100644 index 000000000..6cd9767ea --- /dev/null +++ b/drivers/media/platform/tegra/vi/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | GCOV_PROFILE := y | ||
2 | ccflags-y += -I../nvhost/drivers/video/tegra/host | ||
3 | ccflags-y += -Idrivers/video/tegra/camera | ||
4 | ccflags-y += -Idrivers/media/platform/tegra/ | ||
5 | ccflags-y += -Werror | ||
6 | |||
7 | ifeq ($(CONFIG_ARCH_TEGRA_18x_SOC),y) | ||
8 | ccflags-y += -I../t18x/drivers/video/tegra/host/ | ||
9 | endif | ||
10 | |||
11 | ifneq (,$(filter $(CONFIG_VIDEO_TEGRA_VI),y m)) | ||
12 | obj-y += vi_irq.o | ||
13 | obj-y += tegra_vi.o | ||
14 | endif | ||
15 | |||
16 | nvhost-vi-objs += vi.o | ||
17 | obj-$(CONFIG_VIDEO_TEGRA_VI) += nvhost-vi.o | ||
diff --git a/drivers/media/platform/tegra/vi/tegra_vi.c b/drivers/media/platform/tegra/vi/tegra_vi.c new file mode 100644 index 000000000..4d2e3b714 --- /dev/null +++ b/drivers/media/platform/tegra/vi/tegra_vi.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013-2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/fs.h> | ||
18 | #include <linux/file.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/nvhost.h> | ||
22 | #include <linux/nvhost_vi_ioctl.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/regulator/consumer.h> | ||
25 | #include <linux/uaccess.h> | ||
26 | #include <linux/clk.h> | ||
27 | |||
28 | #include <linux/platform/tegra/latency_allowance.h> | ||
29 | |||
30 | #include "bus_client.h" | ||
31 | #include "nvhost_acm.h" | ||
32 | #include "chip_support.h" | ||
33 | #include "host1x/host1x.h" | ||
34 | #include "vi/vi.h" | ||
35 | #include "vi/vi_irq.h" | ||
36 | |||
37 | #define T12_VI_CFG_CG_CTRL 0xb8 | ||
38 | #define T12_CG_2ND_LEVEL_EN 1 | ||
39 | #define T12_VI_CSI_0_SW_RESET 0x100 | ||
40 | #define T12_CSI_CSI_SW_SENSOR_A_RESET 0x858 | ||
41 | #define T12_CSI_CSICIL_SW_SENSOR_A_RESET 0x94c | ||
42 | #define T12_VI_CSI_0_CSI_IMAGE_DT 0x120 | ||
43 | |||
44 | #define T12_VI_CSI_1_SW_RESET 0x200 | ||
45 | #define T12_CSI_CSI_SW_SENSOR_B_RESET 0x88c | ||
46 | #define T12_CSI_CSICIL_SW_SENSOR_B_RESET 0x980 | ||
47 | #define T12_VI_CSI_1_CSI_IMAGE_DT 0x220 | ||
48 | |||
49 | #define T21_CSI_CILA_PAD_CONFIG0 0x92c | ||
50 | #define T21_CSI1_CILA_PAD_CONFIG0 0x112c | ||
51 | #define T21_CSI2_CILA_PAD_CONFIG0 0x192c | ||
52 | |||
53 | #define VI_MAX_BPP 2 | ||
54 | |||
55 | int nvhost_vi_finalize_poweron(struct platform_device *dev) | ||
56 | { | ||
57 | struct vi *tegra_vi = nvhost_get_private_data(dev); | ||
58 | int ret = 0; | ||
59 | |||
60 | if (!tegra_vi) | ||
61 | return -EINVAL; | ||
62 | |||
63 | if (tegra_vi->reg) { | ||
64 | ret = regulator_enable(tegra_vi->reg); | ||
65 | if (ret) { | ||
66 | dev_err(&dev->dev, | ||
67 | "%s: enable csi regulator failed.\n", | ||
68 | __func__); | ||
69 | return ret; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | #ifdef CONFIG_ARCH_TEGRA_12x_SOC | ||
74 | /* Only do this for vi.0 not for slave device vi.1 */ | ||
75 | if (dev->id == 0) | ||
76 | host1x_writel(dev, T12_VI_CFG_CG_CTRL, T12_CG_2ND_LEVEL_EN); | ||
77 | #endif | ||
78 | |||
79 | ret = vi_enable_irq(tegra_vi); | ||
80 | if (ret) | ||
81 | dev_err(&tegra_vi->ndev->dev, "%s: vi_enable_irq failed\n", | ||
82 | __func__); | ||
83 | |||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | int nvhost_vi_prepare_poweroff(struct platform_device *dev) | ||
88 | { | ||
89 | int ret = 0; | ||
90 | struct vi *tegra_vi = nvhost_get_private_data(dev); | ||
91 | |||
92 | if (!tegra_vi) | ||
93 | return -EINVAL; | ||
94 | |||
95 | ret = vi_disable_irq(tegra_vi); | ||
96 | if (ret) { | ||
97 | dev_err(&tegra_vi->ndev->dev, "%s: vi_disable_irq failed\n", | ||
98 | __func__); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | if (tegra_vi->reg) { | ||
103 | ret = regulator_disable(tegra_vi->reg); | ||
104 | if (ret) | ||
105 | dev_err(&dev->dev, | ||
106 | "%s: disable csi regulator failed.\n", | ||
107 | __func__); | ||
108 | } | ||
109 | |||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
114 | static int vi_isomgr_register(struct vi *tegra_vi) | ||
115 | { | ||
116 | int iso_client_id = TEGRA_ISO_CLIENT_VI_0; | ||
117 | struct clk *vi_clk; | ||
118 | struct nvhost_device_data *pdata = | ||
119 | platform_get_drvdata(tegra_vi->ndev); | ||
120 | |||
121 | dev_dbg(&tegra_vi->ndev->dev, "%s++\n", __func__); | ||
122 | |||
123 | if (WARN_ONCE(pdata == NULL, "pdata not found, %s failed\n", __func__)) | ||
124 | return -ENODEV; | ||
125 | |||
126 | /* Get max VI BW */ | ||
127 | vi_clk = pdata->clk[0]; | ||
128 | tegra_vi->max_bw = | ||
129 | (clk_round_rate(vi_clk, UINT_MAX) / 1000) * VI_MAX_BPP; | ||
130 | |||
131 | /* Register with max possible BW in VI usecases.*/ | ||
132 | tegra_vi->isomgr_handle = tegra_isomgr_register(iso_client_id, | ||
133 | tegra_vi->max_bw, | ||
134 | NULL, /* tegra_isomgr_renegotiate */ | ||
135 | NULL); /* *priv */ | ||
136 | |||
137 | if (!tegra_vi->isomgr_handle) { | ||
138 | dev_err(&tegra_vi->ndev->dev, "%s: unable to register isomgr\n", | ||
139 | __func__); | ||
140 | return -ENOMEM; | ||
141 | } | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int vi_set_isomgr_request(struct vi *tegra_vi, uint vi_bw, uint lt) | ||
147 | { | ||
148 | int ret = 0; | ||
149 | |||
150 | dev_dbg(&tegra_vi->ndev->dev, | ||
151 | "%s++ bw=%u, lt=%u\n", __func__, vi_bw, lt); | ||
152 | |||
153 | /* return value of tegra_isomgr_reserve is dvfs latency in usec */ | ||
154 | ret = tegra_isomgr_reserve(tegra_vi->isomgr_handle, | ||
155 | vi_bw, /* KB/sec */ | ||
156 | lt); /* usec */ | ||
157 | if (!ret) { | ||
158 | dev_err(&tegra_vi->ndev->dev, | ||
159 | "%s: failed to reserve %u KBps\n", __func__, vi_bw); | ||
160 | return -ENOMEM; | ||
161 | } | ||
162 | |||
163 | /* return value of tegra_isomgr_realize is dvfs latency in usec */ | ||
164 | ret = tegra_isomgr_realize(tegra_vi->isomgr_handle); | ||
165 | if (ret) | ||
166 | dev_dbg(&tegra_vi->ndev->dev, | ||
167 | "%s: tegra_vi isomgr latency is %d usec", | ||
168 | __func__, ret); | ||
169 | else { | ||
170 | dev_err(&tegra_vi->ndev->dev, | ||
171 | "%s: failed to realize %u KBps\n", __func__, vi_bw); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int vi_isomgr_release(struct vi *tegra_vi) | ||
178 | { | ||
179 | int ret = 0; | ||
180 | |||
181 | dev_dbg(&tegra_vi->ndev->dev, "%s++\n", __func__); | ||
182 | |||
183 | /* deallocate isomgr bw */ | ||
184 | ret = vi_set_isomgr_request(tegra_vi, 0, 0); | ||
185 | if (ret) { | ||
186 | dev_err(&tegra_vi->ndev->dev, | ||
187 | "%s: failed to deallocate memory in isomgr\n", | ||
188 | __func__); | ||
189 | return -ENOMEM; | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | #endif | ||
195 | |||
196 | static int vi_set_la(u32 vi_bw) | ||
197 | { | ||
198 | int ret = 0; | ||
199 | |||
200 | #ifdef CONFIG_TEGRA_MC | ||
201 | ret = tegra_set_camera_ptsa(TEGRA_LA_VI_W, vi_bw, 1); | ||
202 | |||
203 | if (!ret) { | ||
204 | ret = tegra_set_latency_allowance(TEGRA_LA_VI_W, | ||
205 | vi_bw); | ||
206 | |||
207 | if (ret) | ||
208 | pr_err("%s: set latency failed: %d\n", | ||
209 | __func__, ret); | ||
210 | } else { | ||
211 | pr_err("%s: set ptsa failed: %d\n", __func__, ret); | ||
212 | } | ||
213 | #endif | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | int vi_v4l2_set_la(struct vi *tegra_vi, u32 vi_bypass_bw, bool is_ioctl) | ||
219 | { | ||
220 | int ret = 0; | ||
221 | unsigned long total_bw; | ||
222 | |||
223 | mutex_lock(&tegra_vi->update_la_lock); | ||
224 | if (is_ioctl) | ||
225 | tegra_vi->vi_bypass_bw = vi_bypass_bw; | ||
226 | total_bw = tegra_vi->mc_vi.aggregated_kbyteps + tegra_vi->vi_bypass_bw; | ||
227 | ret = vi_set_la(total_bw); | ||
228 | mutex_unlock(&tegra_vi->update_la_lock); | ||
229 | return ret; | ||
230 | } | ||
231 | EXPORT_SYMBOL(vi_v4l2_set_la); | ||
232 | |||
233 | static long vi_ioctl(struct file *file, | ||
234 | unsigned int cmd, unsigned long arg) | ||
235 | { | ||
236 | struct vi *tegra_vi = file->private_data; | ||
237 | |||
238 | if (_IOC_TYPE(cmd) != NVHOST_VI_IOCTL_MAGIC) | ||
239 | return -EFAULT; | ||
240 | |||
241 | switch (_IOC_NR(cmd)) { | ||
242 | case _IOC_NR(NVHOST_VI_IOCTL_ENABLE_TPG): { | ||
243 | uint enable; | ||
244 | int ret; | ||
245 | struct clk *clk; | ||
246 | |||
247 | if (copy_from_user(&enable, | ||
248 | (const void __user *)arg, sizeof(uint))) { | ||
249 | dev_err(&tegra_vi->ndev->dev, | ||
250 | "%s: Failed to copy arg from user\n", __func__); | ||
251 | return -EFAULT; | ||
252 | } | ||
253 | |||
254 | clk = clk_get(NULL, "pll_d"); | ||
255 | if (IS_ERR(clk)) | ||
256 | return -EINVAL; | ||
257 | |||
258 | if (enable) | ||
259 | ret = tegra_clk_cfg_ex(clk, | ||
260 | TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); | ||
261 | else | ||
262 | ret = tegra_clk_cfg_ex(clk, | ||
263 | TEGRA_CLK_MIPI_CSI_OUT_ENB, 1); | ||
264 | clk_put(clk); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | case _IOC_NR(NVHOST_VI_IOCTL_GET_VI_CLK): { | ||
269 | int ret; | ||
270 | u64 vi_clk_rate = 0; | ||
271 | |||
272 | ret = nvhost_module_get_rate(tegra_vi->ndev, | ||
273 | (unsigned long *)&vi_clk_rate, 0); | ||
274 | if (ret) { | ||
275 | dev_err(&tegra_vi->ndev->dev, | ||
276 | "%s: failed to get vi clk\n", | ||
277 | __func__); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | if (copy_to_user((void __user *)arg, | ||
282 | &vi_clk_rate, sizeof(vi_clk_rate))) { | ||
283 | dev_err(&tegra_vi->ndev->dev, | ||
284 | "%s:Failed to copy vi clk rate to user\n", | ||
285 | __func__); | ||
286 | return -EFAULT; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | case _IOC_NR(NVHOST_VI_IOCTL_SET_VI_LA_BW): { | ||
292 | u32 ret = 0; | ||
293 | u32 vi_la_bw; | ||
294 | |||
295 | if (copy_from_user(&vi_la_bw, | ||
296 | (const void __user *)arg, | ||
297 | sizeof(vi_la_bw))) { | ||
298 | dev_err(&tegra_vi->ndev->dev, | ||
299 | "%s: Failed to copy arg from user\n", __func__); | ||
300 | return -EFAULT; | ||
301 | } | ||
302 | |||
303 | /* Set latency allowance for VI, BW is in MBps */ | ||
304 | ret = vi_v4l2_set_la(tegra_vi, vi_la_bw, 1); | ||
305 | if (ret) { | ||
306 | dev_err(&tegra_vi->ndev->dev, | ||
307 | "%s: failed to set la vi_bw %u MBps\n", | ||
308 | __func__, vi_la_bw); | ||
309 | return -ENOMEM; | ||
310 | } | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | case _IOC_NR(NVHOST_VI_IOCTL_SET_EMC_INFO): { | ||
315 | uint vi_bw; | ||
316 | int ret; | ||
317 | |||
318 | if (copy_from_user(&vi_bw, | ||
319 | (const void __user *)arg, sizeof(uint))) { | ||
320 | dev_err(&tegra_vi->ndev->dev, | ||
321 | "%s: Failed to copy arg from user\n", __func__); | ||
322 | return -EFAULT; | ||
323 | } | ||
324 | |||
325 | ret = vi_set_la(vi_bw/1000); | ||
326 | if (ret) { | ||
327 | dev_err(&tegra_vi->ndev->dev, | ||
328 | "%s: failed to set la for vi_bw %u MBps\n", | ||
329 | __func__, vi_bw/1000); | ||
330 | return -ENOMEM; | ||
331 | } | ||
332 | |||
333 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
334 | /* | ||
335 | * Register VI as isomgr client. | ||
336 | */ | ||
337 | if (!tegra_vi->isomgr_handle) { | ||
338 | ret = vi_isomgr_register(tegra_vi); | ||
339 | if (ret) { | ||
340 | dev_err(&tegra_vi->ndev->dev, | ||
341 | "%s: failed to register VI as isomgr client\n", | ||
342 | __func__); | ||
343 | return -ENOMEM; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | if (vi_bw > tegra_vi->max_bw) { | ||
348 | dev_err(&tegra_vi->ndev->dev, | ||
349 | "%s: Requested ISO BW %u is more than " | ||
350 | "VI's max BW %u possible\n", | ||
351 | __func__, vi_bw, tegra_vi->max_bw); | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Set VI ISO BW requirements. | ||
357 | * There is no way to figure out what latency | ||
358 | * can be tolerated in VI without reading VI | ||
359 | * registers for now. 3 usec is minimum time | ||
360 | * to switch PLL source. Let's put 4 usec as | ||
361 | * latency for now. | ||
362 | */ | ||
363 | ret = vi_set_isomgr_request(tegra_vi, vi_bw, 4); | ||
364 | if (ret) { | ||
365 | dev_err(&tegra_vi->ndev->dev, | ||
366 | "%s: failed to reserve %u KBps\n", | ||
367 | __func__, vi_bw); | ||
368 | return -ENOMEM; | ||
369 | } | ||
370 | #endif | ||
371 | return ret; | ||
372 | } | ||
373 | case _IOC_NR(NVHOST_VI_IOCTL_SET_VI_CLK): { | ||
374 | long vi_clk_rate = 0; | ||
375 | |||
376 | if (copy_from_user(&vi_clk_rate, | ||
377 | (const void __user *)arg, sizeof(long))) { | ||
378 | dev_err(&tegra_vi->ndev->dev, | ||
379 | "%s: Failed to copy arg from user\n", __func__); | ||
380 | return -EFAULT; | ||
381 | } | ||
382 | |||
383 | return nvhost_module_set_rate(tegra_vi->ndev, | ||
384 | tegra_vi, vi_clk_rate, 0, NVHOST_CLOCK); | ||
385 | } | ||
386 | default: | ||
387 | dev_err(&tegra_vi->ndev->dev, | ||
388 | "%s: Unknown vi ioctl.\n", __func__); | ||
389 | return -EINVAL; | ||
390 | } | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int vi_open(struct inode *inode, struct file *file) | ||
395 | { | ||
396 | struct nvhost_device_data *pdata; | ||
397 | struct vi *vi; | ||
398 | int err = 0; | ||
399 | |||
400 | pdata = container_of(inode->i_cdev, | ||
401 | struct nvhost_device_data, ctrl_cdev); | ||
402 | if (WARN_ONCE(pdata == NULL, "pdata not found, %s failed\n", __func__)) | ||
403 | return -ENODEV; | ||
404 | |||
405 | vi = (struct vi *)pdata->private_data; | ||
406 | if (WARN_ONCE(vi == NULL, "vi not found, %s failed\n", __func__)) | ||
407 | return -ENODEV; | ||
408 | |||
409 | file->private_data = vi; | ||
410 | |||
411 | /* add vi client to acm */ | ||
412 | if (nvhost_module_add_client(vi->ndev, vi)) { | ||
413 | dev_err(&vi->ndev->dev, | ||
414 | "%s: failed add vi client\n", | ||
415 | __func__); | ||
416 | return -ENOMEM; | ||
417 | } | ||
418 | |||
419 | return err; | ||
420 | } | ||
421 | |||
422 | static int vi_release(struct inode *inode, struct file *file) | ||
423 | { | ||
424 | int ret = 0; | ||
425 | struct vi *tegra_vi = file->private_data; | ||
426 | |||
427 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
428 | /* nullify isomgr request */ | ||
429 | if (tegra_vi->isomgr_handle) { | ||
430 | ret = vi_isomgr_release(tegra_vi); | ||
431 | if (ret) { | ||
432 | dev_err(&tegra_vi->ndev->dev, | ||
433 | "%s: failed to deallocate memory in isomgr\n", | ||
434 | __func__); | ||
435 | return -ENOMEM; | ||
436 | } | ||
437 | } | ||
438 | #endif | ||
439 | |||
440 | /* remove vi client from acm */ | ||
441 | nvhost_module_remove_client(tegra_vi->ndev, tegra_vi); | ||
442 | |||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | const struct file_operations tegra_vi_ctrl_ops = { | ||
447 | .owner = THIS_MODULE, | ||
448 | .open = vi_open, | ||
449 | .unlocked_ioctl = vi_ioctl, | ||
450 | #ifdef CONFIG_COMPAT | ||
451 | .compat_ioctl = vi_ioctl, | ||
452 | #endif | ||
453 | .release = vi_release, | ||
454 | }; | ||
455 | |||
456 | /* Reset sensor data if respective clk is ON */ | ||
457 | void nvhost_vi_reset_all(struct platform_device *pdev) | ||
458 | { | ||
459 | void __iomem *reset_reg[4]; | ||
460 | int err; | ||
461 | bool enabled = false; | ||
462 | struct nvhost_device_data *pdata = pdev->dev.platform_data; | ||
463 | struct clk *clk; | ||
464 | |||
465 | err = nvhost_clk_get(pdev, "cilab", &clk); | ||
466 | if (!err) { | ||
467 | reset_reg[0] = pdata->aperture[0] + | ||
468 | T12_VI_CSI_0_SW_RESET; | ||
469 | reset_reg[1] = pdata->aperture[0] + | ||
470 | T12_CSI_CSI_SW_SENSOR_A_RESET; | ||
471 | reset_reg[2] = pdata->aperture[0] + | ||
472 | T12_CSI_CSICIL_SW_SENSOR_A_RESET; | ||
473 | reset_reg[3] = pdata->aperture[0] + | ||
474 | T12_VI_CSI_0_CSI_IMAGE_DT; | ||
475 | |||
476 | writel(0, reset_reg[3]); | ||
477 | writel(0x1, reset_reg[2]); | ||
478 | writel(0x1, reset_reg[1]); | ||
479 | writel(0x1f, reset_reg[0]); | ||
480 | |||
481 | udelay(10); | ||
482 | |||
483 | writel(0, reset_reg[2]); | ||
484 | writel(0, reset_reg[1]); | ||
485 | } | ||
486 | |||
487 | err = nvhost_clk_get(pdev, "cilcd", &clk); | ||
488 | if (!err) | ||
489 | enabled = true; | ||
490 | |||
491 | err = nvhost_clk_get(pdev, "cile", &clk); | ||
492 | if (!err) | ||
493 | enabled = true; | ||
494 | |||
495 | if (enabled) { | ||
496 | reset_reg[0] = pdata->aperture[0] + | ||
497 | T12_VI_CSI_1_SW_RESET; | ||
498 | reset_reg[1] = pdata->aperture[0] + | ||
499 | T12_CSI_CSI_SW_SENSOR_B_RESET; | ||
500 | reset_reg[2] = pdata->aperture[0] + | ||
501 | T12_CSI_CSICIL_SW_SENSOR_B_RESET; | ||
502 | reset_reg[3] = pdata->aperture[0] + | ||
503 | T12_VI_CSI_1_CSI_IMAGE_DT; | ||
504 | |||
505 | writel(0, reset_reg[3]); | ||
506 | writel(0x1, reset_reg[2]); | ||
507 | writel(0x1, reset_reg[1]); | ||
508 | writel(0x1f, reset_reg[0]); | ||
509 | |||
510 | udelay(10); | ||
511 | |||
512 | writel(0, reset_reg[2]); | ||
513 | writel(0, reset_reg[1]); | ||
514 | } | ||
515 | } | ||
diff --git a/drivers/media/platform/tegra/vi/vi.c b/drivers/media/platform/tegra/vi/vi.c new file mode 100644 index 000000000..61f47fd40 --- /dev/null +++ b/drivers/media/platform/tegra/vi/vi.c | |||
@@ -0,0 +1,611 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/vi/vi.c | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2012-2017, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/export.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/resource.h> | ||
25 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/of.h> | ||
27 | #include <linux/of_device.h> | ||
28 | #include <linux/of_platform.h> | ||
29 | #include <linux/clk/tegra.h> | ||
30 | #include <linux/tegra-soc.h> | ||
31 | #include <linux/tegra_pm_domains.h> | ||
32 | #include <linux/debugfs.h> | ||
33 | #include <linux/slab.h> | ||
34 | |||
35 | #include <media/tegra_v4l2_camera.h> | ||
36 | |||
37 | #include "dev.h" | ||
38 | #include "bus_client.h" | ||
39 | #include "nvhost_acm.h" | ||
40 | #include "t210/t210.h" | ||
41 | #include "vi/vi.h" | ||
42 | #include "vi/vi_irq.h" | ||
43 | #include "camera/vi/vi2_fops.h" | ||
44 | #include "camera/csi/csi2_fops.h" | ||
45 | |||
46 | #include "tegra_camera_dev_mfi.h" | ||
47 | |||
48 | #define MAX_DEVID_LENGTH 16 | ||
49 | #define TEGRA_VI_NAME "tegra_vi" | ||
50 | |||
51 | struct vi *tegra_vi; | ||
52 | |||
53 | struct vi *tegra_vi_get(void) | ||
54 | { | ||
55 | return tegra_vi; | ||
56 | } | ||
57 | EXPORT_SYMBOL(tegra_vi_get); | ||
58 | |||
59 | static struct tegra_t210_vi_data t21_vi_data = { | ||
60 | .info = (struct nvhost_device_data *)&t21_vi_info, | ||
61 | .vi_fops = &vi2_fops, | ||
62 | .csi_fops = &csi2_fops, | ||
63 | }; | ||
64 | |||
65 | static struct of_device_id tegra_vi_of_match[] = { | ||
66 | { .compatible = "nvidia,tegra210-vi", | ||
67 | .data = (struct tegra_t210_vi_data *)&t21_vi_data }, | ||
68 | { }, | ||
69 | }; | ||
70 | |||
71 | static struct i2c_camera_ctrl *i2c_ctrl; | ||
72 | |||
73 | static void (*mfi_callback)(void *); | ||
74 | static struct mfi_cb_arg *mfi_callback_arg; | ||
75 | static DEFINE_MUTEX(vi_isr_lock); | ||
76 | |||
77 | int tegra_vi_register_mfi_cb(callback cb, void *cb_arg) | ||
78 | { | ||
79 | mutex_lock(&vi_isr_lock); | ||
80 | if (mfi_callback || mfi_callback_arg) { | ||
81 | pr_err("cb already registered\n"); | ||
82 | mutex_unlock(&vi_isr_lock); | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | mfi_callback = cb; | ||
87 | mfi_callback_arg = (struct mfi_cb_arg *)cb_arg; | ||
88 | mutex_unlock(&vi_isr_lock); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | EXPORT_SYMBOL(tegra_vi_register_mfi_cb); | ||
93 | |||
94 | int tegra_vi_unregister_mfi_cb(void) | ||
95 | { | ||
96 | mutex_lock(&vi_isr_lock); | ||
97 | mfi_callback = NULL; | ||
98 | mfi_callback_arg = NULL; | ||
99 | mutex_unlock(&vi_isr_lock); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | EXPORT_SYMBOL(tegra_vi_unregister_mfi_cb); | ||
104 | |||
105 | struct tegra_mfi_chan { | ||
106 | struct work_struct mfi_cb_work; | ||
107 | struct rcu_head rcu; | ||
108 | u8 channel; | ||
109 | }; | ||
110 | |||
111 | struct tegra_vi_mfi_ctx { | ||
112 | u8 num_channels; | ||
113 | struct workqueue_struct *mfi_workqueue; | ||
114 | struct tegra_mfi_chan __rcu *mfi_chans; | ||
115 | }; | ||
116 | |||
117 | static void vi_mfi_worker(struct work_struct *vi_work) | ||
118 | { | ||
119 | struct tegra_mfi_chan *mfi_chan = | ||
120 | container_of(vi_work, struct tegra_mfi_chan, mfi_cb_work); | ||
121 | |||
122 | mutex_lock(&vi_isr_lock); | ||
123 | if (mfi_callback == NULL) { | ||
124 | pr_debug("NULL callback\n"); | ||
125 | mutex_unlock(&vi_isr_lock); | ||
126 | return; | ||
127 | } | ||
128 | if (mfi_callback_arg) | ||
129 | mfi_callback_arg->vi_chan = mfi_chan->channel; | ||
130 | mfi_callback(mfi_callback_arg); | ||
131 | mutex_unlock(&vi_isr_lock); | ||
132 | } | ||
133 | |||
134 | int tegra_vi_mfi_event_notify(struct tegra_vi_mfi_ctx *mfi_ctx, u8 channel) | ||
135 | { | ||
136 | struct tegra_mfi_chan *chan; | ||
137 | |||
138 | if (!mfi_ctx) { | ||
139 | pr_err("Invalid mfi_ctx\n"); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | |||
143 | if (channel > mfi_ctx->num_channels-1) { | ||
144 | pr_err("Invalid mfi channel\n"); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | rcu_read_lock(); | ||
149 | chan = rcu_dereference(mfi_ctx->mfi_chans); | ||
150 | queue_work(mfi_ctx->mfi_workqueue, &chan[channel].mfi_cb_work); | ||
151 | rcu_read_unlock(); | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | EXPORT_SYMBOL(tegra_vi_mfi_event_notify); | ||
156 | |||
157 | bool tegra_vi_has_mfi_callback(void) | ||
158 | { | ||
159 | bool ret; | ||
160 | |||
161 | mutex_lock(&vi_isr_lock); | ||
162 | ret = mfi_callback ? true : false; | ||
163 | mutex_unlock(&vi_isr_lock); | ||
164 | |||
165 | return ret; | ||
166 | } | ||
167 | EXPORT_SYMBOL(tegra_vi_has_mfi_callback); | ||
168 | |||
169 | int tegra_vi_init_mfi(struct tegra_vi_mfi_ctx **pmfi_ctx, u8 num_channels) | ||
170 | { | ||
171 | u8 i; | ||
172 | struct tegra_mfi_chan *chan; | ||
173 | struct tegra_vi_mfi_ctx *mfi_ctx; | ||
174 | |||
175 | if (!pmfi_ctx) { | ||
176 | pr_err("mfi_ctx is invalid\n"); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | mfi_ctx = kzalloc(sizeof(*mfi_ctx), GFP_KERNEL); | ||
181 | if (unlikely(mfi_ctx == NULL)) | ||
182 | return -ENOMEM; | ||
183 | |||
184 | /* create workqueue for mfi callback */ | ||
185 | mfi_ctx->mfi_workqueue = alloc_workqueue("mfi_workqueue", | ||
186 | WQ_HIGHPRI | WQ_UNBOUND, 1); | ||
187 | if (!mfi_ctx->mfi_workqueue) { | ||
188 | pr_err("Failed to allocated mfi_workqueue"); | ||
189 | tegra_vi_deinit_mfi(&mfi_ctx); | ||
190 | return -ENOMEM; | ||
191 | } | ||
192 | |||
193 | chan = kcalloc(num_channels, sizeof(*chan), GFP_KERNEL); | ||
194 | if (unlikely(chan == NULL)) { | ||
195 | tegra_vi_deinit_mfi(&mfi_ctx); | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | |||
199 | mfi_ctx->num_channels = num_channels; | ||
200 | |||
201 | /* Init mfi callback work */ | ||
202 | for (i = 0; i < num_channels; i++) { | ||
203 | INIT_WORK(&chan[i].mfi_cb_work, vi_mfi_worker); | ||
204 | chan[i].channel = i; | ||
205 | } | ||
206 | |||
207 | rcu_assign_pointer(mfi_ctx->mfi_chans, chan); | ||
208 | |||
209 | *pmfi_ctx = mfi_ctx; | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | EXPORT_SYMBOL(tegra_vi_init_mfi); | ||
214 | |||
215 | void tegra_vi_deinit_mfi(struct tegra_vi_mfi_ctx **pmfi_ctx) | ||
216 | { | ||
217 | struct tegra_vi_mfi_ctx *mfi_ctx; | ||
218 | |||
219 | if (!pmfi_ctx || !*pmfi_ctx) | ||
220 | return; | ||
221 | |||
222 | mfi_ctx = *pmfi_ctx; | ||
223 | |||
224 | flush_workqueue(mfi_ctx->mfi_workqueue); | ||
225 | destroy_workqueue(mfi_ctx->mfi_workqueue); | ||
226 | |||
227 | kfree_rcu(mfi_ctx->mfi_chans, rcu); | ||
228 | |||
229 | kfree(mfi_ctx); | ||
230 | mfi_ctx = NULL; | ||
231 | } | ||
232 | EXPORT_SYMBOL(tegra_vi_deinit_mfi); | ||
233 | |||
234 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
235 | static int vi_isomgr_unregister(struct vi *tegra_vi) | ||
236 | { | ||
237 | tegra_isomgr_unregister(tegra_vi->isomgr_handle); | ||
238 | tegra_vi->isomgr_handle = NULL; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | #endif | ||
243 | |||
244 | static int vi_out_show(struct seq_file *s, void *unused) | ||
245 | { | ||
246 | struct vi *vi = s->private; | ||
247 | |||
248 | seq_printf(s, "vi overflow: %u\n", | ||
249 | atomic_read(&(vi->vi_out.overflow))); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int vi_out_open(struct inode *inode, struct file *file) | ||
255 | { | ||
256 | return single_open(file, vi_out_show, inode->i_private); | ||
257 | } | ||
258 | |||
259 | static const struct file_operations vi_out_fops = { | ||
260 | .open = vi_out_open, | ||
261 | .read = seq_read, | ||
262 | .llseek = seq_lseek, | ||
263 | .release = single_release, | ||
264 | }; | ||
265 | |||
266 | static void vi_remove_debugfs(struct vi *vi) | ||
267 | { | ||
268 | debugfs_remove_recursive(vi->debugdir); | ||
269 | vi->debugdir = NULL; | ||
270 | } | ||
271 | |||
272 | static void vi_create_debugfs(struct vi *vi) | ||
273 | { | ||
274 | struct dentry *ret; | ||
275 | char tegra_vi_name[20]; | ||
276 | char debugfs_file_name[20]; | ||
277 | |||
278 | |||
279 | snprintf(tegra_vi_name, sizeof(tegra_vi_name), "%s", TEGRA_VI_NAME); | ||
280 | |||
281 | vi->debugdir = debugfs_create_dir(tegra_vi_name, NULL); | ||
282 | if (!vi->debugdir) { | ||
283 | dev_err(&vi->ndev->dev, | ||
284 | "%s: failed to create %s directory", | ||
285 | __func__, tegra_vi_name); | ||
286 | goto create_debugfs_fail; | ||
287 | } | ||
288 | |||
289 | snprintf(debugfs_file_name, sizeof(debugfs_file_name), "%s", "vi_out"); | ||
290 | |||
291 | ret = debugfs_create_file(debugfs_file_name, S_IRUGO, | ||
292 | vi->debugdir, vi, &vi_out_fops); | ||
293 | if (!ret) { | ||
294 | dev_err(&vi->ndev->dev, | ||
295 | "%s: failed to create %s", __func__, debugfs_file_name); | ||
296 | goto create_debugfs_fail; | ||
297 | } | ||
298 | |||
299 | return; | ||
300 | |||
301 | create_debugfs_fail: | ||
302 | dev_err(&vi->ndev->dev, "%s: could not create debugfs", __func__); | ||
303 | vi_remove_debugfs(vi); | ||
304 | } | ||
305 | |||
306 | static int nvhost_vi_slcg_handler(struct notifier_block *nb, | ||
307 | unsigned long action, void *data) | ||
308 | { | ||
309 | struct clk *clk; | ||
310 | int ret = 0; | ||
311 | |||
312 | struct nvhost_device_data *pdata = | ||
313 | container_of(nb, struct nvhost_device_data, | ||
314 | toggle_slcg_notifier); | ||
315 | struct vi *tegra_vi = (struct vi *)pdata->private_data; | ||
316 | |||
317 | if (tegra_vi->mc_vi.pg_mode) | ||
318 | return NOTIFY_OK; | ||
319 | |||
320 | clk = clk_get(NULL, "pll_d"); | ||
321 | if (IS_ERR(clk)) | ||
322 | return -EINVAL; | ||
323 | |||
324 | /* Make CSI sourced from PLL_D */ | ||
325 | ret = tegra_clk_cfg_ex(clk, TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); | ||
326 | if (ret) { | ||
327 | dev_err(&pdata->pdev->dev, | ||
328 | "%s: failed to select CSI source pll_d: %d\n", | ||
329 | __func__, ret); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | /* Enable PLL_D */ | ||
334 | ret = clk_prepare_enable(clk); | ||
335 | if (ret) { | ||
336 | dev_err(&pdata->pdev->dev, "Can't enable pll_d: %d\n", ret); | ||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | udelay(1); | ||
341 | |||
342 | /* Disable PLL_D */ | ||
343 | clk_disable_unprepare(clk); | ||
344 | |||
345 | /* Restore CSI source */ | ||
346 | ret = tegra_clk_cfg_ex(clk, TEGRA_CLK_MIPI_CSI_OUT_ENB, 1); | ||
347 | if (ret) { | ||
348 | dev_err(&pdata->pdev->dev, | ||
349 | "%s: failed to restore csi source: %d\n", | ||
350 | __func__, ret); | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | clk_put(clk); | ||
355 | |||
356 | return NOTIFY_OK; | ||
357 | } | ||
358 | |||
359 | static int vi_probe(struct platform_device *dev) | ||
360 | { | ||
361 | int err = 0; | ||
362 | struct nvhost_device_data *pdata = NULL; | ||
363 | struct tegra_t210_vi_data *data = NULL; | ||
364 | u8 num_channels; | ||
365 | |||
366 | if (dev->dev.of_node) { | ||
367 | const struct of_device_id *match; | ||
368 | |||
369 | match = of_match_device(tegra_vi_of_match, &dev->dev); | ||
370 | if (match) { | ||
371 | data = (struct tegra_t210_vi_data *)match->data; | ||
372 | pdata = data->info; | ||
373 | dev->dev.platform_data = pdata; | ||
374 | } | ||
375 | /* DT initializes it to -1, use below WAR to set correct value. | ||
376 | * TODO: Once proper fix for dev-id goes in, remove it. | ||
377 | */ | ||
378 | dev->id = dev->dev.id; | ||
379 | } else | ||
380 | pdata = (struct nvhost_device_data *)dev->dev.platform_data; | ||
381 | |||
382 | WARN_ON(!pdata); | ||
383 | if (!pdata) { | ||
384 | dev_info(&dev->dev, "no platform data\n"); | ||
385 | return -ENODATA; | ||
386 | } | ||
387 | |||
388 | err = nvhost_check_bondout(pdata->bond_out_id); | ||
389 | if (err) { | ||
390 | dev_warn(&dev->dev, "No VI unit present. err:%d", err); | ||
391 | return err; | ||
392 | } | ||
393 | |||
394 | pdata->pdev = dev; | ||
395 | mutex_init(&pdata->lock); | ||
396 | platform_set_drvdata(dev, pdata); | ||
397 | |||
398 | dev_info(&dev->dev, "%s: ++\n", __func__); | ||
399 | |||
400 | tegra_vi = devm_kzalloc(&dev->dev, sizeof(struct vi), GFP_KERNEL); | ||
401 | if (!tegra_vi) | ||
402 | return -ENOMEM; | ||
403 | |||
404 | tegra_vi->ndev = dev; | ||
405 | tegra_vi->dev = &dev->dev; | ||
406 | err = nvhost_client_device_get_resources(dev); | ||
407 | if (err) | ||
408 | goto vi_probe_fail; | ||
409 | |||
410 | num_channels = 6; | ||
411 | |||
412 | err = tegra_vi_init_mfi(&tegra_vi->mfi_ctx, num_channels); | ||
413 | if (err) | ||
414 | goto vi_probe_fail; | ||
415 | |||
416 | if (!pdata->aperture[0]) { | ||
417 | dev_err(&dev->dev, "%s: failed to map register base\n", | ||
418 | __func__); | ||
419 | return -ENXIO; | ||
420 | } | ||
421 | |||
422 | /* call vi_intr_init and stats_work */ | ||
423 | INIT_WORK(&tegra_vi->stats_work, vi_stats_worker); | ||
424 | |||
425 | err = vi_intr_init(tegra_vi); | ||
426 | if (err) | ||
427 | goto vi_mfi_init_fail; | ||
428 | |||
429 | vi_create_debugfs(tegra_vi); | ||
430 | |||
431 | i2c_ctrl = pdata->private_data; | ||
432 | pdata->private_data = tegra_vi; | ||
433 | mutex_init(&tegra_vi->update_la_lock); | ||
434 | |||
435 | /* Create I2C Devices according to settings from board file */ | ||
436 | if (i2c_ctrl && i2c_ctrl->new_devices) | ||
437 | i2c_ctrl->new_devices(dev); | ||
438 | |||
439 | tegra_vi->reg = regulator_get(&tegra_vi->ndev->dev, "avdd_dsi_csi"); | ||
440 | if (IS_ERR(tegra_vi->reg)) { | ||
441 | err = PTR_ERR(tegra_vi->reg); | ||
442 | if (err == -ENODEV) | ||
443 | dev_info(&tegra_vi->ndev->dev, | ||
444 | "%s: no regulator device\n", __func__); | ||
445 | else | ||
446 | dev_err(&tegra_vi->ndev->dev, | ||
447 | "%s: couldn't get regulator\n", __func__); | ||
448 | tegra_vi->reg = NULL; | ||
449 | if (tegra_platform_is_silicon()) | ||
450 | goto camera_i2c_unregister; | ||
451 | } | ||
452 | |||
453 | #ifdef CONFIG_TEGRA_CAMERA | ||
454 | tegra_vi->camera = tegra_camera_register(dev); | ||
455 | if (!tegra_vi->camera) { | ||
456 | dev_err(&dev->dev, "%s: can't register tegra_camera\n", | ||
457 | __func__); | ||
458 | goto vi_regulator_put; | ||
459 | } | ||
460 | #endif | ||
461 | |||
462 | if (pdata->slcg_notifier_enable && | ||
463 | (pdata->powergate_id != -1)) { | ||
464 | pdata->toggle_slcg_notifier.notifier_call = | ||
465 | &nvhost_vi_slcg_handler; | ||
466 | |||
467 | slcg_register_notifier(pdata->powergate_id, | ||
468 | &pdata->toggle_slcg_notifier); | ||
469 | } | ||
470 | |||
471 | nvhost_module_init(dev); | ||
472 | |||
473 | err = nvhost_client_device_init(dev); | ||
474 | if (err) | ||
475 | goto camera_unregister; | ||
476 | |||
477 | tegra_vi->mc_vi.vi = tegra_vi; | ||
478 | tegra_vi->mc_vi.csi = &tegra_vi->csi; | ||
479 | tegra_vi->mc_vi.reg = tegra_vi->reg; | ||
480 | tegra_vi->mc_vi.fops = data->vi_fops; | ||
481 | tegra_vi->csi.fops = data->csi_fops; | ||
482 | err = tegra_csi_media_controller_init(&tegra_vi->csi, dev); | ||
483 | if (err) | ||
484 | goto vi_mc_init_error; | ||
485 | |||
486 | err = tegra_vi_media_controller_init(&tegra_vi->mc_vi, dev); | ||
487 | if (err) | ||
488 | goto vi_mc_init_error; | ||
489 | |||
490 | return 0; | ||
491 | |||
492 | vi_mc_init_error: | ||
493 | nvhost_client_device_release(dev); | ||
494 | camera_unregister: | ||
495 | #ifdef CONFIG_TEGRA_CAMERA | ||
496 | tegra_camera_unregister(tegra_vi->camera); | ||
497 | vi_regulator_put: | ||
498 | #endif | ||
499 | |||
500 | regulator_put(tegra_vi->reg); | ||
501 | tegra_vi->reg = NULL; | ||
502 | |||
503 | camera_i2c_unregister: | ||
504 | if (i2c_ctrl && i2c_ctrl->remove_devices) | ||
505 | i2c_ctrl->remove_devices(dev); | ||
506 | pdata->private_data = i2c_ctrl; | ||
507 | vi_mfi_init_fail: | ||
508 | tegra_vi_deinit_mfi(&tegra_vi->mfi_ctx); | ||
509 | vi_probe_fail: | ||
510 | dev_err(&dev->dev, "%s: failed\n", __func__); | ||
511 | return err; | ||
512 | } | ||
513 | |||
514 | static int __exit vi_remove(struct platform_device *dev) | ||
515 | { | ||
516 | #ifdef CONFIG_TEGRA_CAMERA | ||
517 | int err = 0; | ||
518 | #endif | ||
519 | struct nvhost_device_data *pdata = platform_get_drvdata(dev); | ||
520 | struct vi *tegra_vi = (struct vi *)pdata->private_data; | ||
521 | |||
522 | #ifdef CONFIG_PM | ||
523 | if (atomic_read(&dev->dev.power.usage_count) > 0) | ||
524 | return -EBUSY; | ||
525 | #endif | ||
526 | |||
527 | dev_info(&dev->dev, "%s: ++\n", __func__); | ||
528 | |||
529 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
530 | if (tegra_vi->isomgr_handle) | ||
531 | vi_isomgr_unregister(tegra_vi); | ||
532 | #endif | ||
533 | |||
534 | tegra_vi_deinit_mfi(&tegra_vi->mfi_ctx); | ||
535 | |||
536 | vi_remove_debugfs(tegra_vi); | ||
537 | |||
538 | tegra_vi_media_controller_cleanup(&tegra_vi->mc_vi); | ||
539 | |||
540 | nvhost_client_device_release(dev); | ||
541 | |||
542 | if (pdata->slcg_notifier_enable && | ||
543 | (pdata->powergate_id != -1)) | ||
544 | slcg_unregister_notifier(pdata->powergate_id, | ||
545 | &pdata->toggle_slcg_notifier); | ||
546 | |||
547 | vi_intr_free(tegra_vi); | ||
548 | |||
549 | pdata->aperture[0] = NULL; | ||
550 | #ifdef CONFIG_TEGRA_CAMERA | ||
551 | err = tegra_camera_unregister(tegra_vi->camera); | ||
552 | if (err) | ||
553 | return err; | ||
554 | #endif | ||
555 | |||
556 | #ifdef CONFIG_PM_GENERIC_DOMAINS | ||
557 | tegra_pd_remove_device(&dev->dev); | ||
558 | #endif | ||
559 | |||
560 | regulator_put(tegra_vi->reg); | ||
561 | tegra_vi->reg = NULL; | ||
562 | |||
563 | /* Remove I2C Devices according to settings from board file */ | ||
564 | if (i2c_ctrl && i2c_ctrl->remove_devices) | ||
565 | i2c_ctrl->remove_devices(dev); | ||
566 | |||
567 | pdata->private_data = i2c_ctrl; | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | static struct platform_driver vi_driver = { | ||
573 | .probe = vi_probe, | ||
574 | .remove = __exit_p(vi_remove), | ||
575 | .driver = { | ||
576 | .owner = THIS_MODULE, | ||
577 | .name = "vi", | ||
578 | #ifdef CONFIG_PM | ||
579 | .pm = &nvhost_module_pm_ops, | ||
580 | #endif | ||
581 | #ifdef CONFIG_OF | ||
582 | .of_match_table = tegra_vi_of_match, | ||
583 | #endif | ||
584 | } | ||
585 | }; | ||
586 | |||
587 | static struct of_device_id tegra_vi_domain_match[] = { | ||
588 | {.compatible = "nvidia,tegra210-ve-pd", | ||
589 | .data = (struct nvhost_device_data *)&t21_vi_info}, | ||
590 | {}, | ||
591 | }; | ||
592 | |||
593 | static int __init vi_init(void) | ||
594 | { | ||
595 | int ret; | ||
596 | |||
597 | ret = nvhost_domain_init(tegra_vi_domain_match); | ||
598 | if (ret) | ||
599 | return ret; | ||
600 | |||
601 | return platform_driver_register(&vi_driver); | ||
602 | } | ||
603 | |||
604 | static void __exit vi_exit(void) | ||
605 | { | ||
606 | platform_driver_unregister(&vi_driver); | ||
607 | } | ||
608 | |||
609 | late_initcall(vi_init); | ||
610 | module_exit(vi_exit); | ||
611 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/media/platform/tegra/vi/vi.h b/drivers/media/platform/tegra/vi/vi.h new file mode 100644 index 000000000..a03057265 --- /dev/null +++ b/drivers/media/platform/tegra/vi/vi.h | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/vi/vi.h | ||
3 | * | ||
4 | * Tegra Graphics Host VI | ||
5 | * | ||
6 | * Copyright (c) 2012-2016, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #ifndef __NVHOST_VI_H__ | ||
22 | #define __NVHOST_VI_H__ | ||
23 | |||
24 | #include <linux/platform/tegra/isomgr.h> | ||
25 | #include <linux/tegra-powergate.h> | ||
26 | #include <linux/clk/tegra.h> | ||
27 | |||
28 | #include "camera/vi/mc_common.h" | ||
29 | #include "chip_support.h" | ||
30 | |||
31 | #define VI_CFG_INTERRUPT_MASK_0 0x8c | ||
32 | #define VI_CFG_INTERRUPT_STATUS_0 0x98 | ||
33 | |||
34 | #define CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0 0x850 | ||
35 | #define CSI_CSI_PIXEL_PARSER_A_STATUS_0 0x854 | ||
36 | #define PPA_FIFO_OVRF (1 << 5) | ||
37 | |||
38 | #define CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0 0x884 | ||
39 | #define CSI_CSI_PIXEL_PARSER_B_STATUS_0 0x888 | ||
40 | #define PPB_FIFO_OVRF (1 << 5) | ||
41 | |||
42 | #define VI_CSI_0_ERROR_STATUS 0x184 | ||
43 | #define VI_CSI_1_ERROR_STATUS 0x284 | ||
44 | #define VI_CSI_0_WD_CTRL 0x18c | ||
45 | #define VI_CSI_1_WD_CTRL 0x28c | ||
46 | #define VI_CSI_0_ERROR_INT_MASK_0 0x188 | ||
47 | #define VI_CSI_1_ERROR_INT_MASK_0 0x288 | ||
48 | |||
49 | #ifdef TEGRA_21X_OR_HIGHER_CONFIG | ||
50 | #define VI_CSI_2_ERROR_STATUS 0x384 | ||
51 | #define VI_CSI_3_ERROR_STATUS 0x484 | ||
52 | #define VI_CSI_4_ERROR_STATUS 0x584 | ||
53 | #define VI_CSI_5_ERROR_STATUS 0x684 | ||
54 | |||
55 | #define VI_CSI_2_WD_CTRL 0x38c | ||
56 | #define VI_CSI_3_WD_CTRL 0x48c | ||
57 | #define VI_CSI_4_WD_CTRL 0x58c | ||
58 | #define VI_CSI_5_WD_CTRL 0x68c | ||
59 | |||
60 | #define VI_CSI_2_ERROR_INT_MASK_0 0x388 | ||
61 | #define VI_CSI_3_ERROR_INT_MASK_0 0x488 | ||
62 | #define VI_CSI_4_ERROR_INT_MASK_0 0x588 | ||
63 | #define VI_CSI_5_ERROR_INT_MASK_0 0x688 | ||
64 | |||
65 | #define CSI1_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0 0x1050 | ||
66 | #define CSI1_CSI_PIXEL_PARSER_A_STATUS_0 0x1054 | ||
67 | #define CSI1_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0 0x1084 | ||
68 | #define CSI1_CSI_PIXEL_PARSER_B_STATUS_0 0x1088 | ||
69 | #define CSI2_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0 0x1850 | ||
70 | #define CSI2_CSI_PIXEL_PARSER_A_STATUS_0 0x1854 | ||
71 | #define CSI2_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0 0x1884 | ||
72 | #define CSI2_CSI_PIXEL_PARSER_B_STATUS_0 0x1888 | ||
73 | |||
74 | #define NUM_VI_WATCHDOG 6 | ||
75 | #else | ||
76 | #define NUM_VI_WATCHDOG 2 | ||
77 | #endif | ||
78 | |||
79 | typedef void (*callback)(void *); | ||
80 | |||
81 | struct tegra_vi_stats { | ||
82 | atomic_t overflow; | ||
83 | }; | ||
84 | |||
85 | struct tegra_vi_mfi_ctx; | ||
86 | |||
87 | struct vi { | ||
88 | struct tegra_camera *camera; | ||
89 | struct platform_device *ndev; | ||
90 | struct device *dev; | ||
91 | struct tegra_vi_data *data; | ||
92 | struct tegra_mc_vi mc_vi; | ||
93 | struct tegra_csi_device csi; | ||
94 | |||
95 | struct regulator *reg; | ||
96 | struct dentry *debugdir; | ||
97 | struct tegra_vi_stats vi_out; | ||
98 | struct work_struct stats_work; | ||
99 | struct tegra_vi_mfi_ctx *mfi_ctx; | ||
100 | #if defined(CONFIG_TEGRA_ISOMGR) | ||
101 | tegra_isomgr_handle isomgr_handle; | ||
102 | #endif | ||
103 | int vi_irq; | ||
104 | uint vi_bypass_bw; | ||
105 | uint max_bw; | ||
106 | struct mutex update_la_lock; | ||
107 | bool master_deinitialized; | ||
108 | bool tpg_opened; | ||
109 | bool sensor_opened; | ||
110 | bool bypass; | ||
111 | }; | ||
112 | |||
113 | extern const struct file_operations tegra_vi_ctrl_ops; | ||
114 | int nvhost_vi_prepare_poweroff(struct platform_device *); | ||
115 | int nvhost_vi_finalize_poweron(struct platform_device *); | ||
116 | |||
117 | void nvhost_vi_reset_all(struct platform_device *); | ||
118 | struct vi *tegra_vi_get(void); | ||
119 | int vi_v4l2_set_la(struct vi *tegra_vi, u32 vi_bypass_bw, bool is_ioctl); | ||
120 | |||
121 | int tegra_vi_register_mfi_cb(callback cb, void *cb_arg); | ||
122 | int tegra_vi_unregister_mfi_cb(void); | ||
123 | |||
124 | bool tegra_vi_has_mfi_callback(void); | ||
125 | int tegra_vi_mfi_event_notify(struct tegra_vi_mfi_ctx *mfi_ctx, u8 channel); | ||
126 | int tegra_vi_init_mfi(struct tegra_vi_mfi_ctx **mfi_ctx, u8 num_channels); | ||
127 | void tegra_vi_deinit_mfi(struct tegra_vi_mfi_ctx **mfi_ctx); | ||
128 | #endif | ||
diff --git a/drivers/media/platform/tegra/vi/vi_irq.c b/drivers/media/platform/tegra/vi/vi_irq.c new file mode 100644 index 000000000..9f3cc3f3c --- /dev/null +++ b/drivers/media/platform/tegra/vi/vi_irq.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/vi/vi_irq.c | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/nvhost.h> | ||
20 | |||
21 | #include "nvhost_acm.h" | ||
22 | #include "vi/vi.h" | ||
23 | #include "vi/vi_irq.h" | ||
24 | |||
25 | static const int status_reg_table[] = { | ||
26 | VI_CFG_INTERRUPT_STATUS_0, | ||
27 | CSI_CSI_PIXEL_PARSER_A_STATUS_0, | ||
28 | CSI_CSI_PIXEL_PARSER_B_STATUS_0, | ||
29 | VI_CSI_0_ERROR_STATUS, | ||
30 | VI_CSI_1_ERROR_STATUS, | ||
31 | #ifdef TEGRA_21X_OR_HIGHER_CONFIG | ||
32 | VI_CSI_2_ERROR_STATUS, | ||
33 | VI_CSI_3_ERROR_STATUS, | ||
34 | VI_CSI_4_ERROR_STATUS, | ||
35 | VI_CSI_5_ERROR_STATUS, | ||
36 | CSI1_CSI_PIXEL_PARSER_A_STATUS_0, | ||
37 | CSI1_CSI_PIXEL_PARSER_B_STATUS_0, | ||
38 | CSI2_CSI_PIXEL_PARSER_A_STATUS_0, | ||
39 | CSI2_CSI_PIXEL_PARSER_B_STATUS_0, | ||
40 | #endif | ||
41 | }; | ||
42 | |||
43 | static const int mask_reg_table[] = { | ||
44 | VI_CFG_INTERRUPT_MASK_0, | ||
45 | CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0, | ||
46 | CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0, | ||
47 | VI_CSI_0_ERROR_INT_MASK_0, | ||
48 | VI_CSI_1_ERROR_INT_MASK_0, | ||
49 | VI_CSI_0_WD_CTRL, | ||
50 | VI_CSI_1_WD_CTRL, | ||
51 | #ifdef TEGRA_21X_OR_HIGHER_CONFIG | ||
52 | VI_CSI_2_WD_CTRL, | ||
53 | VI_CSI_3_WD_CTRL, | ||
54 | VI_CSI_4_WD_CTRL, | ||
55 | VI_CSI_5_WD_CTRL, | ||
56 | VI_CSI_2_ERROR_INT_MASK_0, | ||
57 | VI_CSI_3_ERROR_INT_MASK_0, | ||
58 | VI_CSI_4_ERROR_INT_MASK_0, | ||
59 | VI_CSI_5_ERROR_INT_MASK_0, | ||
60 | CSI1_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0, | ||
61 | CSI1_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0, | ||
62 | CSI2_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0, | ||
63 | CSI2_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0, | ||
64 | #endif | ||
65 | }; | ||
66 | |||
67 | static inline void clear_state(struct vi *tegra_vi, int addr) | ||
68 | { | ||
69 | int val; | ||
70 | |||
71 | val = host1x_readl(tegra_vi->ndev, addr); | ||
72 | host1x_writel(tegra_vi->ndev, addr, val); | ||
73 | } | ||
74 | |||
75 | int vi_enable_irq(struct vi *tegra_vi) | ||
76 | { | ||
77 | int i; | ||
78 | |||
79 | /* Mask all VI interrupt */ | ||
80 | for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++) | ||
81 | host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0); | ||
82 | |||
83 | /* Clear all VI interrupt state registers */ | ||
84 | for (i = 0; i < ARRAY_SIZE(status_reg_table); i++) | ||
85 | clear_state(tegra_vi, status_reg_table[i]); | ||
86 | |||
87 | enable_irq(tegra_vi->vi_irq); | ||
88 | |||
89 | return 0; | ||
90 | |||
91 | } | ||
92 | EXPORT_SYMBOL(vi_enable_irq); | ||
93 | |||
94 | int vi_disable_irq(struct vi *tegra_vi) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | disable_irq(tegra_vi->vi_irq); | ||
99 | |||
100 | /* Mask all VI interrupt */ | ||
101 | for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++) | ||
102 | host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0); | ||
103 | |||
104 | /* Clear all VI interrupt state registers */ | ||
105 | for (i = 0; i < ARRAY_SIZE(status_reg_table); i++) | ||
106 | clear_state(tegra_vi, status_reg_table[i]); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | EXPORT_SYMBOL(vi_disable_irq); | ||
111 | |||
112 | static irqreturn_t vi_checkwd(struct vi *tegra_vi, int stream) | ||
113 | { | ||
114 | int err_addr, wd_addr, val; | ||
115 | |||
116 | switch (stream) { | ||
117 | case 0: | ||
118 | err_addr = VI_CSI_0_ERROR_STATUS; | ||
119 | wd_addr = VI_CSI_0_WD_CTRL; | ||
120 | break; | ||
121 | case 1: | ||
122 | err_addr = VI_CSI_1_ERROR_STATUS; | ||
123 | wd_addr = VI_CSI_1_WD_CTRL; | ||
124 | break; | ||
125 | #ifdef TEGRA_21X_OR_HIGHER_CONFIG | ||
126 | case 2: | ||
127 | err_addr = VI_CSI_2_ERROR_STATUS; | ||
128 | wd_addr = VI_CSI_2_WD_CTRL; | ||
129 | break; | ||
130 | case 3: | ||
131 | err_addr = VI_CSI_3_ERROR_STATUS; | ||
132 | wd_addr = VI_CSI_3_WD_CTRL; | ||
133 | break; | ||
134 | case 4: | ||
135 | err_addr = VI_CSI_4_ERROR_STATUS; | ||
136 | wd_addr = VI_CSI_4_WD_CTRL; | ||
137 | break; | ||
138 | case 5: | ||
139 | err_addr = VI_CSI_5_ERROR_STATUS; | ||
140 | wd_addr = VI_CSI_5_WD_CTRL; | ||
141 | break; | ||
142 | #endif | ||
143 | default: | ||
144 | return IRQ_NONE; | ||
145 | } | ||
146 | |||
147 | val = host1x_readl(tegra_vi->ndev, err_addr); | ||
148 | if (val & 0x20) { | ||
149 | host1x_writel(tegra_vi->ndev, wd_addr, 0); | ||
150 | host1x_writel(tegra_vi->ndev, err_addr, 0x20); | ||
151 | tegra_vi_mfi_event_notify(tegra_vi->mfi_ctx, stream); | ||
152 | return IRQ_HANDLED; | ||
153 | } | ||
154 | |||
155 | return IRQ_NONE; | ||
156 | } | ||
157 | |||
158 | static irqreturn_t vi_isr(int irq, void *dev_id) | ||
159 | { | ||
160 | struct vi *tegra_vi = (struct vi *)dev_id; | ||
161 | int i, val; | ||
162 | irqreturn_t result; | ||
163 | |||
164 | for (i = 0; i < NUM_VI_WATCHDOG; i++) { | ||
165 | result = vi_checkwd(tegra_vi, i); | ||
166 | if (result == IRQ_HANDLED) | ||
167 | goto handled; | ||
168 | } | ||
169 | |||
170 | dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__); | ||
171 | |||
172 | val = host1x_readl(tegra_vi->ndev, CSI_CSI_PIXEL_PARSER_A_STATUS_0); | ||
173 | |||
174 | /* changes required as per t124 register spec */ | ||
175 | if (val & PPA_FIFO_OVRF) | ||
176 | atomic_inc(&(tegra_vi->vi_out.overflow)); | ||
177 | |||
178 | /* Disable IRQ */ | ||
179 | vi_disable_irq(tegra_vi); | ||
180 | |||
181 | schedule_work(&tegra_vi->stats_work); | ||
182 | |||
183 | handled: | ||
184 | return IRQ_HANDLED; | ||
185 | } | ||
186 | EXPORT_SYMBOL(vi_isr); | ||
187 | |||
188 | void vi_stats_worker(struct work_struct *work) | ||
189 | { | ||
190 | struct vi *tegra_vi = container_of(work, struct vi, stats_work); | ||
191 | |||
192 | dev_dbg(&tegra_vi->ndev->dev, | ||
193 | "%s: vi_out dropped data %u times", __func__, | ||
194 | atomic_read(&(tegra_vi->vi_out.overflow))); | ||
195 | |||
196 | /* Enable IRQ's */ | ||
197 | vi_enable_irq(tegra_vi); | ||
198 | } | ||
199 | EXPORT_SYMBOL(vi_stats_worker); | ||
200 | |||
201 | int vi_intr_init(struct vi *tegra_vi) | ||
202 | { | ||
203 | struct platform_device *ndev = tegra_vi->ndev; | ||
204 | |||
205 | /* Interrupt resources are only associated with | ||
206 | * master dev vi.0 so irq must be programmed | ||
207 | * with it only. | ||
208 | */ | ||
209 | int ret; | ||
210 | |||
211 | dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__); | ||
212 | |||
213 | tegra_vi->vi_irq = platform_get_irq(ndev, 0); | ||
214 | if (IS_ERR_VALUE(tegra_vi->vi_irq)) { | ||
215 | dev_err(&tegra_vi->ndev->dev, "missing camera irq\n"); | ||
216 | return -ENXIO; | ||
217 | } | ||
218 | |||
219 | ret = request_irq(tegra_vi->vi_irq, | ||
220 | vi_isr, | ||
221 | IRQF_SHARED, | ||
222 | dev_name(&tegra_vi->ndev->dev), | ||
223 | tegra_vi); | ||
224 | if (ret) { | ||
225 | dev_err(&tegra_vi->ndev->dev, "failed to get irq\n"); | ||
226 | return -EBUSY; | ||
227 | } | ||
228 | |||
229 | disable_irq(tegra_vi->vi_irq); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | EXPORT_SYMBOL(vi_intr_init); | ||
234 | |||
235 | int vi_intr_free(struct vi *tegra_vi) | ||
236 | { | ||
237 | dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__); | ||
238 | |||
239 | free_irq(tegra_vi->vi_irq, tegra_vi); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | EXPORT_SYMBOL(vi_intr_free); | ||
244 | |||
diff --git a/drivers/media/platform/tegra/vi/vi_irq.h b/drivers/media/platform/tegra/vi/vi_irq.h new file mode 100644 index 000000000..26fed8a00 --- /dev/null +++ b/drivers/media/platform/tegra/vi/vi_irq.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/vi/vi_irq.h | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __DRIVERS_VIDEO_VI_IRQ_H | ||
18 | #define __DRIVERS_VIDEO_VI_IRQ_H | ||
19 | |||
20 | int vi_intr_init(struct vi *vi); | ||
21 | int vi_intr_free(struct vi *vi); | ||
22 | void vi_stats_worker(struct work_struct *work); | ||
23 | int vi_enable_irq(struct vi *vi); | ||
24 | int vi_disable_irq(struct vi *vi); | ||
25 | #endif | ||
26 | |||