.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012-2014 Mentor Graphics Inc. |
---|
3 | 4 | * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify it |
---|
6 | | - * under the terms of the GNU General Public License as published by the |
---|
7 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
8 | | - * option) any later version. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, but |
---|
11 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
---|
12 | | - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
---|
13 | | - * for more details. |
---|
14 | 5 | */ |
---|
15 | 6 | #include <linux/export.h> |
---|
16 | 7 | #include <linux/module.h> |
---|
.. | .. |
---|
232 | 223 | case MEDIA_BUS_FMT_BGR565_2X8_LE: |
---|
233 | 224 | case MEDIA_BUS_FMT_RGB565_2X8_BE: |
---|
234 | 225 | case MEDIA_BUS_FMT_RGB565_2X8_LE: |
---|
235 | | - if (mbus_type == V4L2_MBUS_CSI2) |
---|
| 226 | + if (mbus_type == V4L2_MBUS_CSI2_DPHY) |
---|
236 | 227 | cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; |
---|
237 | 228 | else |
---|
238 | 229 | cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; |
---|
.. | .. |
---|
325 | 316 | return 0; |
---|
326 | 317 | } |
---|
327 | 318 | |
---|
| 319 | +/* translate alternate field mode based on given standard */ |
---|
| 320 | +static inline enum v4l2_field |
---|
| 321 | +ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) |
---|
| 322 | +{ |
---|
| 323 | + return (field != V4L2_FIELD_ALTERNATE) ? field : |
---|
| 324 | + ((std & V4L2_STD_525_60) ? |
---|
| 325 | + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); |
---|
| 326 | +} |
---|
| 327 | + |
---|
328 | 328 | /* |
---|
329 | 329 | * Fill a CSI bus config struct from mbus_config and mbus_framefmt. |
---|
330 | 330 | */ |
---|
331 | 331 | static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, |
---|
332 | | - struct v4l2_mbus_config *mbus_cfg, |
---|
333 | | - struct v4l2_mbus_framefmt *mbus_fmt) |
---|
| 332 | + const struct v4l2_mbus_config *mbus_cfg, |
---|
| 333 | + const struct v4l2_mbus_framefmt *mbus_fmt) |
---|
334 | 334 | { |
---|
335 | 335 | int ret; |
---|
336 | 336 | |
---|
.. | .. |
---|
359 | 359 | else |
---|
360 | 360 | csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; |
---|
361 | 361 | break; |
---|
362 | | - case V4L2_MBUS_CSI2: |
---|
| 362 | + case V4L2_MBUS_CSI2_DPHY: |
---|
363 | 363 | /* |
---|
364 | 364 | * MIPI CSI-2 requires non gated clock mode, all other |
---|
365 | 365 | * parameters are not applicable for MIPI CSI-2 bus. |
---|
.. | .. |
---|
374 | 374 | return 0; |
---|
375 | 375 | } |
---|
376 | 376 | |
---|
| 377 | +static int |
---|
| 378 | +ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, |
---|
| 379 | + const struct v4l2_mbus_framefmt *infmt, |
---|
| 380 | + const struct v4l2_mbus_framefmt *outfmt, |
---|
| 381 | + v4l2_std_id std) |
---|
| 382 | +{ |
---|
| 383 | + enum v4l2_field infield, outfield; |
---|
| 384 | + bool swap_fields; |
---|
| 385 | + |
---|
| 386 | + /* get translated field type of input and output */ |
---|
| 387 | + infield = ipu_csi_translate_field(infmt->field, std); |
---|
| 388 | + outfield = ipu_csi_translate_field(outfmt->field, std); |
---|
| 389 | + |
---|
| 390 | + /* |
---|
| 391 | + * Write the H-V-F codes the CSI will match against the |
---|
| 392 | + * incoming data for start/end of active and blanking |
---|
| 393 | + * field intervals. If input and output field types are |
---|
| 394 | + * sequential but not the same (one is SEQ_BT and the other |
---|
| 395 | + * is SEQ_TB), swap the F-bit so that the CSI will capture |
---|
| 396 | + * field 1 lines before field 0 lines. |
---|
| 397 | + */ |
---|
| 398 | + swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && |
---|
| 399 | + V4L2_FIELD_IS_SEQUENTIAL(outfield) && |
---|
| 400 | + infield != outfield); |
---|
| 401 | + |
---|
| 402 | + if (!swap_fields) { |
---|
| 403 | + /* |
---|
| 404 | + * Field0BlankEnd = 110, Field0BlankStart = 010 |
---|
| 405 | + * Field0ActiveEnd = 100, Field0ActiveStart = 000 |
---|
| 406 | + * Field1BlankEnd = 111, Field1BlankStart = 011 |
---|
| 407 | + * Field1ActiveEnd = 101, Field1ActiveStart = 001 |
---|
| 408 | + */ |
---|
| 409 | + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, |
---|
| 410 | + CSI_CCIR_CODE_1); |
---|
| 411 | + ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); |
---|
| 412 | + } else { |
---|
| 413 | + dev_dbg(csi->ipu->dev, "capture field swap\n"); |
---|
| 414 | + |
---|
| 415 | + /* same as above but with F-bit inverted */ |
---|
| 416 | + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, |
---|
| 417 | + CSI_CCIR_CODE_1); |
---|
| 418 | + ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); |
---|
| 419 | + } |
---|
| 420 | + |
---|
| 421 | + ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); |
---|
| 422 | + |
---|
| 423 | + return 0; |
---|
| 424 | +} |
---|
| 425 | + |
---|
| 426 | + |
---|
377 | 427 | int ipu_csi_init_interface(struct ipu_csi *csi, |
---|
378 | | - struct v4l2_mbus_config *mbus_cfg, |
---|
379 | | - struct v4l2_mbus_framefmt *mbus_fmt) |
---|
| 428 | + const struct v4l2_mbus_config *mbus_cfg, |
---|
| 429 | + const struct v4l2_mbus_framefmt *infmt, |
---|
| 430 | + const struct v4l2_mbus_framefmt *outfmt) |
---|
380 | 431 | { |
---|
381 | 432 | struct ipu_csi_bus_config cfg; |
---|
382 | 433 | unsigned long flags; |
---|
383 | 434 | u32 width, height, data = 0; |
---|
| 435 | + v4l2_std_id std; |
---|
384 | 436 | int ret; |
---|
385 | 437 | |
---|
386 | | - ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); |
---|
| 438 | + ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); |
---|
387 | 439 | if (ret < 0) |
---|
388 | 440 | return ret; |
---|
389 | 441 | |
---|
390 | 442 | /* set default sensor frame width and height */ |
---|
391 | | - width = mbus_fmt->width; |
---|
392 | | - height = mbus_fmt->height; |
---|
| 443 | + width = infmt->width; |
---|
| 444 | + height = infmt->height; |
---|
| 445 | + if (infmt->field == V4L2_FIELD_ALTERNATE) |
---|
| 446 | + height *= 2; |
---|
393 | 447 | |
---|
394 | 448 | /* Set the CSI_SENS_CONF register remaining fields */ |
---|
395 | 449 | data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | |
---|
.. | .. |
---|
416 | 470 | ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); |
---|
417 | 471 | break; |
---|
418 | 472 | case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: |
---|
419 | | - if (mbus_fmt->width == 720 && mbus_fmt->height == 576) { |
---|
420 | | - /* |
---|
421 | | - * PAL case |
---|
422 | | - * |
---|
423 | | - * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, |
---|
424 | | - * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 |
---|
425 | | - * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, |
---|
426 | | - * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 |
---|
427 | | - */ |
---|
428 | | - height = 625; /* framelines for PAL */ |
---|
429 | | - |
---|
430 | | - ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, |
---|
431 | | - CSI_CCIR_CODE_1); |
---|
432 | | - ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); |
---|
433 | | - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); |
---|
434 | | - } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) { |
---|
435 | | - /* |
---|
436 | | - * NTSC case |
---|
437 | | - * |
---|
438 | | - * Field0BlankEnd = 0x7, Field0BlankStart = 0x3, |
---|
439 | | - * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1 |
---|
440 | | - * Field1BlankEnd = 0x6, Field1BlankStart = 0x2, |
---|
441 | | - * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 |
---|
442 | | - */ |
---|
443 | | - height = 525; /* framelines for NTSC */ |
---|
444 | | - |
---|
445 | | - ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, |
---|
446 | | - CSI_CCIR_CODE_1); |
---|
447 | | - ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); |
---|
448 | | - ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); |
---|
| 473 | + if (width == 720 && height == 480) { |
---|
| 474 | + std = V4L2_STD_NTSC; |
---|
| 475 | + height = 525; |
---|
| 476 | + } else if (width == 720 && height == 576) { |
---|
| 477 | + std = V4L2_STD_PAL; |
---|
| 478 | + height = 625; |
---|
449 | 479 | } else { |
---|
450 | 480 | dev_err(csi->ipu->dev, |
---|
451 | | - "Unsupported CCIR656 interlaced video mode\n"); |
---|
452 | | - spin_unlock_irqrestore(&csi->lock, flags); |
---|
453 | | - return -EINVAL; |
---|
| 481 | + "Unsupported interlaced video mode\n"); |
---|
| 482 | + ret = -EINVAL; |
---|
| 483 | + goto out_unlock; |
---|
454 | 484 | } |
---|
| 485 | + |
---|
| 486 | + ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); |
---|
| 487 | + if (ret) |
---|
| 488 | + goto out_unlock; |
---|
455 | 489 | break; |
---|
456 | 490 | case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: |
---|
457 | 491 | case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: |
---|
.. | .. |
---|
476 | 510 | dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", |
---|
477 | 511 | ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); |
---|
478 | 512 | |
---|
| 513 | +out_unlock: |
---|
479 | 514 | spin_unlock_irqrestore(&csi->lock, flags); |
---|
480 | 515 | |
---|
481 | | - return 0; |
---|
| 516 | + return ret; |
---|
482 | 517 | } |
---|
483 | 518 | EXPORT_SYMBOL_GPL(ipu_csi_init_interface); |
---|
484 | 519 | |
---|
.. | .. |
---|
611 | 646 | if (vc > 3) |
---|
612 | 647 | return -EINVAL; |
---|
613 | 648 | |
---|
614 | | - ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2); |
---|
| 649 | + ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); |
---|
615 | 650 | if (ret < 0) |
---|
616 | 651 | return ret; |
---|
617 | 652 | |
---|