diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-05 10:42:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-05 10:42:32 -0700 |
commit | 15ac468614e5e4fee82e1eb32568f427b0e51adc (patch) | |
tree | ab0c2f7a282c390a9aa337c14d9941e963a1049a /drivers/staging | |
parent | 2784d74bcc811e9d743398da38552e6f9c73e96b (diff) | |
parent | c61480a2ea5e5b997d10dfda556d3a63e31f87cd (diff) |
Merge tag 'media/v6.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- Lots of improvement at atomisp driver, which is starting to look in
good shape
- Mediatek vcodec driver has gained support for av1 and hevc stateless
codecs
- New sensor driver: ov01a10
- verisilicon driver has gained AV1 entropy helpers
- tegra-video has gained support for Tegra20 parallel input
- dvb core has gained an extra property to better support DVB-S2X
- as usual, lots of cleanups, fixes and improvements on media drivers
* tag 'media/v6.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (253 commits)
media: wl128x: fix a clang warning
media: dvb: mb86a20s: get rid of a clang-15 warning
media: cec: i2c: ch7322: also select REGMAP
media: add HAS_IOPORT dependencies
media: tc358746: select CONFIG_GENERIC_PHY
media: mediatek: vcodec: Add dbgfs help function
media: mediatek: vcodec: Add encode to support dbgfs
media: mediatek: vcodec: Change dbgfs interface to support encode
media: mediatek: vcodec: Get each instance format type
media: mediatek: vcodec: Get each context resolution information
media: mediatek: vcodec: Add a debugfs file to get different useful information
media: mediatek: vcodec: Add debug params to control different log level
media: mediatek: vcodec: Add debugfs interface to get debug information
media: mediatek: vcodec: support stateless AV1 decoder
media: verisilicon: Conditionally ignore native formats
media: verisilicon: Enable AV1 decoder on rk3588
media: verisilicon: Add film grain feature to AV1 driver
media: verisilicon: Add Rockchip AV1 decoder
media: verisilicon: Add AV1 entropy helpers
media: verisilicon: Compute motion vectors size for AV1 frames
...
Diffstat (limited to 'drivers/staging')
56 files changed, 4044 insertions, 3646 deletions
diff --git a/drivers/staging/media/atomisp/Makefile b/drivers/staging/media/atomisp/Makefile index 532e12ed72e6..38b370124109 100644 --- a/drivers/staging/media/atomisp/Makefile +++ b/drivers/staging/media/atomisp/Makefile @@ -16,6 +16,7 @@ atomisp-objs += \ pci/atomisp_cmd.o \ pci/atomisp_compat_css20.o \ pci/atomisp_csi2.o \ + pci/atomisp_csi2_bridge.o \ pci/atomisp_drvfs.o \ pci/atomisp_fops.o \ pci/atomisp_ioctl.o \ diff --git a/drivers/staging/media/atomisp/TODO b/drivers/staging/media/atomisp/TODO index 43b842043f29..ecf8ba67b7af 100644 --- a/drivers/staging/media/atomisp/TODO +++ b/drivers/staging/media/atomisp/TODO @@ -1,213 +1,113 @@ -For both Cherrytrail (CHT) and Baytrail (BHT) the driver -requires the "candrpv_0415_20150521_0458" firmware version. -It should be noticed that the firmware file is different, -depending on the ISP model, so they're stored with different -names: +Required firmware +================= -- for BHT: /lib/firmware/shisp_2400b0_v21.bin +The atomisp driver requires the following firmware: - Warning: The driver was not tested yet for BHT. +- for BYT: /lib/firmware/shisp_2400b0_v21.bin -- for CHT: /lib/firmware/shisp_2401a0_v21.bin - - https://github.com/intel-aero/meta-intel-aero-base/blob/master/recipes-kernel/linux/linux-yocto/shisp_2401a0_v21.bin - -NOTE: -===== - -This driver currently doesn't work with most V4L2 applications, -as there are still some issues with regards to implementing -certain APIs at the standard way. - -Also, currently only USERPTR streaming mode is working. - -In order to test, it is needed to know what's the sensor's -resolution. This can be checked with: - -$ v4l2-ctl --get-fmt-video - Format Video Capture: - Width/Height : 1600/1200 - ... + With a version of "irci_stable_candrpv_0415_20150423_1753" to check + the version run: "strings shisp_2400b0_v21.bin | head -n1", sha256sum: -It is known to work with: + 3847b95fb9f1f8352c595ba7394d55b33176751372baae17f89aa483ec02a21b shisp_2400b0_v21.bin -- v4l2grab at contrib/test directory at https://git.linuxtv.org/v4l-utils.git/ + The shisp_2400b0_v21.bin file with this version can be found on + the Android factory images of various X86 Android tablets such as + e.g. the Chuwi Hi8 Pro. - The resolution should not be bigger than the max resolution - supported by the sensor, or it will fail. So, if the sensor - reports: - - The driver can be tested with: - - v4l2grab -f YUYV -x 1600 -y 1200 -d /dev/video2 -u +- for CHT: /lib/firmware/shisp_2401a0_v21.bin -- NVT at https://github.com/intel/nvt + With a version of "irci_stable_candrpv_0415_20150521_0458", sha256sum: - $ ./v4l2n -o testimage_@.raw \ - --device /dev/video2 \ - --input 0 \ - --exposure=30000,30000,30000,30000 \ - --parm type=1,capturemode=CI_MODE_PREVIEW \ - --fmt type=1,width=1600,height=1200,pixelformat=YUYV \ - --reqbufs count=2,memory=USERPTR \ - --parameters=wb_config.r=32768,wb_config.gr=21043,wb_config.gb=21043,wb_config.b=30863 \ - --capture=20 + e89359f4e4934c410c83d525e283f34c5fcce9cb5caa75ad8a32d66d3842d95c shisp_2401a0_v21.bin - As the output is in raw format, images need to be converted with: + This can be found here: + https://github.com/intel-aero/meta-intel-aero-base/blob/master/recipes-kernel/linux/linux-yocto/shisp_2401a0_v21.bin - $ for i in $(seq 0 19); do - name="testimage_$(printf "%03i" $i)" - ./raw2pnm -x$WIDTH -y$HEIGHT -f$FORMAT $name.raw $name.pnm - rm $name.raw - done TODO ==== -1. Fix support for MMAP streaming mode. This is required for most - V4L2 applications; - -2. Implement and/or fix V4L2 ioctls in order to allow a normal app to - use it; - -3. Ensure that the driver will pass v4l2-compliance tests; - -4. Get manufacturer's authorization to redistribute the binaries for - the firmware files; - -5. remove VIDEO_ATOMISP_ISP2401, making the driver to auto-detect the - register address differences between ISP2400 and ISP2401; - -6. Cleanup the driver code, removing the abstraction layers inside it; - -7. The atomisp doesn't rely at the usual i2c stuff to discover the - sensors. Instead, it calls a function from atomisp_gmin_platform.c. - There are some hacks added there for it to wait for sensors to be - probed (with a timeout of 2 seconds or so). This should be converted - to the usual way, using V4L2 async subdev framework to wait for - cameras to be probed; - -8. Switch to standard V4L2 sub-device API for sensor and lens. In - particular, the user space API needs to support V4L2 controls as - defined in the V4L2 spec and references to atomisp must be removed from - these drivers. - -9. Use LED flash API for flash LED drivers such as LM3554 (which already - has a LED class driver). - -10. Migrate the sensor drivers out of staging or re-using existing - drivers; - -11. Switch the driver to use pm_runtime stuff. Right now, it probes the - existing PMIC code and sensors call it directly. - -12. There's a problem on sensor drivers: when trying to set a video - format, the atomisp main driver calls the sensor drivers with the - sensor turned off. This causes them to fail. - - This was fixed at atomisp-ov2880, which has a hack inside it - to turn it on when VIDIOC_S_FMT is called, but this has to be - cheked on other drivers as well. - - The right fix seems to power on the sensor when a video device is - opened (or at the first VIDIOC_ ioctl - except for VIDIOC_QUERYCAP), - powering it down at close() syscall. - - Such kind of control would need to be done inside the atomisp driver, - not at the sensors code. - -13. There are several issues related to memory management, that can - cause crashes and/or memory leaks. The atomisp splits the memory - management on three separate regions: - - - dynamic pool; - - reserved pool; - - generic pool - - The code implementing it is at: - - drivers/staging/media/atomisp/pci/hmm/ +1. Items which MUST be fixed before the driver can be moved out of staging: - It also has a separate code for managing DMA buffers at: +* The atomisp ov2680 and ov5693 sensor drivers bind to the same hw-ids as + the standard ov2680 and ov5693 drivers under drivers/media/i2c, which + conflicts. Drop the atomisp private ov2680 and ov5693 drivers: + * Port various ov2680 improvements from atomisp_ov2680.c to regular ov2680.c + and switch to regular ov2680 driver + * Make atomisp work with the regular ov5693 driver and drop atomisp_ov5693 - drivers/staging/media/atomisp/pci/mmu/ +* Fix atomisp causing the whole machine to hang in its probe() error-exit + path taken in the firmware missing case - The code there is really dirty, ugly and probably wrong. I fixed - one bug there already, but the best would be to just trash it and use - something else. Maybe the code from the newer intel driver could - serve as a model: +* Remove/disable private IOCTLs - drivers/staging/media/ipu3/ipu3-mmu.c +* Remove/disable custom v4l2-ctrls - But converting it to use something like that is painful and may - cause some breakages. +* Remove custom sysfs files created by atomisp_drvfs.c -14. The file structure needs to get tidied up to resemble a normal Linux - driver. +* Remove abuse of priv field in various v4l2 userspace API structs -15. Lots of the midlayer glue. Unused code and abstraction needs removing. +* Without a 3A library the capture behaviour is not very good. To take a good + picture, the exposure/gain needs to be tuned using v4l2-ctl on the sensor + subdev. To fix this, support for the atomisp needs to be added to libcamera. -16. The AtomISP driver includes some special IOCTLS (ATOMISP_IOC_XXXX_XXXX) - and controls that require some cleanup. Some of those code may have - been removed during the cleanups. They could be needed in order to - properly support 3A algorithms. + This MUST be done before moving the driver out of staging so that we can + still make changes to e.g. the mediactl topology if necessary for + libcamera integration. Since this would be a userspace API break, this + means that at least proof-of-concept libcamera integration needs to be + ready before moving the driver out of staging. - Such IOCTL interface needs more documentation. The better would - be to use something close to the interface used by the IPU3 IMGU driver. -17. The ISP code has some dependencies of the exact FW version. - The version defined in pci/sh_css_firmware.c: +2. Items which SHOULD also be fixed eventually: - BYT (isp2400): "irci_stable_candrpv_0415_20150521_0458" +* Remove VIDEO_ATOMISP_ISP2401, making the driver to auto-detect the + register address differences between ISP2400 and ISP2401 - CHT (isp2401): "irci_ecr - master_20150911_0724" +* The driver is intended to drive the PCI exposed versions of the device. + It will not detect those devices enumerated via ACPI as a field of the + i915 GPU driver (only a problem on BYT). - Those versions don't seem to be available anymore. On the tests we've - done so far, this version also seems to work for CHT: + There are some patches adding i915 GPU support floating at the Yocto's + Aero repository (so far, untested upstream). - "irci_stable_candrpv_0415_20150521_0458" +* Ensure that the driver will pass v4l2-compliance tests - Which can be obtainable from Yocto Atom ISP respository. +* Fix not all v4l2 apps working, e.g. cheese does not work - but this was not thoroughly tested. +* Get manufacturer's authorization to redistribute the binaries for + the firmware files - At some point we may need to round up a few driver versions and see if - there are any specific things that can be done to fold in support for - multiple firmware versions. +* The atomisp code still has a lot of cruft which needs cleaning up -18. Switch from videobuf1 to videobuf2. Videobuf1 is being removed! +Testing +======= -19. Correct Coding Style. Please refrain sending coding style patches - for this driver until the other work is done, as there will be a lot - of code churn until this driver becomes functional again. +Since libcamera support is not available yet, the easiest way to test for +now is using v4l2-ctl to select the input and gstreamer for streaming. -20. Remove the logic which sets up pipelines inside it, moving it to - libcamera and implement MC support. +To select the input run: +v4l2-ctl -i <input> -Limitations -=========== +Where <input> is 0 (front cam) or 1 (back cam). -1. To test the patches, you also need the ISP firmware +The simplest gstreamer pipeline for testing running the sensor +at its max resolution is: - for BYT: /lib/firmware/shisp_2400b0_v21.bin - for CHT: /lib/firmware/shisp_2401a0_v21.bin +gst-launch-1.0 v4l2src ! videoconvert ! xvimagesink sync=false - The firmware files will usually be found in /etc/firmware on an Android - device but can also be extracted from the upgrade kit if you've managed - to lose them somehow. +To select e.g 640x480 as resolution use: -2. Without a 3A library the capture behaviour is not very good. To take a good - picture, you need tune ISP parameters by IOCTL functions or use a 3A library - such as libxcam. +gst-launch-1.0 v4l2src ! video/x-raw,format=YV12,width=640,height=480 ! \ + videoconvert ! xvimagesink sync=false -3. The driver is intended to drive the PCI exposed versions of the device. - It will not detect those devices enumerated via ACPI as a field of the - i915 GPU driver. +And to show fps use: - There are some patches adding i915 GPU support floating at the Yocto's - Aero repository (so far, untested upstream). +gst-launch-1.0 v4l2src ! video/x-raw,format=YV12,width=640,height=480 ! \ + videoconvert ! fpsdisplaysink video-sink=xvimagesink sync=false -4. The driver supports only v2 of the IPU/Camera. It will not work with the - versions of the hardware in other SoCs. +Often the image will be over / under exposed. This can be fixed by using +v4l2-ctl on the sensor subdev to tweak the exposure ctrl; or by using a GUI +app for v4l2-controls which also supports subdev such as the Fedora patched +gtk-v4l tool. diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 273155308fe3..9a11793f34f7 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -16,27 +16,259 @@ * */ -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/kmod.h> -#include <linux/device.h> #include <linux/delay.h> -#include <linux/slab.h> +#include <linux/errno.h> #include <linux/gpio/consumer.h> -#include <linux/gpio/machine.h> #include <linux/i2c.h> -#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> -#include <linux/io.h> -#include "../include/linux/atomisp_gmin_platform.h" -#include "gc0310.h" +#define GC0310_NATIVE_WIDTH 656 +#define GC0310_NATIVE_HEIGHT 496 + +#define GC0310_FPS 30 +#define GC0310_SKIP_FRAMES 3 + +#define GC0310_FOCAL_LENGTH_NUM 278 /* 2.78mm */ + +#define GC0310_ID 0xa310 + +#define GC0310_RESET_RELATED 0xFE +#define GC0310_REGISTER_PAGE_0 0x0 +#define GC0310_REGISTER_PAGE_3 0x3 + +/* + * GC0310 System control registers + */ +#define GC0310_SW_STREAM 0x10 + +#define GC0310_SC_CMMN_CHIP_ID_H 0xf0 +#define GC0310_SC_CMMN_CHIP_ID_L 0xf1 + +#define GC0310_AEC_PK_EXPO_H 0x03 +#define GC0310_AEC_PK_EXPO_L 0x04 +#define GC0310_AGC_ADJ 0x48 +#define GC0310_DGC_ADJ 0x71 +#define GC0310_GROUP_ACCESS 0x3208 + +#define GC0310_H_CROP_START_H 0x09 +#define GC0310_H_CROP_START_L 0x0A +#define GC0310_V_CROP_START_H 0x0B +#define GC0310_V_CROP_START_L 0x0C +#define GC0310_H_OUTSIZE_H 0x0F +#define GC0310_H_OUTSIZE_L 0x10 +#define GC0310_V_OUTSIZE_H 0x0D +#define GC0310_V_OUTSIZE_L 0x0E +#define GC0310_H_BLANKING_H 0x05 +#define GC0310_H_BLANKING_L 0x06 +#define GC0310_V_BLANKING_H 0x07 +#define GC0310_V_BLANKING_L 0x08 +#define GC0310_SH_DELAY 0x11 + +#define GC0310_START_STREAMING 0x94 /* 8-bit enable */ +#define GC0310_STOP_STREAMING 0x0 /* 8-bit disable */ + +#define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) + +struct gc0310_device { + struct v4l2_subdev sd; + struct media_pad pad; + /* Protect against concurrent changes to controls */ + struct mutex input_lock; + bool is_streaming; + + struct fwnode_handle *ep_fwnode; + struct gpio_desc *reset; + struct gpio_desc *powerdown; + + struct gc0310_mode { + struct v4l2_mbus_framefmt fmt; + } mode; + + struct gc0310_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + } ctrls; +}; + +struct gc0310_reg { + u8 reg; + u8 val; +}; + +static const struct gc0310_reg gc0310_reset_register[] = { + /* System registers */ + { 0xfe, 0xf0 }, + { 0xfe, 0xf0 }, + { 0xfe, 0x00 }, + + { 0xfc, 0x0e }, /* 4e */ + { 0xfc, 0x0e }, /* 16//4e // [0]apwd [6]regf_clk_gate */ + { 0xf2, 0x80 }, /* sync output */ + { 0xf3, 0x00 }, /* 1f//01 data output */ + { 0xf7, 0x33 }, /* f9 */ + { 0xf8, 0x05 }, /* 00 */ + { 0xf9, 0x0e }, /* 0x8e //0f */ + { 0xfa, 0x11 }, + + /* MIPI */ + { 0xfe, 0x03 }, + { 0x01, 0x03 }, /* mipi 1lane */ + { 0x02, 0x22 }, /* 0x33 */ + { 0x03, 0x94 }, + { 0x04, 0x01 }, /* fifo_prog */ + { 0x05, 0x00 }, /* fifo_prog */ + { 0x06, 0x80 }, /* b0 //YUV ISP data */ + { 0x11, 0x2a }, /* 1e //LDI set YUV422 */ + { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ + { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ + { 0x15, 0x12 }, /* 0x10 //DPHYY_MODE read_ready */ + { 0x17, 0x01 }, + { 0x40, 0x08 }, + { 0x41, 0x00 }, + { 0x42, 0x00 }, + { 0x43, 0x00 }, + { 0x21, 0x02 }, /* 0x01 */ + { 0x22, 0x02 }, /* 0x01 */ + { 0x23, 0x01 }, /* 0x05 //Nor:0x05 DOU:0x06 */ + { 0x29, 0x00 }, + { 0x2A, 0x25 }, /* 0x05 //data zero 0x7a de */ + { 0x2B, 0x02 }, + + { 0xfe, 0x00 }, + + /* CISCTL */ + { 0x00, 0x2f }, /* 2f//0f//02//01 */ + { 0x01, 0x0f }, /* 06 */ + { 0x02, 0x04 }, + { 0x4f, 0x00 }, /* AEC 0FF */ + { 0x03, 0x01 }, /* 0x03 //04 */ + { 0x04, 0xc0 }, /* 0xe8 //58 */ + { 0x05, 0x00 }, + { 0x06, 0xb2 }, /* 0x0a //HB */ + { 0x07, 0x00 }, + { 0x08, 0x0c }, /* 0x89 //VB */ + { 0x09, 0x00 }, /* row start */ + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, /* col start */ + { 0x0c, 0x00 }, + { 0x0d, 0x01 }, /* height */ + { 0x0e, 0xf2 }, /* 0xf7 //height */ + { 0x0f, 0x02 }, /* width */ + { 0x10, 0x94 }, /* 0xa0 //height */ + { 0x17, 0x14 }, + { 0x18, 0x1a }, /* 0a//[4]double reset */ + { 0x19, 0x14 }, /* AD pipeline */ + { 0x1b, 0x48 }, + { 0x1e, 0x6b }, /* 3b//col bias */ + { 0x1f, 0x28 }, /* 20//00//08//txlow */ + { 0x20, 0x89 }, /* 88//0c//[3:2]DA15 */ + { 0x21, 0x49 }, /* 48//[3] txhigh */ + { 0x22, 0xb0 }, + { 0x23, 0x04 }, /* [1:0]vcm_r */ + { 0x24, 0x16 }, /* 15 */ + { 0x34, 0x20 }, /* [6:4] rsg high//range */ + + /* BLK */ + { 0x26, 0x23 }, /* [1]dark_current_en [0]offset_en */ + { 0x28, 0xff }, /* BLK_limie_value */ + { 0x29, 0x00 }, /* global offset */ + { 0x33, 0x18 }, /* offset_ratio */ + { 0x37, 0x20 }, /* dark_current_ratio */ + { 0x2a, 0x00 }, + { 0x2b, 0x00 }, + { 0x2c, 0x00 }, + { 0x2d, 0x00 }, + { 0x2e, 0x00 }, + { 0x2f, 0x00 }, + { 0x30, 0x00 }, + { 0x31, 0x00 }, + { 0x47, 0x80 }, /* a7 */ + { 0x4e, 0x66 }, /* select_row */ + { 0xa8, 0x02 }, /* win_width_dark, same with crop_win_width */ + { 0xa9, 0x80 }, + + /* ISP */ + { 0x40, 0x06 }, /* 0xff //ff //48 */ + { 0x41, 0x00 }, /* 0x21 //00//[0]curve_en */ + { 0x42, 0x04 }, /* 0xcf //0a//[1]awn_en */ + { 0x44, 0x18 }, /* 0x18 //02 */ + { 0x46, 0x02 }, /* 0x03 //sync */ + { 0x49, 0x03 }, + { 0x4c, 0x20 }, /* 00[5]pretect exp */ + { 0x50, 0x01 }, /* crop enable */ + { 0x51, 0x00 }, + { 0x52, 0x00 }, + { 0x53, 0x00 }, + { 0x54, 0x01 }, + { 0x55, 0x01 }, /* crop window height */ + { 0x56, 0xf0 }, + { 0x57, 0x02 }, /* crop window width */ + { 0x58, 0x90 }, + + /* Gain */ + { 0x70, 0x70 }, /* 70 //80//global gain */ + { 0x71, 0x20 }, /* pregain gain */ + { 0x72, 0x40 }, /* post gain */ + { 0x5a, 0x84 }, /* 84//analog gain 0 */ + { 0x5b, 0xc9 }, /* c9 */ + { 0x5c, 0xed }, /* ed//not use pga gain highest level */ + { 0x77, 0x40 }, /* R gain 0x74 //awb gain */ + { 0x78, 0x40 }, /* G gain */ + { 0x79, 0x40 }, /* B gain 0x5f */ + + { 0x48, 0x00 }, + { 0xfe, 0x01 }, + { 0x0a, 0x45 }, /* [7]col gain mode */ + + { 0x3e, 0x40 }, + { 0x3f, 0x5c }, + { 0x40, 0x7b }, + { 0x41, 0xbd }, + { 0x42, 0xf6 }, + { 0x43, 0x63 }, + { 0x03, 0x60 }, + { 0x44, 0x03 }, + + /* Dark / Sun mode related */ + { 0xfe, 0x01 }, + { 0x45, 0xa4 }, /* 0xf7 */ + { 0x46, 0xf0 }, /* 0xff //f0//sun value th */ + { 0x48, 0x03 }, /* sun mode */ + { 0x4f, 0x60 }, /* sun_clamp */ + { 0xfe, 0x00 }, +}; + +static const struct gc0310_reg gc0310_VGA_30fps[] = { + { 0xfe, 0x00 }, + { 0x0d, 0x01 }, /* height */ + { 0x0e, 0xf2 }, /* 0xf7 //height */ + { 0x0f, 0x02 }, /* width */ + { 0x10, 0x94 }, /* 0xa0 //height */ + + { 0x50, 0x01 }, /* crop enable */ + { 0x51, 0x00 }, + { 0x52, 0x00 }, + { 0x53, 0x00 }, + { 0x54, 0x01 }, + { 0x55, 0x01 }, /* crop window height */ + { 0x56, 0xf0 }, + { 0x57, 0x02 }, /* crop window width */ + { 0x58, 0x90 }, + + { 0xfe, 0x03 }, + { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ + { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ + + { 0xfe, 0x00 }, +}; /* * gc0310_write_reg_array - Initializes a list of GC0310 registers @@ -179,7 +411,10 @@ static int gc0310_detect(struct i2c_client *client) if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) return -ENODEV; - ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); + ret = pm_runtime_get_sync(&client->dev); + if (ret >= 0) + ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); + pm_runtime_put(&client->dev); if (ret < 0) { dev_err(&client->dev, "read sensor_id failed: %d\n", ret); return -ENODEV; @@ -268,19 +503,6 @@ error_unlock: return ret; } -static int gc0310_s_config(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = pm_runtime_get_sync(&client->dev); - if (ret >= 0) - ret = gc0310_detect(client); - - pm_runtime_put(&client->dev); - return ret; -} - static int gc0310_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval) { @@ -373,12 +595,12 @@ static void gc0310_remove(struct i2c_client *client) dev_dbg(&client->dev, "gc0310_remove...\n"); - atomisp_unregister_subdev(sd); - v4l2_device_unregister_subdev(sd); + v4l2_async_unregister_subdev(sd); media_entity_cleanup(&dev->sd.entity); v4l2_ctrl_handler_free(&dev->ctrls.handler); + mutex_destroy(&dev->input_lock); + fwnode_handle_put(dev->ep_fwnode); pm_runtime_disable(&client->dev); - kfree(dev); } static int gc0310_probe(struct i2c_client *client) @@ -390,19 +612,27 @@ static int gc0310_probe(struct i2c_client *client) if (!dev) return -ENOMEM; - ret = v4l2_get_acpi_sensor_info(&client->dev, NULL); - if (ret) - return ret; + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + dev->ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!dev->ep_fwnode) + return dev_err_probe(&client->dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(dev->reset)) + if (IS_ERR(dev->reset)) { + fwnode_handle_put(dev->ep_fwnode); return dev_err_probe(&client->dev, PTR_ERR(dev->reset), "getting reset GPIO\n"); + } dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); - if (IS_ERR(dev->powerdown)) + if (IS_ERR(dev->powerdown)) { + fwnode_handle_put(dev->ep_fwnode); return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown), "getting powerdown GPIO\n"); + } mutex_init(&dev->input_lock); v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops); @@ -413,7 +643,7 @@ static int gc0310_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); - ret = gc0310_s_config(&dev->sd); + ret = gc0310_detect(client); if (ret) { gc0310_remove(client); return ret; @@ -422,6 +652,7 @@ static int gc0310_probe(struct i2c_client *client) dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + dev->sd.fwnode = dev->ep_fwnode; ret = gc0310_init_controls(dev); if (ret) { @@ -435,8 +666,7 @@ static int gc0310_probe(struct i2c_client *client) return ret; } - ret = atomisp_register_sensor_no_gmin(&dev->sd, 1, ATOMISP_INPUT_FORMAT_RAW_8, - atomisp_bayer_order_grbg); + ret = v4l2_async_register_subdev_sensor(&dev->sd); if (ret) { gc0310_remove(client); return ret; @@ -471,7 +701,6 @@ static int gc0310_resume(struct device *dev) static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL); static const struct acpi_device_id gc0310_acpi_match[] = { - {"XXGC0310"}, {"INT0310"}, {}, }; @@ -483,7 +712,7 @@ static struct i2c_driver gc0310_driver = { .pm = pm_sleep_ptr(&gc0310_pm_ops), .acpi_match_table = gc0310_acpi_match, }, - .probe_new = gc0310_probe, + .probe = gc0310_probe, .remove = gc0310_remove, }; module_i2c_driver(gc0310_driver); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index cb4c79b483ca..9fa390fbc5f3 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -864,7 +864,7 @@ static struct i2c_driver gc2235_driver = { .name = "gc2235", .acpi_match_table = gc2235_acpi_match, }, - .probe_new = gc2235_probe, + .probe = gc2235_probe, .remove = gc2235_remove, }; module_i2c_driver(gc2235_driver); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c b/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c index c4ce4cd445d7..cf5d9317b11a 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-lm3554.c @@ -945,7 +945,7 @@ static struct i2c_driver lm3554_driver = { .pm = &lm3554_pm_ops, .acpi_match_table = lm3554_acpi_match, }, - .probe_new = lm3554_probe, + .probe = lm3554_probe, .remove = lm3554_remove, }; module_i2c_driver(lm3554_driver); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index 0e5a981dd331..1c6643c442ef 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -1600,7 +1600,7 @@ static struct i2c_driver mt9m114_driver = { .name = "mt9m114", .acpi_match_table = mt9m114_acpi_match, }, - .probe_new = mt9m114_probe, + .probe = mt9m114_probe, .remove = mt9m114_remove, }; module_i2c_driver(mt9m114_driver); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c index c079368019e8..4cc2839937af 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2680.c @@ -28,15 +28,13 @@ #include <media/ov_16bit_addr_reg_helpers.h> #include <media/v4l2-device.h> -#include "../include/linux/atomisp_gmin_platform.h" - #include "ov2680.h" -static enum atomisp_bayer_order ov2680_bayer_order_mapping[] = { - atomisp_bayer_order_bggr, - atomisp_bayer_order_grbg, - atomisp_bayer_order_gbrg, - atomisp_bayer_order_rggb, +static const struct v4l2_rect ov2680_default_crop = { + .left = OV2680_ACTIVE_START_LEFT, + .top = OV2680_ACTIVE_START_TOP, + .width = OV2680_ACTIVE_WIDTH, + .height = OV2680_ACTIVE_HEIGHT, }; static int ov2680_write_reg_array(struct i2c_client *client, @@ -54,7 +52,7 @@ static int ov2680_write_reg_array(struct i2c_client *client, return 0; } -static void ov2680_set_bayer_order(struct ov2680_device *sensor, struct v4l2_mbus_framefmt *fmt) +static void ov2680_set_bayer_order(struct ov2680_dev *sensor, struct v4l2_mbus_framefmt *fmt) { static const int ov2680_hv_flip_bayer_order[] = { MEDIA_BUS_FMT_SBGGR10_1X10, @@ -62,7 +60,6 @@ static void ov2680_set_bayer_order(struct ov2680_device *sensor, struct v4l2_mbu MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, }; - struct camera_mipi_info *ov2680_info; int hv_flip = 0; if (sensor->ctrls.vflip->val) @@ -72,14 +69,9 @@ static void ov2680_set_bayer_order(struct ov2680_device *sensor, struct v4l2_mbu hv_flip += 2; fmt->code = ov2680_hv_flip_bayer_order[hv_flip]; - - /* TODO atomisp specific custom API, should be removed */ - ov2680_info = v4l2_get_subdev_hostdata(&sensor->sd); - if (ov2680_info) - ov2680_info->raw_bayer_order = ov2680_bayer_order_mapping[hv_flip]; } -static int ov2680_set_vflip(struct ov2680_device *sensor, s32 val) +static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val) { int ret; @@ -94,7 +86,7 @@ static int ov2680_set_vflip(struct ov2680_device *sensor, s32 val) return 0; } -static int ov2680_set_hflip(struct ov2680_device *sensor, s32 val) +static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val) { int ret; @@ -109,17 +101,17 @@ static int ov2680_set_hflip(struct ov2680_device *sensor, s32 val) return 0; } -static int ov2680_exposure_set(struct ov2680_device *sensor, u32 exp) +static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp) { return ov_write_reg24(sensor->client, OV2680_REG_EXPOSURE_PK_HIGH, exp << 4); } -static int ov2680_gain_set(struct ov2680_device *sensor, u32 gain) +static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain) { return ov_write_reg16(sensor->client, OV2680_REG_GAIN_PK, gain); } -static int ov2680_test_pattern_set(struct ov2680_device *sensor, int value) +static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) { int ret; @@ -140,7 +132,7 @@ static int ov2680_test_pattern_set(struct ov2680_device *sensor, int value) static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); int ret; /* Only apply changes to the controls if the device is powered up */ @@ -183,14 +175,17 @@ static int ov2680_init_registers(struct v4l2_subdev *sd) int ret; ret = ov_write_reg8(client, OV2680_SW_RESET, 0x01); + + /* Wait for sensor reset */ + usleep_range(1000, 2000); + ret |= ov2680_write_reg_array(client, ov2680_global_setting); return ret; } static struct v4l2_mbus_framefmt * -__ov2680_get_pad_format(struct ov2680_device *sensor, - struct v4l2_subdev_state *state, +__ov2680_get_pad_format(struct ov2680_dev *sensor, struct v4l2_subdev_state *state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) @@ -199,7 +194,17 @@ __ov2680_get_pad_format(struct ov2680_device *sensor, return &sensor->mode.fmt; } -static void ov2680_fill_format(struct ov2680_device *sensor, +static struct v4l2_rect * +__ov2680_get_pad_crop(struct ov2680_dev *sensor, struct v4l2_subdev_state *state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); + + return &sensor->mode.crop; +} + +static void ov2680_fill_format(struct ov2680_dev *sensor, struct v4l2_mbus_framefmt *fmt, unsigned int width, unsigned int height) { @@ -210,13 +215,15 @@ static void ov2680_fill_format(struct ov2680_device *sensor, ov2680_set_bayer_order(sensor, fmt); } -static void ov2680_calc_mode(struct ov2680_device *sensor, int width, int height) +static void ov2680_calc_mode(struct ov2680_dev *sensor) { + int width = sensor->mode.fmt.width; + int height = sensor->mode.fmt.height; int orig_width = width; int orig_height = height; - if (width <= (OV2680_NATIVE_WIDTH / 2) && - height <= (OV2680_NATIVE_HEIGHT / 2)) { + if (width <= (sensor->mode.crop.width / 2) && + height <= (sensor->mode.crop.height / 2)) { sensor->mode.binning = true; width *= 2; height *= 2; @@ -224,8 +231,10 @@ static void ov2680_calc_mode(struct ov2680_device *sensor, int width, int height sensor->mode.binning = false; } - sensor->mode.h_start = ((OV2680_NATIVE_WIDTH - width) / 2) & ~1; - sensor->mode.v_start = ((OV2680_NATIVE_HEIGHT - height) / 2) & ~1; + sensor->mode.h_start = + (sensor->mode.crop.left + (sensor->mode.crop.width - width) / 2) & ~1; + sensor->mode.v_start = + (sensor->mode.crop.top + (sensor->mode.crop.height - height) / 2) & ~1; sensor->mode.h_end = min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, OV2680_NATIVE_WIDTH - 1); sensor->mode.v_end = min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, @@ -236,31 +245,25 @@ static void ov2680_calc_mode(struct ov2680_device *sensor, int width, int height sensor->mode.vts = OV2680_LINES_PER_FRAME; } -static int ov2680_set_mode(struct ov2680_device *sensor) +static int ov2680_set_mode(struct ov2680_dev *sensor) { struct i2c_client *client = sensor->client; - u8 pll_div, unknown, inc, fmt1, fmt2; + u8 sensor_ctrl_0a, inc, fmt1, fmt2; int ret; if (sensor->mode.binning) { - pll_div = 1; - unknown = 0x23; + sensor_ctrl_0a = 0x23; inc = 0x31; fmt1 = 0xc2; fmt2 = 0x01; } else { - pll_div = 0; - unknown = 0x21; + sensor_ctrl_0a = 0x21; inc = 0x11; fmt1 = 0xc0; fmt2 = 0x00; } - ret = ov_write_reg8(client, 0x3086, pll_div); - if (ret) - return ret; - - ret = ov_write_reg8(client, 0x370a, unknown); + ret = ov_write_reg8(client, OV2680_REG_SENSOR_CTRL_0A, sensor_ctrl_0a); if (ret) return ret; @@ -337,12 +340,18 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); struct v4l2_mbus_framefmt *fmt; + const struct v4l2_rect *crop; unsigned int width, height; - width = min_t(unsigned int, ALIGN(format->format.width, 2), OV2680_NATIVE_WIDTH); - height = min_t(unsigned int, ALIGN(format->format.height, 2), OV2680_NATIVE_HEIGHT); + crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, format->which); + + /* Limit set_fmt max size to crop width / height */ + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), + OV2680_MIN_CROP_WIDTH, crop->width); + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), + OV2680_MIN_CROP_HEIGHT, crop->height); fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, format->which); ov2680_fill_format(sensor, fmt, width, height); @@ -352,9 +361,9 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - mutex_lock(&sensor->input_lock); - ov2680_calc_mode(sensor, fmt->width, fmt->height); - mutex_unlock(&sensor->input_lock); + mutex_lock(&sensor->lock); + ov2680_calc_mode(sensor); + mutex_unlock(&sensor->lock); return 0; } @@ -362,7 +371,7 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); struct v4l2_mbus_framefmt *fmt; fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, format->which); @@ -370,6 +379,105 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd, return 0; } +static int ov2680_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_sensor(sd); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&sensor->lock); + sel->r = *__ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); + mutex_unlock(&sensor->lock); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV2680_NATIVE_WIDTH; + sel->r.height = OV2680_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = OV2680_ACTIVE_START_TOP; + sel->r.left = OV2680_ACTIVE_START_LEFT; + sel->r.width = OV2680_ACTIVE_WIDTH; + sel->r.height = OV2680_ACTIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov2680_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_sensor(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *__crop; + struct v4l2_rect rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * Clamp the boundaries of the crop rectangle to the size of the sensor + * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't + * disrupted. + */ + rect.left = clamp(ALIGN(sel->r.left, 2), OV2680_NATIVE_START_LEFT, + OV2680_NATIVE_WIDTH); + rect.top = clamp(ALIGN(sel->r.top, 2), OV2680_NATIVE_START_TOP, + OV2680_NATIVE_HEIGHT); + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + OV2680_MIN_CROP_WIDTH, OV2680_NATIVE_WIDTH); + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + OV2680_MIN_CROP_HEIGHT, OV2680_NATIVE_HEIGHT); + + /* Make sure the crop rectangle isn't outside the bounds of the array */ + rect.width = min_t(unsigned int, rect.width, + OV2680_NATIVE_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + OV2680_NATIVE_HEIGHT - rect.top); + + __crop = __ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); + + if (rect.width != __crop->width || rect.height != __crop->height) { + /* + * Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __ov2680_get_pad_format(sensor, state, sel->pad, sel->which); + format->width = rect.width; + format->height = rect.height; + } + + *__crop = rect; + sel->r = rect; + + return 0; +} + +static int ov2680_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .width = 800, + .height = 600, + }, + }; + + sd_state->pads[0].try_crop = ov2680_default_crop; + + return ov2680_set_fmt(sd, sd_state, &fmt); +} + static int ov2680_detect(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -405,11 +513,11 @@ static int ov2680_detect(struct i2c_client *client) static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) { - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; - mutex_lock(&sensor->input_lock); + mutex_lock(&sensor->lock); if (sensor->is_streaming == enable) { dev_warn(&client->dev, "stream already %s\n", enable ? "started" : "stopped"); @@ -442,14 +550,14 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) v4l2_ctrl_activate(sensor->ctrls.vflip, !enable); v4l2_ctrl_activate(sensor->ctrls.hflip, !enable); - mutex_unlock(&sensor->input_lock); + mutex_unlock(&sensor->lock); return 0; error_power_down: pm_runtime_put(sensor->sd.dev); sensor->is_streaming = false; error_unlock: - mutex_unlock(&sensor->input_lock); + mutex_unlock(&sensor->lock); return ret; } @@ -550,11 +658,14 @@ static const struct v4l2_subdev_sensor_ops ov2680_sensor_ops = { }; static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { + .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, .get_fmt = ov2680_get_fmt, .set_fmt = ov2680_set_fmt, + .get_selection = ov2680_get_selection, + .set_selection = ov2680_set_selection, }; static const struct v4l2_subdev_ops ov2680_ops = { @@ -563,7 +674,7 @@ static const struct v4l2_subdev_ops ov2680_ops = { .sensor = &ov2680_sensor_ops, }; -static int ov2680_init_controls(struct ov2680_device *sensor) +static int ov2680_init_controls(struct ov2680_dev *sensor) { static const char * const test_pattern_menu[] = { "Disabled", @@ -579,7 +690,7 @@ static int ov2680_init_controls(struct ov2680_device *sensor) v4l2_ctrl_handler_init(hdl, 4); - hdl->lock = &sensor->input_lock; + hdl->lock = &sensor->lock; ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); @@ -605,39 +716,46 @@ static int ov2680_init_controls(struct ov2680_device *sensor) static void ov2680_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); dev_dbg(&client->dev, "ov2680_remove...\n"); - atomisp_unregister_subdev(sd); - v4l2_device_unregister_subdev(sd); + v4l2_async_unregister_subdev(&sensor->sd); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); + mutex_destroy(&sensor->lock); + fwnode_handle_put(sensor->ep_fwnode); pm_runtime_disable(&client->dev); } static int ov2680_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct ov2680_device *sensor; + struct ov2680_dev *sensor; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; - mutex_init(&sensor->input_lock); + mutex_init(&sensor->lock); sensor->client = client; v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_ops); - ret = v4l2_get_acpi_sensor_info(dev, NULL); - if (ret) - return ret; + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + sensor->ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!sensor->ep_fwnode) + return dev_err_probe(dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); - if (IS_ERR(sensor->powerdown)) + if (IS_ERR(sensor->powerdown)) { + fwnode_handle_put(sensor->ep_fwnode); return dev_err_probe(dev, PTR_ERR(sensor->powerdown), "getting powerdown GPIO\n"); + } pm_runtime_set_suspended(dev); pm_runtime_enable(dev); @@ -653,6 +771,7 @@ static int ov2680_probe(struct i2c_client *client) sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + sensor->sd.fwnode = sensor->ep_fwnode; ret = ov2680_init_controls(sensor); if (ret) { @@ -666,10 +785,11 @@ static int ov2680_probe(struct i2c_client *client) return ret; } + sensor->mode.crop = ov2680_default_crop; ov2680_fill_format(sensor, &sensor->mode.fmt, OV2680_NATIVE_WIDTH, OV2680_NATIVE_HEIGHT); + ov2680_calc_mode(sensor); - ret = atomisp_register_sensor_no_gmin(&sensor->sd, 1, ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_bggr); + ret = v4l2_async_register_subdev_sensor(&sensor->sd); if (ret) { ov2680_remove(client); return ret; @@ -681,7 +801,7 @@ static int ov2680_probe(struct i2c_client *client) static int ov2680_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); gpiod_set_value_cansleep(sensor->powerdown, 1); return 0; @@ -690,7 +810,7 @@ static int ov2680_suspend(struct device *dev) static int ov2680_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov2680_device *sensor = to_ov2680_sensor(sd); + struct ov2680_dev *sensor = to_ov2680_sensor(sd); /* according to DS, at least 5ms is needed after DOVDD (enabled by ACPI) */ usleep_range(5000, 6000); @@ -719,7 +839,7 @@ static struct i2c_driver ov2680_driver = { .pm = pm_sleep_ptr(&ov2680_pm_ops), .acpi_match_table = ov2680_acpi_match, }, - .probe_new = ov2680_probe, + .probe = ov2680_probe, .remove = ov2680_remove, }; module_i2c_driver(ov2680_driver); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c index 5d2e6e2e72f0..6a72691ed5b7 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c @@ -1019,7 +1019,7 @@ static struct i2c_driver ov2722_driver = { .name = "ov2722", .acpi_match_table = ov2722_acpi_match, }, - .probe_new = ov2722_probe, + .probe = ov2722_probe, .remove = ov2722_remove, }; module_i2c_driver(ov2722_driver); diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h deleted file mode 100644 index d40406289598..000000000000 --- a/drivers/staging/media/atomisp/i2c/gc0310.h +++ /dev/null @@ -1,309 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for GalaxyCore GC0310 VGA camera sensor. - * - * Copyright (c) 2013 Intel Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - */ - -#ifndef __GC0310_H__ -#define __GC0310_H__ -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/i2c.h> -#include <linux/acpi.h> -#include <linux/delay.h> -#include <linux/videodev2.h> -#include <linux/spinlock.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <linux/v4l2-mediabus.h> -#include <media/media-entity.h> - -#include "../include/linux/atomisp_platform.h" - -#define GC0310_NATIVE_WIDTH 656 -#define GC0310_NATIVE_HEIGHT 496 - -#define GC0310_FPS 30 -#define GC0310_SKIP_FRAMES 3 - -#define GC0310_FOCAL_LENGTH_NUM 278 /* 2.78mm */ - -#define GC0310_ID 0xa310 - -#define GC0310_RESET_RELATED 0xFE -#define GC0310_REGISTER_PAGE_0 0x0 -#define GC0310_REGISTER_PAGE_3 0x3 - -#define GC0310_FINE_INTG_TIME_MIN 0 -#define GC0310_FINE_INTG_TIME_MAX_MARGIN 0 -#define GC0310_COARSE_INTG_TIME_MIN 1 -#define GC0310_COARSE_INTG_TIME_MAX_MARGIN 6 - -/* - * GC0310 System control registers - */ -#define GC0310_SW_STREAM 0x10 - -#define GC0310_SC_CMMN_CHIP_ID_H 0xf0 -#define GC0310_SC_CMMN_CHIP_ID_L 0xf1 - -#define GC0310_AEC_PK_EXPO_H 0x03 -#define GC0310_AEC_PK_EXPO_L 0x04 -#define GC0310_AGC_ADJ 0x48 -#define GC0310_DGC_ADJ 0x71 -#if 0 -#define GC0310_GROUP_ACCESS 0x3208 -#endif - -#define GC0310_H_CROP_START_H 0x09 -#define GC0310_H_CROP_START_L 0x0A -#define GC0310_V_CROP_START_H 0x0B -#define GC0310_V_CROP_START_L 0x0C -#define GC0310_H_OUTSIZE_H 0x0F -#define GC0310_H_OUTSIZE_L 0x10 -#define GC0310_V_OUTSIZE_H 0x0D -#define GC0310_V_OUTSIZE_L 0x0E -#define GC0310_H_BLANKING_H 0x05 -#define GC0310_H_BLANKING_L 0x06 -#define GC0310_V_BLANKING_H 0x07 -#define GC0310_V_BLANKING_L 0x08 -#define GC0310_SH_DELAY 0x11 - -#define GC0310_START_STREAMING 0x94 /* 8-bit enable */ -#define GC0310_STOP_STREAMING 0x0 /* 8-bit disable */ - -/* - * gc0310 device structure. - */ -struct gc0310_device { - struct v4l2_subdev sd; - struct media_pad pad; - struct mutex input_lock; - bool is_streaming; - - struct gpio_desc *reset; - struct gpio_desc *powerdown; - - struct gc0310_mode { - struct v4l2_mbus_framefmt fmt; - } mode; - - struct gc0310_ctrls { - struct v4l2_ctrl_handler handler; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; - } ctrls; -}; - -/** - * struct gc0310_reg - MI sensor register format - * @reg: 16-bit offset to register - * @val: 8/16/32-bit register value - * - * Define a structure for sensor register initialization values - */ -struct gc0310_reg { - u8 reg; - u8 val; /* @set value for read/mod/write, @mask */ -}; - -#define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) - -/* - * Register settings for various resolution - */ -static const struct gc0310_reg gc0310_reset_register[] = { -///////////////////////////////////////////////// -///////////////// system reg ///////////////// -///////////////////////////////////////////////// - { 0xfe, 0xf0 }, - { 0xfe, 0xf0 }, - { 0xfe, 0x00 }, - - { 0xfc, 0x0e }, /* 4e */ - { 0xfc, 0x0e }, /* 16//4e // [0]apwd [6]regf_clk_gate */ - { 0xf2, 0x80 }, /* sync output */ - { 0xf3, 0x00 }, /* 1f//01 data output */ - { 0xf7, 0x33 }, /* f9 */ - { 0xf8, 0x05 }, /* 00 */ - { 0xf9, 0x0e }, /* 0x8e //0f */ - { 0xfa, 0x11 }, - -///////////////////////////////////////////////// -/////////////////// MIPI //////////////////// -///////////////////////////////////////////////// - { 0xfe, 0x03 }, - { 0x01, 0x03 }, /* mipi 1lane */ - { 0x02, 0x22 }, /* 0x33 */ - { 0x03, 0x94 }, - { 0x04, 0x01 }, /* fifo_prog */ - { 0x05, 0x00 }, /* fifo_prog */ - { 0x06, 0x80 }, /* b0 //YUV ISP data */ - { 0x11, 0x2a }, /* 1e //LDI set YUV422 */ - { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ - { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ - { 0x15, 0x12 }, /* 0x10 //DPHYY_MODE read_ready */ - { 0x17, 0x01 }, - { 0x40, 0x08 }, - { 0x41, 0x00 }, - { 0x42, 0x00 }, - { 0x43, 0x00 }, - { 0x21, 0x02 }, /* 0x01 */ - { 0x22, 0x02 }, /* 0x01 */ - { 0x23, 0x01 }, /* 0x05 //Nor:0x05 DOU:0x06 */ - { 0x29, 0x00 }, - { 0x2A, 0x25 }, /* 0x05 //data zero 0x7a de */ - { 0x2B, 0x02 }, - - { 0xfe, 0x00 }, - -///////////////////////////////////////////////// -///////////////// CISCTL reg ///////////////// -///////////////////////////////////////////////// - { 0x00, 0x2f }, /* 2f//0f//02//01 */ - { 0x01, 0x0f }, /* 06 */ - { 0x02, 0x04 }, - { 0x4f, 0x00 }, /* AEC 0FF */ - { 0x03, 0x01 }, /* 0x03 //04 */ - { 0x04, 0xc0 }, /* 0xe8 //58 */ - { 0x05, 0x00 }, - { 0x06, 0xb2 }, /* 0x0a //HB */ - { 0x07, 0x00 }, - { 0x08, 0x0c }, /* 0x89 //VB */ - { 0x09, 0x00 }, /* row start */ - { 0x0a, 0x00 }, - { 0x0b, 0x00 }, /* col start */ - { 0x0c, 0x00 }, - { 0x0d, 0x01 }, /* height */ - { 0x0e, 0xf2 }, /* 0xf7 //height */ - { 0x0f, 0x02 }, /* width */ - { 0x10, 0x94 }, /* 0xa0 //height */ - { 0x17, 0x14 }, - { 0x18, 0x1a }, /* 0a//[4]double reset */ - { 0x19, 0x14 }, /* AD pipeline */ - { 0x1b, 0x48 }, - { 0x1e, 0x6b }, /* 3b//col bias */ - { 0x1f, 0x28 }, /* 20//00//08//txlow */ - { 0x20, 0x89 }, /* 88//0c//[3:2]DA15 */ - { 0x21, 0x49 }, /* 48//[3] txhigh */ - { 0x22, 0xb0 }, - { 0x23, 0x04 }, /* [1:0]vcm_r */ - { 0x24, 0x16 }, /* 15 */ - { 0x34, 0x20 }, /* [6:4] rsg high//range */ - -///////////////////////////////////////////////// -//////////////////// BLK //////////////////// -///////////////////////////////////////////////// - { 0x26, 0x23 }, /* [1]dark_current_en [0]offset_en */ - { 0x28, 0xff }, /* BLK_limie_value */ - { 0x29, 0x00 }, /* global offset */ - { 0x33, 0x18 }, /* offset_ratio */ - { 0x37, 0x20 }, /* dark_current_ratio */ - { 0x2a, 0x00 }, - { 0x2b, 0x00 }, - { 0x2c, 0x00 }, - { 0x2d, 0x00 }, - { 0x2e, 0x00 }, - { 0x2f, 0x00 }, - { 0x30, 0x00 }, - { 0x31, 0x00 }, - { 0x47, 0x80 }, /* a7 */ - { 0x4e, 0x66 }, /* select_row */ - { 0xa8, 0x02 }, /* win_width_dark, same with crop_win_width */ - { 0xa9, 0x80 }, - -///////////////////////////////////////////////// -////////////////// ISP reg /////////////////// -///////////////////////////////////////////////// - { 0x40, 0x06 }, /* 0xff //ff //48 */ - { 0x41, 0x00 }, /* 0x21 //00//[0]curve_en */ - { 0x42, 0x04 }, /* 0xcf //0a//[1]awn_en */ - { 0x44, 0x18 }, /* 0x18 //02 */ - { 0x46, 0x02 }, /* 0x03 //sync */ - { 0x49, 0x03 }, - { 0x4c, 0x20 }, /* 00[5]pretect exp */ - { 0x50, 0x01 }, /* crop enable */ - { 0x51, 0x00 }, - { 0x52, 0x00 }, - { 0x53, 0x00 }, - { 0x54, 0x01 }, - { 0x55, 0x01 }, /* crop window height */ - { 0x56, 0xf0 }, - { 0x57, 0x02 }, /* crop window width */ - { 0x58, 0x90 }, - -///////////////////////////////////////////////// -/////////////////// GAIN //////////////////// -///////////////////////////////////////////////// - { 0x70, 0x70 }, /* 70 //80//global gain */ - { 0x71, 0x20 }, /* pregain gain */ - { 0x72, 0x40 }, /* post gain */ - { 0x5a, 0x84 }, /* 84//analog gain 0 */ - { 0x5b, 0xc9 }, /* c9 */ - { 0x5c, 0xed }, /* ed//not use pga gain highest level */ - { 0x77, 0x40 }, /* R gain 0x74 //awb gain */ - { 0x78, 0x40 }, /* G gain */ - { 0x79, 0x40 }, /* B gain 0x5f */ - - { 0x48, 0x00 }, - { 0xfe, 0x01 }, - { 0x0a, 0x45 }, /* [7]col gain mode */ - - { 0x3e, 0x40 }, - { 0x3f, 0x5c }, - { 0x40, 0x7b }, - { 0x41, 0xbd }, - { 0x42, 0xf6 }, - { 0x43, 0x63 }, - { 0x03, 0x60 }, - { 0x44, 0x03 }, - -///////////////////////////////////////////////// -///////////////// dark sun ////////////////// -///////////////////////////////////////////////// - { 0xfe, 0x01 }, - { 0x45, 0xa4 }, /* 0xf7 */ - { 0x46, 0xf0 }, /* 0xff //f0//sun value th */ - { 0x48, 0x03 }, /* sun mode */ - { 0x4f, 0x60 }, /* sun_clamp */ - { 0xfe, 0x00 }, -}; - -static struct gc0310_reg const gc0310_VGA_30fps[] = { - { 0xfe, 0x00 }, - { 0x0d, 0x01 }, /* height */ - { 0x0e, 0xf2 }, /* 0xf7 //height */ - { 0x0f, 0x02 }, /* width */ - { 0x10, 0x94 }, /* 0xa0 //height */ - - { 0x50, 0x01 }, /* crop enable */ - { 0x51, 0x00 }, - { 0x52, 0x00 }, - { 0x53, 0x00 }, - { 0x54, 0x01 }, - { 0x55, 0x01 }, /* crop window height */ - { 0x56, 0xf0 }, - { 0x57, 0x02 }, /* crop window width */ - { 0x58, 0x90 }, - - { 0xfe, 0x03 }, - { 0x12, 0x90 }, /* 00 //04 //00 //04//00 //LWC[7:0] */ - { 0x13, 0x02 }, /* 05 //05 //LWC[15:8] */ - - { 0xfe, 0x00 }, -}; - -#endif diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h index baf49eb0659e..d032af245674 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ b/drivers/staging/media/atomisp/i2c/ov2680.h @@ -30,10 +30,16 @@ #include <linux/v4l2-mediabus.h> #include <media/media-entity.h> -#include "../include/linux/atomisp_platform.h" - #define OV2680_NATIVE_WIDTH 1616 #define OV2680_NATIVE_HEIGHT 1216 +#define OV2680_NATIVE_START_LEFT 0 +#define OV2680_NATIVE_START_TOP 0 +#define OV2680_ACTIVE_WIDTH 1600 +#define OV2680_ACTIVE_HEIGHT 1200 +#define OV2680_ACTIVE_START_LEFT 8 +#define OV2680_ACTIVE_START_TOP 8 +#define OV2680_MIN_CROP_WIDTH 2 +#define OV2680_MIN_CROP_HEIGHT 2 /* 1704 * 1294 * 30fps = 66MHz pixel clock */ #define OV2680_PIXELS_PER_LINE 1704 @@ -66,6 +72,8 @@ #define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 #define OV2680_REG_GAIN_PK 0x350a +#define OV2680_REG_SENSOR_CTRL_0A 0x370a + #define OV2680_HORIZONTAL_START_H 0x3800 /* Bit[11:8] */ #define OV2680_HORIZONTAL_START_L 0x3801 /* Bit[7:0] */ #define OV2680_VERTICAL_START_H 0x3802 /* Bit[11:8] */ @@ -108,15 +116,18 @@ /* * ov2680 device structure. */ -struct ov2680_device { +struct ov2680_dev { struct v4l2_subdev sd; struct media_pad pad; - struct mutex input_lock; + /* Protect against concurrent changes to controls */ + struct mutex lock; struct i2c_client *client; struct gpio_desc *powerdown; + struct fwnode_handle *ep_fwnode; bool is_streaming; struct ov2680_mode { + struct v4l2_rect crop; struct v4l2_mbus_framefmt fmt; bool binning; u16 h_start; @@ -152,92 +163,86 @@ struct ov2680_reg { u32 val; /* @set value for read/mod/write, @mask */ }; -#define to_ov2680_sensor(x) container_of(x, struct ov2680_device, sd) +#define to_ov2680_sensor(x) container_of(x, struct ov2680_dev, sd) static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) { - struct ov2680_device *sensor = - container_of(ctrl->handler, struct ov2680_device, ctrls.handler); + struct ov2680_dev *sensor = + container_of(ctrl->handler, struct ov2680_dev, ctrls.handler); return &sensor->sd; } static struct ov2680_reg const ov2680_global_setting[] = { - {0x0103, 0x01}, - {0x3002, 0x00}, + /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */ {0x3016, 0x1c}, - {0x3018, 0x44}, - {0x3020, 0x00}, - {0x3080, 0x02}, + + /* PLL MULT bits 0-7, datasheet default 0x37 for 24MHz extclk, use 0x45 for 19.2 Mhz extclk */ {0x3082, 0x45}, - {0x3084, 0x09}, - {0x3085, 0x04}, + + /* R MANUAL set exposure (0x01) and gain (0x02) to manual (hw does not do auto) */ {0x3503, 0x03}, - {0x350b, 0x36}, - {0x3600, 0xb4}, - {0x3603, 0x39}, - {0x3604, 0x24}, - {0x3605, 0x00}, - {0x3620, 0x26}, - {0x3621, 0x37}, - {0x3622, 0x04}, - {0x3628, 0x00}, - {0x3705, 0x3c}, - {0x370c, 0x50}, - {0x370d, 0xc0}, - {0x3718, 0x88}, - {0x3720, 0x00}, - {0x3721, 0x00}, - {0x3722, 0x00}, - {0x3723, 0x00}, - {0x3738, 0x00}, - {0x3717, 0x58}, - {0x3781, 0x80}, - {0x3789, 0x60}, - {0x3800, 0x00}, - {0x3819, 0x04}, + + /* Analog control register tweaks */ + {0x3603, 0x39}, /* Reset value 0x99 */ + {0x3604, 0x24}, /* Reset value 0x74 */ + {0x3621, 0x37}, /* Reset value 0x44 */ + + /* Sensor control register tweaks */ + {0x3701, 0x64}, /* Reset value 0x61 */ + {0x3705, 0x3c}, /* Reset value 0x21 */ + {0x370c, 0x50}, /* Reset value 0x10 */ + {0x370d, 0xc0}, /* Reset value 0x00 */ + {0x3718, 0x88}, /* Reset value 0x80 */ + + /* PSRAM tweaks */ + {0x3781, 0x80}, /* Reset value 0x00 */ + {0x3784, 0x0c}, /* Reset value 0x00, based on OV2680_R1A_AM10.ovt */ + {0x3789, 0x60}, /* Reset value 0x50 */ + + /* BLC CTRL00 0x01 -> 0x81 set avg_weight to 8 */ {0x4000, 0x81}, - {0x4001, 0x40}, + + /* Set black level compensation range to 0 - 3 (default 0 - 11) */ {0x4008, 0x00}, {0x4009, 0x03}, + + /* VFIFO R2 0x00 -> 0x02 set Frame reset enable */ {0x4602, 0x02}, + + /* MIPI ctrl CLK PREPARE MIN change from 0x26 (38) -> 0x36 (54) */ {0x481f, 0x36}, + + /* MIPI ctrl CLK LPX P MIN change from 0x32 (50) -> 0x36 (54) */ {0x4825, 0x36}, - {0x4837, 0x18}, + + /* R ISP CTRL2 0x20 -> 0x30, set sof_sel bit */ {0x5002, 0x30}, - {0x5004, 0x04},//manual awb 1x - {0x5005, 0x00}, - {0x5006, 0x04}, - {0x5007, 0x00}, - {0x5008, 0x04}, - {0x5009, 0x00}, - {0x5080, 0x00}, - {0x5081, 0x41}, - {0x5708, 0x01}, /* add for full size flip off and mirror off 2014/09/11 */ - {0x3701, 0x64}, //add on 14/05/13 - {0x3784, 0x0c}, //based OV2680_R1A_AM10.ovt add on 14/06/13 - {0x5780, 0x3e}, //based OV2680_R1A_AM10.ovt,Adjust DPC setting (57xx) on 14/06/13 - {0x5781, 0x0f}, - {0x5782, 0x04}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, + + /* + * Window CONTROL 0x00 -> 0x01, enable manual window control, + * this is necessary for full size flip and mirror support. + */ + {0x5708, 0x01}, + + /* + * DPC CTRL0 0x14 -> 0x3e, set enable_tail, enable_3x3_cluster + * and enable_general_tail bits based OV2680_R1A_AM10.ovt. + */ + {0x5780, 0x3e}, + + /* DPC MORE CONNECTION CASE THRE 0x0c (12) -> 0x02 (2) */ {0x5788, 0x02}, - {0x5789, 0x00}, - {0x578a, 0x01}, - {0x578b, 0x02}, - {0x578c, 0x03}, - {0x578d, 0x03}, + + /* DPC GAIN LIST1 0x0f (15) -> 0x08 (8) */ {0x578e, 0x08}, + + /* DPC GAIN LIST2 0x3f (63) -> 0x0c (12) */ {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, + + /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */ {0x5792, 0x00}, - {0x5793, 0x00}, - {0x5794, 0x03}, //based OV2680_R1A_AM10.ovt,Adjust DPC setting (57xx) on 14/06/13 - {0x0100, 0x00}, //stream off + {} }; diff --git a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c index da8c3b1d3bcd..460a4e34c55b 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c +++ b/drivers/staging/media/atomisp/i2c/ov5693/atomisp-ov5693.c @@ -726,51 +726,11 @@ static void *ov5693_otp_read(struct v4l2_subdev *sd) return buf; } -static int ov5693_g_priv_int_data(struct v4l2_subdev *sd, - struct v4l2_private_int_data *priv) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5693_device *dev = to_ov5693_sensor(sd); - u8 __user *to = priv->data; - u32 read_size = priv->size; - int ret; - - /* No need to copy data if size is 0 */ - if (!read_size) - goto out; - - if (IS_ERR(dev->otp_data)) { - dev_err(&client->dev, "OTP data not available"); - return PTR_ERR(dev->otp_data); - } - - /* Correct read_size value only if bigger than maximum */ - if (read_size > OV5693_OTP_DATA_SIZE) - read_size = OV5693_OTP_DATA_SIZE; - - ret = copy_to_user(to, dev->otp_data, read_size); - if (ret) { - dev_err(&client->dev, "%s: failed to copy OTP data to user\n", - __func__); - return -EFAULT; - } - - pr_debug("%s read_size:%d\n", __func__, read_size); - -out: - /* Return correct size */ - priv->size = dev->otp_size; - - return 0; -} - static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { switch (cmd) { case ATOMISP_IOC_S_EXPOSURE: return ov5693_s_exposure(sd, arg); - case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA: - return ov5693_g_priv_int_data(sd, arg); default: return -EINVAL; } @@ -1794,7 +1754,7 @@ static struct i2c_driver ov5693_driver = { .name = "ov5693", .acpi_match_table = ov5693_acpi_match, }, - .probe_new = ov5693_probe, + .probe = ov5693_probe, .remove = ov5693_remove, }; module_i2c_driver(ov5693_driver); diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index c7ec56a1c064..14b1757e6674 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -38,7 +38,6 @@ #define CI_MODE_PREVIEW 0x8000 #define CI_MODE_VIDEO 0x4000 #define CI_MODE_STILL_CAPTURE 0x2000 -#define CI_MODE_CONTINUOUS 0x1000 #define CI_MODE_NONE 0x0000 #define OUTPUT_MODE_FILE 0x0100 @@ -150,12 +149,6 @@ enum atomisp_calibration_type { calibration_type3 }; -struct atomisp_calibration_group { - unsigned int size; - unsigned int type; - unsigned short *calb_grp_values; -}; - struct atomisp_gc_config { __u16 gain_k1; __u16 gain_k2; @@ -266,26 +259,6 @@ enum atomisp_metadata_type { ATOMISP_METADATA_TYPE_NUM, }; -struct atomisp_metadata_with_type { - /* to specify which type of metadata to get */ - enum atomisp_metadata_type type; - void __user *data; - u32 width; - u32 height; - u32 stride; /* in bytes */ - u32 exp_id; /* exposure ID */ - u32 *effective_width; /* mipi packets valid data size */ -}; - -struct atomisp_metadata { - void __user *data; - u32 width; - u32 height; - u32 stride; /* in bytes */ - u32 exp_id; /* exposure ID */ - u32 *effective_width; /* mipi packets valid data size */ -}; - struct atomisp_ext_isp_ctrl { u32 id; u32 data; @@ -299,14 +272,6 @@ struct atomisp_3a_statistics { u32 isp_config_id; /* isp config ID */ }; -struct atomisp_ae_window { - int x_left; - int x_right; - int y_top; - int y_bottom; - int weight; -}; - /* White Balance (Gain Adjust) */ struct atomisp_wb_config { unsigned int integer_bits; @@ -755,53 +720,6 @@ struct atomisp_s_runmode { __u32 mode; }; -struct atomisp_update_exposure { - unsigned int gain; - unsigned int digi_gain; - unsigned int update_gain; - unsigned int update_digi_gain; -}; - -/* - * V4L2 private internal data interface. - * ----------------------------------------------------------------------------- - * struct v4l2_private_int_data - request private data stored in video device - * internal memory. - * @size: sanity check to ensure userspace's buffer fits whole private data. - * If not, kernel will make partial copy (or nothing if @size == 0). - * @size is always corrected for the minimum necessary if IOCTL returns - * no error. - * @data: pointer to userspace buffer. - */ -struct v4l2_private_int_data { - __u32 size; - void __user *data; - __u32 reserved[2]; -}; - -enum atomisp_sensor_ae_bracketing_mode { - SENSOR_AE_BRACKETING_MODE_OFF = 0, - SENSOR_AE_BRACKETING_MODE_SINGLE, /* back to SW standby after bracketing */ - SENSOR_AE_BRACKETING_MODE_SINGLE_TO_STREAMING, /* back to normal streaming after bracketing */ - SENSOR_AE_BRACKETING_MODE_LOOP, /* continue AE bracketing in loop mode */ -}; - -struct atomisp_sensor_ae_bracketing_info { - unsigned int modes; /* bit mask to indicate supported modes */ - unsigned int lut_depth; -}; - -struct atomisp_sensor_ae_bracketing_lut_entry { - __u16 coarse_integration_time; - __u16 analog_gain; - __u16 digital_gain; -}; - -struct atomisp_sensor_ae_bracketing_lut { - struct atomisp_sensor_ae_bracketing_lut_entry *lut; - unsigned int lut_size; -}; - /*Private IOCTLs for ISP */ #define ATOMISP_IOC_G_XNR \ _IOR('v', BASE_VIDIOC_PRIVATE + 0, int) @@ -906,20 +824,12 @@ struct atomisp_sensor_ae_bracketing_lut { #define ATOMISP_IOC_S_EXPOSURE \ _IOW('v', BASE_VIDIOC_PRIVATE + 21, struct atomisp_exposure) -/* sensor calibration registers group */ -#define ATOMISP_IOC_G_SENSOR_CALIBRATION_GROUP \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 22, struct atomisp_calibration_group) - /* white balance Correction */ #define ATOMISP_IOC_G_3A_CONFIG \ _IOR('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config) #define ATOMISP_IOC_S_3A_CONFIG \ _IOW('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config) -/* sensor OTP memory read */ -#define ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 26, struct v4l2_private_int_data) - /* LCS (shading) table write */ #define ATOMISP_IOC_S_ISP_SHD_TAB \ _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table) @@ -931,19 +841,9 @@ struct atomisp_sensor_ae_bracketing_lut { #define ATOMISP_IOC_S_ISP_GAMMA_CORRECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 28, struct atomisp_gc_config) -/* motor internal memory read */ -#define ATOMISP_IOC_G_MOTOR_PRIV_INT_DATA \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 29, struct v4l2_private_int_data) - #define ATOMISP_IOC_S_PARAMETERS \ _IOW('v', BASE_VIDIOC_PRIVATE + 32, struct atomisp_parameters) -#define ATOMISP_IOC_G_METADATA \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 34, struct atomisp_metadata) - -#define ATOMISP_IOC_G_METADATA_BY_TYPE \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 34, struct atomisp_metadata_with_type) - #define ATOMISP_IOC_EXT_ISP_CTRL \ _IOWR('v', BASE_VIDIOC_PRIVATE + 35, struct atomisp_ext_isp_ctrl) @@ -962,27 +862,9 @@ struct atomisp_sensor_ae_bracketing_lut { #define ATOMISP_IOC_S_FORMATS_CONFIG \ _IOW('v', BASE_VIDIOC_PRIVATE + 39, struct atomisp_formats_config) -#define ATOMISP_IOC_S_EXPOSURE_WINDOW \ - _IOW('v', BASE_VIDIOC_PRIVATE + 40, struct atomisp_ae_window) - #define ATOMISP_IOC_INJECT_A_FAKE_EVENT \ _IOW('v', BASE_VIDIOC_PRIVATE + 42, int) -#define ATOMISP_IOC_G_SENSOR_AE_BRACKETING_INFO \ - _IOR('v', BASE_VIDIOC_PRIVATE + 43, struct atomisp_sensor_ae_bracketing_info) - -#define ATOMISP_IOC_S_SENSOR_AE_BRACKETING_MODE \ - _IOW('v', BASE_VIDIOC_PRIVATE + 43, unsigned int) - -#define ATOMISP_IOC_G_SENSOR_AE_BRACKETING_MODE \ - _IOR('v', BASE_VIDIOC_PRIVATE + 43, unsigned int) - -#define ATOMISP_IOC_S_SENSOR_AE_BRACKETING_LUT \ - _IOW('v', BASE_VIDIOC_PRIVATE + 43, struct atomisp_sensor_ae_bracketing_lut) - -#define ATOMISP_IOC_G_INVALID_FRAME_NUM \ - _IOR('v', BASE_VIDIOC_PRIVATE + 44, unsigned int) - #define ATOMISP_IOC_S_ARRAY_RESOLUTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 45, struct atomisp_resolution) @@ -996,9 +878,6 @@ struct atomisp_sensor_ae_bracketing_lut { #define ATOMISP_IOC_S_SENSOR_RUNMODE \ _IOW('v', BASE_VIDIOC_PRIVATE + 48, struct atomisp_s_runmode) -#define ATOMISP_IOC_G_UPDATE_EXPOSURE \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 49, struct atomisp_update_exposure) - /* * Reserved ioctls. We have customer implementing it internally. * We can't use both numbers to not cause ABI conflict. @@ -1059,9 +938,9 @@ struct atomisp_sensor_ae_bracketing_lut { #define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20) #define ATOMISP_RUN_MODE_VIDEO 1 #define ATOMISP_RUN_MODE_STILL_CAPTURE 2 -#define ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE 3 -#define ATOMISP_RUN_MODE_PREVIEW 4 -#define ATOMISP_RUN_MODE_SDV 5 +#define ATOMISP_RUN_MODE_PREVIEW 3 +#define ATOMISP_RUN_MODE_MIN 1 +#define ATOMISP_RUN_MODE_MAX 3 #define V4L2_CID_ENABLE_VFPP (V4L2_CID_CAMERA_LASTP1 + 21) #define V4L2_CID_ATOMISP_CONTINUOUS_MODE (V4L2_CID_CAMERA_LASTP1 + 22) diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index e8e965f73fc8..487ef5846c24 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -125,6 +125,7 @@ struct intel_v4l2_subdev_id { struct intel_v4l2_subdev_table { enum intel_v4l2_subdev_type type; enum atomisp_camera_port port; + unsigned int lanes; struct v4l2_subdev *subdev; }; diff --git a/drivers/staging/media/atomisp/pci/atomisp-regs.h b/drivers/staging/media/atomisp/pci/atomisp-regs.h index 022997f47121..a7b0196686be 100644 --- a/drivers/staging/media/atomisp/pci/atomisp-regs.h +++ b/drivers/staging/media/atomisp/pci/atomisp-regs.h @@ -112,7 +112,6 @@ /* MRFLD CSI lane configuration related */ #define MRFLD_PORT_CONFIG_NUM 8 -#define MRFLD_PORT_NUM 3 #define MRFLD_PORT1_ENABLE_SHIFT 0 #define MRFLD_PORT2_ENABLE_SHIFT 1 #define MRFLD_PORT3_ENABLE_SHIFT 2 diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index aa790ae746f3..e27f9dc8e7aa 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -229,8 +229,8 @@ int atomisp_freq_scaling(struct atomisp_device *isp, goto done; } - curr_rules.width = isp->asd.fmt[isp->asd.capture_pad].fmt.width; - curr_rules.height = isp->asd.fmt[isp->asd.capture_pad].fmt.height; + curr_rules.width = isp->asd.fmt[ATOMISP_SUBDEV_PAD_SOURCE].fmt.width; + curr_rules.height = isp->asd.fmt[ATOMISP_SUBDEV_PAD_SOURCE].fmt.height; curr_rules.fps = fps; curr_rules.run_mode = isp->asd.run_mode->val; @@ -472,39 +472,31 @@ irqreturn_t atomisp_isr(int irq, void *dev) clear_irq_reg(isp); - if (!atomisp_streaming_count(isp)) + if (!isp->asd.streaming) goto out_nowake; - if (isp->asd.streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { - if (irq_infos & IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF) { - atomic_inc(&isp->asd.sof_count); - atomisp_sof_event(&isp->asd); - - /* If sequence_temp and sequence are the same - * there where no frames lost so we can increase - * sequence_temp. - * If not then processing of frame is still in progress - * and driver needs to keep old sequence_temp value. - * NOTE: There is assumption here that ISP will not - * start processing next frame from sensor before old - * one is completely done. */ - if (atomic_read(&isp->asd.sequence) == - atomic_read(&isp->asd.sequence_temp)) - atomic_set(&isp->asd.sequence_temp, - atomic_read(&isp->asd.sof_count)); - } - if (irq_infos & IA_CSS_IRQ_INFO_EVENTS_READY) - atomic_set(&isp->asd.sequence, - atomic_read(&isp->asd.sequence_temp)); - } - if (irq_infos & IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF) { - dev_dbg_ratelimited(isp->dev, - "irq:0x%x (SOF)\n", - irq_infos); + atomic_inc(&isp->asd.sof_count); + atomisp_sof_event(&isp->asd); + + /* + * If sequence_temp and sequence are the same there where no frames + * lost so we can increase sequence_temp. + * If not then processing of frame is still in progress and driver + * needs to keep old sequence_temp value. + * NOTE: There is assumption here that ISP will not start processing + * next frame from sensor before old one is completely done. + */ + if (atomic_read(&isp->asd.sequence) == atomic_read(&isp->asd.sequence_temp)) + atomic_set(&isp->asd.sequence_temp, atomic_read(&isp->asd.sof_count)); + + dev_dbg_ratelimited(isp->dev, "irq:0x%x (SOF)\n", irq_infos); irq_infos &= ~IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF; } + if (irq_infos & IA_CSS_IRQ_INFO_EVENTS_READY) + atomic_set(&isp->asd.sequence, atomic_read(&isp->asd.sequence_temp)); + if ((irq_infos & IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR) || (irq_infos & IA_CSS_IRQ_INFO_IF_ERROR)) { /* handle mipi receiver error */ @@ -640,15 +632,6 @@ void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, enum vb2_buffer_s spin_unlock_irqrestore(&pipe->irq_lock, irqflags); } -/* Returns queued buffers back to video-core */ -void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd) -{ - atomisp_flush_video_pipe(&asd->video_out_capture, VB2_BUF_STATE_ERROR, false); - atomisp_flush_video_pipe(&asd->video_out_vf, VB2_BUF_STATE_ERROR, false); - atomisp_flush_video_pipe(&asd->video_out_preview, VB2_BUF_STATE_ERROR, false); - atomisp_flush_video_pipe(&asd->video_out_video_capture, VB2_BUF_STATE_ERROR, false); -} - /* clean out the parameters that did not apply */ void atomisp_flush_params_queue(struct atomisp_video_pipe *pipe) { @@ -944,53 +927,39 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, atomisp_qbuffers_to_css(asd); } -static void __atomisp_css_recover(struct atomisp_device *isp, bool isp_timeout) +void atomisp_assert_recovery_work(struct work_struct *work) { + struct atomisp_device *isp = container_of(work, struct atomisp_device, + assert_recovery_work); struct pci_dev *pdev = to_pci_dev(isp->dev); - enum ia_css_pipe_id css_pipe_id; - bool stream_restart = false; unsigned long flags; int ret; - lockdep_assert_held(&isp->mutex); + mutex_lock(&isp->mutex); - if (!atomisp_streaming_count(isp)) - return; + if (!isp->asd.streaming) + goto out_unlock; atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); - if (isp->asd.streaming == ATOMISP_DEVICE_STREAMING_ENABLED || - isp->asd.stream_prepared) { - stream_restart = true; - - spin_lock_irqsave(&isp->lock, flags); - isp->asd.streaming = ATOMISP_DEVICE_STREAMING_STOPPING; - spin_unlock_irqrestore(&isp->lock, flags); - - /* stream off sensor */ - ret = v4l2_subdev_call( - isp->inputs[isp->asd.input_curr]. - camera, video, s_stream, 0); - if (ret) - dev_warn(isp->dev, - "can't stop streaming on sensor!\n"); + spin_lock_irqsave(&isp->lock, flags); + isp->asd.streaming = false; + spin_unlock_irqrestore(&isp->lock, flags); - atomisp_clear_css_buffer_counters(&isp->asd); + /* stream off sensor */ + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].camera, video, s_stream, 0); + if (ret) + dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); - css_pipe_id = atomisp_get_css_pipe_id(&isp->asd); - atomisp_css_stop(&isp->asd, css_pipe_id, true); + atomisp_clear_css_buffer_counters(&isp->asd); - spin_lock_irqsave(&isp->lock, flags); - isp->asd.streaming = ATOMISP_DEVICE_STREAMING_DISABLED; - spin_unlock_irqrestore(&isp->lock, flags); + atomisp_css_stop(&isp->asd, true); - isp->asd.preview_exp_id = 1; - isp->asd.postview_exp_id = 1; - /* notify HAL the CSS reset */ - dev_dbg(isp->dev, - "send reset event to %s\n", isp->asd.subdev.devnode->name); - atomisp_reset_event(&isp->asd); - } + isp->asd.preview_exp_id = 1; + isp->asd.postview_exp_id = 1; + /* notify HAL the CSS reset */ + dev_dbg(isp->dev, "send reset event to %s\n", isp->asd.subdev.devnode->name); + atomisp_reset_event(&isp->asd); /* clear irq */ disable_isp_irq(hrt_isp_css_irq_sp); @@ -1001,71 +970,46 @@ static void __atomisp_css_recover(struct atomisp_device *isp, bool isp_timeout) isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); /* reset ISP and restore its state */ - isp->isp_timeout = true; atomisp_reset(isp); - isp->isp_timeout = false; - if (stream_restart) { - atomisp_css_input_set_mode(&isp->asd, IA_CSS_INPUT_MODE_BUFFERED_SENSOR); + atomisp_css_input_set_mode(&isp->asd, IA_CSS_INPUT_MODE_BUFFERED_SENSOR); - css_pipe_id = atomisp_get_css_pipe_id(&isp->asd); - if (atomisp_css_start(&isp->asd, css_pipe_id, true)) { - dev_warn(isp->dev, - "start SP failed, so do not set streaming to be enable!\n"); - } else { - spin_lock_irqsave(&isp->lock, flags); - isp->asd.streaming = ATOMISP_DEVICE_STREAMING_ENABLED; - spin_unlock_irqrestore(&isp->lock, flags); - } + /* Recreate streams destroyed by atomisp_css_stop() */ + atomisp_create_pipes_stream(&isp->asd); + + /* Invalidate caches. FIXME: should flush only necessary buffers */ + wbinvd(); - atomisp_csi2_configure(&isp->asd); + if (atomisp_css_start(&isp->asd)) { + dev_warn(isp->dev, "start SP failed, so do not set streaming to be enable!\n"); + } else { + spin_lock_irqsave(&isp->lock, flags); + isp->asd.streaming = true; + spin_unlock_irqrestore(&isp->lock, flags); } + atomisp_csi2_configure(&isp->asd); + atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, atomisp_css_valid_sof(isp)); if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, true) < 0) dev_dbg(isp->dev, "DFS auto failed while recovering!\n"); - if (stream_restart) { - /* - * dequeueing buffers is not needed. CSS will recycle - * buffers that it has. - */ - atomisp_flush_bufs_and_wakeup(&isp->asd); + /* Dequeueing buffers is not needed, CSS will recycle buffers that it has */ + atomisp_flush_video_pipe(&isp->asd.video_out, VB2_BUF_STATE_ERROR, false); - /* Requeue unprocessed per-frame parameters. */ - atomisp_recover_params_queue(&isp->asd.video_out_capture); - atomisp_recover_params_queue(&isp->asd.video_out_preview); - atomisp_recover_params_queue(&isp->asd.video_out_video_capture); - - ret = v4l2_subdev_call( - isp->inputs[isp->asd.input_curr].camera, video, - s_stream, 1); - if (ret) - dev_warn(isp->dev, - "can't start streaming on sensor!\n"); - } -} + /* Requeue unprocessed per-frame parameters. */ + atomisp_recover_params_queue(&isp->asd.video_out); -void atomisp_assert_recovery_work(struct work_struct *work) -{ - struct atomisp_device *isp = container_of(work, struct atomisp_device, - assert_recovery_work); + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].camera, video, s_stream, 1); + if (ret) + dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); - mutex_lock(&isp->mutex); - __atomisp_css_recover(isp, true); +out_unlock: mutex_unlock(&isp->mutex); } -void atomisp_css_flush(struct atomisp_device *isp) -{ - /* Start recover */ - __atomisp_css_recover(isp, false); - - dev_dbg(isp->dev, "atomisp css flush done\n"); -} - void atomisp_setup_flash(struct atomisp_sub_device *asd) { struct atomisp_device *isp = asd->isp; @@ -1105,7 +1049,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) spin_lock_irqsave(&isp->lock, flags); - if (!atomisp_streaming_count(isp)) { + if (!isp->asd.streaming) { spin_unlock_irqrestore(&isp->lock, flags); return IRQ_HANDLED; } @@ -1141,7 +1085,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) if (atomisp_css_isr_thread(isp)) goto out; - if (isp->asd.streaming == ATOMISP_DEVICE_STREAMING_ENABLED) + if (isp->asd.streaming) atomisp_setup_flash(&isp->asd); out: mutex_unlock(&isp->mutex); @@ -1298,7 +1242,7 @@ static void atomisp_update_capture_mode(struct atomisp_sub_device *asd) atomisp_css_capture_set_mode(asd, IA_CSS_CAPTURE_MODE_ADVANCED); else if (asd->params.low_light) atomisp_css_capture_set_mode(asd, IA_CSS_CAPTURE_MODE_LOW_LIGHT); - else if (asd->video_out_capture.sh_fmt == IA_CSS_FRAME_FORMAT_RAW) + else if (asd->video_out.sh_fmt == IA_CSS_FRAME_FORMAT_RAW) atomisp_css_capture_set_mode(asd, IA_CSS_CAPTURE_MODE_RAW); else atomisp_css_capture_set_mode(asd, IA_CSS_CAPTURE_MODE_PRIMARY); @@ -1553,13 +1497,12 @@ void atomisp_free_internal_buffers(struct atomisp_sub_device *asd) } static void atomisp_update_grid_info(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, - int source_pad) + enum ia_css_pipe_id pipe_id) { struct atomisp_device *isp = asd->isp; int err; - if (atomisp_css_get_grid_info(asd, pipe_id, source_pad)) + if (atomisp_css_get_grid_info(asd, pipe_id)) return; /* We must free all buffers because they no longer match @@ -1908,161 +1851,6 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, return 0; } -int atomisp_get_metadata(struct atomisp_sub_device *asd, int flag, - struct atomisp_metadata *md) -{ - struct atomisp_device *isp = asd->isp; - struct ia_css_stream_info *stream_info; - struct camera_mipi_info *mipi_info; - struct atomisp_metadata_buf *md_buf; - enum atomisp_metadata_type md_type = ATOMISP_MAIN_METADATA; - int ret, i; - - if (flag != 0) - return -EINVAL; - - stream_info = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. - stream_info; - - /* We always return the resolution and stride even if there is - * no valid metadata. This allows the caller to get the information - * needed to allocate user-space buffers. */ - md->width = stream_info->metadata_info.resolution.width; - md->height = stream_info->metadata_info.resolution.height; - md->stride = stream_info->metadata_info.stride; - - /* sanity check to avoid writing into unallocated memory. - * This does not return an error because it is a valid way - * for applications to detect that metadata is not enabled. */ - if (md->width == 0 || md->height == 0 || !md->data) - return 0; - - /* This is done in the atomisp_buf_done() */ - if (list_empty(&asd->metadata_ready[md_type])) { - dev_warn(isp->dev, "Metadata queue is empty now!\n"); - return -EAGAIN; - } - - mipi_info = atomisp_to_sensor_mipi_info( - isp->inputs[asd->input_curr].camera); - if (!mipi_info) - return -EINVAL; - - if (mipi_info->metadata_effective_width) { - for (i = 0; i < md->height; i++) - md->effective_width[i] = - mipi_info->metadata_effective_width[i]; - } - - md_buf = list_entry(asd->metadata_ready[md_type].next, - struct atomisp_metadata_buf, list); - md->exp_id = md_buf->metadata->exp_id; - if (md_buf->md_vptr) { - ret = copy_to_user(md->data, - md_buf->md_vptr, - stream_info->metadata_info.size); - } else { - hmm_load(md_buf->metadata->address, - asd->params.metadata_user[md_type], - stream_info->metadata_info.size); - - ret = copy_to_user(md->data, - asd->params.metadata_user[md_type], - stream_info->metadata_info.size); - } - if (ret) { - dev_err(isp->dev, "copy to user failed: copied %d bytes\n", - ret); - return -EFAULT; - } - - list_del_init(&md_buf->list); - list_add_tail(&md_buf->list, &asd->metadata[md_type]); - - dev_dbg(isp->dev, "%s: HAL de-queued metadata type %d with exp_id %d\n", - __func__, md_type, md->exp_id); - return 0; -} - -int atomisp_get_metadata_by_type(struct atomisp_sub_device *asd, int flag, - struct atomisp_metadata_with_type *md) -{ - struct atomisp_device *isp = asd->isp; - struct ia_css_stream_info *stream_info; - struct camera_mipi_info *mipi_info; - struct atomisp_metadata_buf *md_buf; - enum atomisp_metadata_type md_type; - int ret, i; - - if (flag != 0) - return -EINVAL; - - stream_info = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]. - stream_info; - - /* We always return the resolution and stride even if there is - * no valid metadata. This allows the caller to get the information - * needed to allocate user-space buffers. */ - md->width = stream_info->metadata_info.resolution.width; - md->height = stream_info->metadata_info.resolution.height; - md->stride = stream_info->metadata_info.stride; - - /* sanity check to avoid writing into unallocated memory. - * This does not return an error because it is a valid way - * for applications to detect that metadata is not enabled. */ - if (md->width == 0 || md->height == 0 || !md->data) - return 0; - - md_type = md->type; - if (md_type < 0 || md_type >= ATOMISP_METADATA_TYPE_NUM) - return -EINVAL; - - /* This is done in the atomisp_buf_done() */ - if (list_empty(&asd->metadata_ready[md_type])) { - dev_warn(isp->dev, "Metadata queue is empty now!\n"); - return -EAGAIN; - } - - mipi_info = atomisp_to_sensor_mipi_info( - isp->inputs[asd->input_curr].camera); - if (!mipi_info) - return -EINVAL; - - if (mipi_info->metadata_effective_width) { - for (i = 0; i < md->height; i++) - md->effective_width[i] = - mipi_info->metadata_effective_width[i]; - } - - md_buf = list_entry(asd->metadata_ready[md_type].next, - struct atomisp_metadata_buf, list); - md->exp_id = md_buf->metadata->exp_id; - if (md_buf->md_vptr) { - ret = copy_to_user(md->data, - md_buf->md_vptr, - stream_info->metadata_info.size); - } else { - hmm_load(md_buf->metadata->address, - asd->params.metadata_user[md_type], - stream_info->metadata_info.size); - - ret = copy_to_user(md->data, - asd->params.metadata_user[md_type], - stream_info->metadata_info.size); - } - if (ret) { - dev_err(isp->dev, "copy to user failed: copied %d bytes\n", - ret); - return -EFAULT; - } else { - list_del_init(&md_buf->list); - list_add_tail(&md_buf->list, &asd->metadata[md_type]); - } - dev_dbg(isp->dev, "%s: HAL de-queued metadata type %d with exp_id %d\n", - __func__, md_type, md->exp_id); - return 0; -} - /* * Function to calculate real zoom region for every pipe */ @@ -3221,14 +3009,11 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) lockdep_assert_held(&asd->isp->mutex); - if (atomisp_is_vf_pipe(pipe)) - return; - /* * CSS/FW requires set parameter and enqueue buffer happen after ISP * is streamon. */ - if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) + if (!asd->streaming) return; if (list_empty(&pipe->per_frame_params) || @@ -3299,15 +3084,7 @@ int atomisp_set_parameters(struct video_device *vdev, dev_dbg(asd->isp->dev, "set parameter(per_frame_setting %d) isp_config_id %d of %s\n", arg->per_frame_setting, arg->isp_config_id, vdev->name); - if (IS_ISP2401) { - if (atomisp_is_vf_pipe(pipe) && arg->per_frame_setting) { - dev_err(asd->isp->dev, "%s: vf pipe not support per_frame_setting", - __func__); - return -EINVAL; - } - } - - if (arg->per_frame_setting && !atomisp_is_vf_pipe(pipe)) { + if (arg->per_frame_setting) { /* * Per-frame setting enabled, we allocate a new parameter * buffer to cache the parameters and only when frame buffers @@ -3346,7 +3123,7 @@ int atomisp_set_parameters(struct video_device *vdev, if (ret) goto apply_parameter_failed; - if (!(arg->per_frame_setting && !atomisp_is_vf_pipe(pipe))) { + if (!arg->per_frame_setting) { /* indicate to CSS that we have parameters to be updated */ asd->params.css_update_params_needed = true; } else { @@ -3880,66 +3657,202 @@ static void __atomisp_init_stream_info(u16 stream_index, } } +static void atomisp_fill_pix_format(struct v4l2_pix_format *f, + u32 width, u32 height, + const struct atomisp_format_bridge *br_fmt) +{ + u32 bytes; + + f->width = width; + f->height = height; + f->pixelformat = br_fmt->pixelformat; + + /* Adding padding to width for bytesperline calculation */ + width = ia_css_frame_pad_width(width, br_fmt->sh_fmt); + bytes = BITS_TO_BYTES(br_fmt->depth * width); + + if (br_fmt->planar) + f->bytesperline = width; + else + f->bytesperline = bytes; + + f->sizeimage = PAGE_ALIGN(height * bytes); + + if (f->field == V4L2_FIELD_ANY) + f->field = V4L2_FIELD_NONE; + + /* + * FIXME: do we need to set this up differently, depending on the + * sensor or the pipeline? + */ + f->colorspace = V4L2_COLORSPACE_REC709; + f->ycbcr_enc = V4L2_YCBCR_ENC_709; + f->xfer_func = V4L2_XFER_FUNC_709; +} + +/* Get sensor padding values for the non padded width x height resolution */ +void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height, + u32 *padding_w, u32 *padding_h) +{ + struct atomisp_input_subdev *input = &isp->inputs[isp->asd.input_curr]; + struct v4l2_rect native_rect = input->native_rect; + const struct atomisp_in_fmt_conv *fc = NULL; + u32 min_pad_w = ISP2400_MIN_PAD_W; + u32 min_pad_h = ISP2400_MIN_PAD_H; + struct v4l2_mbus_framefmt *sink; + + if (!input->crop_support) { + *padding_w = pad_w; + *padding_h = pad_h; + return; + } + + width = min(width, input->active_rect.width); + height = min(height, input->active_rect.height); + + if (input->binning_support && width <= (input->active_rect.width / 2) && + height <= (input->active_rect.height / 2)) { + native_rect.width /= 2; + native_rect.height /= 2; + } + + *padding_w = min_t(u32, (native_rect.width - width) & ~1, pad_w); + *padding_h = min_t(u32, (native_rect.height - height) & ~1, pad_h); + + /* The below minimum padding requirements are for BYT / ISP2400 only */ + if (IS_ISP2401) + return; + + sink = atomisp_subdev_get_ffmt(&isp->asd.subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, + ATOMISP_SUBDEV_PAD_SINK); + if (sink) + fc = atomisp_find_in_fmt_conv(sink->code); + if (!fc) { + dev_warn(isp->dev, "%s: Could not get sensor format\n", __func__); + goto apply_min_padding; + } + + /* + * The ISP only supports GRBG for other bayer-orders additional padding + * is used so that the raw sensor data can be cropped to fix the order. + */ + if (fc->bayer_order == IA_CSS_BAYER_ORDER_RGGB || + fc->bayer_order == IA_CSS_BAYER_ORDER_GBRG) + min_pad_w += 2; + + if (fc->bayer_order == IA_CSS_BAYER_ORDER_BGGR || + fc->bayer_order == IA_CSS_BAYER_ORDER_GBRG) + min_pad_h += 2; + +apply_min_padding: + *padding_w = max_t(u32, *padding_w, min_pad_w); + *padding_h = max_t(u32, *padding_h, min_pad_h); +} + +static int atomisp_set_crop(struct atomisp_device *isp, + const struct v4l2_mbus_framefmt *format, + int which) +{ + struct atomisp_input_subdev *input = &isp->inputs[isp->asd.input_curr]; + struct v4l2_subdev_state pad_state = { + .pads = &input->pad_cfg, + }; + struct v4l2_subdev_selection sel = { + .which = which, + .target = V4L2_SEL_TGT_CROP, + .r.width = format->width, + .r.height = format->height, + }; + int ret; + + if (!input->crop_support) + return 0; + + /* Cropping is done before binning, when binning double the crop rect */ + if (input->binning_support && sel.r.width <= (input->native_rect.width / 2) && + sel.r.height <= (input->native_rect.height / 2)) { + sel.r.width *= 2; + sel.r.height *= 2; + } + + /* Clamp to avoid top/left calculations overflowing */ + sel.r.width = min(sel.r.width, input->native_rect.width); + sel.r.height = min(sel.r.height, input->native_rect.height); + + sel.r.left = ((input->native_rect.width - sel.r.width) / 2) & ~1; + sel.r.top = ((input->native_rect.height - sel.r.height) / 2) & ~1; + + ret = v4l2_subdev_call(input->camera, pad, set_selection, &pad_state, &sel); + if (ret) + dev_err(isp->dev, "Error setting crop to %ux%u @%ux%u: %d\n", + sel.r.width, sel.r.height, sel.r.left, sel.r.top, ret); + + return ret; +} + /* This function looks up the closest available resolution. */ -int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, - bool *res_overflow) +int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f, + const struct atomisp_format_bridge **fmt_ret, + const struct atomisp_format_bridge **snr_fmt_ret) { - struct atomisp_device *isp = video_get_drvdata(vdev); - struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; - struct v4l2_subdev_pad_config pad_cfg; + const struct atomisp_format_bridge *fmt, *snr_fmt; + struct atomisp_sub_device *asd = &isp->asd; + struct atomisp_input_subdev *input = &isp->inputs[asd->input_curr]; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, + .pads = &input->pad_cfg, }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_TRY, }; - const struct atomisp_format_bridge *fmt; + u32 padding_w, padding_h; int ret; - if (!asd) { - dev_err(isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - - if (!isp->inputs[asd->input_curr].camera) + if (!input->camera) return -EINVAL; fmt = atomisp_get_format_bridge(f->pixelformat); - if (!fmt) { - dev_err(isp->dev, "unsupported pixelformat!\n"); - fmt = atomisp_output_fmts; - } - - if (f->width <= 0 || f->height <= 0) - return -EINVAL; + /* Currently, raw formats are broken!!! */ + if (!fmt || fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { + f->pixelformat = V4L2_PIX_FMT_YUV420; - format.format.code = fmt->mbus_code; - format.format.width = f->width; - format.format.height = f->height; + fmt = atomisp_get_format_bridge(f->pixelformat); + if (!fmt) + return -EINVAL; + } - __atomisp_init_stream_info(ATOMISP_INPUT_STREAM_GENERAL, - (struct atomisp_input_stream_info *)format.format.reserved); + /* + * atomisp_set_fmt() will set the sensor resolution to the requested + * resolution + padding. Add padding here and remove it again after + * the set_fmt call, like atomisp_set_fmt_to_snr() does. + */ + atomisp_get_padding(isp, f->width, f->height, &padding_w, &padding_h); + v4l2_fill_mbus_format(&format.format, f, fmt->mbus_code); + format.format.width += padding_w; + format.format.height += padding_h; dev_dbg(isp->dev, "try_mbus_fmt: asking for %ux%u\n", format.format.width, format.format.height); - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, set_fmt, &pad_state, &format); + ret = atomisp_set_crop(isp, &format.format, V4L2_SUBDEV_FORMAT_TRY); + if (ret) + return ret; + + ret = v4l2_subdev_call(input->camera, pad, set_fmt, &pad_state, &format); if (ret) return ret; dev_dbg(isp->dev, "try_mbus_fmt: got %ux%u\n", format.format.width, format.format.height); - fmt = atomisp_get_format_bridge_from_mbus(format.format.code); - if (!fmt) { + snr_fmt = atomisp_get_format_bridge_from_mbus(format.format.code); + if (!snr_fmt) { dev_err(isp->dev, "unknown sensor format 0x%8.8x\n", format.format.code); return -EINVAL; } - f->pixelformat = fmt->pixelformat; + f->width = format.format.width - padding_w; + f->height = format.format.height - padding_h; /* * If the format is jpeg or custom RAW, then the width and height will @@ -3948,22 +3861,8 @@ int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, * the sensor driver. */ if (f->pixelformat == V4L2_PIX_FMT_JPEG || - f->pixelformat == V4L2_PIX_FMT_CUSTOM_M10MO_RAW) { - f->width = format.format.width; - f->height = format.format.height; - return 0; - } - - if (!res_overflow || (format.format.width < f->width && - format.format.height < f->height)) { - f->width = format.format.width; - f->height = format.format.height; - /* Set the flag when resolution requested is - * beyond the max value supported by sensor - */ - if (res_overflow) - *res_overflow = true; - } + f->pixelformat == V4L2_PIX_FMT_CUSTOM_M10MO_RAW) + goto out_fill_pix_format; /* app vs isp */ f->width = rounddown(clamp_t(u32, f->width, ATOM_ISP_MIN_WIDTH, @@ -3971,11 +3870,20 @@ int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, f->height = rounddown(clamp_t(u32, f->height, ATOM_ISP_MIN_HEIGHT, ATOM_ISP_MAX_HEIGHT), ATOM_ISP_STEP_HEIGHT); +out_fill_pix_format: + atomisp_fill_pix_format(f, f->width, f->height, fmt); + + if (fmt_ret) + *fmt_ret = fmt; + + if (snr_fmt_ret) + *snr_fmt_ret = snr_fmt; + return 0; } -enum mipi_port_id __get_mipi_port(struct atomisp_device *isp, - enum atomisp_camera_port port) +enum mipi_port_id atomisp_port_to_mipi_port(struct atomisp_device *isp, + enum atomisp_camera_port port) { switch (port) { case ATOMISP_CAMERA_PORT_PRIMARY: @@ -3983,9 +3891,7 @@ enum mipi_port_id __get_mipi_port(struct atomisp_device *isp, case ATOMISP_CAMERA_PORT_SECONDARY: return MIPI_PORT1_ID; case ATOMISP_CAMERA_PORT_TERTIARY: - if (MIPI_PORT1_ID + 1 != N_MIPI_PORT_ID) - return MIPI_PORT1_ID + 1; - fallthrough; + return MIPI_PORT2_ID; default: dev_err(isp->dev, "unsupported port: %d\n", port); return MIPI_PORT0_ID; @@ -3999,13 +3905,15 @@ static inline int atomisp_set_sensor_mipi_to_isp( { struct v4l2_control ctrl; struct atomisp_device *isp = asd->isp; + struct atomisp_input_subdev *input = &isp->inputs[asd->input_curr]; const struct atomisp_in_fmt_conv *fc; int mipi_freq = 0; unsigned int input_format, bayer_order; + enum atomisp_input_format metadata_format = ATOMISP_INPUT_FORMAT_EMBEDDED; + u32 mipi_port, metadata_width = 0, metadata_height = 0; ctrl.id = V4L2_CID_LINK_FREQ; - if (v4l2_g_ctrl - (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl) == 0) + if (v4l2_g_ctrl(input->camera->ctrl_handler, &ctrl) == 0) mipi_freq = ctrl.value; if (asd->stream_env[stream_id].isys_configs == 1) { @@ -4029,7 +3937,7 @@ static inline int atomisp_set_sensor_mipi_to_isp( /* Compatibility for sensors which provide no media bus code * in s_mbus_framefmt() nor support pad formats. */ - if (mipi_info->input_format != -1) { + if (mipi_info && mipi_info->input_format != -1) { bayer_order = mipi_info->raw_bayer_order; /* Input stream config is still needs configured */ @@ -4039,6 +3947,9 @@ static inline int atomisp_set_sensor_mipi_to_isp( if (!fc) return -EINVAL; input_format = fc->atomisp_in_fmt; + metadata_format = mipi_info->metadata_format; + metadata_width = mipi_info->metadata_width; + metadata_height = mipi_info->metadata_height; } else { struct v4l2_mbus_framefmt *sink; @@ -4055,18 +3966,17 @@ static inline int atomisp_set_sensor_mipi_to_isp( atomisp_css_input_set_format(asd, stream_id, input_format); atomisp_css_input_set_bayer_order(asd, stream_id, bayer_order); - fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt( - mipi_info->metadata_format); + fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt(metadata_format); if (!fc) return -EINVAL; + input_format = fc->atomisp_in_fmt; - atomisp_css_input_configure_port(asd, - __get_mipi_port(asd->isp, mipi_info->port), - mipi_info->num_lanes, + mipi_port = atomisp_port_to_mipi_port(isp, input->port); + atomisp_css_input_configure_port(asd, mipi_port, + isp->sensor_lanes[mipi_port], 0xffff4, mipi_freq, input_format, - mipi_info->metadata_width, - mipi_info->metadata_height); + metadata_width, metadata_height); return 0; } @@ -4134,16 +4044,15 @@ static int css_input_resolution_changed(struct atomisp_sub_device *asd, static int atomisp_set_fmt_to_isp(struct video_device *vdev, struct ia_css_frame_info *output_info, - struct v4l2_pix_format *pix, - unsigned int source_pad) + const struct v4l2_pix_format *pix) { struct camera_mipi_info *mipi_info; struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; + struct atomisp_input_subdev *input = &isp->inputs[asd->input_curr]; const struct atomisp_format_bridge *format; struct v4l2_rect *isp_sink_crop; enum ia_css_pipe_id pipe_id; - struct v4l2_subdev_fh fh; int (*configure_output)(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, unsigned int min_width, @@ -4155,7 +4064,7 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, int (*configure_pp_input)(struct atomisp_sub_device *asd, unsigned int width, unsigned int height) = configure_pp_input_nop; - const struct atomisp_in_fmt_conv *fc; + const struct atomisp_in_fmt_conv *fc = NULL; int ret, i; if (!asd) { @@ -4164,8 +4073,6 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, return -EINVAL; } - v4l2_fh_init(&fh.vfh, vdev); - isp_sink_crop = atomisp_subdev_get_rect( &asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); @@ -4174,18 +4081,16 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, if (!format) return -EINVAL; - if (isp->inputs[asd->input_curr].type != TEST_PATTERN) { - mipi_info = atomisp_to_sensor_mipi_info( - isp->inputs[asd->input_curr].camera); - if (!mipi_info) { - dev_err(isp->dev, "mipi_info is NULL\n"); - return -EINVAL; - } + if (input->type != TEST_PATTERN) { + mipi_info = atomisp_to_sensor_mipi_info(input->camera); + if (atomisp_set_sensor_mipi_to_isp(asd, ATOMISP_INPUT_STREAM_GENERAL, mipi_info)) return -EINVAL; - fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt( - mipi_info->input_format); + + if (mipi_info) + fc = atomisp_find_in_fmt_conv_by_atomisp_in_fmt(mipi_info->input_format); + if (!fc) fc = atomisp_find_in_fmt_conv( atomisp_subdev_get_ffmt(&asd->subdev, @@ -4204,49 +4109,24 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, * CSS still requires viewfinder configuration. */ { - struct v4l2_rect vf_size = {0}; - struct v4l2_mbus_framefmt vf_ffmt = {0}; + u32 width, height; if (pix->width < 640 || pix->height < 480) { - vf_size.width = pix->width; - vf_size.height = pix->height; + width = pix->width; + height = pix->height; } else { - vf_size.width = 640; - vf_size.height = 480; + width = 640; + height = 480; } - /* FIXME: proper format name for this one. See - atomisp_output_fmts[] in atomisp_v4l2.c */ - vf_ffmt.code = V4L2_MBUS_FMT_CUSTOM_YUV420; - - atomisp_subdev_set_selection(&asd->subdev, fh.state, - V4L2_SUBDEV_FORMAT_ACTIVE, - ATOMISP_SUBDEV_PAD_SOURCE_VF, - V4L2_SEL_TGT_COMPOSE, 0, &vf_size); - atomisp_subdev_set_ffmt(&asd->subdev, fh.state, - V4L2_SUBDEV_FORMAT_ACTIVE, - ATOMISP_SUBDEV_PAD_SOURCE_VF, &vf_ffmt); - asd->video_out_vf.sh_fmt = IA_CSS_FRAME_FORMAT_NV12; - - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { - atomisp_css_video_configure_viewfinder(asd, - vf_size.width, vf_size.height, 0, - asd->video_out_vf.sh_fmt); - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW || - source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) - atomisp_css_video_configure_viewfinder(asd, - vf_size.width, vf_size.height, 0, - asd->video_out_vf.sh_fmt); - else - atomisp_css_capture_configure_viewfinder(asd, - vf_size.width, vf_size.height, 0, - asd->video_out_vf.sh_fmt); - } else if (source_pad != ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW || + if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO || + asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { + atomisp_css_video_configure_viewfinder(asd, width, height, 0, + IA_CSS_FRAME_FORMAT_NV12); + } else if (asd->run_mode->val == ATOMISP_RUN_MODE_STILL_CAPTURE || asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { - atomisp_css_capture_configure_viewfinder(asd, - vf_size.width, vf_size.height, 0, - asd->video_out_vf.sh_fmt); + atomisp_css_capture_configure_viewfinder(asd, width, height, 0, + IA_CSS_FRAME_FORMAT_NV12); } } @@ -4268,7 +4148,7 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, configure_output = atomisp_css_video_configure_output; get_frame_info = atomisp_css_video_get_output_frame_info; pipe_id = IA_CSS_PIPE_ID_VIDEO; - } else if (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW) { + } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { configure_output = atomisp_css_preview_configure_output; get_frame_info = atomisp_css_preview_get_output_frame_info; configure_pp_input = atomisp_css_preview_configure_pp_input; @@ -4333,7 +4213,7 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, return ret; } - atomisp_update_grid_info(asd, pipe_id, source_pad); + atomisp_update_grid_info(asd, pipe_id); return 0; } @@ -4357,7 +4237,7 @@ static void atomisp_get_dis_envelop(struct atomisp_sub_device *asd, } static void atomisp_check_copy_mode(struct atomisp_sub_device *asd, - int source_pad, const struct v4l2_pix_format *f) + const struct v4l2_pix_format *f) { struct v4l2_mbus_framefmt *sink, *src; @@ -4370,7 +4250,7 @@ static void atomisp_check_copy_mode(struct atomisp_sub_device *asd, sink = atomisp_subdev_get_ffmt(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK); src = atomisp_subdev_get_ffmt(&asd->subdev, NULL, - V4L2_SUBDEV_FORMAT_ACTIVE, source_pad); + V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SOURCE); if (sink->code == src->code && sink->width == f->width && sink->height == f->height) asd->copy_mode = true; @@ -4381,26 +4261,23 @@ static void atomisp_check_copy_mode(struct atomisp_sub_device *asd, } static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_pix_format *f, - unsigned int padding_w, unsigned int padding_h, unsigned int dvs_env_w, unsigned int dvs_env_h) { struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; + struct atomisp_device *isp = asd->isp; + struct atomisp_input_subdev *input = &isp->inputs[asd->input_curr]; const struct atomisp_format_bridge *format; - struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, + .pads = &input->pad_cfg, }; struct v4l2_subdev_format vformat = { .which = V4L2_SUBDEV_FORMAT_TRY, }; struct v4l2_mbus_framefmt *ffmt = &vformat.format; struct v4l2_mbus_framefmt *req_ffmt; - struct atomisp_device *isp; struct atomisp_input_stream_info *stream_info = (struct atomisp_input_stream_info *)ffmt->reserved; - int source_pad = atomisp_subdev_source_pad(vdev); - struct v4l2_subdev_fh fh; int ret; if (!asd) { @@ -4409,20 +4286,16 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p return -EINVAL; } - isp = asd->isp; - - v4l2_fh_init(&fh.vfh, vdev); - format = atomisp_get_format_bridge(f->pixelformat); if (!format) return -EINVAL; v4l2_fill_mbus_format(ffmt, f, format->mbus_code); - ffmt->height += padding_h + dvs_env_h; - ffmt->width += padding_w + dvs_env_w; + ffmt->height += asd->sink_pad_padding_h + dvs_env_h; + ffmt->width += asd->sink_pad_padding_w + dvs_env_w; dev_dbg(isp->dev, "s_mbus_fmt: ask %ux%u (padding %ux%u, dvs %ux%u)\n", - ffmt->width, ffmt->height, padding_w, padding_h, + ffmt->width, ffmt->height, asd->sink_pad_padding_w, asd->sink_pad_padding_h, dvs_env_w, dvs_env_h); __atomisp_init_stream_info(ATOMISP_INPUT_STREAM_GENERAL, stream_info); @@ -4430,11 +4303,13 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p req_ffmt = ffmt; /* Disable dvs if resolution can't be supported by sensor */ - if (asd->params.video_dis_en && - source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO) { + if (asd->params.video_dis_en && asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { + ret = atomisp_set_crop(isp, &vformat.format, V4L2_SUBDEV_FORMAT_TRY); + if (ret) + return ret; + vformat.which = V4L2_SUBDEV_FORMAT_TRY; - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, set_fmt, &pad_state, &vformat); + ret = v4l2_subdev_call(input->camera, pad, set_fmt, &pad_state, &vformat); if (ret) return ret; @@ -4451,9 +4326,13 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p asd->params.video_dis_en = false; } } + + ret = atomisp_set_crop(isp, &vformat.format, V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret) + return ret; + vformat.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, pad, - set_fmt, NULL, &vformat); + ret = v4l2_subdev_call(input->camera, pad, set_fmt, NULL, &vformat); if (ret) return ret; @@ -4466,15 +4345,14 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, const struct v4l2_p ffmt->height < ATOM_ISP_STEP_HEIGHT) return -EINVAL; - if (asd->params.video_dis_en && - source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO && + if (asd->params.video_dis_en && asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && (ffmt->width < req_ffmt->width || ffmt->height < req_ffmt->height)) { dev_warn(isp->dev, "can not enable video dis due to sensor limitation."); asd->params.video_dis_en = false; } - atomisp_subdev_set_ffmt(&asd->subdev, fh.state, + atomisp_subdev_set_ffmt(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK, ffmt); @@ -4490,65 +4368,25 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) const struct atomisp_format_bridge *snr_format_bridge; struct ia_css_frame_info output_info; unsigned int dvs_env_w = 0, dvs_env_h = 0; - unsigned int padding_w = pad_w, padding_h = pad_h; struct v4l2_mbus_framefmt isp_source_fmt = {0}; - struct v4l2_subdev_format vformat = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; struct v4l2_rect isp_sink_crop; - u16 source_pad = atomisp_subdev_source_pad(vdev); - struct v4l2_subdev_fh fh; int ret; ret = atomisp_pipe_check(pipe, true); if (ret) return ret; - if (source_pad >= ATOMISP_SUBDEV_PADS_NUM) - return -EINVAL; - dev_dbg(isp->dev, - "setting resolution %ux%u on pad %u bytesperline %u\n", - f->fmt.pix.width, f->fmt.pix.height, source_pad, f->fmt.pix.bytesperline); - - v4l2_fh_init(&fh.vfh, vdev); - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - if (!format_bridge) - return -EINVAL; - - /* Currently, raw formats are broken!!! */ - - if (format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - if (!format_bridge) - return -EINVAL; - } - pipe->sh_fmt = format_bridge->sh_fmt; - pipe->pix.pixelformat = f->fmt.pix.pixelformat; + "setting resolution %ux%u bytesperline %u\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.bytesperline); /* Ensure that the resolution is equal or below the maximum supported */ - - vformat.which = V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_fill_mbus_format(&vformat.format, &f->fmt.pix, format_bridge->mbus_code); - vformat.format.height += padding_h; - vformat.format.width += padding_w; - - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, pad, - set_fmt, NULL, &vformat); + ret = atomisp_try_fmt(isp, &f->fmt.pix, &format_bridge, &snr_format_bridge); if (ret) return ret; - f->fmt.pix.width = vformat.format.width - padding_w; - f->fmt.pix.height = vformat.format.height - padding_h; - - snr_format_bridge = atomisp_get_format_bridge_from_mbus(vformat.format.code); - if (!snr_format_bridge) { - dev_warn(isp->dev, "Can't find bridge format\n"); - return -EINVAL; - } + pipe->sh_fmt = format_bridge->sh_fmt; + pipe->pix.pixelformat = format_bridge->pixelformat; atomisp_subdev_get_ffmt(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, @@ -4556,22 +4394,22 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) snr_format_bridge->mbus_code; isp_source_fmt.code = format_bridge->mbus_code; - atomisp_subdev_set_ffmt(&asd->subdev, fh.state, + atomisp_subdev_set_ffmt(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, - source_pad, &isp_source_fmt); + ATOMISP_SUBDEV_PAD_SOURCE, &isp_source_fmt); - if (!atomisp_subdev_format_conversion(asd, source_pad)) { - padding_w = 0; - padding_h = 0; + if (atomisp_subdev_format_conversion(asd)) { + atomisp_get_padding(isp, f->fmt.pix.width, f->fmt.pix.height, + &asd->sink_pad_padding_w, &asd->sink_pad_padding_h); + } else { + asd->sink_pad_padding_w = 0; + asd->sink_pad_padding_h = 0; } atomisp_get_dis_envelop(asd, f->fmt.pix.width, f->fmt.pix.height, &dvs_env_w, &dvs_env_h); - asd->capture_pad = source_pad; - - ret = atomisp_set_fmt_to_snr(vdev, &f->fmt.pix, - padding_w, padding_h, dvs_env_w, dvs_env_h); + ret = atomisp_set_fmt_to_snr(vdev, &f->fmt.pix, dvs_env_w, dvs_env_h); if (ret) { dev_warn(isp->dev, "Set format to sensor failed with %d\n", ret); @@ -4580,7 +4418,7 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) atomisp_csi_lane_config(isp); - atomisp_check_copy_mode(asd, source_pad, &f->fmt.pix); + atomisp_check_copy_mode(asd, &f->fmt.pix); isp_sink_crop = *atomisp_subdev_get_rect(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, @@ -4589,25 +4427,20 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) /* Try to enable YUV downscaling if ISP input is 10 % (either * width or height) bigger than the desired result. */ - if (isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || + if (!IS_MOFD || + isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || isp_sink_crop.height * 9 / 10 < f->fmt.pix.height || - (atomisp_subdev_format_conversion(asd, source_pad) && + (atomisp_subdev_format_conversion(asd) && (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO || asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER))) { isp_sink_crop.width = f->fmt.pix.width; isp_sink_crop.height = f->fmt.pix.height; - atomisp_subdev_set_selection(&asd->subdev, fh.state, + atomisp_subdev_set_selection(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, - ATOMISP_SUBDEV_PAD_SINK, - V4L2_SEL_TGT_CROP, - V4L2_SEL_FLAG_KEEP_CONFIG, - &isp_sink_crop); - atomisp_subdev_set_selection(&asd->subdev, fh.state, - V4L2_SUBDEV_FORMAT_ACTIVE, - source_pad, V4L2_SEL_TGT_COMPOSE, + ATOMISP_SUBDEV_PAD_SOURCE, V4L2_SEL_TGT_COMPOSE, 0, &isp_sink_crop); - } else if (IS_MOFD) { + } else { struct v4l2_rect main_compose = {0}; main_compose.width = isp_sink_crop.width; @@ -4622,104 +4455,25 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) f->fmt.pix.height); } - atomisp_subdev_set_selection(&asd->subdev, fh.state, + atomisp_subdev_set_selection(&asd->subdev, NULL, V4L2_SUBDEV_FORMAT_ACTIVE, - source_pad, - V4L2_SEL_TGT_COMPOSE, 0, - &main_compose); - } else { - struct v4l2_rect sink_crop = {0}; - struct v4l2_rect main_compose = {0}; - - main_compose.width = f->fmt.pix.width; - main_compose.height = f->fmt.pix.height; - - /* WORKAROUND: this override is universally enabled in - * GMIN to work around a CTS failures (GMINL-539) - * which appears to be related by a hardware - * performance limitation. It's unclear why this - * particular code triggers the issue. */ - if (isp_sink_crop.width * main_compose.height > - isp_sink_crop.height * main_compose.width) { - sink_crop.height = isp_sink_crop.height; - sink_crop.width = - DIV_NEAREST_STEP(sink_crop.height * f->fmt.pix.width, - f->fmt.pix.height, - ATOM_ISP_STEP_WIDTH); - } else { - sink_crop.width = isp_sink_crop.width; - sink_crop.height = - DIV_NEAREST_STEP(sink_crop.width * f->fmt.pix.height, - f->fmt.pix.width, - ATOM_ISP_STEP_HEIGHT); - } - atomisp_subdev_set_selection(&asd->subdev, fh.state, - V4L2_SUBDEV_FORMAT_ACTIVE, - ATOMISP_SUBDEV_PAD_SINK, - V4L2_SEL_TGT_CROP, - V4L2_SEL_FLAG_KEEP_CONFIG, - &sink_crop); - - atomisp_subdev_set_selection(&asd->subdev, fh.state, - V4L2_SUBDEV_FORMAT_ACTIVE, - source_pad, + ATOMISP_SUBDEV_PAD_SOURCE, V4L2_SEL_TGT_COMPOSE, 0, &main_compose); } - ret = atomisp_set_fmt_to_isp(vdev, &output_info, &f->fmt.pix, source_pad); + ret = atomisp_set_fmt_to_isp(vdev, &output_info, &f->fmt.pix); if (ret) { dev_warn(isp->dev, "Can't set format on ISP. Error %d\n", ret); return -EINVAL; } - pipe->pix.width = f->fmt.pix.width; - pipe->pix.height = f->fmt.pix.height; - pipe->pix.pixelformat = f->fmt.pix.pixelformat; - /* - * FIXME: do we need to setup this differently, depending on the - * sensor or the pipeline? - */ - pipe->pix.colorspace = V4L2_COLORSPACE_REC709; - pipe->pix.ycbcr_enc = V4L2_YCBCR_ENC_709; - pipe->pix.xfer_func = V4L2_XFER_FUNC_709; - - if (format_bridge->planar) { - pipe->pix.bytesperline = output_info.padded_width; - pipe->pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * - DIV_ROUND_UP(format_bridge->depth * - output_info.padded_width, 8)); - } else { - pipe->pix.bytesperline = - DIV_ROUND_UP(format_bridge->depth * - output_info.padded_width, 8); - pipe->pix.sizeimage = - PAGE_ALIGN(f->fmt.pix.height * pipe->pix.bytesperline); - } - dev_dbg(isp->dev, "%s: image size: %d, %d bytes per line\n", - __func__, pipe->pix.sizeimage, pipe->pix.bytesperline); - - if (f->fmt.pix.field == V4L2_FIELD_ANY) - f->fmt.pix.field = V4L2_FIELD_NONE; - pipe->pix.field = f->fmt.pix.field; + atomisp_fill_pix_format(&pipe->pix, f->fmt.pix.width, f->fmt.pix.height, format_bridge); f->fmt.pix = pipe->pix; f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width * pipe->pix.height * 2); - /* - * If in video 480P case, no GFX throttle - */ - if (asd->run_mode->val == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO && - f->fmt.pix.width == 720 && f->fmt.pix.height == 480) - isp->need_gfx_throttle = false; - else - isp->need_gfx_throttle = true; - - /* Report the needed sizes */ - f->fmt.pix.sizeimage = pipe->pix.sizeimage; - f->fmt.pix.bytesperline = pipe->pix.bytesperline; - dev_dbg(isp->dev, "%s: %dx%d, image size: %d, %d bytes per line\n", __func__, f->fmt.pix.width, f->fmt.pix.height, @@ -4790,30 +4544,6 @@ out: return ret; } -/* - * set auto exposure metering window to camera sensor - */ -int atomisp_s_ae_window(struct atomisp_sub_device *asd, - struct atomisp_ae_window *arg) -{ - struct atomisp_device *isp = asd->isp; - /* Coverity CID 298071 - initialzize struct */ - struct v4l2_subdev_selection sel = { 0 }; - - sel.r.left = arg->x_left; - sel.r.top = arg->y_top; - sel.r.width = arg->x_right - arg->x_left + 1; - sel.r.height = arg->y_bottom - arg->y_top + 1; - - if (v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, set_selection, NULL, &sel)) { - dev_err(isp->dev, "failed to call sensor set_selection.\n"); - return -EINVAL; - } - - return 0; -} - int atomisp_flash_enable(struct atomisp_sub_device *asd, int num_frames) { struct atomisp_device *isp = asd->isp; @@ -4836,26 +4566,6 @@ int atomisp_flash_enable(struct atomisp_sub_device *asd, int num_frames) return 0; } -bool atomisp_is_vf_pipe(struct atomisp_video_pipe *pipe) -{ - struct atomisp_sub_device *asd = pipe->asd; - - if (!asd) { - dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, pipe->vdev.name); - return false; - } - - if (pipe == &asd->video_out_vf) - return true; - - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && - pipe == &asd->video_out_preview) - return true; - - return false; -} - static int __checking_exp_id(struct atomisp_sub_device *asd, int exp_id) { struct atomisp_device *isp = asd->isp; @@ -4864,7 +4574,7 @@ static int __checking_exp_id(struct atomisp_sub_device *asd, int exp_id) dev_warn(isp->dev, "%s Raw Buffer Lock is disable.\n", __func__); return -EINVAL; } - if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) { + if (!asd->streaming) { dev_err(isp->dev, "%s streaming %d invalid exp_id %d.\n", __func__, exp_id, asd->streaming); return -EINVAL; @@ -4986,7 +4696,7 @@ int atomisp_enable_dz_capt_pipe(struct atomisp_sub_device *asd, int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event) { - if (!event || asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) + if (!event || !asd->streaming) return -EINVAL; lockdep_assert_held(&asd->isp->mutex); @@ -5013,69 +4723,3 @@ int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event) return 0; } - -static int atomisp_get_pipe_id(struct atomisp_video_pipe *pipe) -{ - struct atomisp_sub_device *asd = pipe->asd; - - if (!asd) { - dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, pipe->vdev.name); - return -EINVAL; - } - - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { - return IA_CSS_PIPE_ID_VIDEO; - } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { - return IA_CSS_PIPE_ID_CAPTURE; - } else if (pipe == &asd->video_out_video_capture) { - return IA_CSS_PIPE_ID_VIDEO; - } else if (pipe == &asd->video_out_vf) { - return IA_CSS_PIPE_ID_CAPTURE; - } else if (pipe == &asd->video_out_preview) { - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) - return IA_CSS_PIPE_ID_VIDEO; - else - return IA_CSS_PIPE_ID_PREVIEW; - } else if (pipe == &asd->video_out_capture) { - if (asd->copy_mode) - return IA_CSS_PIPE_ID_COPY; - else - return IA_CSS_PIPE_ID_CAPTURE; - } - - /* fail through */ - dev_warn(asd->isp->dev, "%s failed to find proper pipe\n", - __func__); - return IA_CSS_PIPE_ID_CAPTURE; -} - -int atomisp_get_invalid_frame_num(struct video_device *vdev, - int *invalid_frame_num) -{ - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - enum ia_css_pipe_id pipe_id; - struct ia_css_pipe_info p_info; - int ret; - - pipe_id = atomisp_get_pipe_id(pipe); - if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].pipes[pipe_id]) { - dev_warn(asd->isp->dev, - "%s pipe %d has not been created yet, do SET_FMT first!\n", - __func__, pipe_id); - return -EINVAL; - } - - ret = ia_css_pipe_get_info( - asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] - .pipes[pipe_id], &p_info); - if (!ret) { - *invalid_frame_num = p_info.num_invalid_frames; - return 0; - } else { - dev_warn(asd->isp->dev, "%s get pipe infor failed %d\n", - __func__, ret); - return -EINVAL; - } -} diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index 399b549bcf83..8305161d2062 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -59,7 +59,6 @@ int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe); void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state); void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, enum vb2_buffer_state state, bool warn_on_css_frames); -void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd); void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd); /* Interrupt functions */ @@ -160,13 +159,6 @@ int atomisp_set_dis_vector(struct atomisp_sub_device *asd, int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, struct atomisp_3a_statistics *config); -/* Function to get metadata from isp */ -int atomisp_get_metadata(struct atomisp_sub_device *asd, int flag, - struct atomisp_metadata *config); - -int atomisp_get_metadata_by_type(struct atomisp_sub_device *asd, int flag, - struct atomisp_metadata_with_type *config); - int atomisp_set_parameters(struct video_device *vdev, struct atomisp_parameters *arg); @@ -258,9 +250,14 @@ int atomisp_makeup_css_parameters(struct atomisp_sub_device *asd, int atomisp_compare_grid(struct atomisp_sub_device *asd, struct atomisp_grid_info *atomgrid); +/* Get sensor padding values for the non padded width x height resolution */ +void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height, + u32 *padding_w, u32 *padding_h); + /* This function looks up the closest available resolution. */ -int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, - bool *res_overflow); +int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f, + const struct atomisp_format_bridge **fmt_ret, + const struct atomisp_format_bridge **snr_fmt_ret); int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f); @@ -269,9 +266,6 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd, void atomisp_free_internal_buffers(struct atomisp_sub_device *asd); -int atomisp_s_ae_window(struct atomisp_sub_device *asd, - struct atomisp_ae_window *arg); - int atomisp_flash_enable(struct atomisp_sub_device *asd, int num_frames); @@ -284,15 +278,11 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, enum ia_css_pipe_id css_pipe_id, bool q_buffers, enum atomisp_input_stream_id stream_id); -void atomisp_css_flush(struct atomisp_device *isp); - /* Events. Only one event has to be exported for now. */ void atomisp_eof_event(struct atomisp_sub_device *asd, uint8_t exp_id); -enum mipi_port_id __get_mipi_port(struct atomisp_device *isp, - enum atomisp_camera_port port); - -bool atomisp_is_vf_pipe(struct atomisp_video_pipe *pipe); +enum mipi_port_id atomisp_port_to_mipi_port(struct atomisp_device *isp, + enum atomisp_camera_port port); void atomisp_apply_css_parameters( struct atomisp_sub_device *asd, diff --git a/drivers/staging/media/atomisp/pci/atomisp_common.h b/drivers/staging/media/atomisp/pci/atomisp_common.h index 07c38e487a66..9d23a6ccfc33 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_common.h +++ b/drivers/staging/media/atomisp/pci/atomisp_common.h @@ -37,6 +37,10 @@ extern int mipicsi_flag; extern int pad_w; extern int pad_h; +/* Minimum padding requirements for ISP2400 (BYT) */ +#define ISP2400_MIN_PAD_W 12 +#define ISP2400_MIN_PAD_H 12 + #define CSS_DTRACE_VERBOSITY_LEVEL 5 /* Controls trace verbosity */ #define CSS_DTRACE_VERBOSITY_TIMEOUT 9 /* Verbosity on ISP timeout */ #define MRFLD_MAX_ZOOM_FACTOR 1024 diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat.h b/drivers/staging/media/atomisp/pci/atomisp_compat.h index 218e8ac276c8..e9e4bfb0f5f9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat.h @@ -78,8 +78,7 @@ int atomisp_q_dis_buffer_to_css(struct atomisp_sub_device *asd, void ia_css_mmu_invalidate_cache(void); -int atomisp_css_start(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, bool in_reset); +int atomisp_css_start(struct atomisp_sub_device *asd); void atomisp_css_update_isp_params(struct atomisp_sub_device *asd); void atomisp_css_update_isp_params_on_pipe(struct atomisp_sub_device *asd, @@ -113,8 +112,7 @@ void atomisp_css_free_metadata_buffer(struct atomisp_metadata_buf *metadata_buf); int atomisp_css_get_grid_info(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, - int source_pad); + enum ia_css_pipe_id pipe_id); int atomisp_alloc_3a_output_buf(struct atomisp_sub_device *asd); @@ -151,10 +149,6 @@ int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd, enum atomisp_input_stream_id stream_id, struct v4l2_mbus_framefmt *ffmt); -int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum atomisp_input_format input_format); - void atomisp_css_isys_two_stream_cfg_update_stream1( struct atomisp_sub_device *asd, enum atomisp_input_stream_id stream_id, @@ -210,15 +204,6 @@ void atomisp_css_capture_enable_online(struct atomisp_sub_device *asd, void atomisp_css_preview_enable_online(struct atomisp_sub_device *asd, unsigned short stream_index, bool enable); -void atomisp_css_video_enable_online(struct atomisp_sub_device *asd, - bool enable); - -void atomisp_css_enable_continuous(struct atomisp_sub_device *asd, - bool enable); - -void atomisp_css_enable_cvf(struct atomisp_sub_device *asd, - bool enable); - int atomisp_css_input_configure_port(struct atomisp_sub_device *asd, enum mipi_port_id port, unsigned int num_lanes, @@ -229,10 +214,9 @@ int atomisp_css_input_configure_port(struct atomisp_sub_device *asd, unsigned int metadata_height); int atomisp_create_pipes_stream(struct atomisp_sub_device *asd); -void atomisp_destroy_pipes_stream_force(struct atomisp_sub_device *asd); +void atomisp_destroy_pipes_stream(struct atomisp_sub_device *asd); -void atomisp_css_stop(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, bool in_reset); +void atomisp_css_stop(struct atomisp_sub_device *asd, bool in_reset); void atomisp_css_continuous_set_num_raw_frames( struct atomisp_sub_device *asd, @@ -244,22 +228,6 @@ int atomisp_css_copy_configure_output(struct atomisp_sub_device *asd, unsigned int padded_width, enum ia_css_frame_format format); -int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int padded_width, - enum ia_css_frame_format format); - -int atomisp_css_yuvpp_get_output_frame_info( - struct atomisp_sub_device *asd, - unsigned int stream_index, - struct ia_css_frame_info *info); - -int atomisp_css_yuvpp_get_viewfinder_frame_info( - struct atomisp_sub_device *asd, - unsigned int stream_index, - struct ia_css_frame_info *info); - int atomisp_css_preview_configure_output(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, unsigned int min_width, @@ -276,7 +244,6 @@ int atomisp_css_video_configure_output(struct atomisp_sub_device *asd, enum ia_css_frame_format format); int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, - u16 source_pad, struct ia_css_frame_info *frame_info); int atomisp_css_video_configure_viewfinder(struct atomisp_sub_device *asd, diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index 1dae2a7cfdd9..b13d1cb4668d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -548,7 +548,7 @@ static int __destroy_pipes(struct atomisp_sub_device *asd) return 0; } -void atomisp_destroy_pipes_stream_force(struct atomisp_sub_device *asd) +void atomisp_destroy_pipes_stream(struct atomisp_sub_device *asd) { if (__destroy_streams(asd)) dev_warn(asd->isp->dev, "destroy stream failed.\n"); @@ -650,23 +650,11 @@ static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd, return true; return false; - case ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE: - if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || - pipe_id == IA_CSS_PIPE_ID_PREVIEW) - return true; - - return false; case ATOMISP_RUN_MODE_VIDEO: if (pipe_id == IA_CSS_PIPE_ID_VIDEO || pipe_id == IA_CSS_PIPE_ID_YUVPP) return true; return false; - case ATOMISP_RUN_MODE_SDV: - if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || - pipe_id == IA_CSS_PIPE_ID_VIDEO) - return true; - - return false; } return false; @@ -758,7 +746,7 @@ int atomisp_create_pipes_stream(struct atomisp_sub_device *asd) int atomisp_css_update_stream(struct atomisp_sub_device *asd) { - atomisp_destroy_pipes_stream_force(asd); + atomisp_destroy_pipes_stream(asd); return atomisp_create_pipes_stream(asd); } @@ -997,57 +985,22 @@ int atomisp_q_dis_buffer_to_css(struct atomisp_sub_device *asd, return 0; } -int atomisp_css_start(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, bool in_reset) +int atomisp_css_start(struct atomisp_sub_device *asd) { struct atomisp_device *isp = asd->isp; bool sp_is_started = false; int ret = 0, i = 0; - if (in_reset) { - ret = atomisp_css_update_stream(asd); - if (ret) - return ret; + if (!sh_css_hrt_system_is_idle()) + dev_err(isp->dev, "CSS HW not idle before starting SP\n"); - /* Invalidate caches. FIXME: should flush only necessary buffers */ - wbinvd(); + if (ia_css_start_sp()) { + dev_err(isp->dev, "start sp error.\n"); + ret = -EINVAL; + goto start_err; } - /* - * For dual steam case, it is possible that: - * 1: for this stream, it is at the stage that: - * - after set_fmt is called - * - before stream on is called - * 2: for the other stream, the stream off is called which css reset - * has been done. - * - * Thus the stream created in set_fmt get destroyed and need to be - * recreated in the next stream on. - */ - if (!asd->stream_prepared) { - ret = atomisp_create_pipes_stream(asd); - if (ret) - return ret; - } - /* - * SP can only be started one time - * if atomisp_subdev_streaming_count() tell there already has some - * subdev at streamming, then SP should already be started previously, - * so need to skip start sp procedure - */ - if (atomisp_streaming_count(isp)) { - dev_dbg(isp->dev, "skip start sp\n"); - } else { - if (!sh_css_hrt_system_is_idle()) - dev_err(isp->dev, "CSS HW not idle before starting SP\n"); - if (ia_css_start_sp()) { - dev_err(isp->dev, "start sp error.\n"); - ret = -EINVAL; - goto start_err; - } else { - sp_is_started = true; - } - } + sp_is_started = true; for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) { if (asd->stream_env[i].stream) { @@ -1066,16 +1019,15 @@ int atomisp_css_start(struct atomisp_sub_device *asd, return 0; start_err: - atomisp_destroy_pipes_stream_force(asd); - - /* css 2.0 API limitation: ia_css_stop_sp() could be only called after - * destroy all pipes - */ /* - * SP can not be stop if other streams are in use + * CSS 2.0 API limitation: ia_css_stop_sp() can only be called after + * destroying all pipes. */ - if ((atomisp_streaming_count(isp) == 0) && sp_is_started) + if (sp_is_started) { + atomisp_destroy_pipes_stream(asd); ia_css_stop_sp(); + atomisp_create_pipes_stream(asd); + } return ret; } @@ -1340,8 +1292,7 @@ void atomisp_css_free_stat_buffers(struct atomisp_sub_device *asd) } int atomisp_css_get_grid_info(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, - int source_pad) + enum ia_css_pipe_id pipe_id) { struct ia_css_pipe_info p_info; struct ia_css_grid_info old_info; @@ -1624,29 +1575,6 @@ int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd, return 0; } -int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum atomisp_input_format input_format) -{ - struct ia_css_stream_config *s_config = - &asd->stream_env[stream_id].stream_config; - - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].input_res.width = - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.width; - - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].input_res.height = - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.height / 2; - - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].linked_isys_stream_id - = IA_CSS_STREAM_ISYS_STREAM_0; - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].format = - ATOMISP_INPUT_FORMAT_USER_DEF1; - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].format = - ATOMISP_INPUT_FORMAT_USER_DEF2; - s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].valid = true; - return 0; -} - void atomisp_css_isys_two_stream_cfg_update_stream1( struct atomisp_sub_device *asd, enum atomisp_input_stream_id stream_id, @@ -1832,49 +1760,6 @@ void atomisp_css_preview_enable_online(struct atomisp_sub_device *asd, } } -void atomisp_css_video_enable_online(struct atomisp_sub_device *asd, - bool enable) -{ - struct atomisp_stream_env *stream_env = - &asd->stream_env[ATOMISP_INPUT_STREAM_VIDEO]; - int i; - - if (stream_env->stream_config.online != enable) { - stream_env->stream_config.online = enable; - for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) - stream_env->update_pipe[i] = true; - } -} - -void atomisp_css_enable_continuous(struct atomisp_sub_device *asd, - bool enable) -{ - struct atomisp_stream_env *stream_env = - &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; - int i; - - if (stream_env->stream_config.continuous != !!enable) { - stream_env->stream_config.continuous = !!enable; - stream_env->stream_config.pack_raw_pixels = true; - for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) - stream_env->update_pipe[i] = true; - } -} - -void atomisp_css_enable_cvf(struct atomisp_sub_device *asd, - bool enable) -{ - struct atomisp_stream_env *stream_env = - &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; - int i; - - if (stream_env->stream_config.disable_cont_viewfinder != !enable) { - stream_env->stream_config.disable_cont_viewfinder = !enable; - for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) - stream_env->update_pipe[i] = true; - } -} - int atomisp_css_input_configure_port( struct atomisp_sub_device *asd, enum mipi_port_id port, @@ -1919,23 +1804,20 @@ int atomisp_css_input_configure_port( return 0; } -void atomisp_css_stop(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, bool in_reset) +void atomisp_css_stop(struct atomisp_sub_device *asd, bool in_reset) { - struct atomisp_device *isp = asd->isp; unsigned long irqflags; unsigned int i; - /* if is called in atomisp_reset(), force destroy streams and pipes */ - atomisp_destroy_pipes_stream_force(asd); + /* + * CSS 2.0 API limitation: ia_css_stop_sp() can only be called after + * destroying all pipes. + */ + atomisp_destroy_pipes_stream(asd); atomisp_init_raw_buffer_bitmap(asd); - /* - * SP can not be stop if other streams are in use - */ - if (atomisp_streaming_count(isp) == 0) - ia_css_stop_sp(); + ia_css_stop_sp(); if (!in_reset) { struct atomisp_stream_env *stream_env; @@ -1970,10 +1852,7 @@ void atomisp_css_stop(struct atomisp_sub_device *asd, list_splice_init(&asd->metadata_ready[i], &asd->metadata[i]); } - atomisp_flush_params_queue(&asd->video_out_capture); - atomisp_flush_params_queue(&asd->video_out_vf); - atomisp_flush_params_queue(&asd->video_out_preview); - atomisp_flush_params_queue(&asd->video_out_video_capture); + atomisp_flush_params_queue(&asd->video_out); atomisp_free_css_parameters(&asd->params.css_param); memset(&asd->params.css_param, 0, sizeof(asd->params.css_param)); } @@ -2424,54 +2303,33 @@ static int __get_frame_info(struct atomisp_sub_device *asd, return 0; get_info_err: - atomisp_destroy_pipes_stream_force(asd); + atomisp_destroy_pipes_stream(asd); return -EINVAL; } -static unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd, - uint16_t source_pad) +static unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd) { - struct atomisp_device *isp = asd->isp; - - switch (source_pad) { - case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: - if (asd->copy_mode) - return IA_CSS_PIPE_ID_COPY; - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO - || asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) - return IA_CSS_PIPE_ID_VIDEO; - - return IA_CSS_PIPE_ID_CAPTURE; - case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: - if (asd->copy_mode) - return IA_CSS_PIPE_ID_COPY; + if (asd->copy_mode) + return IA_CSS_PIPE_ID_COPY; + switch (asd->run_mode->val) { + case ATOMISP_RUN_MODE_VIDEO: + return IA_CSS_PIPE_ID_VIDEO; + case ATOMISP_RUN_MODE_STILL_CAPTURE: return IA_CSS_PIPE_ID_CAPTURE; - case ATOMISP_SUBDEV_PAD_SOURCE_VF: - if (!atomisp_is_mbuscode_raw(asd->fmt[asd->capture_pad].fmt.code)) { - return IA_CSS_PIPE_ID_CAPTURE; - } - fallthrough; - case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: - if (asd->copy_mode) - return IA_CSS_PIPE_ID_COPY; - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) - return IA_CSS_PIPE_ID_VIDEO; - + case ATOMISP_RUN_MODE_PREVIEW: return IA_CSS_PIPE_ID_PREVIEW; } - dev_warn(isp->dev, - "invalid source pad:%d, return default preview pipe index.\n", - source_pad); + + dev_warn(asd->isp->dev, "cannot determine pipe-index return default preview pipe\n"); return IA_CSS_PIPE_ID_PREVIEW; } int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, - u16 source_pad, struct ia_css_frame_info *frame_info) { struct ia_css_pipe_info info; - int pipe_index = atomisp_get_pipe_index(asd, source_pad); + int pipe_index = atomisp_get_pipe_index(asd); int stream_index; struct atomisp_device *isp = asd->isp; @@ -2485,34 +2343,8 @@ int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, return -EINVAL; } - switch (source_pad) { - case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: - *frame_info = info.output_info[0]; - break; - case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: - *frame_info = info.output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; - break; - case ATOMISP_SUBDEV_PAD_SOURCE_VF: - if (stream_index == ATOMISP_INPUT_STREAM_POSTVIEW) - *frame_info = info.output_info[0]; - else - *frame_info = info.vf_output_info[0]; - break; - case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && - (pipe_index == IA_CSS_PIPE_ID_VIDEO || - pipe_index == IA_CSS_PIPE_ID_YUVPP)) - *frame_info = info.vf_output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; - else - *frame_info = - info.output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; - - break; - default: - frame_info = NULL; - break; - } - return frame_info ? 0 : -EINVAL; + *frame_info = info.output_info[0]; + return 0; } int atomisp_css_copy_configure_output(struct atomisp_sub_device *asd, @@ -2530,39 +2362,6 @@ int atomisp_css_copy_configure_output(struct atomisp_sub_device *asd, return 0; } -int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int padded_width, - enum ia_css_frame_format format) -{ - asd->stream_env[stream_index].pipe_configs[IA_CSS_PIPE_ID_YUVPP]. - default_capture_config.mode = - IA_CSS_CAPTURE_MODE_RAW; - - __configure_output(asd, stream_index, width, height, padded_width, - format, IA_CSS_PIPE_ID_YUVPP); - return 0; -} - -int atomisp_css_yuvpp_get_output_frame_info( - struct atomisp_sub_device *asd, - unsigned int stream_index, - struct ia_css_frame_info *info) -{ - return __get_frame_info(asd, stream_index, info, - ATOMISP_CSS_OUTPUT_FRAME, IA_CSS_PIPE_ID_YUVPP); -} - -int atomisp_css_yuvpp_get_viewfinder_frame_info( - struct atomisp_sub_device *asd, - unsigned int stream_index, - struct ia_css_frame_info *info) -{ - return __get_frame_info(asd, stream_index, info, - ATOMISP_CSS_VF_FRAME, IA_CSS_PIPE_ID_YUVPP); -} - int atomisp_css_preview_configure_output(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, unsigned int min_width, @@ -3271,7 +3070,7 @@ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd, return -EINVAL; /* isp needs to be streaming to get DIS statistics */ - if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) + if (!asd->streaming) return -EINVAL; if (atomisp_compare_dvs_grid(asd, &stats->dvs2_stat.grid_info) != 0) @@ -3400,7 +3199,7 @@ static bool atomisp_css_isr_get_stream_id(struct ia_css_pipe *css_pipe, struct atomisp_stream_env *stream_env; int i, j; - if (isp->asd.streaming == ATOMISP_DEVICE_STREAMING_DISABLED) + if (!isp->asd.streaming) return false; for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) { diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h b/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h index 33821b51d90e..762520ed87a5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h @@ -67,26 +67,6 @@ struct atomisp_3a_statistics32 { u32 isp_config_id; }; -struct atomisp_metadata_with_type32 { - /* to specify which type of metadata to get */ - enum atomisp_metadata_type type; - compat_uptr_t data; - u32 width; - u32 height; - u32 stride; /* in bytes */ - u32 exp_id; /* exposure ID */ - compat_uptr_t effective_width; -}; - -struct atomisp_metadata32 { - compat_uptr_t data; - u32 width; - u32 height; - u32 stride; - u32 exp_id; - compat_uptr_t effective_width; -}; - struct atomisp_morph_table32 { unsigned int enabled; unsigned int height; @@ -134,18 +114,6 @@ struct atomisp_overlay32 { unsigned int overlay_start_y; }; -struct atomisp_calibration_group32 { - unsigned int size; - unsigned int type; - compat_uptr_t calb_grp_values; -}; - -struct v4l2_private_int_data32 { - __u32 size; - compat_uptr_t data; - __u32 reserved[2]; -}; - struct atomisp_shading_table32 { __u32 enable; __u32 sensor_width; @@ -249,11 +217,6 @@ struct atomisp_dvs_6axis_config32 { compat_uptr_t ycoords_uv; }; -struct atomisp_sensor_ae_bracketing_lut32 { - compat_uptr_t lut; - unsigned int lut_size; -}; - #define ATOMISP_IOC_G_HISTOGRAM32 \ _IOWR('v', BASE_VIDIOC_PRIVATE + 3, struct atomisp_histogram32) #define ATOMISP_IOC_S_HISTOGRAM32 \ @@ -283,28 +246,10 @@ struct atomisp_sensor_ae_bracketing_lut32 { #define ATOMISP_IOC_S_ISP_OVERLAY32 \ _IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay32) -#define ATOMISP_IOC_G_SENSOR_CALIBRATION_GROUP32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 22, struct atomisp_calibration_group32) - -#define ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 26, struct v4l2_private_int_data32) - #define ATOMISP_IOC_S_ISP_SHD_TAB32 \ _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table32) -#define ATOMISP_IOC_G_MOTOR_PRIV_INT_DATA32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 29, struct v4l2_private_int_data32) - #define ATOMISP_IOC_S_PARAMETERS32 \ _IOW('v', BASE_VIDIOC_PRIVATE + 32, struct atomisp_parameters32) -#define ATOMISP_IOC_G_METADATA32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 34, struct atomisp_metadata32) - -#define ATOMISP_IOC_G_METADATA_BY_TYPE32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 34, struct atomisp_metadata_with_type32) - -#define ATOMISP_IOC_S_SENSOR_AE_BRACKETING_LUT32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 43, struct atomisp_sensor_ae_bracketing_lut32) - #endif /* __ATOMISP_COMPAT_IOCTL32_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c index b00bc0b7aaad..abf55a86f795 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c @@ -322,15 +322,11 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd) struct v4l2_control ctrl; struct atomisp_device *isp = asd->isp; - struct camera_mipi_info *mipi_info; int mipi_freq = 0; enum atomisp_camera_port port; - int n; - mipi_info = atomisp_to_sensor_mipi_info( - isp->inputs[asd->input_curr].camera); - port = mipi_info->port; + port = isp->inputs[asd->input_curr].port; ctrl.id = V4L2_CID_LINK_FREQ; if (v4l2_g_ctrl @@ -375,6 +371,10 @@ int atomisp_mipi_csi2_init(struct atomisp_device *isp) unsigned int i; int ret; + ret = atomisp_csi2_bridge_init(isp); + if (ret < 0) + return ret; + for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { csi2_port = &isp->csi2_port[i]; csi2_port->isp = isp; diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h index b245b2f5ce99..16ddb3ab2963 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h @@ -18,17 +18,107 @@ #ifndef __ATOMISP_CSI2_H__ #define __ATOMISP_CSI2_H__ +#include <linux/gpio/consumer.h> +#include <linux/property.h> + #include <media/v4l2-subdev.h> #include <media/v4l2-ctrls.h> +#include "../../include/linux/atomisp.h" + #define CSI2_PAD_SINK 0 #define CSI2_PAD_SOURCE 1 #define CSI2_PADS_NUM 2 -struct atomisp_device; +#define CSI2_MAX_LANES 4 +#define CSI2_MAX_LINK_FREQS 3 + +#define CSI2_MAX_ACPI_GPIOS 2u + +struct acpi_device; struct v4l2_device; + +struct atomisp_device; struct atomisp_sub_device; +struct atomisp_csi2_acpi_gpio_map { + struct acpi_gpio_params params[CSI2_MAX_ACPI_GPIOS]; + struct acpi_gpio_mapping mapping[CSI2_MAX_ACPI_GPIOS + 1]; +}; + +struct atomisp_csi2_acpi_gpio_parsing_data { + struct acpi_device *adev; + struct atomisp_csi2_acpi_gpio_map *map; + u32 settings[CSI2_MAX_ACPI_GPIOS]; + unsigned int settings_count; + unsigned int res_count; + unsigned int map_count; +}; + +enum atomisp_csi2_sensor_swnodes { + SWNODE_SENSOR, + SWNODE_SENSOR_PORT, + SWNODE_SENSOR_ENDPOINT, + SWNODE_CSI2_PORT, + SWNODE_CSI2_ENDPOINT, + SWNODE_COUNT +}; + +struct atomisp_csi2_property_names { + char clock_frequency[16]; + char rotation[9]; + char bus_type[9]; + char data_lanes[11]; + char remote_endpoint[16]; + char link_frequencies[17]; +}; + +struct atomisp_csi2_node_names { + char port[7]; + char endpoint[11]; + char remote_port[7]; +}; + +struct atomisp_csi2_sensor_config { + const char *hid; + int lanes; + int nr_link_freqs; + u64 link_freqs[CSI2_MAX_LINK_FREQS]; +}; + +struct atomisp_csi2_sensor { + /* Append port in "-%u" format as suffix of HID */ + char name[ACPI_ID_LEN + 4]; + struct acpi_device *adev; + int port; + int lanes; + + /* SWNODE_COUNT + 1 for terminating NULL */ + const struct software_node *group[SWNODE_COUNT + 1]; + struct software_node swnodes[SWNODE_COUNT]; + struct atomisp_csi2_node_names node_names; + struct atomisp_csi2_property_names prop_names; + /* "clock-frequency", "rotation" + terminating entry */ + struct property_entry dev_properties[3]; + /* "bus-type", "data-lanes", "remote-endpoint" + "link-freq" + terminating entry */ + struct property_entry ep_properties[5]; + /* "data-lanes", "remote-endpoint" + terminating entry */ + struct property_entry csi2_properties[3]; + struct software_node_ref_args local_ref[1]; + struct software_node_ref_args remote_ref[1]; + struct software_node_ref_args vcm_ref[1]; + /* GPIO mappings storage */ + struct atomisp_csi2_acpi_gpio_map gpio_map; +}; + +struct atomisp_csi2_bridge { + struct software_node csi2_node; + char csi2_node_name[14]; + u32 data_lanes[CSI2_MAX_LANES]; + unsigned int n_sensors; + struct atomisp_csi2_sensor sensors[ATOMISP_CAMERA_NR_PORTS]; +}; + struct atomisp_mipi_csi2_device { struct v4l2_subdev subdev; struct media_pad pads[CSI2_PADS_NUM]; @@ -48,6 +138,8 @@ void atomisp_mipi_csi2_unregister_entities( struct atomisp_mipi_csi2_device *csi2); int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2, struct v4l2_device *vdev); +int atomisp_csi2_bridge_init(struct atomisp_device *isp); +int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp); void atomisp_csi2_configure(struct atomisp_sub_device *asd); diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c new file mode 100644 index 000000000000..0d12ba78d9c1 --- /dev/null +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -0,0 +1,874 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Code to build software firmware node graph for atomisp2 connected sensors + * from ACPI tables. + * + * Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com> + * + * Based on drivers/media/pci/intel/ipu3/cio2-bridge.c written by: + * Dan Scally <djrscally@gmail.com> + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/property.h> +#include <media/v4l2-fwnode.h> + +#include "atomisp_cmd.h" +#include "atomisp_csi2.h" +#include "atomisp_internal.h" + +#define NODE_SENSOR(_HID, _PROPS) \ + ((const struct software_node) { \ + .name = _HID, \ + .properties = _PROPS, \ + }) + +#define NODE_PORT(_PORT, _SENSOR_NODE) \ + ((const struct software_node) { \ + .name = _PORT, \ + .parent = _SENSOR_NODE, \ + }) + +#define NODE_ENDPOINT(_EP, _PORT, _PROPS) \ + ((const struct software_node) { \ + .name = _EP, \ + .parent = _PORT, \ + .properties = _PROPS, \ + }) + +#define PMC_CLK_RATE_19_2MHZ 19200000 + +/* + * 79234640-9e10-4fea-a5c1-b5aa8b19756f + * This _DSM GUID returns information about the GPIO lines mapped to a sensor. + * Function number 1 returns a count of the GPIO lines that are mapped. + * Subsequent functions return 32 bit ints encoding information about the GPIO. + */ +static const guid_t intel_sensor_gpio_info_guid = + GUID_INIT(0x79234640, 0x9e10, 0x4fea, + 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f); + +#define INTEL_GPIO_DSM_TYPE_SHIFT 0 +#define INTEL_GPIO_DSM_TYPE_MASK GENMASK(7, 0) +#define INTEL_GPIO_DSM_PIN_SHIFT 8 +#define INTEL_GPIO_DSM_PIN_MASK GENMASK(15, 8) +#define INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT 24 +#define INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK GENMASK(31, 24) + +#define INTEL_GPIO_DSM_TYPE(x) \ + (((x) & INTEL_GPIO_DSM_TYPE_MASK) >> INTEL_GPIO_DSM_TYPE_SHIFT) +#define INTEL_GPIO_DSM_PIN(x) \ + (((x) & INTEL_GPIO_DSM_PIN_MASK) >> INTEL_GPIO_DSM_PIN_SHIFT) +#define INTEL_GPIO_DSM_SENSOR_ON_VAL(x) \ + (((x) & INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK) >> INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT) + +/* + * 822ace8f-2814-4174-a56b-5f029fe079ee + * This _DSM GUID returns a string from the sensor device, which acts as a + * module identifier. + */ +static const guid_t intel_sensor_module_guid = + GUID_INIT(0x822ace8f, 0x2814, 0x4174, + 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); + +/* + * dc2f6c4f-045b-4f1d-97b9-882a6860a4be + * This _DSM GUID returns a package with n*2 strings, with each set of 2 strings + * forming a key, value pair for settings like e.g. "CsiLanes" = "1". + */ +static const guid_t atomisp_dsm_guid = + GUID_INIT(0xdc2f6c4f, 0x045b, 0x4f1d, + 0x97, 0xb9, 0x88, 0x2a, 0x68, 0x60, 0xa4, 0xbe); + +/* + * Extend this array with ACPI Hardware IDs of sensors known to be working + * plus the default number of links + link-frequencies. + * + * Do not add an entry for a sensor that is not actually supported, + * or which have not yet been converted to work without atomisp_gmin + * power-management and with v4l2-async probing. + */ +static const struct atomisp_csi2_sensor_config supported_sensors[] = { + /* GalaxyCore GC0310 */ + { "INT0310", 1 }, + /* Omnivision OV2680 */ + { "OVTI2680", 1 }, +}; + +/* + * gmin_cfg parsing code. This is a cleaned up version of the gmin_cfg parsing + * code from atomisp_gmin_platform.c. + * Once all sensors are moved to v4l2-async probing atomisp_gmin_platform.c can + * be removed and the duplication of this code goes away. + */ +struct gmin_cfg_var { + const char *acpi_dev_name; + const char *key; + const char *val; +}; + +static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = { + /* _DSM contains the wrong CsiPort! */ + { "OVTI2680:01", "CsiPort", "0" }, + {} +}; + +static const struct dmi_system_id gmin_cfg_dmi_overrides[] = { + { + /* Lenovo Ideapad Miix 310 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10"), + }, + .driver_data = lenovo_ideapad_miix_310_vars, + }, + {} +}; + +static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key) +{ + union acpi_object *obj, *key_el, *val_el; + char *val = NULL; + int i; + + obj = acpi_evaluate_dsm_typed(adev->handle, &atomisp_dsm_guid, 0, 0, + NULL, ACPI_TYPE_PACKAGE); + if (!obj) + return NULL; + + for (i = 0; i < obj->package.count - 1; i += 2) { + key_el = &obj->package.elements[i + 0]; + val_el = &obj->package.elements[i + 1]; + + if (key_el->type != ACPI_TYPE_STRING || val_el->type != ACPI_TYPE_STRING) + break; + + if (!strcmp(key_el->string.pointer, key)) { + val = kstrdup(val_el->string.pointer, GFP_KERNEL); + if (!val) + break; + + acpi_handle_info(adev->handle, "Using DSM entry %s=%s\n", key, val); + break; + } + } + + ACPI_FREE(obj); + return val; +} + +static char *gmin_cfg_get_dmi_override(struct acpi_device *adev, const char *key) +{ + const struct dmi_system_id *id; + struct gmin_cfg_var *gv; + + id = dmi_first_match(gmin_cfg_dmi_overrides); + if (!id) + return NULL; + + for (gv = id->driver_data; gv->acpi_dev_name; gv++) { + if (strcmp(gv->acpi_dev_name, acpi_dev_name(adev))) + continue; + + if (strcmp(key, gv->key)) + continue; + + acpi_handle_info(adev->handle, "Using DMI entry %s=%s\n", key, gv->val); + return kstrdup(gv->val, GFP_KERNEL); + } + + return NULL; +} + +static char *gmin_cfg_get(struct acpi_device *adev, const char *key) +{ + char *val; + + val = gmin_cfg_get_dmi_override(adev, key); + if (val) + return val; + + return gmin_cfg_get_dsm(adev, key); +} + +static int gmin_cfg_get_int(struct acpi_device *adev, const char *key, int default_val) +{ + char *str_val; + long int_val; + int ret; + + str_val = gmin_cfg_get(adev, key); + if (!str_val) + goto out_use_default; + + ret = kstrtoul(str_val, 0, &int_val); + kfree(str_val); + if (ret) + goto out_use_default; + + return int_val; + +out_use_default: + acpi_handle_info(adev->handle, "Using default %s=%d\n", key, default_val); + return default_val; +} + +static int atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(struct acpi_device *adev) +{ + /* ACPI_PATH_SEGMENT_LENGTH is guaranteed to be big enough for name + 0 term. */ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer b_name = { sizeof(name), name }; + union acpi_object *package, *element; + int i, ret = -ENOENT; + acpi_handle rhandle; + acpi_status status; + u8 clock_num; + + status = acpi_evaluate_object_typed(adev->handle, "_PR0", NULL, &buffer, ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENOENT; + + package = buffer.pointer; + for (i = 0; i < package->package.count; i++) { + element = &package->package.elements[i]; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) + continue; + + rhandle = element->reference.handle; + if (!rhandle) + continue; + + acpi_get_name(rhandle, ACPI_SINGLE_NAME, &b_name); + + if (str_has_prefix(name, "CLK") && !kstrtou8(&name[3], 10, &clock_num) && + clock_num <= 4) { + ret = clock_num; + break; + } + } + + ACPI_FREE(buffer.pointer); + + if (ret < 0) + acpi_handle_warn(adev->handle, "Could not find PMC clk in _PR0\n"); + + return ret; +} + +static int atomisp_csi2_set_pmc_clk_freq(struct acpi_device *adev, int clock_num) +{ + struct clk *clk; + char name[14]; + int ret; + + if (clock_num < 0) + return 0; + + snprintf(name, sizeof(name), "pmc_plt_clk_%d", clock_num); + + clk = clk_get(NULL, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + acpi_handle_err(adev->handle, "Error getting clk %s:%d\n", name, ret); + return ret; + } + + /* + * The firmware might enable the clock at boot, to change + * the rate we must ensure the clock is disabled. + */ + ret = clk_prepare_enable(clk); + if (!ret) + clk_disable_unprepare(clk); + if (!ret) + ret = clk_set_rate(clk, PMC_CLK_RATE_19_2MHZ); + if (ret) + acpi_handle_err(adev->handle, "Error setting clk-rate for %s:%d\n", name, ret); + + clk_put(clk); + return ret; +} + +static int atomisp_csi2_get_port(struct acpi_device *adev, int clock_num) +{ + int port; + + /* + * Compare clock-number to the PMC-clock used for CsiPort 1 + * in the CHT/BYT reference designs. + */ + if (IS_ISP2401) + port = clock_num == 4 ? 1 : 0; + else + port = clock_num == 0 ? 1 : 0; + + /* Intel DSM or DMI quirk overrides _PR0 CLK derived default */ + return gmin_cfg_get_int(adev, "CsiPort", port); +} + +/* Note this always returns 1 to continue looping so that res_count is accurate */ +static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_data) +{ + struct atomisp_csi2_acpi_gpio_parsing_data *data = _data; + struct acpi_resource_gpio *agpio; + const char *name; + bool active_low; + unsigned int i; + u32 settings = 0; + u16 pin; + + if (!acpi_gpio_get_io_resource(ares, &agpio)) + return 1; /* Not a GPIO, continue the loop */ + + data->res_count++; + + pin = agpio->pin_table[0]; + for (i = 0; i < data->settings_count; i++) { + if (INTEL_GPIO_DSM_PIN(data->settings[i]) == pin) { + settings = data->settings[i]; + break; + } + } + + if (i == data->settings_count) { + acpi_handle_warn(data->adev->handle, + "Could not find DSM GPIO settings for pin %u\n", pin); + return 1; + } + + switch (INTEL_GPIO_DSM_TYPE(settings)) { + case 0: + name = "reset-gpios"; + break; + case 1: + name = "powerdown-gpios"; + break; + default: + acpi_handle_warn(data->adev->handle, "Unknown GPIO type 0x%02lx for pin %u\n", + INTEL_GPIO_DSM_TYPE(settings), pin); + return 1; + } + + /* + * Both reset and power-down need to be logical false when the sensor + * is on (sensor should not be in reset and not be powered-down). So + * when the sensor-on-value (which is the physical pin value) is high, + * then the signal is active-low. + */ + active_low = INTEL_GPIO_DSM_SENSOR_ON_VAL(settings); + + i = data->map_count; + if (i == CSI2_MAX_ACPI_GPIOS) + return 1; + + /* res_count is already incremented */ + data->map->params[i].crs_entry_index = data->res_count - 1; + data->map->params[i].active_low = active_low; + data->map->mapping[i].name = name; + data->map->mapping[i].data = &data->map->params[i]; + data->map->mapping[i].size = 1; + data->map_count++; + + acpi_handle_info(data->adev->handle, "%s crs %d %s pin %u active-%s\n", name, + data->res_count - 1, agpio->resource_source.string_ptr, + pin, active_low ? "low" : "high"); + + return 1; +} + +/* + * Helper function to create an ACPI GPIO lookup table for sensor reset and + * powerdown signals on Intel Bay Trail (BYT) and Cherry Trail (CHT) devices, + * including setting the correct polarity for the GPIO. + * + * This uses the "79234640-9e10-4fea-a5c1-b5aa8b19756f" DSM method directly + * on the sensor device's ACPI node. This is different from later Intel + * hardware which has a separate INT3472 acpi_device with this info. + * + * This function must be called before creating the sw-noded describing + * the fwnode graph endpoint. And sensor drivers used on these devices + * must return -EPROBE_DEFER when there is no endpoint description yet. + * Together this guarantees that the GPIO lookups are in place before + * the sensor driver tries to get GPIOs with gpiod_get(). + * + * Note this code uses the same DSM GUID as the int3472_gpio_guid in + * the INT3472 discrete.c code and there is some overlap, but there are + * enough differences that it is difficult to share the code. + */ +static int atomisp_csi2_add_gpio_mappings(struct atomisp_csi2_sensor *sensor, + struct acpi_device *adev) +{ + struct atomisp_csi2_acpi_gpio_parsing_data data = { }; + LIST_HEAD(resource_list); + union acpi_object *obj; + unsigned int i, j; + int ret; + + obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid, + 0x00, 1, NULL, ACPI_TYPE_STRING); + if (obj) { + acpi_handle_info(adev->handle, "Sensor module id: '%s'\n", obj->string.pointer); + ACPI_FREE(obj); + } + + /* + * First get the GPIO-settings count and then get count GPIO-settings + * values. Note the order of these may differ from the order in which + * the GPIOs are listed on the ACPI resources! So we first store them all + * and then enumerate the ACPI resources and match them up by pin number. + */ + obj = acpi_evaluate_dsm_typed(adev->handle, + &intel_sensor_gpio_info_guid, 0x00, 1, + NULL, ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(adev->handle, "No _DSM entry for GPIO pin count\n"); + return -EIO; + } + + data.settings_count = obj->integer.value; + ACPI_FREE(obj); + + if (data.settings_count > CSI2_MAX_ACPI_GPIOS) { + acpi_handle_err(adev->handle, "Too many GPIOs %u > %u\n", data.settings_count, CSI2_MAX_ACPI_GPIOS); + return -EOVERFLOW; + } + + for (i = 0; i < data.settings_count; i++) { + /* + * i + 2 because the index of this _DSM function is 1-based + * and the first function is just a count. + */ + obj = acpi_evaluate_dsm_typed(adev->handle, + &intel_sensor_gpio_info_guid, + 0x00, i + 2, + NULL, ACPI_TYPE_INTEGER); + if (!obj) { + acpi_handle_err(adev->handle, "No _DSM entry for pin %u\n", i); + return -EIO; + } + + data.settings[i] = obj->integer.value; + ACPI_FREE(obj); + } + + /* Since we match up by pin-number the pin-numbers must be unique */ + for (i = 0; i < data.settings_count; i++) { + for (j = i + 1; j < data.settings_count; j++) { + if (INTEL_GPIO_DSM_PIN(data.settings[i]) != + INTEL_GPIO_DSM_PIN(data.settings[j])) + continue; + + acpi_handle_err(adev->handle, "Duplicate pin number %lu\n", + INTEL_GPIO_DSM_PIN(data.settings[i])); + return -EIO; + } + } + + /* Now parse the ACPI resources and build the lookup table */ + data.adev = adev; + data.map = &sensor->gpio_map; + ret = acpi_dev_get_resources(adev, &resource_list, + atomisp_csi2_handle_acpi_gpio_res, &data); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resource_list); + + if (data.map_count != data.settings_count || + data.res_count != data.settings_count) + acpi_handle_warn(adev->handle, "ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n", + data.settings_count, data.res_count, data.map_count); + + ret = acpi_dev_add_driver_gpios(adev, data.map->mapping); + if (ret) + acpi_handle_err(adev->handle, "Error adding driver GPIOs: %d\n", ret); + + return ret; +} + +static const struct atomisp_csi2_property_names prop_names = { + .clock_frequency = "clock-frequency", + .rotation = "rotation", + .bus_type = "bus-type", + .data_lanes = "data-lanes", + .remote_endpoint = "remote-endpoint", + .link_frequencies = "link-frequencies", +}; + +static void atomisp_csi2_create_fwnode_properties(struct atomisp_csi2_sensor *sensor, + struct atomisp_csi2_bridge *bridge, + const struct atomisp_csi2_sensor_config *cfg) +{ + sensor->prop_names = prop_names; + + sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CSI2_ENDPOINT]); + sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]); + + sensor->dev_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.clock_frequency, + PMC_CLK_RATE_19_2MHZ); + sensor->dev_properties[1] = PROPERTY_ENTRY_U32(sensor->prop_names.rotation, 0); + + sensor->ep_properties[0] = PROPERTY_ENTRY_U32(sensor->prop_names.bus_type, + V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes, + bridge->data_lanes, + sensor->lanes); + sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint, + sensor->local_ref); + if (cfg->nr_link_freqs > 0) + sensor->ep_properties[3] = + PROPERTY_ENTRY_U64_ARRAY_LEN(sensor->prop_names.link_frequencies, + cfg->link_freqs, cfg->nr_link_freqs); + + sensor->csi2_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(sensor->prop_names.data_lanes, + bridge->data_lanes, + sensor->lanes); + sensor->csi2_properties[1] = PROPERTY_ENTRY_REF_ARRAY(sensor->prop_names.remote_endpoint, + sensor->remote_ref); +} + +static void atomisp_csi2_init_swnode_names(struct atomisp_csi2_sensor *sensor) +{ + snprintf(sensor->node_names.remote_port, + sizeof(sensor->node_names.remote_port), + SWNODE_GRAPH_PORT_NAME_FMT, sensor->port); + snprintf(sensor->node_names.port, + sizeof(sensor->node_names.port), + SWNODE_GRAPH_PORT_NAME_FMT, 0); /* Always port 0 */ + snprintf(sensor->node_names.endpoint, + sizeof(sensor->node_names.endpoint), + SWNODE_GRAPH_ENDPOINT_NAME_FMT, 0); /* And endpoint 0 */ +} + +static void atomisp_csi2_init_swnode_group(struct atomisp_csi2_sensor *sensor) +{ + struct software_node *nodes = sensor->swnodes; + + sensor->group[SWNODE_SENSOR] = &nodes[SWNODE_SENSOR]; + sensor->group[SWNODE_SENSOR_PORT] = &nodes[SWNODE_SENSOR_PORT]; + sensor->group[SWNODE_SENSOR_ENDPOINT] = &nodes[SWNODE_SENSOR_ENDPOINT]; + sensor->group[SWNODE_CSI2_PORT] = &nodes[SWNODE_CSI2_PORT]; + sensor->group[SWNODE_CSI2_ENDPOINT] = &nodes[SWNODE_CSI2_ENDPOINT]; +} + +static void atomisp_csi2_create_connection_swnodes(struct atomisp_csi2_bridge *bridge, + struct atomisp_csi2_sensor *sensor) +{ + struct software_node *nodes = sensor->swnodes; + + atomisp_csi2_init_swnode_names(sensor); + + nodes[SWNODE_SENSOR] = NODE_SENSOR(sensor->name, + sensor->dev_properties); + nodes[SWNODE_SENSOR_PORT] = NODE_PORT(sensor->node_names.port, + &nodes[SWNODE_SENSOR]); + nodes[SWNODE_SENSOR_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint, + &nodes[SWNODE_SENSOR_PORT], + sensor->ep_properties); + nodes[SWNODE_CSI2_PORT] = NODE_PORT(sensor->node_names.remote_port, + &bridge->csi2_node); + nodes[SWNODE_CSI2_ENDPOINT] = NODE_ENDPOINT(sensor->node_names.endpoint, + &nodes[SWNODE_CSI2_PORT], + sensor->csi2_properties); + + atomisp_csi2_init_swnode_group(sensor); +} + +static void atomisp_csi2_unregister_sensors(struct atomisp_csi2_bridge *bridge) +{ + struct atomisp_csi2_sensor *sensor; + unsigned int i; + + for (i = 0; i < bridge->n_sensors; i++) { + sensor = &bridge->sensors[i]; + software_node_unregister_node_group(sensor->group); + acpi_dev_remove_driver_gpios(sensor->adev); + acpi_dev_put(sensor->adev); + } +} + +static int atomisp_csi2_connect_sensor(const struct atomisp_csi2_sensor_config *cfg, + struct atomisp_csi2_bridge *bridge, + struct atomisp_device *isp) +{ + struct fwnode_handle *fwnode, *primary; + struct atomisp_csi2_sensor *sensor; + struct acpi_device *adev; + int ret, clock_num; + + for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) { + if (!adev->status.enabled) + continue; + + if (bridge->n_sensors >= ATOMISP_CAMERA_NR_PORTS) { + dev_err(isp->dev, "Exceeded available CSI2 ports\n"); + ret = -EOVERFLOW; + goto err_put_adev; + } + + sensor = &bridge->sensors[bridge->n_sensors]; + + /* + * ACPI takes care of turning the PMC clock on and off, but on BYT + * the clock defaults to 25 MHz instead of the expected 19.2 MHz. + * Get the PMC-clock number from ACPI _PR0 method and set it to 19.2 MHz. + * The PMC-clock number is also used to determine the default CSI port. + */ + clock_num = atomisp_csi2_get_pmc_clk_nr_from_acpi_pr0(adev); + + ret = atomisp_csi2_set_pmc_clk_freq(adev, clock_num); + if (ret) + goto err_put_adev; + + sensor->port = atomisp_csi2_get_port(adev, clock_num); + if (sensor->port >= ATOMISP_CAMERA_NR_PORTS) { + acpi_handle_err(adev->handle, "Invalid port: %d\n", sensor->port); + ret = -EINVAL; + goto err_put_adev; + } + + sensor->lanes = gmin_cfg_get_int(adev, "CsiLanes", cfg->lanes); + if (sensor->lanes > CSI2_MAX_LANES) { + acpi_handle_err(adev->handle, "Invalid number of lanes: %d\n", sensor->lanes); + ret = -EINVAL; + goto err_put_adev; + } + + ret = atomisp_csi2_add_gpio_mappings(sensor, adev); + if (ret) + goto err_put_adev; + + snprintf(sensor->name, sizeof(sensor->name), "%s-%u", + cfg->hid, sensor->port); + + atomisp_csi2_create_fwnode_properties(sensor, bridge, cfg); + atomisp_csi2_create_connection_swnodes(bridge, sensor); + + ret = software_node_register_node_group(sensor->group); + if (ret) + goto err_remove_mappings; + + fwnode = software_node_fwnode(&sensor->swnodes[SWNODE_SENSOR]); + if (!fwnode) { + ret = -ENODEV; + goto err_free_swnodes; + } + + sensor->adev = acpi_dev_get(adev); + + primary = acpi_fwnode_handle(adev); + primary->secondary = fwnode; + + bridge->n_sensors++; + } + + return 0; + +err_free_swnodes: + software_node_unregister_node_group(sensor->group); +err_remove_mappings: + acpi_dev_remove_driver_gpios(adev); +err_put_adev: + acpi_dev_put(adev); + return ret; +} + +static int atomisp_csi2_connect_sensors(struct atomisp_csi2_bridge *bridge, + struct atomisp_device *isp) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(supported_sensors); i++) { + const struct atomisp_csi2_sensor_config *cfg = &supported_sensors[i]; + + ret = atomisp_csi2_connect_sensor(cfg, bridge, isp); + if (ret) + goto err_unregister_sensors; + } + + return 0; + +err_unregister_sensors: + atomisp_csi2_unregister_sensors(bridge); + return ret; +} + +int atomisp_csi2_bridge_init(struct atomisp_device *isp) +{ + struct atomisp_csi2_bridge *bridge; + struct device *dev = isp->dev; + struct fwnode_handle *fwnode; + int i, ret; + + /* + * This function is intended to run only once and then leave + * the created nodes attached even after a rmmod, therefore: + * 1. The bridge memory is leaked deliberately on success + * 2. If a secondary fwnode is already set exit early. + */ + fwnode = dev_fwnode(dev); + if (fwnode && fwnode->secondary) + return 0; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + strscpy(bridge->csi2_node_name, "atomisp-csi2", sizeof(bridge->csi2_node_name)); + bridge->csi2_node.name = bridge->csi2_node_name; + + ret = software_node_register(&bridge->csi2_node); + if (ret < 0) { + dev_err(dev, "Failed to register the CSI2 HID node\n"); + goto err_free_bridge; + } + + /* + * Map the lane arrangement, which is fixed for the ISP2 (meaning we + * only need one, rather than one per sensor). We include it as a + * member of the bridge struct rather than a global variable so + * that it survives if the module is unloaded along with the rest of + * the struct. + */ + for (i = 0; i < CSI2_MAX_LANES; i++) + bridge->data_lanes[i] = i + 1; + + ret = atomisp_csi2_connect_sensors(bridge, isp); + if (ret || bridge->n_sensors == 0) + goto err_unregister_csi2; + + fwnode = software_node_fwnode(&bridge->csi2_node); + if (!fwnode) { + dev_err(dev, "Error getting fwnode from csi2 software_node\n"); + ret = -ENODEV; + goto err_unregister_sensors; + } + + set_secondary_fwnode(dev, fwnode); + + return 0; + +err_unregister_sensors: + atomisp_csi2_unregister_sensors(bridge); +err_unregister_csi2: + software_node_unregister(&bridge->csi2_node); +err_free_bridge: + kfree(bridge); + + return ret; +} + +/******* V4L2 sub-device asynchronous registration callbacks***********/ + +struct sensor_async_subdev { + struct v4l2_async_subdev asd; + int port; +}; + +#define to_sensor_asd(a) container_of(a, struct sensor_async_subdev, asd) +#define notifier_to_atomisp(n) container_of(n, struct atomisp_device, notifier) + +/* .bound() notifier callback when a match is found */ +static int atomisp_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct atomisp_device *isp = notifier_to_atomisp(notifier); + struct sensor_async_subdev *s_asd = to_sensor_asd(asd); + + if (s_asd->port >= ATOMISP_CAMERA_NR_PORTS) { + dev_err(isp->dev, "port %d not supported\n", s_asd->port); + return -EINVAL; + } + + if (isp->sensor_subdevs[s_asd->port]) { + dev_err(isp->dev, "port %d already has a sensor attached\n", s_asd->port); + return -EBUSY; + } + + isp->sensor_subdevs[s_asd->port] = sd; + return 0; +} + +/* The .unbind callback */ +static void atomisp_notifier_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct atomisp_device *isp = notifier_to_atomisp(notifier); + struct sensor_async_subdev *s_asd = to_sensor_asd(asd); + + isp->sensor_subdevs[s_asd->port] = NULL; +} + +/* .complete() is called after all subdevices have been located */ +static int atomisp_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct atomisp_device *isp = notifier_to_atomisp(notifier); + + return atomisp_register_device_nodes(isp); +} + +static const struct v4l2_async_notifier_operations atomisp_async_ops = { + .bound = atomisp_notifier_bound, + .unbind = atomisp_notifier_unbind, + .complete = atomisp_notifier_complete, +}; + +int atomisp_csi2_bridge_parse_firmware(struct atomisp_device *isp) +{ + int i, mipi_port, ret; + + v4l2_async_nf_init(&isp->notifier); + isp->notifier.ops = &atomisp_async_ops; + + for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct sensor_async_subdev *s_asd; + struct fwnode_handle *ep; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), i, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + continue; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_parse; + + if (vep.base.port >= ATOMISP_CAMERA_NR_PORTS) { + dev_err(isp->dev, "port %d not supported\n", vep.base.port); + ret = -EINVAL; + goto err_parse; + } + + mipi_port = atomisp_port_to_mipi_port(isp, vep.base.port); + isp->sensor_lanes[mipi_port] = vep.bus.mipi_csi2.num_data_lanes; + + s_asd = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep, + struct sensor_async_subdev); + if (IS_ERR(s_asd)) { + ret = PTR_ERR(s_asd); + goto err_parse; + } + + s_asd->port = vep.base.port; + + fwnode_handle_put(ep); + continue; + +err_parse: + fwnode_handle_put(ep); + return ret; + } + + return 0; +} diff --git a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c b/drivers/staging/media/atomisp/pci/atomisp_drvfs.c index 3ddc935ec01d..1df534bf54d3 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c +++ b/drivers/staging/media/atomisp/pci/atomisp_drvfs.c @@ -69,7 +69,7 @@ static inline int iunit_dump_dbgopt(struct atomisp_device *isp, } if (opt & OPTION_BIN_RUN) { - if (atomisp_streaming_count(isp)) { + if (isp->asd.streaming) { atomisp_css_dump_sp_raw_copy_linecount(true); atomisp_css_debug_dump_isp_binary(); } else { diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c index fa362c8a37e8..54466d2f323a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c @@ -47,7 +47,6 @@ static int atomisp_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct atomisp_video_pipe *pipe = container_of(vq, struct atomisp_video_pipe, vb_queue); - u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev); int ret; mutex_lock(&pipe->asd->isp->mutex); /* for get_css_frame_info() / set_fmt() */ @@ -56,7 +55,7 @@ static int atomisp_queue_setup(struct vb2_queue *vq, * When VIDIOC_S_FMT has not been called before VIDIOC_REQBUFS, then * this will fail. Call atomisp_set_fmt() ourselves and try again. */ - ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + ret = atomisp_get_css_frame_info(pipe->asd, &pipe->frame_info); if (ret) { struct v4l2_format f = { .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420, @@ -68,7 +67,7 @@ static int atomisp_queue_setup(struct vb2_queue *vq, if (ret) goto out; - ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + ret = atomisp_get_css_frame_info(pipe->asd, &pipe->frame_info); if (ret) goto out; } @@ -342,119 +341,29 @@ static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, return 0; } -static int atomisp_get_css_buf_type(struct atomisp_sub_device *asd, - enum ia_css_pipe_id pipe_id, - uint16_t source_pad) -{ - if (pipe_id == IA_CSS_PIPE_ID_COPY || - source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE || - source_pad == ATOMISP_SUBDEV_PAD_SOURCE_VIDEO || - (source_pad == ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW && - asd->run_mode->val != ATOMISP_RUN_MODE_VIDEO)) - return IA_CSS_BUFFER_TYPE_OUTPUT_FRAME; - else - return IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME; -} - /* queue all available buffers to css */ int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd) { - enum ia_css_buffer_type buf_type; - enum ia_css_pipe_id css_capture_pipe_id = IA_CSS_PIPE_ID_NUM; - enum ia_css_pipe_id css_preview_pipe_id = IA_CSS_PIPE_ID_NUM; - enum ia_css_pipe_id css_video_pipe_id = IA_CSS_PIPE_ID_NUM; - enum atomisp_input_stream_id input_stream_id; - struct atomisp_video_pipe *capture_pipe = NULL; - struct atomisp_video_pipe *vf_pipe = NULL; - struct atomisp_video_pipe *preview_pipe = NULL; - struct atomisp_video_pipe *video_pipe = NULL; - bool raw_mode = atomisp_is_mbuscode_raw( - asd->fmt[asd->capture_pad].fmt.code); - - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { - video_pipe = &asd->video_out_video_capture; - css_video_pipe_id = IA_CSS_PIPE_ID_VIDEO; + enum ia_css_pipe_id pipe_id; + + if (asd->copy_mode) { + pipe_id = IA_CSS_PIPE_ID_COPY; + } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { + pipe_id = IA_CSS_PIPE_ID_VIDEO; } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { - preview_pipe = &asd->video_out_capture; - css_preview_pipe_id = IA_CSS_PIPE_ID_CAPTURE; + pipe_id = IA_CSS_PIPE_ID_CAPTURE; } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - video_pipe = &asd->video_out_video_capture; - preview_pipe = &asd->video_out_preview; - css_video_pipe_id = IA_CSS_PIPE_ID_VIDEO; - css_preview_pipe_id = IA_CSS_PIPE_ID_VIDEO; + pipe_id = IA_CSS_PIPE_ID_VIDEO; } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { - preview_pipe = &asd->video_out_preview; - css_preview_pipe_id = IA_CSS_PIPE_ID_PREVIEW; + pipe_id = IA_CSS_PIPE_ID_PREVIEW; } else { /* ATOMISP_RUN_MODE_STILL_CAPTURE */ - capture_pipe = &asd->video_out_capture; - if (!raw_mode) - vf_pipe = &asd->video_out_vf; - css_capture_pipe_id = IA_CSS_PIPE_ID_CAPTURE; - } - - if (IS_ISP2401 && asd->copy_mode) { - css_capture_pipe_id = IA_CSS_PIPE_ID_COPY; - css_preview_pipe_id = IA_CSS_PIPE_ID_COPY; - css_video_pipe_id = IA_CSS_PIPE_ID_COPY; - } - - if (capture_pipe) { - buf_type = atomisp_get_css_buf_type( - asd, css_capture_pipe_id, - atomisp_subdev_source_pad(&capture_pipe->vdev)); - input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; - - atomisp_q_video_buffers_to_css(asd, capture_pipe, - input_stream_id, - buf_type, css_capture_pipe_id); - } - - if (vf_pipe) { - buf_type = atomisp_get_css_buf_type( - asd, css_capture_pipe_id, - atomisp_subdev_source_pad(&vf_pipe->vdev)); - if (asd->stream_env[ATOMISP_INPUT_STREAM_POSTVIEW].stream) - input_stream_id = ATOMISP_INPUT_STREAM_POSTVIEW; - else - input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; - - atomisp_q_video_buffers_to_css(asd, vf_pipe, - input_stream_id, - buf_type, css_capture_pipe_id); - } - - if (preview_pipe) { - buf_type = atomisp_get_css_buf_type( - asd, css_preview_pipe_id, - atomisp_subdev_source_pad(&preview_pipe->vdev)); - - if (css_preview_pipe_id == IA_CSS_PIPE_ID_YUVPP) - input_stream_id = ATOMISP_INPUT_STREAM_VIDEO; - else if (asd->stream_env[ATOMISP_INPUT_STREAM_PREVIEW].stream) - input_stream_id = ATOMISP_INPUT_STREAM_PREVIEW; - else - input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; - - atomisp_q_video_buffers_to_css(asd, preview_pipe, - input_stream_id, - buf_type, css_preview_pipe_id); - } - - if (video_pipe) { - buf_type = atomisp_get_css_buf_type( - asd, css_video_pipe_id, - atomisp_subdev_source_pad(&video_pipe->vdev)); - if (asd->stream_env[ATOMISP_INPUT_STREAM_VIDEO].stream) - input_stream_id = ATOMISP_INPUT_STREAM_VIDEO; - else - input_stream_id = ATOMISP_INPUT_STREAM_GENERAL; - - atomisp_q_video_buffers_to_css(asd, video_pipe, - input_stream_id, - buf_type, css_video_pipe_id); + pipe_id = IA_CSS_PIPE_ID_CAPTURE; } + atomisp_q_video_buffers_to_css(asd, &asd->video_out, + ATOMISP_INPUT_STREAM_GENERAL, + IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, pipe_id); return 0; } @@ -493,9 +402,8 @@ static void atomisp_buf_queue(struct vb2_buffer *vb) * is put to waiting list until previous per-frame parameter buffers * get enqueued. */ - if (!atomisp_is_vf_pipe(pipe) && - (pipe->frame_request_config_id[vb->index] || - !list_empty(&pipe->buffers_waiting_for_param))) + if (pipe->frame_request_config_id[vb->index] || + !list_empty(&pipe->buffers_waiting_for_param)) list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param); else list_add_tail(&frame->queue, &pipe->activeq); @@ -503,7 +411,7 @@ static void atomisp_buf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irq_lock, irqflags); /* TODO: do this better, not best way to queue to css */ - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { + if (asd->streaming) { if (!list_empty(&pipe->buffers_waiting_for_param)) atomisp_handle_parameter_and_buffer(pipe); else @@ -539,9 +447,7 @@ static void atomisp_dev_init_struct(struct atomisp_device *isp) { unsigned int i; - isp->need_gfx_throttle = true; isp->isp_fatal_error = false; - isp->mipi_frame_size = 0; for (i = 0; i < isp->input_cnt; i++) isp->inputs[i].asd = NULL; @@ -568,10 +474,6 @@ static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd) /* s3a grid not enabled for any pipe */ asd->params.s3a_enabled_pipe = IA_CSS_PIPE_ID_NUM; - /* Add for channel */ - asd->input_curr = 0; - - asd->mipi_frame_size = 0; asd->copy_mode = false; asd->stream_prepared = false; @@ -584,19 +486,6 @@ static void atomisp_subdev_init_struct(struct atomisp_sub_device *asd) /* * file operation functions */ -static unsigned int atomisp_subdev_users(struct atomisp_sub_device *asd) -{ - return asd->video_out_preview.users + - asd->video_out_vf.users + - asd->video_out_capture.users + - asd->video_out_video_capture.users; -} - -unsigned int atomisp_dev_users(struct atomisp_device *isp) -{ - return atomisp_subdev_users(&isp->asd); -} - static int atomisp_open(struct file *file) { struct video_device *vdev = video_devdata(file); @@ -613,8 +502,6 @@ static int atomisp_open(struct file *file) mutex_lock(&isp->mutex); - asd->subdev.devnode = vdev; - if (!isp->input_cnt) { dev_err(isp->dev, "no camera attached\n"); ret = -EINVAL; @@ -630,11 +517,6 @@ static int atomisp_open(struct file *file) return -EBUSY; } - if (atomisp_dev_users(isp)) { - dev_dbg(isp->dev, "skip init isp in open\n"); - goto init_subdev; - } - /* runtime power management, turn on ISP */ ret = pm_runtime_resume_and_get(vdev->v4l2_dev->dev); if (ret < 0) { @@ -650,19 +532,12 @@ static int atomisp_open(struct file *file) goto css_error; } -init_subdev: - if (atomisp_subdev_users(asd)) - goto done; - atomisp_subdev_init_struct(asd); /* Ensure that a mode is set */ - v4l2_ctrl_s_ctrl(asd->run_mode, pipe->default_run_mode); + v4l2_ctrl_s_ctrl(asd->run_mode, ATOMISP_RUN_MODE_PREVIEW); -done: pipe->users++; mutex_unlock(&isp->mutex); - - return 0; css_error: @@ -681,23 +556,18 @@ static int atomisp_release(struct file *file) struct atomisp_sub_device *asd = pipe->asd; struct v4l2_subdev_fh fh; struct v4l2_rect clear_compose = {0}; - unsigned long flags; int ret; v4l2_fh_init(&fh.vfh, vdev); dev_dbg(isp->dev, "release device %s\n", vdev->name); - asd->subdev.devnode = vdev; - /* Note file must not be used after this! */ vb2_fop_release(file); mutex_lock(&isp->mutex); pipe->users--; - if (pipe->users) - goto done; /* * A little trick here: @@ -714,9 +584,6 @@ static int atomisp_release(struct file *file) ATOMISP_SUBDEV_PAD_SINK, &isp_sink_fmt); } - if (atomisp_subdev_users(asd)) - goto done; - atomisp_css_free_stat_buffers(asd); atomisp_free_internal_buffers(asd); @@ -730,14 +597,7 @@ static int atomisp_release(struct file *file) isp->inputs[asd->input_curr].asd = NULL; } - spin_lock_irqsave(&isp->lock, flags); - asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; - spin_unlock_irqrestore(&isp->lock, flags); - - if (atomisp_dev_users(isp)) - goto done; - - atomisp_destroy_pipes_stream_force(asd); + atomisp_destroy_pipes_stream(asd); ret = v4l2_subdev_call(isp->flash, core, s_power, 0); if (ret < 0 && ret != -ENODEV && ret != -ENOIOCTLCMD) @@ -746,10 +606,9 @@ static int atomisp_release(struct file *file) if (pm_runtime_put_sync(vdev->v4l2_dev->dev) < 0) dev_err(isp->dev, "Failed to power off device\n"); -done: atomisp_subdev_set_selection(&asd->subdev, fh.state, V4L2_SUBDEV_FORMAT_ACTIVE, - atomisp_subdev_source_pad(vdev), + ATOMISP_SUBDEV_PAD_SOURCE, V4L2_SEL_TGT_COMPOSE, 0, &clear_compose); mutex_unlock(&isp->mutex); diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.h b/drivers/staging/media/atomisp/pci/atomisp_fops.h index 883c1851c1c9..ad1cb1ac8aa4 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.h +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.h @@ -22,9 +22,6 @@ #define __ATOMISP_FOPS_H__ #include "atomisp_subdev.h" -unsigned int atomisp_dev_users(struct atomisp_device *isp); -unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd); - /* * Memory help functions for image frame and private parameters */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index c718a74ea70a..139ad7ad1dcf 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -189,6 +189,7 @@ int atomisp_register_i2c_module(struct v4l2_subdev *subdev, pdata.subdevs[i].type = type; pdata.subdevs[i].port = gs->csi_port; + pdata.subdevs[i].lanes = gs->csi_lanes; pdata.subdevs[i].subdev = subdev; return 0; } @@ -1150,6 +1151,7 @@ int atomisp_register_sensor_no_gmin(struct v4l2_subdev *subdev, u32 lanes, pdata.subdevs[i].type = RAW_CAMERA; pdata.subdevs[i].port = port; + pdata.subdevs[i].lanes = lanes; pdata.subdevs[i].subdev = subdev; return 0; } @@ -1357,7 +1359,7 @@ static int gmin_get_config_dsm_var(struct device *dev, dev_info(dev, "found _DSM entry for '%s': %s\n", var, cur->string.pointer); strscpy(out, cur->string.pointer, *out_len); - *out_len = strlen(cur->string.pointer); + *out_len = strlen(out); ACPI_FREE(obj); return 0; @@ -1427,8 +1429,8 @@ static int gmin_get_config_var(struct device *maindev, int gmin_get_var_int(struct device *dev, bool is_gmin, const char *var, int def) { - char val[CFG_VAR_NAME_MAX]; - size_t len = sizeof(val); + char val[CFG_VAR_NAME_MAX + 1]; + size_t len = CFG_VAR_NAME_MAX; long result; int ret; @@ -1458,243 +1460,3 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0f38, isp_pm_cap_fixup); MODULE_DESCRIPTION("Ancillary routines for binding ACPI devices"); MODULE_LICENSE("GPL"); - -/* - * The below helper functions don't really belong here and should eventually be - * moved to some place under drivers/media/v4l2-core. - */ -#include <linux/platform_data/x86/soc.h> - -/* - * 79234640-9e10-4fea-a5c1-b5aa8b19756f - * This _DSM GUID returns information about the GPIO lines mapped to a sensor. - * Function number 1 returns a count of the GPIO lines that are mapped. - * Subsequent functions return 32 bit ints encoding information about the GPIO. - */ -static const guid_t intel_sensor_gpio_info_guid = - GUID_INIT(0x79234640, 0x9e10, 0x4fea, - 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f); - -/* - * 822ace8f-2814-4174-a56b-5f029fe079ee - * This _DSM GUID returns a string from the sensor device, which acts as a - * module identifier. - */ -static const guid_t intel_sensor_module_guid = - GUID_INIT(0x822ace8f, 0x2814, 0x4174, - 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); - -#define INTEL_DSM_TYPE_SHIFT 0 -#define INTEL_DSM_TYPE_MASK GENMASK(7, 0) -#define INTEL_DSM_PIN_SHIFT 8 -#define INTEL_DSM_PIN_MASK GENMASK(15, 8) -#define INTEL_DSM_SENSOR_ON_VAL_SHIFT 24 -#define INTEL_DSM_SENSOR_ON_VAL_MASK GENMASK(31, 24) - -#define INTEL_DSM_TYPE(x) \ - (((x) & INTEL_DSM_TYPE_MASK) >> INTEL_DSM_TYPE_SHIFT) -#define INTEL_DSM_PIN(x) \ - (((x) & INTEL_DSM_PIN_MASK) >> INTEL_DSM_PIN_SHIFT) -#define INTEL_DSM_SENSOR_ON_VAL(x) \ - (((x) & INTEL_DSM_SENSOR_ON_VAL_MASK) >> INTEL_DSM_SENSOR_ON_VAL_SHIFT) - -#define V4L2_SENSOR_MAX_ACPI_GPIOS 2u - -struct v4l2_acpi_gpio_map { - struct acpi_gpio_params params[V4L2_SENSOR_MAX_ACPI_GPIOS]; - struct acpi_gpio_mapping mapping[V4L2_SENSOR_MAX_ACPI_GPIOS + 1]; -}; - -struct v4l2_acpi_gpio_parsing_data { - struct device *dev; - u32 settings[V4L2_SENSOR_MAX_ACPI_GPIOS]; - unsigned int settings_count; - unsigned int res_count; - unsigned int map_count; - struct v4l2_acpi_gpio_map *map; -}; - -/* Note this always returns 1 to continue looping so that res_count is accurate */ -static int v4l2_acpi_handle_gpio_res(struct acpi_resource *ares, void *_data) -{ - struct v4l2_acpi_gpio_parsing_data *data = _data; - struct acpi_resource_gpio *agpio; - const char *name; - bool active_low; - unsigned int i; - u32 settings; - u8 pin; - - if (!acpi_gpio_get_io_resource(ares, &agpio)) - return 1; /* Not a GPIO, continue the loop */ - - data->res_count++; - - pin = agpio->pin_table[0]; - for (i = 0; i < data->settings_count; i++) { - if (INTEL_DSM_PIN(data->settings[i]) == pin) { - settings = data->settings[i]; - break; - } - } - - if (i == data->settings_count) { - dev_warn(data->dev, "Could not find DSM GPIO settings for pin %d\n", pin); - return 1; - } - - switch (INTEL_DSM_TYPE(settings)) { - case 0: - name = "reset-gpios"; - break; - case 1: - name = "powerdown-gpios"; - break; - default: - dev_warn(data->dev, "Unknown GPIO type 0x%02lx for pin %d\n", - INTEL_DSM_TYPE(settings), pin); - return 1; - } - - /* - * Both reset and power-down need to be logical false when the sensor - * is on (sensor should not be in reset and not be powered-down). So - * when the sensor-on-value (which is the physical pin value) is high, - * then the signal is active-low. - */ - active_low = INTEL_DSM_SENSOR_ON_VAL(settings) ? true : false; - - i = data->map_count; - if (i == V4L2_SENSOR_MAX_ACPI_GPIOS) - return 1; - - /* res_count is already incremented */ - data->map->params[i].crs_entry_index = data->res_count - 1; - data->map->params[i].active_low = active_low; - data->map->mapping[i].name = name; - data->map->mapping[i].data = &data->map->params[i]; - data->map->mapping[i].size = 1; - data->map_count++; - - dev_info(data->dev, "%s crs %d %s pin %d active-%s\n", name, - data->res_count - 1, agpio->resource_source.string_ptr, - pin, active_low ? "low" : "high"); - - return 1; -} - -/* - * Helper function to create an ACPI GPIO lookup table for sensor reset and - * powerdown signals on Intel Bay Trail (BYT) and Cherry Trail (CHT) devices, - * including setting the correct polarity for the GPIO. - * - * This uses the "79234640-9e10-4fea-a5c1-b5aa8b19756f" DSM method directly - * on the sensor device's ACPI node. This is different from later Intel - * hardware which has a separate INT3472 with this info. Since there is - * no separate firmware-node to which we can bind to register the GPIO lookups - * this unfortunately means that all sensor drivers which may be used on - * BYT or CHT hw need to call this function. This also means that this function - * may only fail when it is actually called on BYT/CHT hw. In all other cases - * it must always succeed. - * - * Note this code uses the same DSM GUID as the INT3472 discrete.c code - * and there is some overlap, but there are enough differences that it is - * difficult to share the code. - */ -int v4l2_get_acpi_sensor_info(struct device *dev, char **module_id_str) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - struct v4l2_acpi_gpio_parsing_data data = { }; - LIST_HEAD(resource_list); - union acpi_object *obj; - unsigned int i, j; - int ret; - - if (module_id_str) - *module_id_str = NULL; - - if (!adev) - return 0; - - obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid, - 0x00, 0x01, NULL, ACPI_TYPE_STRING); - if (obj) { - dev_info(dev, "Sensor module id: '%s'\n", obj->string.pointer); - if (module_id_str) - *module_id_str = kstrdup(obj->string.pointer, GFP_KERNEL); - - ACPI_FREE(obj); - } - - if (!soc_intel_is_byt() && !soc_intel_is_cht()) - return 0; - - /* - * First get the GPIO-settings count and then get count GPIO-settings - * values. Note the order of these may differ from the order in which - * the GPIOs are listed on the ACPI resources! So we first store them all - * and then enumerate the ACPI resources and match them up by pin number. - */ - obj = acpi_evaluate_dsm_typed(adev->handle, - &intel_sensor_gpio_info_guid, 0x00, 1, - NULL, ACPI_TYPE_INTEGER); - if (!obj) - return dev_err_probe(dev, -EIO, "No _DSM entry for GPIO pin count\n"); - - data.settings_count = obj->integer.value; - ACPI_FREE(obj); - - if (data.settings_count > V4L2_SENSOR_MAX_ACPI_GPIOS) - return dev_err_probe(dev, -EIO, "Too many GPIOs %u > %u\n", - data.settings_count, V4L2_SENSOR_MAX_ACPI_GPIOS); - - for (i = 0; i < data.settings_count; i++) { - /* - * i + 2 because the index of this _DSM function is 1-based - * and the first function is just a count. - */ - obj = acpi_evaluate_dsm_typed(adev->handle, - &intel_sensor_gpio_info_guid, - 0x00, i + 2, - NULL, ACPI_TYPE_INTEGER); - if (!obj) - return dev_err_probe(dev, -EIO, "No _DSM entry for GPIO pin %u\n", i); - - data.settings[i] = obj->integer.value; - ACPI_FREE(obj); - } - - /* Since we match up by pin-number the pin-numbers must be unique */ - for (i = 0; i < data.settings_count; i++) { - for (j = i + 1; j < data.settings_count; j++) { - if (INTEL_DSM_PIN(data.settings[i]) != - INTEL_DSM_PIN(data.settings[j])) - continue; - - return dev_err_probe(dev, -EIO, "Duplicate pin number %lu\n", - INTEL_DSM_PIN(data.settings[i])); - } - } - - /* Use devm_kzalloc() for the mappings + params to auto-free them */ - data.map = devm_kzalloc(dev, sizeof(*data.map), GFP_KERNEL); - if (!data.map) - return -ENOMEM; - - /* Now parse the ACPI resources and build the lookup table */ - data.dev = dev; - ret = acpi_dev_get_resources(adev, &resource_list, - v4l2_acpi_handle_gpio_res, &data); - if (ret < 0) - return ret; - - acpi_dev_free_resource_list(&resource_list); - - if (data.map_count != data.settings_count || - data.res_count != data.settings_count) - dev_warn(dev, "ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n", - data.settings_count, data.res_count, data.map_count); - - return devm_acpi_dev_add_driver_gpios(dev, data.map->mapping); -} -EXPORT_SYMBOL_GPL(v4l2_get_acpi_sensor_info); diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h index 1fac99f4e2b0..f7b4bee9574b 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h @@ -27,6 +27,7 @@ #include <linux/idr.h> #include <media/media-device.h> +#include <media/v4l2-async.h> #include <media/v4l2-subdev.h> /* ISP2400*/ @@ -124,17 +125,23 @@ struct atomisp_input_subdev { unsigned int type; enum atomisp_camera_port port; + u32 code; /* MEDIA_BUS_FMT_* */ + bool binning_support; + bool crop_support; struct v4l2_subdev *camera; + /* Sensor rects for sensors which support crop */ + struct v4l2_rect native_rect; + struct v4l2_rect active_rect; + /* Sensor pad_cfg for which == V4L2_SUBDEV_FORMAT_TRY calls */ + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev *motor; - struct v4l2_frmsizeenum frame_size; /* * To show this resource is used by * which stream, in ISP multiple stream mode */ struct atomisp_sub_device *asd; - - int sensor_index; }; enum atomisp_dfs_mode { @@ -168,10 +175,6 @@ struct atomisp_regs { u32 csi_access_viol; }; -#define ATOMISP_DEVICE_STREAMING_DISABLED 0 -#define ATOMISP_DEVICE_STREAMING_ENABLED 1 -#define ATOMISP_DEVICE_STREAMING_STOPPING 2 - /* * ci device struct */ @@ -180,6 +183,7 @@ struct atomisp_device { struct v4l2_device v4l2_dev; struct media_device media_dev; struct atomisp_sub_device asd; + struct v4l2_async_notifier notifier; struct atomisp_platform_data *pdata; void *mmu_l1_base; void __iomem *base; @@ -196,6 +200,12 @@ struct atomisp_device { * structures and css API calls. */ struct mutex mutex; + /* + * Number of lanes used by each sensor per port. + * Note this is indexed by mipi_port_id not atomisp_camera_port. + */ + int sensor_lanes[N_MIPI_PORT_ID]; + struct v4l2_subdev *sensor_subdevs[ATOMISP_CAMERA_NR_PORTS]; unsigned int input_cnt; struct atomisp_input_subdev inputs[ATOM_ISP_MAX_INPUTS]; struct v4l2_subdev *flash; @@ -204,16 +214,11 @@ struct atomisp_device { struct atomisp_regs saved_regs; struct atomisp_css_env css_env; - /* isp timeout status flag */ - bool isp_timeout; bool isp_fatal_error; struct work_struct assert_recovery_work; spinlock_t lock; /* Protects asd.streaming */ - bool need_gfx_throttle; - - unsigned int mipi_frame_size; const struct atomisp_dfs_config *dfs; unsigned int hpll_freq; unsigned int running_freq; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index 384f31fc66c5..d2174156573a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -528,22 +528,6 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool settings_change) return -EBUSY; } - switch (pipe->asd->streaming) { - case ATOMISP_DEVICE_STREAMING_DISABLED: - break; - case ATOMISP_DEVICE_STREAMING_ENABLED: - if (settings_change) { - dev_err(pipe->isp->dev, "Set fmt/input IOCTL while streaming\n"); - return -EBUSY; - } - break; - case ATOMISP_DEVICE_STREAMING_STOPPING: - dev_err(pipe->isp->dev, "IOCTL issued while stopping\n"); - return -EBUSY; - default: - return -EINVAL; - } - return 0; } @@ -615,20 +599,6 @@ static int atomisp_enum_input(struct file *file, void *fh, return 0; } -static unsigned int -atomisp_subdev_streaming_count(struct atomisp_sub_device *asd) -{ - return vb2_start_streaming_called(&asd->video_out_preview.vb_queue) + - vb2_start_streaming_called(&asd->video_out_capture.vb_queue) + - vb2_start_streaming_called(&asd->video_out_video_capture.vb_queue) + - vb2_start_streaming_called(&asd->video_out_vf.vb_queue); -} - -unsigned int atomisp_streaming_count(struct atomisp_device *isp) -{ - return isp->asd.streaming == ATOMISP_DEVICE_STREAMING_ENABLED; -} - /* * get input are used to get current primary/secondary camera */ @@ -703,7 +673,7 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input) /* select operating sensor */ ret = v4l2_subdev_call(isp->inputs[input].camera, video, s_routing, - 0, isp->inputs[input].sensor_index, 0); + 0, 0, 0); if (ret && (ret != -ENOIOCTLCMD)) { dev_err(isp->dev, "Failed to select sensor\n"); return ret; @@ -727,20 +697,98 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input) return 0; } +/* + * With crop any framesize <= sensor-size can be made, give + * userspace a list of sizes to choice from. + */ +static int atomisp_enum_framesizes_crop_inner(struct atomisp_device *isp, + struct v4l2_frmsizeenum *fsize, + const struct v4l2_rect *active, + const struct v4l2_rect *native, + int *valid_sizes) +{ + static const struct v4l2_frmsize_discrete frame_sizes[] = { + { 1600, 1200 }, + { 1600, 1080 }, + { 1600, 900 }, + { 1440, 1080 }, + { 1280, 960 }, + { 1280, 720 }, + { 800, 600 }, + { 640, 480 }, + }; + u32 padding_w, padding_h; + int i; + + for (i = 0; i < ARRAY_SIZE(frame_sizes); i++) { + atomisp_get_padding(isp, frame_sizes[i].width, frame_sizes[i].height, + &padding_w, &padding_h); + + if ((frame_sizes[i].width + padding_w) > native->width || + (frame_sizes[i].height + padding_h) > native->height) + continue; + + /* + * Skip sizes where width and height are less then 2/3th of the + * sensor size to avoid sizes with a too small field of view. + */ + if (frame_sizes[i].width < (active->width * 2 / 3) && + frame_sizes[i].height < (active->height * 2 / 3)) + continue; + + if (*valid_sizes == fsize->index) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = frame_sizes[i]; + return 0; + } + + (*valid_sizes)++; + } + + return -EINVAL; +} + +static int atomisp_enum_framesizes_crop(struct atomisp_device *isp, + struct v4l2_frmsizeenum *fsize) +{ + struct atomisp_input_subdev *input = &isp->inputs[isp->asd.input_curr]; + struct v4l2_rect active = input->active_rect; + struct v4l2_rect native = input->native_rect; + int ret, valid_sizes = 0; + + ret = atomisp_enum_framesizes_crop_inner(isp, fsize, &active, &native, &valid_sizes); + if (ret == 0) + return 0; + + if (!input->binning_support) + return -EINVAL; + + active.width /= 2; + active.height /= 2; + native.width /= 2; + native.height /= 2; + + return atomisp_enum_framesizes_crop_inner(isp, fsize, &active, &native, &valid_sizes); +} + static int atomisp_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; + struct atomisp_input_subdev *input = &isp->inputs[asd->input_curr]; struct v4l2_subdev_frame_size_enum fse = { .index = fsize->index, .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .code = input->code, }; int ret; - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - pad, enum_frame_size, NULL, &fse); + if (input->crop_support) + return atomisp_enum_framesizes_crop(isp, fsize); + + ret = v4l2_subdev_call(input->camera, pad, enum_frame_size, NULL, &fse); if (ret) return ret; @@ -836,77 +884,14 @@ static int atomisp_enum_fmt_cap(struct file *file, void *fh, return -EINVAL; } -static int atomisp_adjust_fmt(struct v4l2_format *f) -{ - const struct atomisp_format_bridge *format_bridge; - u32 padded_width; - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - /* Currently, raw formats are broken!!! */ - if (!format_bridge || format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - if (!format_bridge) - return -EINVAL; - } - - padded_width = f->fmt.pix.width + pad_w; - - if (format_bridge->planar) { - f->fmt.pix.bytesperline = padded_width; - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * - DIV_ROUND_UP(format_bridge->depth * - padded_width, 8)); - } else { - f->fmt.pix.bytesperline = DIV_ROUND_UP(format_bridge->depth * - padded_width, 8); - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * f->fmt.pix.bytesperline); - } - - if (f->fmt.pix.field == V4L2_FIELD_ANY) - f->fmt.pix.field = V4L2_FIELD_NONE; - - /* - * FIXME: do we need to setup this differently, depending on the - * sensor or the pipeline? - */ - f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_709; - f->fmt.pix.xfer_func = V4L2_XFER_FUNC_709; - - f->fmt.pix.width -= pad_w; - f->fmt.pix.height -= pad_h; - - return 0; -} - /* This function looks up the closest available resolution. */ static int atomisp_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); - u32 pixfmt = f->fmt.pix.pixelformat; - int ret; - - /* - * atomisp_try_fmt() gived results with padding included, note - * (this gets removed again by the atomisp_adjust_fmt() call below. - */ - f->fmt.pix.width += pad_w; - f->fmt.pix.height += pad_h; - - ret = atomisp_try_fmt(vdev, &f->fmt.pix, NULL); - if (ret) - return ret; - - /* - * atomisp_try_fmt() replaces pixelformat with the sensors native - * format, restore the actual format requested by userspace. - */ - f->fmt.pix.pixelformat = pixfmt; + struct atomisp_device *isp = video_get_drvdata(vdev); - return atomisp_adjust_fmt(f); + return atomisp_try_fmt(isp, &f->fmt.pix, NULL, NULL); } static int atomisp_g_fmt_cap(struct file *file, void *fh, @@ -1048,8 +1033,7 @@ static int atomisp_qbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer if (buf->index >= vdev->queue->num_buffers) return -EINVAL; - if (!atomisp_is_vf_pipe(pipe) && - (buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING)) { + if (buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING) { /* this buffer will have a per-frame parameter */ pipe->frame_request_config_id[buf->index] = buf->reserved2 & ~ATOMISP_BUFFER_HAS_PER_FRAME_SETTING; @@ -1099,48 +1083,6 @@ static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer return 0; } -enum ia_css_pipe_id atomisp_get_css_pipe_id(struct atomisp_sub_device *asd) -{ - /* - * Disable vf_pp and run CSS in video mode. This allows using ISP - * scaling but it has one frame delay due to CSS internal buffering. - */ - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) - return IA_CSS_PIPE_ID_VIDEO; - - /* - * Disable vf_pp and run CSS in still capture mode. In this mode - * CSS does not cause extra latency with buffering, but scaling - * is not available. - */ - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) - return IA_CSS_PIPE_ID_CAPTURE; - - switch (asd->run_mode->val) { - case ATOMISP_RUN_MODE_PREVIEW: - return IA_CSS_PIPE_ID_PREVIEW; - case ATOMISP_RUN_MODE_VIDEO: - return IA_CSS_PIPE_ID_VIDEO; - case ATOMISP_RUN_MODE_STILL_CAPTURE: - default: - return IA_CSS_PIPE_ID_CAPTURE; - } -} - -static unsigned int atomisp_sensor_start_stream(struct atomisp_sub_device *asd) -{ - if (asd->vfpp->val != ATOMISP_VFPP_ENABLE || - asd->copy_mode) - return 1; - - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO || - (asd->run_mode->val == ATOMISP_RUN_MODE_STILL_CAPTURE && - !atomisp_is_mbuscode_raw(asd->fmt[asd->capture_pad].fmt.code))) - return 2; - else - return 1; -} - /* Input system HW workaround */ /* Input system address translation corrupts burst during */ /* invalidate. SW workaround for this is to set burst length */ @@ -1163,17 +1105,14 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) { struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct video_device *vdev = &pipe->vdev; struct atomisp_device *isp = asd->isp; struct pci_dev *pdev = to_pci_dev(isp->dev); - enum ia_css_pipe_id css_pipe_id; - unsigned int sensor_start_stream; unsigned long irqflags; int ret; - mutex_lock(&isp->mutex); + dev_dbg(isp->dev, "Start stream\n"); - dev_dbg(isp->dev, "Start stream on pad %d\n", atomisp_subdev_source_pad(vdev)); + mutex_lock(&isp->mutex); ret = atomisp_pipe_check(pipe, false); if (ret) @@ -1182,25 +1121,6 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) /* Input system HW workaround */ atomisp_dma_burst_len_cfg(asd); - /* - * The number of streaming video nodes is based on which - * binary is going to be run. - */ - sensor_start_stream = atomisp_sensor_start_stream(asd); - - if (atomisp_subdev_streaming_count(asd) > sensor_start_stream) { - atomisp_qbuffers_to_css(asd); - ret = 0; - goto out_unlock; - } - - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { - atomisp_qbuffers_to_css(asd); - goto start_sensor; - } - - css_pipe_id = atomisp_get_css_pipe_id(asd); - /* Invalidate caches. FIXME: should flush only necessary buffers */ wbinvd(); @@ -1215,14 +1135,14 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) } asd->params.dvs_6axis = NULL; - ret = atomisp_css_start(asd, css_pipe_id, false); + ret = atomisp_css_start(asd); if (ret) { atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_QUEUED, true); goto out_unlock; } spin_lock_irqsave(&isp->lock, irqflags); - asd->streaming = ATOMISP_DEVICE_STREAMING_ENABLED; + asd->streaming = true; spin_unlock_irqrestore(&isp->lock, irqflags); atomic_set(&asd->sof_count, -1); atomic_set(&asd->sequence, -1); @@ -1238,13 +1158,6 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) atomisp_qbuffers_to_css(asd); - /* Only start sensor when the last streaming instance started */ - if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) { - ret = 0; - goto out_unlock; - } - -start_sensor: if (isp->flash) { asd->params.num_flash_frames = 0; asd->params.flash_state = ATOMISP_FLASH_IDLE; @@ -1254,19 +1167,9 @@ start_sensor: atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, atomisp_css_valid_sof(isp)); atomisp_csi2_configure(asd); - /* - * set freq to max when streaming count > 1 which indicate - * dual camera would run - */ - if (atomisp_streaming_count(isp) > 1) { - if (atomisp_freq_scaling(isp, - ATOMISP_DFS_MODE_MAX, false) < 0) - dev_dbg(isp->dev, "DFS max mode failed!\n"); - } else { - if (atomisp_freq_scaling(isp, - ATOMISP_DFS_MODE_AUTO, false) < 0) - dev_dbg(isp->dev, "DFS auto mode failed!\n"); - } + + if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, false) < 0) + dev_dbg(isp->dev, "DFS auto mode failed!\n"); /* Enable the CSI interface on ANN B0/K0 */ if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 << @@ -1279,8 +1182,9 @@ start_sensor: ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, video, s_stream, 1); if (ret) { + dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); spin_lock_irqsave(&isp->lock, irqflags); - asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; + asd->streaming = false; spin_unlock_irqrestore(&isp->lock, irqflags); ret = -EINVAL; goto out_unlock; @@ -1295,19 +1199,14 @@ void atomisp_stop_streaming(struct vb2_queue *vq) { struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct video_device *vdev = &pipe->vdev; struct atomisp_device *isp = asd->isp; struct pci_dev *pdev = to_pci_dev(isp->dev); - enum ia_css_pipe_id css_pipe_id; - bool recreate_stream = false; - bool first_streamoff = false; unsigned long flags; int ret; - mutex_lock(&isp->mutex); - - dev_dbg(isp->dev, "Stop stream on pad %d\n", atomisp_subdev_source_pad(vdev)); + dev_dbg(isp->dev, "Stop stream\n"); + mutex_lock(&isp->mutex); /* * There is no guarantee that the buffers queued to / owned by the ISP * will properly be returned to the queue when stopping. Set a flag to @@ -1324,46 +1223,29 @@ void atomisp_stop_streaming(struct vb2_queue *vq) if (ret == 0) dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) - first_streamoff = true; - spin_lock_irqsave(&isp->lock, flags); - if (atomisp_subdev_streaming_count(asd) == 1) - asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; - else - asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING; + asd->streaming = false; spin_unlock_irqrestore(&isp->lock, flags); - if (!first_streamoff) - goto stopsensor; - atomisp_clear_css_buffer_counters(asd); atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); - css_pipe_id = atomisp_get_css_pipe_id(asd); - atomisp_css_stop(asd, css_pipe_id, false); + atomisp_css_stop(asd, false); atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_ERROR, true); atomisp_subdev_cleanup_pending_events(asd); -stopsensor: - if (atomisp_subdev_streaming_count(asd) != atomisp_sensor_start_stream(asd)) - goto out_unlock; ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, video, s_stream, 0); + if (ret) + dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); if (isp->flash) { asd->params.num_flash_frames = 0; asd->params.flash_state = ATOMISP_FLASH_IDLE; } - /* if other streams are running, isp should not be powered off */ - if (atomisp_streaming_count(isp)) { - atomisp_css_flush(isp); - goto out_unlock; - } - /* Disable the CSI interface on ANN B0/K0 */ if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 << ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) { @@ -1373,45 +1255,21 @@ stopsensor: if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, false)) dev_warn(isp->dev, "DFS failed.\n"); + /* - * ISP work around, need to reset isp - * Is it correct time to reset ISP when first node does streamoff? - */ - if (isp->isp_timeout) - dev_err(isp->dev, "%s: Resetting with WA activated", - __func__); - /* - * It is possible that the other asd stream is in the stage - * that v4l2_setfmt is just get called on it, which will - * create css stream on that stream. But at this point, there - * is no way to destroy the css stream created on that stream. - * - * So force stream destroy here. + * ISP work around, need to reset ISP to allow next stream on to work. + * Streams have already been destroyed by atomisp_css_stop(). + * Disable PUNIT/ISP acknowlede/handshake - SRSE=3 and then reset. */ - if (isp->asd.stream_prepared) { - atomisp_destroy_pipes_stream_force(&isp->asd); - recreate_stream = true; - } - - /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */ pci_write_config_dword(pdev, PCI_I_CONTROL, isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); - dev_err(isp->dev, "atomisp_reset"); atomisp_reset(isp); - if (recreate_stream) { - int ret2; - - ret2 = atomisp_create_pipes_stream(&isp->asd); - if (ret2) { - dev_err(isp->dev, "%s error re-creating streams: %d\n", __func__, ret2); - if (!ret) - ret = ret2; - } - } + /* Streams were destroyed by atomisp_css_stop(), recreate them. */ + ret = atomisp_create_pipes_stream(&isp->asd); + if (ret) + dev_warn(isp->dev, "Recreating streams failed: %d\n", ret); - isp->isp_timeout = false; -out_unlock: mutex_unlock(&isp->mutex); } @@ -1902,9 +1760,6 @@ static int atomisp_s_parm(struct file *file, void *fh, case CI_MODE_STILL_CAPTURE: mode = ATOMISP_RUN_MODE_STILL_CAPTURE; break; - case CI_MODE_CONTINUOUS: - mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE; - break; case CI_MODE_PREVIEW: mode = ATOMISP_RUN_MODE_PREVIEW; break; @@ -1923,14 +1778,8 @@ static long atomisp_vidioc_default(struct file *file, void *fh, struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_sub_device *asd = atomisp_to_video_pipe(vdev)->asd; - struct v4l2_subdev *motor; int err; - if (!IS_ISP2401) - motor = isp->inputs[asd->input_curr].motor; - else - motor = isp->motor; - switch (cmd) { case ATOMISP_IOC_S_SENSOR_RUNMODE: if (IS_ISP2401) @@ -2081,31 +1930,10 @@ static long atomisp_vidioc_default(struct file *file, void *fh, err = atomisp_fixed_pattern_table(asd, arg); break; - case ATOMISP_IOC_G_MOTOR_PRIV_INT_DATA: - if (motor) - err = v4l2_subdev_call(motor, core, ioctl, cmd, arg); - else - err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - core, ioctl, cmd, arg); - break; - case ATOMISP_IOC_S_EXPOSURE: - case ATOMISP_IOC_G_SENSOR_CALIBRATION_GROUP: - case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA: - case ATOMISP_IOC_G_SENSOR_AE_BRACKETING_INFO: - case ATOMISP_IOC_S_SENSOR_AE_BRACKETING_MODE: - case ATOMISP_IOC_G_SENSOR_AE_BRACKETING_MODE: - case ATOMISP_IOC_S_SENSOR_AE_BRACKETING_LUT: err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, core, ioctl, cmd, arg); break; - case ATOMISP_IOC_G_UPDATE_EXPOSURE: - if (IS_ISP2401) - err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, - core, ioctl, cmd, arg); - else - err = -EINVAL; - break; case ATOMISP_IOC_S_ISP_SHD_TAB: err = atomisp_set_shading_table(asd, arg); @@ -2123,12 +1951,6 @@ static long atomisp_vidioc_default(struct file *file, void *fh, err = atomisp_set_parameters(vdev, arg); break; - case ATOMISP_IOC_G_METADATA: - err = atomisp_get_metadata(asd, 0, arg); - break; - case ATOMISP_IOC_G_METADATA_BY_TYPE: - err = atomisp_get_metadata_by_type(asd, 0, arg); - break; case ATOMISP_IOC_EXT_ISP_CTRL: err = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, core, ioctl, cmd, arg); @@ -2149,15 +1971,9 @@ static long atomisp_vidioc_default(struct file *file, void *fh, case ATOMISP_IOC_S_FORMATS_CONFIG: err = atomisp_formats(asd, 1, arg); break; - case ATOMISP_IOC_S_EXPOSURE_WINDOW: - err = atomisp_s_ae_window(asd, arg); - break; case ATOMISP_IOC_INJECT_A_FAKE_EVENT: err = atomisp_inject_a_fake_event(asd, arg); break; - case ATOMISP_IOC_G_INVALID_FRAME_NUM: - err = atomisp_get_invalid_frame_num(vdev, arg); - break; case ATOMISP_IOC_S_ARRAY_RESOLUTION: err = atomisp_set_array_res(asd, arg); break; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h index db6da77df06b..56d3df86c706 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h @@ -42,13 +42,8 @@ int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd, int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count); void atomisp_stop_streaming(struct vb2_queue *vq); -enum ia_css_pipe_id atomisp_get_css_pipe_id(struct atomisp_sub_device - *asd); - extern const struct v4l2_ioctl_ops atomisp_ioctl_ops; -unsigned int atomisp_streaming_count(struct atomisp_device *isp); - /* compat_ioctl for 32bit userland app and 64bit kernel */ long atomisp_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index a0acfdb87177..45073e401bac 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -117,35 +117,19 @@ const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_by_atomisp_in_fmt( return NULL; } -bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd, - unsigned int source_pad) +bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd) { struct v4l2_mbus_framefmt *sink, *src; sink = atomisp_subdev_get_ffmt(&asd->subdev, NULL, - V4L2_SUBDEV_FORMAT_ACTIVE, - ATOMISP_SUBDEV_PAD_SINK); + V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SINK); src = atomisp_subdev_get_ffmt(&asd->subdev, NULL, - V4L2_SUBDEV_FORMAT_ACTIVE, source_pad); + V4L2_SUBDEV_FORMAT_ACTIVE, ATOMISP_SUBDEV_PAD_SOURCE); return atomisp_is_mbuscode_raw(sink->code) && !atomisp_is_mbuscode_raw(src->code); } -uint16_t atomisp_subdev_source_pad(struct video_device *vdev) -{ - struct media_link *link; - u16 ret = 0; - - list_for_each_entry(link, &vdev->entity.links, list) { - if (link->source) { - ret = link->source->index; - break; - } - } - return ret; -} - /* * V4L2 subdev operations */ @@ -355,10 +339,7 @@ static const char *atomisp_pad_str(unsigned int pad) { static const char *const pad_str[] = { "ATOMISP_SUBDEV_PAD_SINK", - "ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE", - "ATOMISP_SUBDEV_PAD_SOURCE_VF", - "ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW", - "ATOMISP_SUBDEV_PAD_SOURCE_VIDEO", + "ATOMISP_SUBDEV_PAD_SOURCE", }; if (pad >= ARRAY_SIZE(pad_str)) @@ -376,9 +357,10 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *ffmt[ATOMISP_SUBDEV_PADS_NUM]; struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM], *comp[ATOMISP_SUBDEV_PADS_NUM]; - unsigned int i; - unsigned int padding_w = pad_w; - unsigned int padding_h = pad_h; + + if ((pad == ATOMISP_SUBDEV_PAD_SINK && target != V4L2_SEL_TGT_CROP) || + (pad == ATOMISP_SUBDEV_PAD_SOURCE && target != V4L2_SEL_TGT_COMPOSE)) + return -EINVAL; isp_get_fmt_rect(sd, sd_state, which, ffmt, crop, comp); @@ -393,27 +375,17 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, r->width = rounddown(r->width, ATOM_ISP_STEP_WIDTH); r->height = rounddown(r->height, ATOM_ISP_STEP_HEIGHT); - switch (pad) { - case ATOMISP_SUBDEV_PAD_SINK: { + if (pad == ATOMISP_SUBDEV_PAD_SINK) { /* Only crop target supported on sink pad. */ unsigned int dvs_w, dvs_h; crop[pad]->width = ffmt[pad]->width; crop[pad]->height = ffmt[pad]->height; - /* Workaround for BYT 1080p perfectshot since the maxinum resolution of - * front camera ov2722 is 1932x1092 and cannot use pad_w > 12*/ - if (!strncmp(isp->inputs[isp_sd->input_curr].camera->name, - "ov2722", 6) && crop[pad]->height == 1092) { - padding_w = 12; - padding_h = 12; - } - - if (atomisp_subdev_format_conversion(isp_sd, - isp_sd->capture_pad) + if (atomisp_subdev_format_conversion(isp_sd) && crop[pad]->width && crop[pad]->height) { - crop[pad]->width -= padding_w; - crop[pad]->height -= padding_h; + crop[pad]->width -= isp_sd->sink_pad_padding_w; + crop[pad]->height -= isp_sd->sink_pad_padding_h; } if (isp_sd->params.video_dis_en && @@ -433,19 +405,15 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, crop[pad]->height = min(crop[pad]->height, r->height); if (!(flags & V4L2_SEL_FLAG_KEEP_CONFIG)) { - for (i = ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE; - i < ATOMISP_SUBDEV_PADS_NUM; i++) { - struct v4l2_rect tmp = *crop[pad]; - - atomisp_subdev_set_selection( - sd, sd_state, which, i, - V4L2_SEL_TGT_COMPOSE, - flags, &tmp); - } + struct v4l2_rect tmp = *crop[pad]; + + atomisp_subdev_set_selection(sd, sd_state, which, + ATOMISP_SUBDEV_PAD_SOURCE, + V4L2_SEL_TGT_COMPOSE, flags, &tmp); } if (which == V4L2_SUBDEV_FORMAT_TRY) - break; + goto get_rect; if (isp_sd->params.video_dis_en && isp_sd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { @@ -468,12 +436,8 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, ATOMISP_INPUT_STREAM_GENERAL, crop[pad]->width, crop[pad]->height); - break; - } - case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: - case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: { + } else if (isp_sd->run_mode->val != ATOMISP_RUN_MODE_PREVIEW) { /* Only compose target is supported on source pads. */ - if (isp_sd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { /* Scaling is disabled in this mode */ r->width = crop[ATOMISP_SUBDEV_PAD_SINK]->width; @@ -492,7 +456,7 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, if (r->width == 0 || r->height == 0 || crop[ATOMISP_SUBDEV_PAD_SINK]->width == 0 || crop[ATOMISP_SUBDEV_PAD_SINK]->height == 0) - break; + goto get_rect; /* * do cropping on sensor input if ratio of required resolution * is different with sensor output resolution ratio: @@ -522,18 +486,12 @@ int atomisp_subdev_set_selection(struct v4l2_subdev *sd, rounddown(crop[ATOMISP_SUBDEV_PAD_SINK]-> width * r->height / r->width, ATOM_ISP_STEP_WIDTH)); - - break; - } - case ATOMISP_SUBDEV_PAD_SOURCE_VF: - case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: + } else { comp[pad]->width = r->width; comp[pad]->height = r->height; - break; - default: - return -EINVAL; } +get_rect: /* Set format dimensions on non-sink pads as well. */ if (pad != ATOMISP_SUBDEV_PAD_SINK) { ffmt[pad]->width = comp[pad]->width; @@ -612,10 +570,7 @@ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, break; } - case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: - case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: - case ATOMISP_SUBDEV_PAD_SOURCE_VF: - case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: + case ATOMISP_SUBDEV_PAD_SOURCE: __ffmt->code = ffmt->code; break; } @@ -755,11 +710,9 @@ static const struct v4l2_ctrl_ops ctrl_ops = { }; static const char *const ctrl_run_mode_menu[] = { - NULL, - "Video", - "Still capture", - "Continuous capture", - "Preview", + [ATOMISP_RUN_MODE_VIDEO] = "Video", + [ATOMISP_RUN_MODE_STILL_CAPTURE] = "Still capture", + [ATOMISP_RUN_MODE_PREVIEW] = "Preview", }; static const struct v4l2_ctrl_config ctrl_run_mode = { @@ -767,9 +720,9 @@ static const struct v4l2_ctrl_config ctrl_run_mode = { .id = V4L2_CID_RUN_MODE, .name = "Atomisp run mode", .type = V4L2_CTRL_TYPE_MENU, - .min = 1, - .def = 1, - .max = 4, + .min = ATOMISP_RUN_MODE_MIN, + .def = ATOMISP_RUN_MODE_PREVIEW, + .max = ATOMISP_RUN_MODE_MAX, .qmenu = ctrl_run_mode_menu, }; @@ -921,23 +874,13 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) sprintf(sd->name, "ATOMISP_SUBDEV"); v4l2_set_subdevdata(sd, asd); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->devnode = &asd->video_out.vdev; pads[ATOMISP_SUBDEV_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - pads[ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW].flags = MEDIA_PAD_FL_SOURCE; - pads[ATOMISP_SUBDEV_PAD_SOURCE_VF].flags = MEDIA_PAD_FL_SOURCE; - pads[ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE].flags = MEDIA_PAD_FL_SOURCE; - pads[ATOMISP_SUBDEV_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE; - - asd->fmt[ATOMISP_SUBDEV_PAD_SINK].fmt.code = - MEDIA_BUS_FMT_SBGGR10_1X10; - asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW].fmt.code = - MEDIA_BUS_FMT_SBGGR10_1X10; - asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE_VF].fmt.code = - MEDIA_BUS_FMT_SBGGR10_1X10; - asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE].fmt.code = - MEDIA_BUS_FMT_SBGGR10_1X10; - asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE_VIDEO].fmt.code = - MEDIA_BUS_FMT_SBGGR10_1X10; + pads[ATOMISP_SUBDEV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + asd->fmt[ATOMISP_SUBDEV_PAD_SINK].fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; + asd->fmt[ATOMISP_SUBDEV_PAD_SOURCE].fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; me->ops = &isp_subdev_media_ops; me->function = MEDIA_ENT_F_PROC_VIDEO_ISP; @@ -945,43 +888,11 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) if (ret < 0) return ret; - ret = atomisp_init_subdev_pipe(asd, &asd->video_out_preview, - V4L2_BUF_TYPE_VIDEO_CAPTURE); + ret = atomisp_init_subdev_pipe(asd, &asd->video_out, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret) return ret; - ret = atomisp_init_subdev_pipe(asd, &asd->video_out_vf, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret) - return ret; - - ret = atomisp_init_subdev_pipe(asd, &asd->video_out_capture, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret) - return ret; - - ret = atomisp_init_subdev_pipe(asd, &asd->video_out_video_capture, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret) - return ret; - - ret = atomisp_video_init(&asd->video_out_capture, "CAPTURE", - ATOMISP_RUN_MODE_STILL_CAPTURE); - if (ret < 0) - return ret; - - ret = atomisp_video_init(&asd->video_out_vf, "VIEWFINDER", - ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE); - if (ret < 0) - return ret; - - ret = atomisp_video_init(&asd->video_out_preview, "PREVIEW", - ATOMISP_RUN_MODE_PREVIEW); - if (ret < 0) - return ret; - - ret = atomisp_video_init(&asd->video_out_video_capture, "VIDEO", - ATOMISP_RUN_MODE_VIDEO); + ret = atomisp_video_init(&asd->video_out); if (ret < 0) return ret; @@ -1016,57 +927,6 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) return asd->ctrl_handler.error; } -int atomisp_create_pads_links(struct atomisp_device *isp) -{ - int i, ret; - - for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { - ret = media_create_pad_link(&isp->csi2_port[i].subdev.entity, - CSI2_PAD_SOURCE, &isp->asd.subdev.entity, - ATOMISP_SUBDEV_PAD_SINK, 0); - if (ret < 0) - return ret; - } - - for (i = 0; i < isp->input_cnt; i++) { - /* Don't create links for the test-pattern-generator */ - if (isp->inputs[i].type == TEST_PATTERN) - continue; - - ret = media_create_pad_link(&isp->inputs[i].camera->entity, 0, - &isp->csi2_port[isp->inputs[i]. - port].subdev.entity, - CSI2_PAD_SINK, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - return ret; - } - - ret = media_create_pad_link(&isp->asd.subdev.entity, - ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW, - &isp->asd.video_out_preview.vdev.entity, 0, 0); - if (ret < 0) - return ret; - ret = media_create_pad_link(&isp->asd.subdev.entity, - ATOMISP_SUBDEV_PAD_SOURCE_VF, - &isp->asd.video_out_vf.vdev.entity, 0, 0); - if (ret < 0) - return ret; - ret = media_create_pad_link(&isp->asd.subdev.entity, - ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE, - &isp->asd.video_out_capture.vdev.entity, 0, 0); - if (ret < 0) - return ret; - ret = media_create_pad_link(&isp->asd.subdev.entity, - ATOMISP_SUBDEV_PAD_SOURCE_VIDEO, - &isp->asd.video_out_video_capture.vdev.entity, 0, 0); - if (ret < 0) - return ret; - - return 0; -} - static void atomisp_subdev_cleanup_entities(struct atomisp_sub_device *asd) { v4l2_ctrl_handler_free(&asd->ctrl_handler); @@ -1092,10 +952,7 @@ void atomisp_subdev_unregister_entities(struct atomisp_sub_device *asd) { atomisp_subdev_cleanup_entities(asd); v4l2_device_unregister_subdev(&asd->subdev); - atomisp_video_unregister(&asd->video_out_preview); - atomisp_video_unregister(&asd->video_out_vf); - atomisp_video_unregister(&asd->video_out_capture); - atomisp_video_unregister(&asd->video_out_video_capture); + atomisp_video_unregister(&asd->video_out); } int atomisp_subdev_register_subdev(struct atomisp_sub_device *asd, @@ -1104,51 +961,6 @@ int atomisp_subdev_register_subdev(struct atomisp_sub_device *asd, return v4l2_device_register_subdev(vdev, &asd->subdev); } -int atomisp_subdev_register_video_nodes(struct atomisp_sub_device *asd, - struct v4l2_device *vdev) -{ - int ret; - - /* - * FIXME: check if all device caps are properly initialized. - * Should any of those use V4L2_CAP_META_CAPTURE? Probably yes. - */ - - asd->video_out_preview.vdev.v4l2_dev = vdev; - asd->video_out_preview.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - ret = video_register_device(&asd->video_out_preview.vdev, - VFL_TYPE_VIDEO, -1); - if (ret < 0) - goto error; - - asd->video_out_capture.vdev.v4l2_dev = vdev; - asd->video_out_capture.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - ret = video_register_device(&asd->video_out_capture.vdev, - VFL_TYPE_VIDEO, -1); - if (ret < 0) - goto error; - - asd->video_out_vf.vdev.v4l2_dev = vdev; - asd->video_out_vf.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - ret = video_register_device(&asd->video_out_vf.vdev, - VFL_TYPE_VIDEO, -1); - if (ret < 0) - goto error; - - asd->video_out_video_capture.vdev.v4l2_dev = vdev; - asd->video_out_video_capture.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - ret = video_register_device(&asd->video_out_video_capture.vdev, - VFL_TYPE_VIDEO, -1); - if (ret < 0) - goto error; - - return 0; - -error: - atomisp_subdev_unregister_entities(asd); - return ret; -} - /* * atomisp_subdev_init - ISP Subdevice initialization. * @dev: Device pointer specific to the ATOM ISP. diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index fee663bc415a..9a04511b9efd 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -32,15 +32,8 @@ #define ATOMISP_MAX_EXP_ID (250) #define ATOMISP_SUBDEV_PAD_SINK 0 -/* capture output for still frames */ -#define ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE 1 -/* viewfinder output for downscaled capture output */ -#define ATOMISP_SUBDEV_PAD_SOURCE_VF 2 -/* preview output for display */ -#define ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW 3 -/* main output for video pipeline */ -#define ATOMISP_SUBDEV_PAD_SOURCE_VIDEO 4 -#define ATOMISP_SUBDEV_PADS_NUM 5 +#define ATOMISP_SUBDEV_PAD_SOURCE 1 +#define ATOMISP_SUBDEV_PADS_NUM 2 struct atomisp_in_fmt_conv { u32 code; @@ -74,8 +67,6 @@ struct atomisp_video_pipe { /* Filled through atomisp_get_css_frame_info() on queue setup */ struct ia_css_frame_info frame_info; - /* Store here the initial run mode */ - unsigned int default_run_mode; /* Set from streamoff to disallow queuing further buffers in CSS */ bool stopping; @@ -248,15 +239,12 @@ struct atomisp_sub_device { struct v4l2_subdev subdev; struct media_pad pads[ATOMISP_SUBDEV_PADS_NUM]; struct atomisp_pad_format fmt[ATOMISP_SUBDEV_PADS_NUM]; - u16 capture_pad; /* main capture pad; defines much of isp config */ + /* Padding for currently set sink-pad fmt */ + u32 sink_pad_padding_w; + u32 sink_pad_padding_h; unsigned int output; - struct atomisp_video_pipe video_out_capture; /* capture output */ - struct atomisp_video_pipe video_out_vf; /* viewfinder output */ - struct atomisp_video_pipe video_out_preview; /* preview output */ - /* video pipe main output */ - struct atomisp_video_pipe video_out_video_capture; - /* struct isp_subdev_params params; */ + struct atomisp_video_pipe video_out; struct atomisp_device *isp; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *run_mode; @@ -312,13 +300,12 @@ struct atomisp_sub_device { * Writers of streaming must hold both isp->mutex and isp->lock. * Readers of streaming need to hold only one of the two locks. */ - unsigned int streaming; + bool streaming; bool stream_prepared; /* whether css stream is created */ + bool recreate_streams_on_resume; unsigned int latest_preview_exp_id; /* CSS ZSL/SDV raw buffer id */ - unsigned int mipi_frame_size; - bool copy_mode; /* CSI2+ use copy mode */ int raw_buffer_bitmap[ATOMISP_MAX_EXP_ID / 32 + @@ -352,9 +339,7 @@ const struct atomisp_in_fmt_conv atomisp_in_fmt); const struct atomisp_in_fmt_conv *atomisp_find_in_fmt_conv_compressed(u32 code); -bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd, - unsigned int source_pad); -uint16_t atomisp_subdev_source_pad(struct video_device *vdev); +bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd); /* Get pointer to appropriate format */ struct v4l2_mbus_framefmt @@ -382,10 +367,7 @@ void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd); void atomisp_subdev_unregister_entities(struct atomisp_sub_device *asd); int atomisp_subdev_register_subdev(struct atomisp_sub_device *asd, struct v4l2_device *vdev); -int atomisp_subdev_register_video_nodes(struct atomisp_sub_device *asd, - struct v4l2_device *vdev); int atomisp_subdev_init(struct atomisp_device *isp); void atomisp_subdev_cleanup(struct atomisp_device *isp); -int atomisp_create_pads_links(struct atomisp_device *isp); #endif /* __ATOMISP_SUBDEV_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 3f315dabbeeb..c43b916a006e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -27,6 +27,7 @@ #include <linux/dmi.h> #include <linux/interrupt.h> #include <linux/bits.h> +#include <media/v4l2-fwnode.h> #include <asm/iosf_mbi.h> @@ -124,22 +125,8 @@ static const struct atomisp_freq_scaling_rule dfs_rules_merr[] = { .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_457MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; /* Merrifield and Moorefield DFS rules */ @@ -171,22 +158,8 @@ static const struct atomisp_freq_scaling_rule dfs_rules_merr_1179[] = { .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; static const struct atomisp_dfs_config dfs_config_merr_1179 = { @@ -251,23 +224,9 @@ static const struct atomisp_freq_scaling_rule dfs_rules_merr_117a[] = { .width = ISP_FREQ_RULE_ANY, .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_200MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; static struct atomisp_dfs_config dfs_config_merr_117a = { @@ -298,22 +257,8 @@ static const struct atomisp_freq_scaling_rule dfs_rules_byt[] = { .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_400MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; static const struct atomisp_dfs_config dfs_config_byt = { @@ -344,29 +289,8 @@ static const struct atomisp_freq_scaling_rule dfs_rules_cht[] = { .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_320MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_320MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = 1280, - .height = 720, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_320MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_356MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; static const struct atomisp_freq_scaling_rule dfs_rules_cht_soc[] = { @@ -389,22 +313,8 @@ static const struct atomisp_freq_scaling_rule dfs_rules_cht_soc[] = { .height = ISP_FREQ_RULE_ANY, .fps = ISP_FREQ_RULE_ANY, .isp_freq = ISP_FREQ_320MHZ, - .run_mode = ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE, - }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_320MHZ, .run_mode = ATOMISP_RUN_MODE_PREVIEW, }, - { - .width = ISP_FREQ_RULE_ANY, - .height = ISP_FREQ_RULE_ANY, - .fps = ISP_FREQ_RULE_ANY, - .isp_freq = ISP_FREQ_356MHZ, - .run_mode = ATOMISP_RUN_MODE_SDV, - }, }; static const struct atomisp_dfs_config dfs_config_cht = { @@ -424,34 +334,22 @@ const struct atomisp_dfs_config dfs_config_cht_soc = { .dfs_table_size = ARRAY_SIZE(dfs_rules_cht_soc), }; -int atomisp_video_init(struct atomisp_video_pipe *video, const char *name, - unsigned int run_mode) +int atomisp_video_init(struct atomisp_video_pipe *video) { int ret; - const char *direction; - - switch (video->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - direction = "output"; - video->pad.flags = MEDIA_PAD_FL_SINK; - video->vdev.fops = &atomisp_fops; - video->vdev.ioctl_ops = &atomisp_ioctl_ops; - video->vdev.lock = &video->isp->mutex; - break; - default: - return -EINVAL; - } + video->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&video->vdev.entity, 1, &video->pad); if (ret < 0) return ret; /* Initialize the video device. */ - snprintf(video->vdev.name, sizeof(video->vdev.name), - "ATOMISP ISP %s %s", name, direction); + strscpy(video->vdev.name, "ATOMISP video output", sizeof(video->vdev.name)); + video->vdev.fops = &atomisp_fops; + video->vdev.ioctl_ops = &atomisp_ioctl_ops; + video->vdev.lock = &video->isp->mutex; video->vdev.release = video_device_release_empty; video_set_drvdata(&video->vdev, video->isp); - video->default_run_mode = run_mode; return 0; } @@ -755,15 +653,9 @@ static int atomisp_suspend(struct device *dev) dev_get_drvdata(dev); unsigned long flags; - /* - * FIXME: Suspend is not supported by sensors. Abort if any video - * node was opened. - */ - if (atomisp_dev_users(isp)) - return -EBUSY; - + /* FIXME: Suspend is not supported by sensors. Abort if streaming. */ spin_lock_irqsave(&isp->lock, flags); - if (isp->asd.streaming != ATOMISP_DEVICE_STREAMING_DISABLED) { + if (isp->asd.streaming) { spin_unlock_irqrestore(&isp->lock, flags); dev_err(isp->dev, "atomisp cannot suspend at this time.\n"); return -EINVAL; @@ -772,12 +664,25 @@ static int atomisp_suspend(struct device *dev) pm_runtime_resume(dev); + isp->asd.recreate_streams_on_resume = isp->asd.stream_prepared; + atomisp_destroy_pipes_stream(&isp->asd); + return atomisp_power_off(dev); } static int atomisp_resume(struct device *dev) { - return atomisp_power_on(dev); + struct atomisp_device *isp = dev_get_drvdata(dev); + int ret; + + ret = atomisp_power_on(dev); + if (ret) + return ret; + + if (isp->asd.recreate_streams_on_resume) + ret = atomisp_create_pipes_stream(&isp->asd); + + return ret; } int atomisp_csi_lane_config(struct atomisp_device *isp) @@ -785,7 +690,7 @@ int atomisp_csi_lane_config(struct atomisp_device *isp) struct pci_dev *pdev = to_pci_dev(isp->dev); static const struct { u8 code; - u8 lanes[MRFLD_PORT_NUM]; + u8 lanes[N_MIPI_PORT_ID]; } portconfigs[] = { /* Tangier/Merrifield available lane configurations */ { 0x00, { 4, 1, 0 } }, /* 00000 */ @@ -809,7 +714,6 @@ int atomisp_csi_lane_config(struct atomisp_device *isp) }; unsigned int i, j; - u8 sensor_lanes[MRFLD_PORT_NUM] = { 0 }; u32 csi_control; int nportconfigs; u32 port_config_mask; @@ -837,41 +741,13 @@ int atomisp_csi_lane_config(struct atomisp_device *isp) nportconfigs = ARRAY_SIZE(portconfigs); } - for (i = 0; i < isp->input_cnt; i++) { - struct camera_mipi_info *mipi_info; - - if (isp->inputs[i].type != RAW_CAMERA) - continue; - - mipi_info = atomisp_to_sensor_mipi_info(isp->inputs[i].camera); - if (!mipi_info) - continue; - - switch (mipi_info->port) { - case ATOMISP_CAMERA_PORT_PRIMARY: - sensor_lanes[0] = mipi_info->num_lanes; - break; - case ATOMISP_CAMERA_PORT_SECONDARY: - sensor_lanes[1] = mipi_info->num_lanes; - break; - case ATOMISP_CAMERA_PORT_TERTIARY: - sensor_lanes[2] = mipi_info->num_lanes; - break; - default: - dev_err(isp->dev, - "%s: invalid port: %d for the %dth sensor\n", - __func__, mipi_info->port, i); - return -EINVAL; - } - } - for (i = 0; i < nportconfigs; i++) { - for (j = 0; j < MRFLD_PORT_NUM; j++) - if (sensor_lanes[j] && - sensor_lanes[j] != portconfigs[i].lanes[j]) + for (j = 0; j < N_MIPI_PORT_ID; j++) + if (isp->sensor_lanes[j] && + isp->sensor_lanes[j] != portconfigs[i].lanes[j]) break; - if (j == MRFLD_PORT_NUM) + if (j == N_MIPI_PORT_ID) break; /* Found matching setting */ } @@ -879,7 +755,7 @@ int atomisp_csi_lane_config(struct atomisp_device *isp) dev_err(isp->dev, "%s: could not find the CSI port setting for %d-%d-%d\n", __func__, - sensor_lanes[0], sensor_lanes[1], sensor_lanes[2]); + isp->sensor_lanes[0], isp->sensor_lanes[1], isp->sensor_lanes[2]); return -EINVAL; } @@ -907,7 +783,11 @@ static int atomisp_subdev_probe(struct atomisp_device *isp) { const struct atomisp_platform_data *pdata; struct intel_v4l2_subdev_table *subdevs; - int ret, raw_index = -1, count; + int ret, mipi_port; + + ret = atomisp_csi2_bridge_parse_firmware(isp); + if (ret) + return ret; pdata = atomisp_get_platform_data(); if (!pdata) { @@ -915,23 +795,12 @@ static int atomisp_subdev_probe(struct atomisp_device *isp) return 0; } - /* FIXME: should return -EPROBE_DEFER if not all subdevs were probed */ - for (count = 0; count < SUBDEV_WAIT_TIMEOUT_MAX_COUNT; count++) { - int camera_count = 0; - - for (subdevs = pdata->subdevs; subdevs->type; ++subdevs) { - if (subdevs->type == RAW_CAMERA) - camera_count++; - } - if (camera_count) - break; - msleep(SUBDEV_WAIT_TIMEOUT); - } - /* Wait more time to give more time for subdev init code to finish */ - msleep(5 * SUBDEV_WAIT_TIMEOUT); - - /* FIXME: should, instead, use I2C probe */ - + /* + * TODO: this is left here for now to allow testing atomisp-sensor + * drivers which are still using the atomisp_gmin_platform infra before + * converting them to standard v4l2 sensor drivers using runtime-pm + + * ACPI for pm and v4l2_async_register_subdev_sensor() registration. + */ for (subdevs = pdata->subdevs; subdevs->type; ++subdevs) { ret = v4l2_device_register_subdev(&isp->v4l2_dev, subdevs->subdev); if (ret) @@ -939,25 +808,20 @@ static int atomisp_subdev_probe(struct atomisp_device *isp) switch (subdevs->type) { case RAW_CAMERA: - dev_dbg(isp->dev, "raw_index: %d\n", raw_index); - raw_index = isp->input_cnt; - if (isp->input_cnt >= ATOM_ISP_MAX_INPUTS) { - dev_warn(isp->dev, - "too many atomisp inputs, ignored\n"); + if (subdevs->port >= ATOMISP_CAMERA_NR_PORTS) { + dev_err(isp->dev, "port %d not supported\n", subdevs->port); + break; + } + + if (isp->sensor_subdevs[subdevs->port]) { + dev_err(isp->dev, "port %d already has a sensor attached\n", + subdevs->port); break; } - isp->inputs[isp->input_cnt].type = subdevs->type; - isp->inputs[isp->input_cnt].port = subdevs->port; - isp->inputs[isp->input_cnt].camera = subdevs->subdev; - isp->inputs[isp->input_cnt].sensor_index = 0; - /* - * initialize the subdev frame size, then next we can - * judge whether frame_size store effective value via - * pixel_format. - */ - isp->inputs[isp->input_cnt].frame_size.pixel_format = 0; - isp->input_cnt++; + mipi_port = atomisp_port_to_mipi_port(isp, subdevs->port); + isp->sensor_lanes[mipi_port] = subdevs->lanes; + isp->sensor_subdevs[subdevs->port] = subdevs->subdev; break; case CAMERA_MOTOR: if (isp->motor) { @@ -979,21 +843,6 @@ static int atomisp_subdev_probe(struct atomisp_device *isp) } } - /* - * HACK: Currently VCM belongs to primary sensor only, but correct - * approach must be to acquire from platform code which sensor - * owns it. - */ - if (isp->motor && raw_index >= 0) - isp->inputs[raw_index].motor = isp->motor; - - /* Proceed even if no modules detected. For COS mode and no modules. */ - if (!isp->input_cnt) - dev_warn(isp->dev, "no camera attached or fail to detect\n"); - else - dev_info(isp->dev, "detected %d camera sensors\n", - isp->input_cnt); - return atomisp_csi_lane_config(isp); } @@ -1067,29 +916,8 @@ static int atomisp_register_entities(struct atomisp_device *isp) goto subdev_register_failed; } - for (i = 0; i < isp->input_cnt; i++) { - if (isp->inputs[i].port >= ATOMISP_CAMERA_NR_PORTS) { - dev_err(isp->dev, "isp->inputs port %d not supported\n", - isp->inputs[i].port); - ret = -EINVAL; - goto link_failed; - } - } - - if (isp->input_cnt < ATOM_ISP_MAX_INPUTS) { - dev_dbg(isp->dev, - "TPG detected, camera_cnt: %d\n", isp->input_cnt); - isp->inputs[isp->input_cnt].type = TEST_PATTERN; - isp->inputs[isp->input_cnt].port = -1; - isp->inputs[isp->input_cnt++].camera = &isp->tpg.sd; - } else { - dev_warn(isp->dev, "too many atomisp inputs, TPG ignored.\n"); - } - return 0; -link_failed: - atomisp_subdev_unregister_entities(&isp->asd); subdev_register_failed: atomisp_tpg_unregister_entities(&isp->tpg); tpg_register_failed: @@ -1103,15 +931,152 @@ v4l2_device_failed: return ret; } -static int atomisp_register_device_nodes(struct atomisp_device *isp) +static void atomisp_init_sensor(struct atomisp_input_subdev *input) +{ + struct v4l2_subdev_mbus_code_enum mbus_code_enum = { }; + struct v4l2_subdev_frame_size_enum fse = { }; + struct v4l2_subdev_state sd_state = { + .pads = &input->pad_cfg, + }; + struct v4l2_subdev_selection sel = { }; + int i, err; + + mbus_code_enum.which = V4L2_SUBDEV_FORMAT_ACTIVE; + err = v4l2_subdev_call(input->camera, pad, enum_mbus_code, NULL, &mbus_code_enum); + if (!err) + input->code = mbus_code_enum.code; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.target = V4L2_SEL_TGT_NATIVE_SIZE; + err = v4l2_subdev_call(input->camera, pad, get_selection, NULL, &sel); + if (err) + return; + + input->native_rect = sel.r; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP_DEFAULT; + err = v4l2_subdev_call(input->camera, pad, get_selection, NULL, &sel); + if (err) + return; + + input->active_rect = sel.r; + + /* + * Check for a framesize with half active_rect width and height, + * if found assume the sensor supports binning. + * Do this before changing the crop-rect since that may influence + * enum_frame_size results. + */ + for (i = 0; ; i++) { + fse.index = i; + fse.code = input->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + err = v4l2_subdev_call(input->camera, pad, enum_frame_size, NULL, &fse); + if (err) + break; + + if (fse.min_width <= (input->active_rect.width / 2) && + fse.min_height <= (input->active_rect.height / 2)) { + input->binning_support = true; + break; + } + } + + /* + * The ISP also wants the non-active pixels at the border of the sensor + * for padding, set the crop rect to cover the entire sensor instead + * of only the default active area. + * + * Do this for both try and active formats since the try_crop rect in + * pad_cfg may influence (clamp) future try_fmt calls with which == try. + */ + sel.which = V4L2_SUBDEV_FORMAT_TRY; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = input->native_rect; + err = v4l2_subdev_call(input->camera, pad, set_selection, &sd_state, &sel); + if (err) + return; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = input->native_rect; + err = v4l2_subdev_call(input->camera, pad, set_selection, NULL, &sel); + if (err) + return; + + dev_info(input->camera->dev, "Supports crop native %dx%d active %dx%d binning %d\n", + input->native_rect.width, input->native_rect.height, + input->active_rect.width, input->active_rect.height, + input->binning_support); + + input->crop_support = true; +} + +int atomisp_register_device_nodes(struct atomisp_device *isp) { - int err; + struct atomisp_input_subdev *input; + int i, err; + + for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { + err = media_create_pad_link(&isp->csi2_port[i].subdev.entity, + CSI2_PAD_SOURCE, &isp->asd.subdev.entity, + ATOMISP_SUBDEV_PAD_SINK, 0); + if (err) + return err; + + if (!isp->sensor_subdevs[i]) + continue; - err = atomisp_subdev_register_video_nodes(&isp->asd, &isp->v4l2_dev); + input = &isp->inputs[isp->input_cnt]; + + input->type = RAW_CAMERA; + input->port = i; + input->camera = isp->sensor_subdevs[i]; + + atomisp_init_sensor(input); + + /* + * HACK: Currently VCM belongs to primary sensor only, but correct + * approach must be to acquire from platform code which sensor + * owns it. + */ + if (i == ATOMISP_CAMERA_PORT_PRIMARY) + input->motor = isp->motor; + + err = media_create_pad_link(&input->camera->entity, 0, + &isp->csi2_port[i].subdev.entity, + CSI2_PAD_SINK, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (err) + return err; + + isp->input_cnt++; + } + + if (!isp->input_cnt) + dev_warn(isp->dev, "no camera attached or fail to detect\n"); + else + dev_info(isp->dev, "detected %d camera sensors\n", isp->input_cnt); + + if (isp->input_cnt < ATOM_ISP_MAX_INPUTS) { + dev_dbg(isp->dev, "TPG detected, camera_cnt: %d\n", isp->input_cnt); + isp->inputs[isp->input_cnt].type = TEST_PATTERN; + isp->inputs[isp->input_cnt].port = -1; + isp->inputs[isp->input_cnt++].camera = &isp->tpg.sd; + } else { + dev_warn(isp->dev, "too many atomisp inputs, TPG ignored.\n"); + } + + isp->asd.video_out.vdev.v4l2_dev = &isp->v4l2_dev; + isp->asd.video_out.vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + err = video_register_device(&isp->asd.video_out.vdev, VFL_TYPE_VIDEO, -1); if (err) return err; - err = atomisp_create_pads_links(isp); + err = media_create_pad_link(&isp->asd.subdev.entity, ATOMISP_SUBDEV_PAD_SOURCE, + &isp->asd.video_out.vdev.entity, 0, 0); if (err) return err; @@ -1543,9 +1508,11 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i isp->firmware = NULL; isp->css_env.isp_css_fw.data = NULL; - err = atomisp_register_device_nodes(isp); - if (err) + err = v4l2_async_nf_register(&isp->v4l2_dev, &isp->notifier); + if (err) { + dev_err(isp->dev, "failed to register async notifier : %d\n", err); goto css_init_fail; + } atomisp_drvfs_init(isp); diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.h b/drivers/staging/media/atomisp/pci/atomisp_v4l2.h index ccf1c0ac17b2..fad9573374b3 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.h +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.h @@ -26,10 +26,10 @@ struct v4l2_device; struct atomisp_device; struct firmware; -int atomisp_video_init(struct atomisp_video_pipe *video, const char *name, - unsigned int run_mode); +int atomisp_video_init(struct atomisp_video_pipe *video); void atomisp_video_unregister(struct atomisp_video_pipe *video); const struct firmware *atomisp_load_firmware(struct atomisp_device *isp); int atomisp_csi_lane_config(struct atomisp_device *isp); +int atomisp_register_device_nodes(struct atomisp_device *isp); #endif /* __ATOMISP_V4L2_H__ */ diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame.h b/drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame.h index 700070c58eda..90c17884968b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame.h +++ b/drivers/staging/media/atomisp/pci/runtime/frame/interface/ia_css_frame.h @@ -138,4 +138,6 @@ bool ia_css_frame_is_same_type( int ia_css_dma_configure_from_info(struct dma_port_config *config, const struct ia_css_frame_info *info); +unsigned int ia_css_frame_pad_width(unsigned int width, enum ia_css_frame_format format); + #endif /* __IA_CSS_FRAME_H__ */ diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c index 83bb42e05421..2d7fddb114f6 100644 --- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c +++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c @@ -269,6 +269,34 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) return 0; } +unsigned int ia_css_frame_pad_width(unsigned int width, enum ia_css_frame_format format) +{ + switch (format) { + /* + * Frames with a U and V plane of 8 bits per pixel need to have + * all planes aligned, this means double the alignment for the + * Y plane if the horizontal decimation is 2. + */ + case IA_CSS_FRAME_FORMAT_YUV420: + case IA_CSS_FRAME_FORMAT_YV12: + case IA_CSS_FRAME_FORMAT_NV12: + case IA_CSS_FRAME_FORMAT_NV21: + case IA_CSS_FRAME_FORMAT_BINARY_8: + case IA_CSS_FRAME_FORMAT_YUV_LINE: + return CEIL_MUL(width, 2 * HIVE_ISP_DDR_WORD_BYTES); + + case IA_CSS_FRAME_FORMAT_NV12_TILEY: + return CEIL_MUL(width, NV12_TILEY_TILE_WIDTH); + + case IA_CSS_FRAME_FORMAT_RAW: + case IA_CSS_FRAME_FORMAT_RAW_PACKED: + return CEIL_MUL(width, 2 * ISP_VEC_NELEMS); + + default: + return CEIL_MUL(width, HIVE_ISP_DDR_WORD_BYTES); + } +} + void ia_css_frame_info_set_width(struct ia_css_frame_info *info, unsigned int width, unsigned int min_padded_width) @@ -285,25 +313,8 @@ void ia_css_frame_info_set_width(struct ia_css_frame_info *info, align = max(min_padded_width, width); info->res.width = width; - /* frames with a U and V plane of 8 bits per pixel need to have - all planes aligned, this means double the alignment for the - Y plane if the horizontal decimation is 2. */ - if (info->format == IA_CSS_FRAME_FORMAT_YUV420 || - info->format == IA_CSS_FRAME_FORMAT_YV12 || - info->format == IA_CSS_FRAME_FORMAT_NV12 || - info->format == IA_CSS_FRAME_FORMAT_NV21 || - info->format == IA_CSS_FRAME_FORMAT_BINARY_8 || - info->format == IA_CSS_FRAME_FORMAT_YUV_LINE) - info->padded_width = - CEIL_MUL(align, 2 * HIVE_ISP_DDR_WORD_BYTES); - else if (info->format == IA_CSS_FRAME_FORMAT_NV12_TILEY) - info->padded_width = CEIL_MUL(align, NV12_TILEY_TILE_WIDTH); - else if (info->format == IA_CSS_FRAME_FORMAT_RAW || - info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED) - info->padded_width = CEIL_MUL(align, 2 * ISP_VEC_NELEMS); - else { - info->padded_width = CEIL_MUL(align, HIVE_ISP_DDR_WORD_BYTES); - } + info->padded_width = ia_css_frame_pad_width(align, info->format); + IA_CSS_LEAVE_PRIVATE(""); } @@ -601,9 +612,6 @@ static void frame_init_qplane6_planes(struct ia_css_frame *frame) static int frame_allocate_buffer_data(struct ia_css_frame *frame) { -#ifdef ISP2401 - IA_CSS_ENTER_LEAVE_PRIVATE("frame->data_bytes=%d\n", frame->data_bytes); -#endif frame->data = hmm_alloc(frame->data_bytes); if (frame->data == mmgr_NULL) return -ENOMEM; @@ -635,15 +643,11 @@ static int frame_allocate_with_data(struct ia_css_frame **frame, if (err) { kvfree(me); -#ifndef ISP2401 - return err; -#else - me = NULL; -#endif + *frame = NULL; + } else { + *frame = me; } - *frame = me; - return err; } diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 93789500416f..4b3fa6d93fe0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -1529,15 +1529,14 @@ ia_css_init(struct device *dev, const struct ia_css_env *env, mipi_init(); -#ifndef ISP2401 /* * In case this has been programmed already, update internal * data structure ... * DEPRECATED */ - my_css.page_table_base_index = mmu_get_page_table_base_index(MMU0_ID); + if (!IS_ISP2401) + my_css.page_table_base_index = mmu_get_page_table_base_index(MMU0_ID); -#endif my_css.irq_type = irq_type; my_css_save.irq_type = irq_type; @@ -1596,10 +1595,8 @@ ia_css_init(struct device *dev, const struct ia_css_env *env, * sh_css_init_buffer_queues(); */ -#if defined(ISP2401) - gp_device_reg_store(GP_DEVICE0_ID, _REG_GP_SWITCH_ISYS2401_ADDR, 1); -#endif - + if (IS_ISP2401) + gp_device_reg_store(GP_DEVICE0_ID, _REG_GP_SWITCH_ISYS2401_ADDR, 1); if (!IS_ISP2401) dma_set_max_burst_size(DMA0_ID, HIVE_DMA_BUS_DDR_CONN, @@ -2128,13 +2125,8 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) err); } } -#ifndef ISP2401 ia_css_frame_free_multiple(NUM_VIDEO_TNR_FRAMES, pipe->pipe_settings.video.tnr_frames); -#else - ia_css_frame_free_multiple(NUM_VIDEO_TNR_FRAMES, - pipe->pipe_settings.video.tnr_frames); -#endif ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES, pipe->pipe_settings.video.delay_frames); break; @@ -2238,11 +2230,10 @@ int ia_css_irq_translate( case virq_isys_csi: infos |= IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR; break; -#if !defined(ISP2401) case virq_ifmt0_id: - infos |= IA_CSS_IRQ_INFO_IF_ERROR; + if (!IS_ISP2401) + infos |= IA_CSS_IRQ_INFO_IF_ERROR; break; -#endif case virq_dma: infos |= IA_CSS_IRQ_INFO_DMA_ERROR; break; @@ -2277,27 +2268,34 @@ int ia_css_irq_enable( IA_CSS_ENTER("info=%d, enable=%d", info, enable); switch (info) { -#if !defined(ISP2401) case IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF: + if (IS_ISP2401) + /* Just ignore those unused IRQs without printing errors */ + return 0; + irq = virq_isys_sof; break; case IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF: + if (IS_ISP2401) + /* Just ignore those unused IRQs without printing errors */ + return 0; + irq = virq_isys_eof; break; case IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR: + if (IS_ISP2401) + /* Just ignore those unused IRQs without printing errors */ + return 0; + irq = virq_isys_csi; break; case IA_CSS_IRQ_INFO_IF_ERROR: + if (IS_ISP2401) + /* Just ignore those unused IRQs without printing errors */ + return 0; + irq = virq_ifmt0_id; break; -#else - case IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF: - case IA_CSS_IRQ_INFO_CSS_RECEIVER_EOF: - case IA_CSS_IRQ_INFO_INPUT_SYSTEM_ERROR: - case IA_CSS_IRQ_INFO_IF_ERROR: - /* Just ignore those unused IRQs without printing errors */ - return 0; -#endif case IA_CSS_IRQ_INFO_DMA_ERROR: irq = virq_dma; break; @@ -2413,14 +2411,14 @@ alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time) return -EINVAL; } -#if defined(ISP2401) - /* For CSI2+, the continuous frame will hold the full input frame */ - ref_info.res.width = pipe->stream->config.input_config.input_res.width; - ref_info.res.height = pipe->stream->config.input_config.input_res.height; + if (IS_ISP2401) { + /* For CSI2+, the continuous frame will hold the full input frame */ + ref_info.res.width = pipe->stream->config.input_config.input_res.width; + ref_info.res.height = pipe->stream->config.input_config.input_res.height; - /* Ensure padded width is aligned for 2401 */ - ref_info.padded_width = CEIL_MUL(ref_info.res.width, 2 * ISP_VEC_NELEMS); -#endif + /* Ensure padded width is aligned for 2401 */ + ref_info.padded_width = CEIL_MUL(ref_info.res.width, 2 * ISP_VEC_NELEMS); + } if (pipe->stream->config.pack_raw_pixels) { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, @@ -2499,11 +2497,9 @@ load_preview_binaries(struct ia_css_pipe *pipe) int err = 0; bool need_vf_pp = false; bool need_isp_copy_binary = false; -#ifdef ISP2401 bool sensor = false; -#else bool continuous; -#endif + /* preview only have 1 output pin now */ struct ia_css_frame_info *pipe_out_info = &pipe->output_info[0]; struct ia_css_preview_settings *mycs = &pipe->pipe_settings.preview; @@ -2514,11 +2510,9 @@ load_preview_binaries(struct ia_css_pipe *pipe) assert(pipe->mode == IA_CSS_PIPE_ID_PREVIEW); online = pipe->stream->config.online; -#ifdef ISP2401 + sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; -#else continuous = pipe->stream->config.continuous; -#endif if (mycs->preview_binary.info) return 0; @@ -2627,24 +2621,22 @@ load_preview_binaries(struct ia_css_pipe *pipe) return err; } -#ifdef ISP2401 - /* - * When the input system is 2401, only the Direct Sensor Mode - * Offline Preview uses the ISP copy binary. - */ - need_isp_copy_binary = !online && sensor; -#else - /* - * About pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY: - * This is typical the case with SkyCam (which has no input system) but it also applies to all cases - * where the driver chooses for memory based input frames. In these cases, a copy binary (which typical - * copies sensor data to DDR) does not have much use. - */ - if (!IS_ISP2401) + if (IS_ISP2401) { + /* + * When the input system is 2401, only the Direct Sensor Mode + * Offline Preview uses the ISP copy binary. + */ + need_isp_copy_binary = !online && sensor; + } else { + /* + * About pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY: + * This is typical the case with SkyCam (which has no input system) but it also + * applies to all cases where the driver chooses for memory based input frames. + * In these cases, a copy binary (which typical copies sensor data to DDR) does + * not have much use. + */ need_isp_copy_binary = !online && !continuous; - else - need_isp_copy_binary = !online && !continuous && !(pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY); -#endif + } /* Copy */ if (need_isp_copy_binary) { @@ -3125,11 +3117,10 @@ init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe, in_frame->frame_info.format = format; -#ifdef ISP2401 - if (format == IA_CSS_FRAME_FORMAT_RAW) + if (IS_ISP2401 && format == IA_CSS_FRAME_FORMAT_RAW) { in_frame->frame_info.format = (pipe->stream->config.pack_raw_pixels) ? IA_CSS_FRAME_FORMAT_RAW_PACKED : IA_CSS_FRAME_FORMAT_RAW; -#endif + } in_frame->frame_info.res.width = pipe->stream->config.input_config.input_res.width; in_frame->frame_info.res.height = pipe->stream->config.input_config.input_res.height; @@ -3211,18 +3202,18 @@ static int create_host_video_pipeline(struct ia_css_pipe *pipe) me->dvs_frame_delay = pipe->dvs_frame_delay; -#ifdef ISP2401 - /* - * When the input system is 2401, always enable 'in_frameinfo_memory' - * except for the following: online or continuous - */ - need_in_frameinfo_memory = !(pipe->stream->config.online || - pipe->stream->config.continuous); -#else - /* Construct in_frame info (only in case we have dynamic input */ - need_in_frameinfo_memory = pipe->stream->config.mode == - IA_CSS_INPUT_MODE_MEMORY; -#endif + if (IS_ISP2401) { + /* + * When the input system is 2401, always enable 'in_frameinfo_memory' + * except for the following: online or continuous + */ + need_in_frameinfo_memory = !(pipe->stream->config.online || + pipe->stream->config.continuous); + } else { + /* Construct in_frame info (only in case we have dynamic input */ + need_in_frameinfo_memory = pipe->stream->config.mode == + IA_CSS_INPUT_MODE_MEMORY; + } /* Construct in_frame info (only in case we have dynamic input */ if (need_in_frameinfo_memory) { @@ -3268,15 +3259,14 @@ static int create_host_video_pipeline(struct ia_css_pipe *pipe) goto ERR; in_frame = me->stages->args.out_frame[0]; } else if (pipe->stream->config.continuous) { -#ifdef ISP2401 - /* - * When continuous is enabled, configure in_frame with the - * last pipe, which is the copy pipe. - */ - in_frame = pipe->stream->last_pipe->continuous_frames[0]; -#else - in_frame = pipe->continuous_frames[0]; -#endif + if (IS_ISP2401) + /* + * When continuous is enabled, configure in_frame with the + * last pipe, which is the copy pipe. + */ + in_frame = pipe->stream->last_pipe->continuous_frames[0]; + else + in_frame = pipe->continuous_frames[0]; } ia_css_pipe_util_set_output_frames(out_frames, 0, @@ -3373,12 +3363,10 @@ create_host_preview_pipeline(struct ia_css_pipe *pipe) struct ia_css_frame *out_frame; struct ia_css_frame *out_frames[IA_CSS_BINARY_MAX_OUTPUT_PORTS]; bool need_in_frameinfo_memory = false; -#ifdef ISP2401 bool sensor = false; bool buffered_sensor = false; bool online = false; bool continuous = false; -#endif IA_CSS_ENTER_PRIVATE("pipe = %p", pipe); if ((!pipe) || (!pipe->stream) || (pipe->mode != IA_CSS_PIPE_ID_PREVIEW)) { @@ -3391,25 +3379,26 @@ create_host_preview_pipeline(struct ia_css_pipe *pipe) me = &pipe->pipeline; ia_css_pipeline_clean(me); -#ifdef ISP2401 - /* - * When the input system is 2401, always enable 'in_frameinfo_memory' - * except for the following: - * - Direct Sensor Mode Online Preview - * - Buffered Sensor Mode Online Preview - * - Direct Sensor Mode Continuous Preview - * - Buffered Sensor Mode Continuous Preview - */ - sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR); - buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR); - online = pipe->stream->config.online; - continuous = pipe->stream->config.continuous; - need_in_frameinfo_memory = - !((sensor && (online || continuous)) || (buffered_sensor && (online || continuous))); -#else - /* Construct in_frame info (only in case we have dynamic input */ - need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; -#endif + if (IS_ISP2401) { + /* + * When the input system is 2401, always enable 'in_frameinfo_memory' + * except for the following: + * - Direct Sensor Mode Online Preview + * - Buffered Sensor Mode Online Preview + * - Direct Sensor Mode Continuous Preview + * - Buffered Sensor Mode Continuous Preview + */ + sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR); + buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR); + online = pipe->stream->config.online; + continuous = pipe->stream->config.continuous; + need_in_frameinfo_memory = + !((sensor && (online || continuous)) || (buffered_sensor && + (online || continuous))); + } else { + /* Construct in_frame info (only in case we have dynamic input */ + need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; + } if (need_in_frameinfo_memory) { err = init_in_frameinfo_memory_defaults(pipe, &me->in_frame, IA_CSS_FRAME_FORMAT_RAW); @@ -3420,7 +3409,6 @@ create_host_preview_pipeline(struct ia_css_pipe *pipe) } else { in_frame = NULL; } - err = init_out_frameinfo_defaults(pipe, &me->out_frame[0], 0); if (err) goto ERR; @@ -3441,17 +3429,16 @@ create_host_preview_pipeline(struct ia_css_pipe *pipe) goto ERR; in_frame = me->stages->args.out_frame[0]; } else if (pipe->stream->config.continuous) { -#ifdef ISP2401 - /* - * When continuous is enabled, configure in_frame with the - * last pipe, which is the copy pipe. - */ - if (continuous || !online) - in_frame = pipe->stream->last_pipe->continuous_frames[0]; - -#else - in_frame = pipe->continuous_frames[0]; -#endif + if (IS_ISP2401) { + /* + * When continuous is enabled, configure in_frame with the + * last pipe, which is the copy pipe. + */ + if (continuous || !online) + in_frame = pipe->stream->last_pipe->continuous_frames[0]; + } else { + in_frame = pipe->continuous_frames[0]; + } } if (vf_pp_binary) { @@ -3925,19 +3912,19 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: if (pipe && pipe->stop_requested) { -#if !defined(ISP2401) - /* - * free mipi frames only for old input - * system for 2401 it is done in - * ia_css_stream_destroy call - */ - return_err = free_mipi_frames(pipe); - if (return_err) { - IA_CSS_LOG("free_mipi_frames() failed"); - IA_CSS_LEAVE_ERR(return_err); - return return_err; + if (!IS_ISP2401) { + /* + * free mipi frames only for old input + * system for 2401 it is done in + * ia_css_stream_destroy call + */ + return_err = free_mipi_frames(pipe); + if (return_err) { + IA_CSS_LOG("free_mipi_frames() failed"); + IA_CSS_LEAVE_ERR(return_err); + return return_err; + } } -#endif pipe->stop_requested = false; } fallthrough; @@ -3959,12 +3946,11 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, pipe->num_invalid_frames--; if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_BINARY_8) { -#ifdef ISP2401 - frame->planes.binary.size = frame->data_bytes; -#else - frame->planes.binary.size = - sh_css_sp_get_binary_copy_size(); -#endif + if (IS_ISP2401) + frame->planes.binary.size = frame->data_bytes; + else + frame->planes.binary.size = + sh_css_sp_get_binary_copy_size(); } if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) { IA_CSS_LOG("pfp: dequeued OF %d with config id %d thread %d", @@ -4880,22 +4866,20 @@ static int load_video_binaries(struct ia_css_pipe *pipe) pipe->num_invalid_frames, pipe->dvs_frame_delay); /* pqiao TODO: temp hack for PO, should be removed after offline YUVPP is enabled */ -#if !defined(ISP2401) - /* Copy */ - if (!online && !continuous) { - /* - * TODO: what exactly needs doing, prepend the copy binary to - * video base this only on !online? - */ - err = load_copy_binary(pipe, - &mycs->copy_binary, - &mycs->video_binary); - if (err) - return err; + if (!IS_ISP2401) { + /* Copy */ + if (!online && !continuous) { + /* + * TODO: what exactly needs doing, prepend the copy binary to + * video base this only on !online? + */ + err = load_copy_binary(pipe, + &mycs->copy_binary, + &mycs->video_binary); + if (err) + return err; + } } -#else - (void)continuous; -#endif if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && need_vf_pp) { struct ia_css_binary_descr vf_pp_descr; @@ -5227,11 +5211,8 @@ static int load_primary_binaries( bool need_pp = false; bool need_isp_copy_binary = false; bool need_ldc = false; -#ifdef ISP2401 bool sensor = false; -#else bool memory, continuous; -#endif struct ia_css_frame_info prim_in_info, prim_out_info, capt_pp_out_info, vf_info, @@ -5251,12 +5232,9 @@ static int load_primary_binaries( pipe->mode == IA_CSS_PIPE_ID_COPY); online = pipe->stream->config.online; -#ifdef ISP2401 sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR); -#else memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; continuous = pipe->stream->config.continuous; -#endif mycs = &pipe->pipe_settings.capture; pipe_out_info = &pipe->output_info[0]; @@ -5462,15 +5440,14 @@ static int load_primary_binaries( if (err) return err; -#ifdef ISP2401 - /* - * When the input system is 2401, only the Direct Sensor Mode - * Offline Capture uses the ISP copy binary. - */ - need_isp_copy_binary = !online && sensor; -#else - need_isp_copy_binary = !online && !continuous && !memory; -#endif + if (IS_ISP2401) + /* + * When the input system is 2401, only the Direct Sensor Mode + * Offline Capture uses the ISP copy binary. + */ + need_isp_copy_binary = !online && sensor; + else + need_isp_copy_binary = !online && !continuous && !memory; /* ISP Copy */ if (need_isp_copy_binary) { @@ -5681,10 +5658,10 @@ static int load_advanced_binaries(struct ia_css_pipe *pipe) } /* Copy */ -#ifdef ISP2401 - /* For CSI2+, only the direct sensor mode/online requires ISP copy */ - need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; -#endif + if (IS_ISP2401) + /* For CSI2+, only the direct sensor mode/online requires ISP copy */ + need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; + if (need_isp_copy) load_copy_binary(pipe, &pipe->pipe_settings.capture.copy_binary, @@ -5829,10 +5806,10 @@ static int load_low_light_binaries(struct ia_css_pipe *pipe) } /* Copy */ -#ifdef ISP2401 - /* For CSI2+, only the direct sensor mode/online requires ISP copy */ - need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; -#endif + if (IS_ISP2401) + /* For CSI2+, only the direct sensor mode/online requires ISP copy */ + need_isp_copy = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; + if (need_isp_copy) err = load_copy_binary(pipe, &pipe->pipe_settings.capture.copy_binary, @@ -5902,10 +5879,9 @@ static int load_capture_binaries(struct ia_css_pipe *pipe) switch (pipe->config.default_capture_config.mode) { case IA_CSS_CAPTURE_MODE_RAW: err = load_copy_binaries(pipe); -#if defined(ISP2401) - if (!err) + if (!err && IS_ISP2401) pipe->pipe_settings.capture.copy_binary.online = pipe->stream->config.online; -#endif + break; case IA_CSS_CAPTURE_MODE_BAYER: err = load_bayer_isp_binaries(pipe); @@ -6409,7 +6385,6 @@ load_yuvpp_binaries(struct ia_css_pipe *pipe) else next_binary = NULL; -#if defined(ISP2401) /* * NOTES * - Why does the "yuvpp" pipe needs "isp_copy_binary" (i.e. ISP Copy) when @@ -6427,11 +6402,11 @@ load_yuvpp_binaries(struct ia_css_pipe *pipe) * pp_defs.h" for the list of input-frame formats that are supported by the * "yuv_scale_binary". */ - need_isp_copy_binary = - (pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV422_8); -#else /* !ISP2401 */ - need_isp_copy_binary = true; -#endif /* ISP2401 */ + if (IS_ISP2401) + need_isp_copy_binary = + (pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_YUV422_8); + else + need_isp_copy_binary = true; if (need_isp_copy_binary) { err = load_copy_binary(pipe, @@ -6678,12 +6653,10 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) struct ia_css_frame *vf_frame[IA_CSS_PIPE_MAX_OUTPUT_STAGE]; struct ia_css_pipeline_stage_desc stage_desc; bool need_in_frameinfo_memory = false; -#ifdef ISP2401 bool sensor = false; bool buffered_sensor = false; bool online = false; bool continuous = false; -#endif IA_CSS_ENTER_PRIVATE("pipe = %p", pipe); if ((!pipe) || (!pipe->stream) || (pipe->mode != IA_CSS_PIPE_ID_YUVPP)) { @@ -6700,24 +6673,24 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) num_stage = pipe->pipe_settings.yuvpp.num_yuv_scaler; num_output_stage = pipe->pipe_settings.yuvpp.num_output; -#ifdef ISP2401 - /* - * When the input system is 2401, always enable 'in_frameinfo_memory' - * except for the following: - * - Direct Sensor Mode Online Capture - * - Direct Sensor Mode Continuous Capture - * - Buffered Sensor Mode Continuous Capture - */ - sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; - buffered_sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR; - online = pipe->stream->config.online; - continuous = pipe->stream->config.continuous; - need_in_frameinfo_memory = - !((sensor && (online || continuous)) || (buffered_sensor && continuous)); -#else - /* Construct in_frame info (only in case we have dynamic input */ - need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; -#endif + if (IS_ISP2401) { + /* + * When the input system is 2401, always enable 'in_frameinfo_memory' + * except for the following: + * - Direct Sensor Mode Online Capture + * - Direct Sensor Mode Continuous Capture + * - Buffered Sensor Mode Continuous Capture + */ + sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR; + buffered_sensor = pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR; + online = pipe->stream->config.online; + continuous = pipe->stream->config.continuous; + need_in_frameinfo_memory = + !((sensor && (online || continuous)) || (buffered_sensor && continuous)); + } else { + /* Construct in_frame info (only in case we have dynamic input */ + need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; + } /* * the input frame can come from: * @@ -6808,11 +6781,10 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) if (pipe->pipe_settings.yuvpp.copy_binary.info) { struct ia_css_frame *in_frame_local = NULL; -#ifdef ISP2401 - /* After isp copy is enabled in_frame needs to be passed. */ - if (!online) + if (IS_ISP2401 && !online) { + /* After isp copy is enabled in_frame needs to be passed. */ in_frame_local = in_frame; -#endif + } if (need_scaler) { ia_css_pipe_util_set_output_frames(bin_out_frame, @@ -7031,12 +7003,10 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe) struct ia_css_frame *vf_frame; struct ia_css_pipeline_stage_desc stage_desc; bool need_in_frameinfo_memory = false; -#ifdef ISP2401 bool sensor = false; bool buffered_sensor = false; bool online = false; bool continuous = false; -#endif unsigned int i, num_yuv_scaler, num_primary_stage; bool need_yuv_pp = false; bool *is_output_stage = NULL; @@ -7054,25 +7024,27 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe) ia_css_pipeline_clean(me); ia_css_pipe_util_create_output_frames(out_frames); -#ifdef ISP2401 - /* - * When the input system is 2401, always enable 'in_frameinfo_memory' - * except for the following: - * - Direct Sensor Mode Online Capture - * - Direct Sensor Mode Online Capture - * - Direct Sensor Mode Continuous Capture - * - Buffered Sensor Mode Continuous Capture - */ - sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR); - buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR); - online = pipe->stream->config.online; - continuous = pipe->stream->config.continuous; - need_in_frameinfo_memory = - !((sensor && (online || continuous)) || (buffered_sensor && (online || continuous))); -#else - /* Construct in_frame info (only in case we have dynamic input */ - need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; -#endif + if (IS_ISP2401) { + /* + * When the input system is 2401, always enable 'in_frameinfo_memory' + * except for the following: + * - Direct Sensor Mode Online Capture + * - Direct Sensor Mode Online Capture + * - Direct Sensor Mode Continuous Capture + * - Buffered Sensor Mode Continuous Capture + */ + sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_SENSOR); + buffered_sensor = (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR); + online = pipe->stream->config.online; + continuous = pipe->stream->config.continuous; + need_in_frameinfo_memory = + !((sensor && (online || continuous)) || (buffered_sensor && + (online || continuous))); + } else { + /* Construct in_frame info (only in case we have dynamic input */ + need_in_frameinfo_memory = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; + } + if (need_in_frameinfo_memory) { err = init_in_frameinfo_memory_defaults(pipe, &me->in_frame, IA_CSS_FRAME_FORMAT_RAW); @@ -7135,27 +7107,27 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe) if (pipe->pipe_settings.capture.copy_binary.info) { if (raw) { ia_css_pipe_util_set_output_frames(out_frames, 0, out_frame); -#if defined(ISP2401) - if (!continuous) { - ia_css_pipe_get_generic_stage_desc(&stage_desc, - copy_binary, - out_frames, - in_frame, - NULL); + if (IS_ISP2401) { + if (!continuous) { + ia_css_pipe_get_generic_stage_desc(&stage_desc, + copy_binary, + out_frames, + in_frame, + NULL); + } else { + in_frame = pipe->stream->last_pipe->continuous_frames[0]; + ia_css_pipe_get_generic_stage_desc(&stage_desc, + copy_binary, + out_frames, + in_frame, + NULL); + } } else { - in_frame = pipe->stream->last_pipe->continuous_frames[0]; ia_css_pipe_get_generic_stage_desc(&stage_desc, copy_binary, out_frames, - in_frame, - NULL); + NULL, NULL); } -#else - ia_css_pipe_get_generic_stage_desc(&stage_desc, - copy_binary, - out_frames, - NULL, NULL); -#endif } else { ia_css_pipe_util_set_output_frames(out_frames, 0, in_frame); @@ -7185,11 +7157,7 @@ create_host_regular_capture_pipeline(struct ia_css_pipe *pipe) local_in_frame = in_frame; else local_in_frame = NULL; -#ifndef ISP2401 - if (!need_pp && (i == num_primary_stage - 1)) -#else - if (!need_pp && (i == num_primary_stage - 1) && !need_ldc) -#endif + if (!need_pp && (i == num_primary_stage - 1) && (!IS_ISP2401 || !need_ldc)) local_out_frame = out_frame; else local_out_frame = NULL; @@ -7400,23 +7368,14 @@ static int capture_start(struct ia_css_pipe *pipe) return err; } } - -#if !defined(ISP2401) /* old isys: need to send_mipi_frames() in all pipe modes */ - err = send_mipi_frames(pipe); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; - } -#else - if (pipe->config.mode != IA_CSS_PIPE_MODE_COPY) { + if (!IS_ISP2401 || (IS_ISP2401 && pipe->config.mode != IA_CSS_PIPE_MODE_COPY)) { err = send_mipi_frames(pipe); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; } } -#endif ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); copy_ovrd = 1 << thread_id; @@ -8123,24 +8082,22 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, return err; } -#if !defined(ISP2401) - /* We don't support metadata for JPEG stream, since they both use str2mem */ - if (stream_config->input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8 && - stream_config->metadata_config.resolution.height > 0) { - err = -EINVAL; - IA_CSS_LEAVE_ERR(err); - return err; - } -#endif - -#ifdef ISP2401 - if (stream_config->online && stream_config->pack_raw_pixels) { - IA_CSS_LOG("online and pack raw is invalid on input system 2401"); - err = -EINVAL; - IA_CSS_LEAVE_ERR(err); - return err; + if (!IS_ISP2401) { + /* We don't support metadata for JPEG stream, since they both use str2mem */ + if (stream_config->input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8 && + stream_config->metadata_config.resolution.height > 0) { + err = -EINVAL; + IA_CSS_LEAVE_ERR(err); + return err; + } + } else { + if (stream_config->online && stream_config->pack_raw_pixels) { + IA_CSS_LOG("online and pack raw is invalid on input system 2401"); + err = -EINVAL; + IA_CSS_LEAVE_ERR(err); + return err; + } } -#endif ia_css_debug_pipe_graph_dump_stream_config(stream_config); @@ -8223,19 +8180,17 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* take over stream config */ curr_stream->config = *stream_config; -#if defined(ISP2401) - if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR && - stream_config->online) - curr_stream->config.online = false; -#endif + if (IS_ISP2401) { + if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR && + stream_config->online) + curr_stream->config.online = false; -#ifdef ISP2401 - if (curr_stream->config.online) { - curr_stream->config.source.port.num_lanes = - stream_config->source.port.num_lanes; - curr_stream->config.mode = IA_CSS_INPUT_MODE_BUFFERED_SENSOR; + if (curr_stream->config.online) { + curr_stream->config.source.port.num_lanes = + stream_config->source.port.num_lanes; + curr_stream->config.mode = IA_CSS_INPUT_MODE_BUFFERED_SENSOR; + } } -#endif /* in case driver doesn't configure init number of raw buffers, configure it here */ if (curr_stream->config.target_num_cont_raw_buf == 0) curr_stream->config.target_num_cont_raw_buf = NUM_CONTINUOUS_FRAMES; @@ -9162,11 +9117,10 @@ void ia_css_pipe_map_queue(struct ia_css_pipe *pipe, bool map) ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id); -#if defined(ISP2401) - need_input_queue = true; -#else - need_input_queue = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; -#endif + if (IS_ISP2401) + need_input_queue = true; + else + need_input_queue = pipe->stream->config.mode == IA_CSS_INPUT_MODE_MEMORY; /* map required buffer queues to resources */ /* TODO: to be improved */ diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index e7ef578db8ab..197ab2085e8d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -56,11 +56,8 @@ static struct firmware_header *firmware_header; * which will be replaced with the actual RELEASE_VERSION * during package generation. Please do not modify */ -#ifdef ISP2401 -static const char *release_version = STR(irci_stable_candrpv_0415_20150521_0458); -#else -static const char *release_version = STR(irci_stable_candrpv_0415_20150423_1753); -#endif +static const char *release_version_2401 = STR(irci_stable_candrpv_0415_20150521_0458); +static const char *release_version_2400 = STR(irci_stable_candrpv_0415_20150423_1753); #define MAX_FW_REL_VER_NAME 300 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---"; @@ -191,8 +188,14 @@ sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, bool sh_css_check_firmware_version(struct device *dev, const char *fw_data) { + const char *release_version; struct sh_css_fw_bi_file_h *file_header; + if (IS_ISP2401) + release_version = release_version_2401; + else + release_version = release_version_2400; + firmware_header = (struct firmware_header *)fw_data; file_header = &firmware_header->file_header; @@ -225,15 +228,28 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, unsigned int fw_size) { unsigned int i; + const char *release_version; struct ia_css_fw_info *binaries; struct sh_css_fw_bi_file_h *file_header; int ret; + /* some sanity checks */ + if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h)) + return -EINVAL; + firmware_header = (struct firmware_header *)fw_data; file_header = &firmware_header->file_header; + + if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h)) + return -EINVAL; + binaries = &firmware_header->binary_header; strscpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version))); + if (IS_ISP2401) + release_version = release_version_2401; + else + release_version = release_version_2400; ret = sh_css_check_firmware_version(dev, fw_data); if (ret) { IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!", @@ -243,13 +259,6 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, IA_CSS_LOG("successfully load firmware version %s", release_version); } - /* some sanity checks */ - if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h)) - return -EINVAL; - - if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h)) - return -EINVAL; - sh_css_num_binaries = file_header->binary_nr; /* Only allocate memory for ISP blob info */ if (sh_css_num_binaries > NUM_OF_SPS) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/sh_css_mipi.c index bc6e8598a776..b20acaab0595 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_mipi.c +++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.c @@ -67,13 +67,12 @@ ia_css_mipi_frame_calculate_size(const unsigned int width, unsigned int mem_words = 0; unsigned int width_padded = width; -#if defined(ISP2401) /* The changes will be reverted as soon as RAW * Buffers are deployed by the 2401 Input System * in the non-continuous use scenario. */ - width_padded += (2 * ISP_VEC_NELEMS); -#endif + if (IS_ISP2401) + width_padded += (2 * ISP_VEC_NELEMS); IA_CSS_ENTER("padded_width=%d, height=%d, format=%d, hasSOLandEOL=%d, embedded_data_size_words=%d\n", width_padded, height, format, hasSOLandEOL, embedded_data_size_words); @@ -235,7 +234,6 @@ bool mipi_is_free(void) return true; } -#if defined(ISP2401) /* * @brief Calculate the required MIPI buffer sizes. * Based on the stream configuration, calculate the @@ -342,7 +340,6 @@ static int calculate_mipi_buff_size(struct ia_css_stream_config *stream_cfg, IA_CSS_LEAVE_ERR(err); return err; } -#endif int allocate_mipi_frames(struct ia_css_pipe *pipe, @@ -363,15 +360,13 @@ allocate_mipi_frames(struct ia_css_pipe *pipe, return -EINVAL; } -#ifdef ISP2401 - if (pipe->stream->config.online) { + if (IS_ISP2401 && pipe->stream->config.online) { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "allocate_mipi_frames(%p) exit: no buffers needed for 2401 pipe mode.\n", pipe); return 0; } -#endif if (pipe->stream->config.mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "allocate_mipi_frames(%p) exit: no buffers needed for pipe mode.\n", @@ -386,9 +381,10 @@ allocate_mipi_frames(struct ia_css_pipe *pipe, return -EINVAL; } -#ifdef ISP2401 - err = calculate_mipi_buff_size(&pipe->stream->config, - &my_css.mipi_frame_size[port]); + if (IS_ISP2401) + err = calculate_mipi_buff_size(&pipe->stream->config, + &my_css.mipi_frame_size[port]); + /* * 2401 system allows multiple streams to use same physical port. This is not * true for 2400 system. Currently 2401 uses MIPI buffers as a temporary solution. @@ -396,20 +392,14 @@ allocate_mipi_frames(struct ia_css_pipe *pipe, * In that case only 2400 related code should remain. */ if (ref_count_mipi_allocation[port] != 0) { - ref_count_mipi_allocation[port]++; + if (IS_ISP2401) + ref_count_mipi_allocation[port]++; + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "allocate_mipi_frames(%p) leave: nothing to do, already allocated for this port (port=%d).\n", pipe, port); return 0; } -#else - if (ref_count_mipi_allocation[port] != 0) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "allocate_mipi_frames(%p) exit: already allocated for this port (port=%d).\n", - pipe, port); - return 0; - } -#endif ref_count_mipi_allocation[port]++; @@ -503,14 +493,14 @@ free_mipi_frames(struct ia_css_pipe *pipe) } if (ref_count_mipi_allocation[port] > 0) { -#if !defined(ISP2401) - assert(ref_count_mipi_allocation[port] == 1); - if (ref_count_mipi_allocation[port] != 1) { - IA_CSS_ERROR("free_mipi_frames(%p) exit: wrong ref_count (ref_count=%d).", - pipe, ref_count_mipi_allocation[port]); - return err; + if (!IS_ISP2401) { + assert(ref_count_mipi_allocation[port] == 1); + if (ref_count_mipi_allocation[port] != 1) { + IA_CSS_ERROR("free_mipi_frames(%p) exit: wrong ref_count (ref_count=%d).", + pipe, ref_count_mipi_allocation[port]); + return err; + } } -#endif ref_count_mipi_allocation[port]--; @@ -534,18 +524,6 @@ free_mipi_frames(struct ia_css_pipe *pipe) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "free_mipi_frames(%p) exit (deallocated).\n", pipe); } -#if defined(ISP2401) - else { - /* 2401 system allows multiple streams to use same physical port. This is not - * true for 2400 system. Currently 2401 uses MIPI buffers as a temporary solution. - * TODO AM: Once that is changed (removed) this code should be removed as well. - * In that case only 2400 related code should remain. - */ - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "free_mipi_frames(%p) leave: nothing to do, other streams still use this port (port=%d).\n", - pipe, port); - } -#endif } } else { /* pipe ==NULL */ /* AM TEMP: free-ing all mipi buffers just like a legacy code. */ diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 0dd58a7fe2cc..297e1b981720 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -952,12 +952,10 @@ sh_css_sp_init_stage(struct ia_css_binary *binary, return 0; } -#if defined(ISP2401) - (void)continuous; - sh_css_sp_stage.deinterleaved = 0; -#else - sh_css_sp_stage.deinterleaved = ((stage == 0) && continuous); -#endif + if (IS_ISP2401) + sh_css_sp_stage.deinterleaved = 0; + else + sh_css_sp_stage.deinterleaved = ((stage == 0) && continuous); initialize_stage_frames(&sh_css_sp_stage.frames); /* diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index a5c5bebad306..00dd6a7fea64 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -78,7 +78,7 @@ static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, s int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) { - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; + struct dvb_demux_feed *dvbdmxfeed = p2t->priv; if (!(dvbdmxfeed->ts_type & TS_PACKET)) return 0; @@ -837,7 +837,7 @@ static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, s int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) { struct dvb_demux *demux = feed->demux; - struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct av7110 *av7110 = demux->priv; dprintk(2, "av7110:%p, \n", av7110); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 2d712eda2c5d..064dc562bc96 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -7,7 +7,7 @@ #include <linux/module.h> #include "imx-media.h" -#define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0} +#define IMX_BUS_FMTS(fmt...) ((const u32[]) {fmt, 0}) /* * List of supported pixel formats for the subdevs. diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index c07994ea6e96..ab565b4e29ec 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/of_graph.h> #include <linux/platform_device.h> +#include <media/v4l2-common.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-mc.h> @@ -564,6 +565,49 @@ static int csi2_registered(struct v4l2_subdev *sd) V4L2_FIELD_NONE, NULL); } +/* --------------- CORE OPS --------------- */ + +static int csi2_log_status(struct v4l2_subdev *sd) +{ + struct csi2_dev *csi2 = sd_to_dev(sd); + + v4l2_info(sd, "-----MIPI CSI status-----\n"); + v4l2_info(sd, "VERSION: 0x%x\n", + readl(csi2->base + CSI2_VERSION)); + v4l2_info(sd, "N_LANES: 0x%x\n", + readl(csi2->base + CSI2_N_LANES)); + v4l2_info(sd, "PHY_SHUTDOWNZ: 0x%x\n", + readl(csi2->base + CSI2_PHY_SHUTDOWNZ)); + v4l2_info(sd, "DPHY_RSTZ: 0x%x\n", + readl(csi2->base + CSI2_DPHY_RSTZ)); + v4l2_info(sd, "RESETN: 0x%x\n", + readl(csi2->base + CSI2_RESETN)); + v4l2_info(sd, "PHY_STATE: 0x%x\n", + readl(csi2->base + CSI2_PHY_STATE)); + v4l2_info(sd, "DATA_IDS_1: 0x%x\n", + readl(csi2->base + CSI2_DATA_IDS_1)); + v4l2_info(sd, "DATA_IDS_2: 0x%x\n", + readl(csi2->base + CSI2_DATA_IDS_2)); + v4l2_info(sd, "ERR1: 0x%x\n", + readl(csi2->base + CSI2_ERR1)); + v4l2_info(sd, "ERR2: 0x%x\n", + readl(csi2->base + CSI2_ERR2)); + v4l2_info(sd, "MSK1: 0x%x\n", + readl(csi2->base + CSI2_MSK1)); + v4l2_info(sd, "MSK2: 0x%x\n", + readl(csi2->base + CSI2_MSK2)); + v4l2_info(sd, "PHY_TST_CTRL0: 0x%x\n", + readl(csi2->base + CSI2_PHY_TST_CTRL0)); + v4l2_info(sd, "PHY_TST_CTRL1: 0x%x\n", + readl(csi2->base + CSI2_PHY_TST_CTRL1)); + + return 0; +} + +static const struct v4l2_subdev_core_ops csi2_core_ops = { + .log_status = csi2_log_status, +}; + static const struct media_entity_operations csi2_entity_ops = { .link_setup = csi2_link_setup, .link_validate = v4l2_subdev_link_validate, @@ -581,6 +625,7 @@ static const struct v4l2_subdev_pad_ops csi2_pad_ops = { }; static const struct v4l2_subdev_ops csi2_subdev_ops = { + .core = &csi2_core_ops, .video = &csi2_video_ops, .pad = &csi2_pad_ops, }; diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index 99b333b68198..c44145284aa1 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -30,6 +30,7 @@ struct max96712_priv { struct regmap *regmap; struct gpio_desc *gpiod_pwdn; + bool cphy; struct v4l2_mbus_config_mipi_csi2 mipi; struct v4l2_subdev sd; @@ -127,10 +128,18 @@ static void max96712_mipi_configure(struct max96712_priv *priv) /* Select 2x4 mode. */ max96712_write(priv, 0x8a0, 0x04); - /* Configure a 4-lane DPHY using PHY0 and PHY1. */ /* TODO: Add support for 2-lane and 1-lane configurations. */ - /* TODO: Add support CPHY mode. */ - max96712_write(priv, 0x94a, 0xc0); + if (priv->cphy) { + /* Configure a 3-lane C-PHY using PHY0 and PHY1. */ + max96712_write(priv, 0x94a, 0xa0); + + /* Configure C-PHY timings. */ + max96712_write(priv, 0x8ad, 0x3f); + max96712_write(priv, 0x8ae, 0x7d); + } else { + /* Configure a 4-lane D-PHY using PHY0 and PHY1. */ + max96712_write(priv, 0x94a, 0xc0); + } /* Configure lane mapping for PHY0 and PHY1. */ /* TODO: Add support for lane swapping. */ @@ -332,8 +341,9 @@ static int max96712_parse_dt(struct max96712_priv *priv) { struct fwnode_handle *ep; struct v4l2_fwnode_endpoint v4l2_ep = { - .bus_type = V4L2_MBUS_CSI2_DPHY + .bus_type = V4L2_MBUS_UNKNOWN, }; + unsigned int supported_lanes; int ret; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(&priv->client->dev), 4, @@ -350,8 +360,24 @@ static int max96712_parse_dt(struct max96712_priv *priv) return -EINVAL; } - if (v4l2_ep.bus.mipi_csi2.num_data_lanes != 4) { - dev_err(&priv->client->dev, "Only 4 data lanes supported\n"); + switch (v4l2_ep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + supported_lanes = 4; + priv->cphy = false; + break; + case V4L2_MBUS_CSI2_CPHY: + supported_lanes = 3; + priv->cphy = true; + break; + default: + dev_err(&priv->client->dev, "Unsupported bus-type %u\n", + v4l2_ep.bus_type); + return -EINVAL; + } + + if (v4l2_ep.bus.mipi_csi2.num_data_lanes != supported_lanes) { + dev_err(&priv->client->dev, "Only %u data lanes supported\n", + supported_lanes); return -EINVAL; } @@ -427,7 +453,7 @@ static struct i2c_driver max96712_i2c_driver = { .name = "max96712", .of_match_table = of_match_ptr(max96712_of_table), }, - .probe_new = max96712_probe, + .probe = max96712_probe, .remove = max96712_remove, }; diff --git a/drivers/staging/media/tegra-video/Kconfig b/drivers/staging/media/tegra-video/Kconfig index df1b2cff2417..c53441822fdf 100644 --- a/drivers/staging/media/tegra-video/Kconfig +++ b/drivers/staging/media/tegra-video/Kconfig @@ -15,5 +15,6 @@ config VIDEO_TEGRA config VIDEO_TEGRA_TPG bool "NVIDIA Tegra VI driver TPG mode" depends on VIDEO_TEGRA + depends on ARCH_TEGRA_210_SOC help Say yes here to enable Tegra internal TPG mode diff --git a/drivers/staging/media/tegra-video/Makefile b/drivers/staging/media/tegra-video/Makefile index dfa2ef8f99ef..6c7552e05109 100644 --- a/drivers/staging/media/tegra-video/Makefile +++ b/drivers/staging/media/tegra-video/Makefile @@ -2,7 +2,9 @@ tegra-video-objs := \ video.o \ vi.o \ + vip.o \ csi.o +tegra-video-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o tegra-video-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o obj-$(CONFIG_VIDEO_TEGRA) += tegra-video.o diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 36ca639622c9..052172017b3b 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -328,12 +328,42 @@ static int tegra_csi_enable_stream(struct v4l2_subdev *subdev) } csi_chan->pg_mode = chan->pg_mode; + + /* + * Tegra CSI receiver can detect the first LP to HS transition. + * So, start the CSI stream-on prior to sensor stream-on and + * vice-versa for stream-off. + */ ret = csi->ops->csi_start_streaming(csi_chan); if (ret < 0) goto finish_calibration; + if (csi_chan->mipi) { + struct v4l2_subdev *src_subdev; + /* + * TRM has incorrectly documented to wait for done status from + * calibration logic after CSI interface power on. + * As per the design, calibration results are latched and applied + * to the pads only when the link is in LP11 state which will happen + * during the sensor stream-on. + * CSI subdev stream-on triggers start of MIPI pads calibration. + * Wait for calibration to finish here after sensor subdev stream-on. + */ + src_subdev = tegra_channel_get_remote_source_subdev(chan); + ret = v4l2_subdev_call(src_subdev, video, s_stream, true); + + if (ret < 0 && ret != -ENOIOCTLCMD) + goto disable_csi_stream; + + err = tegra_mipi_finish_calibration(csi_chan->mipi); + if (err < 0) + dev_warn(csi->dev, "MIPI calibration failed: %d\n", err); + } + return 0; +disable_csi_stream: + csi->ops->csi_stop_streaming(csi_chan); finish_calibration: if (csi_chan->mipi) tegra_mipi_finish_calibration(csi_chan->mipi); @@ -352,10 +382,24 @@ rpm_put: static int tegra_csi_disable_stream(struct v4l2_subdev *subdev) { + struct tegra_vi_channel *chan = v4l2_get_subdev_hostdata(subdev); struct tegra_csi_channel *csi_chan = to_csi_chan(subdev); struct tegra_csi *csi = csi_chan->csi; int err; + /* + * Stream-off subdevices in reverse order to stream-on. + * Remote source subdev in TPG mode is same as CSI subdev. + */ + if (csi_chan->mipi) { + struct v4l2_subdev *src_subdev; + + src_subdev = tegra_channel_get_remote_source_subdev(chan); + err = v4l2_subdev_call(src_subdev, video, s_stream, false); + if (err < 0 && err != -ENOIOCTLCMD) + dev_err_probe(csi->dev, err, "source subdev stream off failed\n"); + } + csi->ops->csi_stop_streaming(csi_chan); if (csi_chan->mipi) { @@ -786,6 +830,10 @@ static int tegra_csi_remove(struct platform_device *pdev) return 0; } +#if defined(CONFIG_ARCH_TEGRA_210_SOC) +extern const struct tegra_csi_soc tegra210_csi_soc; +#endif + static const struct of_device_id tegra_csi_of_id_table[] = { #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc }, diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 6960ea2e3d36..3e6e5ee1bb1e 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -151,10 +151,6 @@ struct tegra_csi { struct list_head csi_chans; }; -#if defined(CONFIG_ARCH_TEGRA_210_SOC) -extern const struct tegra_csi_soc tegra210_csi_soc; -#endif - void tegra_csi_error_recover(struct v4l2_subdev *subdev); void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan, u8 csi_port_num, diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c new file mode 100644 index 000000000000..c25286772603 --- /dev/null +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -0,0 +1,661 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tegra20-specific VI implementation + * + * Copyright (C) 2023 SKIDATA GmbH + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> + */ + +/* + * This source file contains Tegra20 supported video formats, + * VI and VIP SoC specific data, operations and registers accessors. + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/host1x.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/v4l2-mediabus.h> + +#include "vip.h" +#include "vi.h" + +#define TEGRA_VI_SYNCPT_WAIT_TIMEOUT msecs_to_jiffies(200) + +/* This are just good-sense numbers. The actual min/max is not documented. */ +#define TEGRA20_MIN_WIDTH 32U +#define TEGRA20_MIN_HEIGHT 32U +#define TEGRA20_MAX_WIDTH 2048U +#define TEGRA20_MAX_HEIGHT 2048U + +/* -------------------------------------------------------------------------- + * Registers + */ + +#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060 +#define VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT BIT(8) +#define VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT 0 + +#define TEGRA_VI_VI_INPUT_CONTROL 0x0088 +#define VI_INPUT_FIELD_DETECT BIT(27) +#define VI_INPUT_BT656 BIT(25) +#define VI_INPUT_YUV_INPUT_FORMAT_SFT 8 /* bits [9:8] */ +#define VI_INPUT_YUV_INPUT_FORMAT_UYVY (0 << VI_INPUT_YUV_INPUT_FORMAT_SFT) +#define VI_INPUT_YUV_INPUT_FORMAT_VYUY (1 << VI_INPUT_YUV_INPUT_FORMAT_SFT) +#define VI_INPUT_YUV_INPUT_FORMAT_YUYV (2 << VI_INPUT_YUV_INPUT_FORMAT_SFT) +#define VI_INPUT_YUV_INPUT_FORMAT_YVYU (3 << VI_INPUT_YUV_INPUT_FORMAT_SFT) +#define VI_INPUT_INPUT_FORMAT_SFT 2 /* bits [5:2] */ +#define VI_INPUT_INPUT_FORMAT_YUV422 (0 << VI_INPUT_INPUT_FORMAT_SFT) +#define VI_INPUT_VIP_INPUT_ENABLE BIT(1) + +#define TEGRA_VI_VI_CORE_CONTROL 0x008c +#define VI_VI_CORE_CONTROL_PLANAR_CONV_IN_SEL_EXT BIT(31) +#define VI_VI_CORE_CONTROL_CSC_INPUT_SEL_EXT BIT(30) +#define VI_VI_CORE_CONTROL_INPUT_TO_ALT_MUX_SFT 27 +#define VI_VI_CORE_CONTROL_INPUT_TO_CORE_EXT_SFT 24 +#define VI_VI_CORE_CONTROL_OUTPUT_TO_ISP_EXT_SFT 21 +#define VI_VI_CORE_CONTROL_ISP_HOST_STALL_OFF BIT(20) +#define VI_VI_CORE_CONTROL_V_DOWNSCALING BIT(19) +#define VI_VI_CORE_CONTROL_V_AVERAGING BIT(18) +#define VI_VI_CORE_CONTROL_H_DOWNSCALING BIT(17) +#define VI_VI_CORE_CONTROL_H_AVERAGING BIT(16) +#define VI_VI_CORE_CONTROL_CSC_INPUT_SEL BIT(11) +#define VI_VI_CORE_CONTROL_PLANAR_CONV_INPUT_SEL BIT(10) +#define VI_VI_CORE_CONTROL_INPUT_TO_CORE_SFT 8 +#define VI_VI_CORE_CONTROL_ISP_DOWNSAMPLE_SFT 5 +#define VI_VI_CORE_CONTROL_OUTPUT_TO_EPP_SFT 2 +#define VI_VI_CORE_CONTROL_OUTPUT_TO_ISP_SFT 0 + +#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090 +#define VI_OUTPUT_FORMAT_EXT BIT(22) +#define VI_OUTPUT_V_DIRECTION BIT(20) +#define VI_OUTPUT_H_DIRECTION BIT(19) +#define VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT 17 +#define VI_OUTPUT_YUV_OUTPUT_FORMAT_UYVY (0 << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_YUV_OUTPUT_FORMAT_VYUY (1 << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_YUV_OUTPUT_FORMAT_YUYV (2 << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_YUV_OUTPUT_FORMAT_YVYU (3 << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_OUTPUT_BYTE_SWAP BIT(16) +#define VI_OUTPUT_LAST_PIXEL_DUPLICATION BIT(8) +#define VI_OUTPUT_OUTPUT_FORMAT_SFT 0 +#define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT) +#define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT) + +#define TEGRA_VI_VIP_H_ACTIVE 0x00a4 +#define VI_VIP_H_ACTIVE_PERIOD_SFT 16 /* active pixels/line, must be even */ +#define VI_VIP_H_ACTIVE_START_SFT 0 + +#define TEGRA_VI_VIP_V_ACTIVE 0x00a8 +#define VI_VIP_V_ACTIVE_PERIOD_SFT 16 /* active lines */ +#define VI_VIP_V_ACTIVE_START_SFT 0 + +#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4 +#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8 +#define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc +#define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0 +#define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4 +#define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8 + +#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0 +#define VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT 16 +#define VI_FIRST_OUTPUT_FRAME_WIDTH_SFT 0 + +#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4 + +#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8 +#define VI_VB0_SIZE_FIRST_V_SFT 16 +#define VI_VB0_SIZE_FIRST_H_SFT 0 + +#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec +#define VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT 30 +#define VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT 0 + +#define TEGRA_VI_H_LPF_CONTROL 0x0108 +#define VI_H_LPF_CONTROL_CHROMA_SFT 16 +#define VI_H_LPF_CONTROL_LUMA_SFT 0 + +#define TEGRA_VI_H_DOWNSCALE_CONTROL 0x010c +#define TEGRA_VI_V_DOWNSCALE_CONTROL 0x0110 + +#define TEGRA_VI_VIP_INPUT_STATUS 0x0144 + +#define TEGRA_VI_VI_DATA_INPUT_CONTROL 0x0168 +#define VI_DATA_INPUT_SFT 0 /* [11:0] = mask pin inputs to VI core */ + +#define TEGRA_VI_PIN_INPUT_ENABLE 0x016c +#define VI_PIN_INPUT_VSYNC BIT(14) +#define VI_PIN_INPUT_HSYNC BIT(13) +#define VI_PIN_INPUT_VD_SFT 0 /* [11:0] = data bin N input enable */ + +#define TEGRA_VI_PIN_INVERSION 0x0174 +#define VI_PIN_INVERSION_VSYNC_ACTIVE_HIGH BIT(1) +#define VI_PIN_INVERSION_HSYNC_ACTIVE_HIGH BIT(0) + +#define TEGRA_VI_CAMERA_CONTROL 0x01a0 +#define VI_CAMERA_CONTROL_STOP_CAPTURE BIT(2) +#define VI_CAMERA_CONTROL_TEST_MODE BIT(1) +#define VI_CAMERA_CONTROL_VIP_ENABLE BIT(0) + +#define TEGRA_VI_VI_ENABLE 0x01a4 +#define VI_VI_ENABLE_SW_FLOW_CONTROL_OUT1 BIT(1) +#define VI_VI_ENABLE_FIRST_OUTPUT_TO_MEM_DISABLE BIT(0) + +#define TEGRA_VI_VI_RAISE 0x01ac +#define VI_VI_RAISE_ON_EDGE BIT(0) + +/* -------------------------------------------------------------------------- + * VI + */ + +static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val) +{ + writel(val, chan->vi->iomem + addr); +} + +/* + * Get the main input format (YUV/RGB...) and the YUV variant as values to + * be written into registers for the current VI input mbus code. + */ +static void tegra20_vi_get_input_formats(struct tegra_vi_channel *chan, + unsigned int *main_input_format, + unsigned int *yuv_input_format) +{ + unsigned int input_mbus_code = chan->fmtinfo->code; + + (*main_input_format) = VI_INPUT_INPUT_FORMAT_YUV422; + + switch (input_mbus_code) { + case MEDIA_BUS_FMT_UYVY8_2X8: + (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_VYUY; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + (*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_YVYU; + break; + } +} + +/* + * Get the main output format (YUV/RGB...) and the YUV variant as values to + * be written into registers for the current VI output pixel format. + */ +static void tegra20_vi_get_output_formats(struct tegra_vi_channel *chan, + unsigned int *main_output_format, + unsigned int *yuv_output_format) +{ + u32 output_fourcc = chan->format.pixelformat; + + /* Default to YUV422 non-planar (U8Y8V8Y8) after downscaling */ + (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_YUV422POST; + (*yuv_output_format) = VI_OUTPUT_YUV_OUTPUT_FORMAT_UYVY; + + switch (output_fourcc) { + case V4L2_PIX_FMT_UYVY: + (*yuv_output_format) = VI_OUTPUT_YUV_OUTPUT_FORMAT_UYVY; + break; + case V4L2_PIX_FMT_VYUY: + (*yuv_output_format) = VI_OUTPUT_YUV_OUTPUT_FORMAT_VYUY; + break; + case V4L2_PIX_FMT_YUYV: + (*yuv_output_format) = VI_OUTPUT_YUV_OUTPUT_FORMAT_YUYV; + break; + case V4L2_PIX_FMT_YVYU: + (*yuv_output_format) = VI_OUTPUT_YUV_OUTPUT_FORMAT_YVYU; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR; + break; + } +} + +/* + * Make the VI accessible (needed on Tegra20). + * + * This function writes an unknown bit into an unknown register. The code + * comes from a downstream 3.1 kernel that has a working VIP driver for + * Tegra20, and removing it makes the VI completely unaccessible. It should + * be rewritten and possibly moved elsewhere, but the appropriate location + * and implementation is unknown due to a total lack of documentation. + */ +static int tegra20_vi_enable(struct tegra_vi *vi, bool on) +{ + /* from arch/arm/mach-tegra/iomap.h */ + const phys_addr_t TEGRA_APB_MISC_BASE = 0x70000000; + const unsigned long reg_offset = 0x42c; + void __iomem *apb_misc; + u32 val; + + apb_misc = ioremap(TEGRA_APB_MISC_BASE, PAGE_SIZE); + if (!apb_misc) + apb_misc = ERR_PTR(-ENOENT); + if (IS_ERR(apb_misc)) + return dev_err_probe(vi->dev, PTR_ERR(apb_misc), "cannot access APB_MISC"); + + val = readl(apb_misc + reg_offset); + val &= ~BIT(0); + val |= on ? BIT(0) : 0; + writel(val, apb_misc + reg_offset); + iounmap(apb_misc); + + return 0; +} + +static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) +{ + struct tegra_vi *vi = chan->vi; + struct host1x_syncpt *out_sp; + + out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED); + if (!out_sp) + return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n"); + + chan->mw_ack_sp[0] = out_sp; + + return 0; +} + +static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan) +{ + host1x_syncpt_put(chan->mw_ack_sp[0]); +} + +static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp) +{ + pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH); + pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + pix->bytesperline = roundup(pix->width, 2) * 2; + pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + pix->bytesperline = roundup(pix->width, 8); + pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2; + break; + } +} + +/* + * Compute buffer offsets once per stream so that + * tegra20_channel_vi_buffer_setup() only has to do very simple maths for + * each buffer. + */ +static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan) +{ + unsigned int stride = chan->format.bytesperline; + unsigned int height = chan->format.height; + + chan->start_offset = 0; + + switch (chan->format.pixelformat) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + if (chan->vflip) + chan->start_offset += stride * (height - 1); + if (chan->hflip) + chan->start_offset += stride - 1; + break; + + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + chan->addr_offset_u = stride * height; + chan->addr_offset_v = chan->addr_offset_u + stride * height / 4; + + /* For YVU420, we swap the locations of the U and V planes. */ + if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420) { + unsigned long temp; + + temp = chan->addr_offset_u; + chan->addr_offset_u = chan->addr_offset_v; + chan->addr_offset_v = temp; + } + + chan->start_offset_u = chan->addr_offset_u; + chan->start_offset_v = chan->addr_offset_v; + + if (chan->vflip) { + chan->start_offset += stride * (height - 1); + chan->start_offset_u += (stride / 2) * ((height / 2) - 1); + chan->start_offset_v += (stride / 2) * ((height / 2) - 1); + } + if (chan->hflip) { + chan->start_offset += stride - 1; + chan->start_offset_u += (stride / 2) - 1; + chan->start_offset_v += (stride / 2) - 1; + } + break; + } +} + +static void release_buffer(struct tegra_vi_channel *chan, + struct tegra_channel_buffer *buf, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb = &buf->buf; + + vb->sequence = chan->sequence++; + vb->field = V4L2_FIELD_NONE; + vb->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vb->vb2_buf, state); +} + +static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, + struct tegra_channel_buffer *buf) +{ + dma_addr_t base = buf->addr; + + switch (chan->fmtinfo->fourcc) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_U, base + chan->addr_offset_u); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_U, base + chan->start_offset_u); + tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_V, base + chan->addr_offset_v); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_V, base + chan->start_offset_v); + fallthrough; + + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, base); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_FIRST, base + chan->start_offset); + break; + } +} + +static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan, + struct tegra_channel_buffer *buf) +{ + int err; + + chan->next_out_sp_idx++; + + tegra20_channel_vi_buffer_setup(chan, buf); + + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE); + + /* Wait for syncpt counter to reach frame start event threshold */ + err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx, + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL); + if (err) { + host1x_syncpt_incr(chan->mw_ack_sp[0]); + dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err); + release_buffer(chan, buf, VB2_BUF_STATE_ERROR); + return err; + } + + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE); + + release_buffer(chan, buf, VB2_BUF_STATE_DONE); + + return 0; +} + +static int tegra20_chan_capture_kthread_start(void *data) +{ + struct tegra_vi_channel *chan = data; + struct tegra_channel_buffer *buf; + unsigned int retries = 0; + int err = 0; + + while (1) { + /* + * Source is not streaming if error is non-zero. + * So, do not dequeue buffers on error and let the thread sleep + * till kthread stop signal is received. + */ + wait_event_interruptible(chan->start_wait, + kthread_should_stop() || + (!list_empty(&chan->capture) && !err)); + + if (kthread_should_stop()) + break; + + /* dequeue the buffer and start capture */ + spin_lock(&chan->start_lock); + if (list_empty(&chan->capture)) { + spin_unlock(&chan->start_lock); + continue; + } + + buf = list_first_entry(&chan->capture, struct tegra_channel_buffer, queue); + list_del_init(&buf->queue); + spin_unlock(&chan->start_lock); + + err = tegra20_channel_capture_frame(chan, buf); + if (!err) { + retries = 0; + continue; + } + + if (retries++ > chan->syncpt_timeout_retry) + vb2_queue_error(&chan->queue); + else + err = 0; + } + + return 0; +} + +static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan) +{ + u32 output_fourcc = chan->format.pixelformat; + int width = chan->format.width; + int height = chan->format.height; + int stride_l = chan->format.bytesperline; + int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 || + output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0; + int main_output_format; + int yuv_output_format; + + tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format); + + /* + * Set up low pass filter. Use 0x240 for chromaticity and 0x240 + * for luminance, which is the default and means not to touch + * anything. + */ + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL, + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT | + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT); + + /* Set up raise-on-edge, so we get an interrupt on end of frame. */ + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE); + + tegra20_vi_write(chan, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL, + (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) | + (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) | + yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT | + main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT); + + /* Set up frame size */ + tegra20_vi_write(chan, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE, + height << VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT | + width << VI_FIRST_OUTPUT_FRAME_WIDTH_SFT); + + /* First output memory enabled */ + tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0); + + /* Set the number of frames in the buffer */ + tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT_FIRST, 1); + + /* Set up buffer frame size */ + tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE_FIRST, + height << VI_VB0_SIZE_FIRST_V_SFT | + width << VI_VB0_SIZE_FIRST_H_SFT); + + tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, + stride_l << VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT | + stride_c << VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT); + + tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0); +} + +static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count) +{ + struct tegra_vi_channel *chan = vb2_get_drv_priv(vq); + struct media_pipeline *pipe = &chan->video.pipe; + int err; + + chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]); + + err = video_device_pipeline_start(&chan->video, pipe); + if (err) + goto error_pipeline_start; + + tegra20_camera_capture_setup(chan); + + err = tegra_channel_set_stream(chan, true); + if (err) + goto error_set_stream; + + chan->sequence = 0; + + chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start, + chan, "%s:0", chan->video.name); + if (IS_ERR(chan->kthread_start_capture)) { + err = PTR_ERR(chan->kthread_start_capture); + chan->kthread_start_capture = NULL; + dev_err_probe(&chan->video.dev, err, "failed to run capture kthread\n"); + goto error_kthread_start; + } + + return 0; + +error_kthread_start: + tegra_channel_set_stream(chan, false); +error_set_stream: + video_device_pipeline_stop(&chan->video); +error_pipeline_start: + tegra_channel_release_buffers(chan, VB2_BUF_STATE_QUEUED); + + return err; +} + +static void tegra20_vi_stop_streaming(struct vb2_queue *vq) +{ + struct tegra_vi_channel *chan = vb2_get_drv_priv(vq); + + if (chan->kthread_start_capture) { + kthread_stop(chan->kthread_start_capture); + chan->kthread_start_capture = NULL; + } + + tegra_channel_release_buffers(chan, VB2_BUF_STATE_ERROR); + tegra_channel_set_stream(chan, false); + video_device_pipeline_stop(&chan->video); +} + +static const struct tegra_vi_ops tegra20_vi_ops = { + .vi_enable = tegra20_vi_enable, + .channel_host1x_syncpt_init = tegra20_channel_host1x_syncpt_init, + .channel_host1x_syncpt_free = tegra20_channel_host1x_syncpt_free, + .vi_fmt_align = tegra20_fmt_align, + .channel_queue_setup = tegra20_channel_queue_setup, + .vi_start_streaming = tegra20_vi_start_streaming, + .vi_stop_streaming = tegra20_vi_stop_streaming, +}; + +#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \ +{ \ + .code = MEDIA_BUS_FMT_##MBUS_CODE, \ + .bpp = BPP, \ + .fourcc = V4L2_PIX_FMT_##FOURCC, \ +} + +static const struct tegra_video_format tegra20_video_formats[] = { + TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY), + TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY), + TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV), + TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU), + TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420), + TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420), +}; + +const struct tegra_vi_soc tegra20_vi_soc = { + .video_formats = tegra20_video_formats, + .nformats = ARRAY_SIZE(tegra20_video_formats), + .default_video_format = &tegra20_video_formats[0], + .ops = &tegra20_vi_ops, + .vi_max_channels = 1, /* parallel input (VIP) */ + .vi_max_clk_hz = 150000000, + .has_h_v_flip = true, +}; + +/* -------------------------------------------------------------------------- + * VIP + */ + +/* + * VIP-specific configuration for stream start. + * + * Whatever is common among VIP and CSI is done by the VI component (see + * tegra20_vi_start_streaming()). Here we do what is VIP-specific. + */ +static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan) +{ + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&vip_chan->subdev); + int width = vi_chan->format.width; + int height = vi_chan->format.height; + + unsigned int main_input_format; + unsigned int yuv_input_format; + + tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format); + + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0); + + tegra20_vi_write(vi_chan, TEGRA_VI_VI_INPUT_CONTROL, + VI_INPUT_VIP_INPUT_ENABLE | main_input_format | yuv_input_format); + + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, 0); + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, 0); + + tegra20_vi_write(vi_chan, TEGRA_VI_VIP_V_ACTIVE, height << VI_VIP_V_ACTIVE_PERIOD_SFT); + tegra20_vi_write(vi_chan, TEGRA_VI_VIP_H_ACTIVE, + roundup(width, 2) << VI_VIP_H_ACTIVE_PERIOD_SFT); + + /* + * For VIP, D9..D2 is mapped to the video decoder's P7..P0. + * Disable/mask out the other Dn wires. When not in BT656 + * mode we also need the V/H sync. + */ + tegra20_vi_write(vi_chan, TEGRA_VI_PIN_INPUT_ENABLE, + GENMASK(9, 2) << VI_PIN_INPUT_VD_SFT | + VI_PIN_INPUT_HSYNC | VI_PIN_INPUT_VSYNC); + tegra20_vi_write(vi_chan, TEGRA_VI_VI_DATA_INPUT_CONTROL, + GENMASK(9, 2) << VI_DATA_INPUT_SFT); + tegra20_vi_write(vi_chan, TEGRA_VI_PIN_INVERSION, 0); + + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT_1, + VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT | + host1x_syncpt_id(vi_chan->mw_ack_sp[0]) + << VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT); + + tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE); + + return 0; +} + +static const struct tegra_vip_ops tegra20_vip_ops = { + .vip_start_streaming = tegra20_vip_start_streaming, +}; + +const struct tegra_vip_soc tegra20_vip_soc = { + .ops = &tegra20_vip_ops, +}; diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index d58370a84737..da99f19a39e7 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -17,6 +17,13 @@ #include "csi.h" #include "vi.h" +#define TEGRA210_MIN_WIDTH 32U +#define TEGRA210_MAX_WIDTH 32768U +#define TEGRA210_MIN_HEIGHT 32U +#define TEGRA210_MAX_HEIGHT 32768U + +#define SURFACE_ALIGN_BYTES 64 + #define TEGRA_VI_SYNCPT_WAIT_TIMEOUT msecs_to_jiffies(200) /* Tegra210 VI registers */ @@ -172,6 +179,84 @@ static u32 vi_csi_read(struct tegra_vi_channel *chan, u8 portno, /* * Tegra210 VI channel capture operations */ + +static int tegra210_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) +{ + struct tegra_vi *vi = chan->vi; + unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; + struct host1x_syncpt *fs_sp; + struct host1x_syncpt *mw_sp; + int ret, i; + + for (i = 0; i < chan->numgangports; i++) { + fs_sp = host1x_syncpt_request(&vi->client, flags); + if (!fs_sp) { + dev_err(vi->dev, "failed to request frame start syncpoint\n"); + ret = -ENOMEM; + goto free_syncpts; + } + + mw_sp = host1x_syncpt_request(&vi->client, flags); + if (!mw_sp) { + dev_err(vi->dev, "failed to request memory ack syncpoint\n"); + host1x_syncpt_put(fs_sp); + ret = -ENOMEM; + goto free_syncpts; + } + + chan->frame_start_sp[i] = fs_sp; + chan->mw_ack_sp[i] = mw_sp; + spin_lock_init(&chan->sp_incr_lock[i]); + } + + return 0; + +free_syncpts: + for (i = 0; i < chan->numgangports; i++) { + host1x_syncpt_put(chan->mw_ack_sp[i]); + host1x_syncpt_put(chan->frame_start_sp[i]); + } + return ret; +} + +static void tegra210_channel_host1x_syncpt_free(struct tegra_vi_channel *chan) +{ + int i; + + for (i = 0; i < chan->numgangports; i++) { + host1x_syncpt_put(chan->mw_ack_sp[i]); + host1x_syncpt_put(chan->frame_start_sp[i]); + } +} + +static void tegra210_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp) +{ + unsigned int min_bpl; + unsigned int max_bpl; + unsigned int bpl; + + /* + * The transfer alignment requirements are expressed in bytes. + * Clamp the requested width and height to the limits. + */ + pix->width = clamp(pix->width, TEGRA210_MIN_WIDTH, TEGRA210_MAX_WIDTH); + pix->height = clamp(pix->height, TEGRA210_MIN_HEIGHT, TEGRA210_MAX_HEIGHT); + + /* Clamp the requested bytes per line value. If the maximum bytes per + * line value is zero, the module doesn't support user configurable + * line sizes. Override the requested value with the minimum in that + * case. + */ + min_bpl = pix->width * bpp; + max_bpl = rounddown(TEGRA210_MAX_WIDTH, SURFACE_ALIGN_BYTES); + bpl = roundup(pix->bytesperline, SURFACE_ALIGN_BYTES); + + pix->bytesperline = clamp(bpl, min_bpl, max_bpl); + pix->sizeimage = pix->bytesperline * pix->height; + if (pix->pixelformat == V4L2_PIX_FMT_NV16) + pix->sizeimage *= 2; +} + static int tegra_channel_capture_setup(struct tegra_vi_channel *chan, u8 portno) { @@ -718,6 +803,9 @@ static const struct tegra_video_format tegra210_video_formats[] = { /* Tegra210 VI operations */ static const struct tegra_vi_ops tegra210_vi_ops = { + .channel_host1x_syncpt_init = tegra210_channel_host1x_syncpt_init, + .channel_host1x_syncpt_free = tegra210_channel_host1x_syncpt_free, + .vi_fmt_align = tegra210_fmt_align, .vi_start_streaming = tegra210_vi_start_streaming, .vi_stop_streaming = tegra210_vi_stop_streaming, }; @@ -730,8 +818,10 @@ const struct tegra_vi_soc tegra210_vi_soc = { .hw_revision = 3, .vi_max_channels = 6, #if IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG) + .default_video_format = &tegra210_video_formats[0], .vi_max_clk_hz = 499200000, #else + .default_video_format = &tegra210_video_formats[4], .vi_max_clk_hz = 998400000, #endif }; diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 2f1aff7e8717..79284c3b6cae 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -30,15 +30,19 @@ #include "vi.h" #include "video.h" -#define MAX_CID_CONTROLS 1 - -static const struct tegra_video_format tegra_default_format = { - .img_dt = TEGRA_IMAGE_DT_RAW10, - .bit_width = 10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 2, - .img_fmt = TEGRA_IMAGE_FORMAT_DEF, - .fourcc = V4L2_PIX_FMT_SRGGB10, +#define MAX_CID_CONTROLS 3 + +/** + * struct tegra_vi_graph_entity - Entity in the video graph + * + * @asd: subdev asynchronous registration information + * @entity: media entity from the corresponding V4L2 subdev + * @subdev: V4L2 subdev + */ +struct tegra_vi_graph_entity { + struct v4l2_async_subdev asd; + struct media_entity *entity; + struct v4l2_subdev *subdev; }; static inline struct tegra_vi * @@ -98,6 +102,7 @@ tegra_get_format_by_fourcc(struct tegra_vi *vi, u32 fourcc) /* * videobuf2 queue operations */ + static int tegra_channel_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -113,6 +118,9 @@ static int tegra_channel_queue_setup(struct vb2_queue *vq, sizes[0] = chan->format.sizeimage; alloc_devs[0] = chan->vi->dev; + if (chan->vi->ops->channel_queue_setup) + chan->vi->ops->channel_queue_setup(chan); + return 0; } @@ -164,6 +172,9 @@ tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan) return media_entity_to_v4l2_subdev(pad->entity); } +/* + * Walk up the chain until the initial source (e.g. image sensor) + */ struct v4l2_subdev * tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan) { @@ -190,49 +201,15 @@ tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan) static int tegra_channel_enable_stream(struct tegra_vi_channel *chan) { - struct v4l2_subdev *csi_subdev, *src_subdev; - struct tegra_csi_channel *csi_chan; - int ret, err; + struct v4l2_subdev *subdev; + int ret; - /* - * Tegra CSI receiver can detect the first LP to HS transition. - * So, start the CSI stream-on prior to sensor stream-on and - * vice-versa for stream-off. - */ - csi_subdev = tegra_channel_get_remote_csi_subdev(chan); - ret = v4l2_subdev_call(csi_subdev, video, s_stream, true); + subdev = tegra_channel_get_remote_csi_subdev(chan); + ret = v4l2_subdev_call(subdev, video, s_stream, true); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; - if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) - return 0; - - csi_chan = v4l2_get_subdevdata(csi_subdev); - /* - * TRM has incorrectly documented to wait for done status from - * calibration logic after CSI interface power on. - * As per the design, calibration results are latched and applied - * to the pads only when the link is in LP11 state which will happen - * during the sensor stream-on. - * CSI subdev stream-on triggers start of MIPI pads calibration. - * Wait for calibration to finish here after sensor subdev stream-on. - */ - src_subdev = tegra_channel_get_remote_source_subdev(chan); - ret = v4l2_subdev_call(src_subdev, video, s_stream, true); - err = tegra_mipi_finish_calibration(csi_chan->mipi); - - if (ret < 0 && ret != -ENOIOCTLCMD) - goto err_disable_csi_stream; - - if (err < 0) - dev_warn(csi_chan->csi->dev, - "MIPI calibration failed: %d\n", err); - return 0; - -err_disable_csi_stream: - v4l2_subdev_call(csi_subdev, video, s_stream, false); - return ret; } static int tegra_channel_disable_stream(struct tegra_vi_channel *chan) @@ -240,18 +217,6 @@ static int tegra_channel_disable_stream(struct tegra_vi_channel *chan) struct v4l2_subdev *subdev; int ret; - /* - * Stream-off subdevices in reverse order to stream-on. - * Remote source subdev in TPG mode is same as CSI subdev. - */ - subdev = tegra_channel_get_remote_source_subdev(chan); - ret = v4l2_subdev_call(subdev, video, s_stream, false); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) - return 0; - subdev = tegra_channel_get_remote_csi_subdev(chan); ret = v4l2_subdev_call(subdev, video, s_stream, false); if (ret < 0 && ret != -ENOIOCTLCMD) @@ -457,36 +422,6 @@ static int tegra_channel_get_format(struct file *file, void *fh, return 0; } -static void tegra_channel_fmt_align(struct tegra_vi_channel *chan, - struct v4l2_pix_format *pix, - unsigned int bpp) -{ - unsigned int min_bpl; - unsigned int max_bpl; - unsigned int bpl; - - /* - * The transfer alignment requirements are expressed in bytes. - * Clamp the requested width and height to the limits. - */ - pix->width = clamp(pix->width, TEGRA_MIN_WIDTH, TEGRA_MAX_WIDTH); - pix->height = clamp(pix->height, TEGRA_MIN_HEIGHT, TEGRA_MAX_HEIGHT); - - /* Clamp the requested bytes per line value. If the maximum bytes per - * line value is zero, the module doesn't support user configurable - * line sizes. Override the requested value with the minimum in that - * case. - */ - min_bpl = pix->width * bpp; - max_bpl = rounddown(TEGRA_MAX_WIDTH, SURFACE_ALIGN_BYTES); - bpl = roundup(pix->bytesperline, SURFACE_ALIGN_BYTES); - - pix->bytesperline = clamp(bpl, min_bpl, max_bpl); - pix->sizeimage = pix->bytesperline * pix->height; - if (pix->pixelformat == V4L2_PIX_FMT_NV16) - pix->sizeimage *= 2; -} - static int __tegra_channel_try_format(struct tegra_vi_channel *chan, struct v4l2_pix_format *pix) { @@ -563,7 +498,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, return ret; v4l2_fill_pix_format(pix, &fmt.format); - tegra_channel_fmt_align(chan, pix, fmtinfo->bpp); + chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp); __v4l2_subdev_state_free(sd_state); @@ -616,7 +551,7 @@ static int tegra_channel_set_format(struct file *file, void *fh, return ret; v4l2_fill_pix_format(pix, &fmt.format); - tegra_channel_fmt_align(chan, pix, fmtinfo->bpp); + chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp); chan->format = *pix; chan->fmtinfo = fmtinfo; @@ -652,7 +587,7 @@ static int tegra_channel_set_subdev_active_fmt(struct tegra_vi_channel *chan) chan->format.bytesperline = chan->format.width * chan->fmtinfo->bpp; chan->format.sizeimage = chan->format.bytesperline * chan->format.height; - tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + chan->vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp); tegra_channel_update_gangports(chan); return 0; @@ -821,7 +756,7 @@ static int tegra_channel_s_dv_timings(struct file *file, void *fh, chan->format.height = bt->height; chan->format.bytesperline = bt->width * chan->fmtinfo->bpp; chan->format.sizeimage = chan->format.bytesperline * bt->height; - tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + chan->vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp); tegra_channel_update_gangports(chan); return 0; @@ -977,6 +912,12 @@ static int vi_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY: chan->syncpt_timeout_retry = ctrl->val; break; + case V4L2_CID_HFLIP: + chan->hflip = ctrl->val; + break; + case V4L2_CID_VFLIP: + chan->vflip = ctrl->val; + break; default: return -EINVAL; } @@ -1048,6 +989,12 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan) v4l2_ctrl_handler_free(&chan->ctrl_handler); return ret; } + + if (chan->vi->soc->has_h_v_flip) { + v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + } + #endif /* setup the controls */ @@ -1119,7 +1066,7 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan) * there are no matched formats. */ if (!match_code) { - match_code = tegra_default_format.code; + match_code = chan->vi->soc->default_video_format->code; index = tegra_get_format_idx_by_code(chan->vi, match_code, 0); if (WARN_ON(index < 0)) return -EINVAL; @@ -1133,21 +1080,11 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan) return 0; } -static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan) -{ - int i; - - for (i = 0; i < chan->numgangports; i++) { - host1x_syncpt_put(chan->mw_ack_sp[i]); - host1x_syncpt_put(chan->frame_start_sp[i]); - } -} - static void tegra_channel_cleanup(struct tegra_vi_channel *chan) { v4l2_ctrl_handler_free(&chan->ctrl_handler); media_entity_cleanup(&chan->video.entity); - tegra_channel_host1x_syncpts_free(chan); + chan->vi->ops->channel_host1x_syncpt_free(chan); mutex_destroy(&chan->video_lock); } @@ -1165,42 +1102,6 @@ void tegra_channels_cleanup(struct tegra_vi *vi) } } -static int tegra_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) -{ - struct tegra_vi *vi = chan->vi; - unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; - struct host1x_syncpt *fs_sp; - struct host1x_syncpt *mw_sp; - int ret, i; - - for (i = 0; i < chan->numgangports; i++) { - fs_sp = host1x_syncpt_request(&vi->client, flags); - if (!fs_sp) { - dev_err(vi->dev, "failed to request frame start syncpoint\n"); - ret = -ENOMEM; - goto free_syncpts; - } - - mw_sp = host1x_syncpt_request(&vi->client, flags); - if (!mw_sp) { - dev_err(vi->dev, "failed to request memory ack syncpoint\n"); - host1x_syncpt_put(fs_sp); - ret = -ENOMEM; - goto free_syncpts; - } - - chan->frame_start_sp[i] = fs_sp; - chan->mw_ack_sp[i] = mw_sp; - spin_lock_init(&chan->sp_incr_lock[i]); - } - - return 0; - -free_syncpts: - tegra_channel_host1x_syncpts_free(chan); - return ret; -} - static int tegra_channel_init(struct tegra_vi_channel *chan) { struct tegra_vi *vi = chan->vi; @@ -1216,7 +1117,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) init_waitqueue_head(&chan->done_wait); /* initialize the video format */ - chan->fmtinfo = &tegra_default_format; + chan->fmtinfo = chan->vi->soc->default_video_format; chan->format.pixelformat = chan->fmtinfo->fourcc; chan->format.colorspace = V4L2_COLORSPACE_SRGB; chan->format.field = V4L2_FIELD_NONE; @@ -1224,9 +1125,9 @@ static int tegra_channel_init(struct tegra_vi_channel *chan) chan->format.height = TEGRA_DEF_HEIGHT; chan->format.bytesperline = TEGRA_DEF_WIDTH * chan->fmtinfo->bpp; chan->format.sizeimage = chan->format.bytesperline * TEGRA_DEF_HEIGHT; - tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp); + vi->ops->vi_fmt_align(&chan->format, chan->fmtinfo->bpp); - ret = tegra_channel_host1x_syncpt_init(chan); + ret = vi->ops->channel_host1x_syncpt_init(chan); if (ret) return ret; @@ -1289,7 +1190,7 @@ free_v4l2_ctrl_hdl: cleanup_media: media_entity_cleanup(&chan->video.entity); free_syncpts: - tegra_channel_host1x_syncpts_free(chan); + vi->ops->channel_host1x_syncpt_free(chan); return ret; } @@ -1351,7 +1252,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) struct device_node *node = vi->dev->of_node; struct device_node *ep = NULL; struct device_node *ports; - struct device_node *port; + struct device_node *port = NULL; unsigned int port_num; struct device_node *parent; struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; @@ -1360,7 +1261,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) ports = of_get_child_by_name(node, "ports"); if (!ports) - return -ENODEV; + return dev_err_probe(vi->dev, -ENODEV, "%pOF: missing 'ports' node\n", node); for_each_child_of_node(ports, port) { if (!of_node_name_eq(port, "port")) @@ -1374,7 +1275,6 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) dev_err(vi->dev, "invalid port num %d for %pOF\n", port_num, port); ret = -EINVAL; - of_node_put(port); goto cleanup; } @@ -1397,13 +1297,12 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; ret = tegra_vi_channel_alloc(vi, port_num, port, lanes); - if (ret < 0) { - of_node_put(port); + if (ret < 0) goto cleanup; - } } cleanup: + of_node_put(port); of_node_put(ports); return ret; } @@ -1858,10 +1757,10 @@ static int tegra_vi_graph_init(struct tegra_vi *vi) * Walk the links to parse the full graph. Each channel will have * one endpoint of the composite node. Start by parsing the * composite node and parse the remote entities in turn. - * Each channel will register v4l2 async notifier to make the graph - * independent between the channels so we can the current channel + * Each channel will register a v4l2 async notifier to make the graph + * independent between the channels so we can skip the current channel * in case of something wrong during graph parsing and continue with - * next channels. + * the next channels. */ list_for_each_entry(chan, &vi->vi_chans, list) { struct fwnode_handle *ep, *remote; @@ -1920,11 +1819,8 @@ static int tegra_vi_init(struct host1x_client *client) ret = tegra_vi_tpg_channels_alloc(vi); else ret = tegra_vi_channels_alloc(vi); - if (ret < 0) { - dev_err(vi->dev, - "failed to allocate vi channels: %d\n", ret); + if (ret < 0) goto free_chans; - } ret = tegra_vi_channels_init(vi); if (ret < 0) @@ -2026,6 +1922,9 @@ static int tegra_vi_probe(struct platform_device *pdev) vi->client.ops = &vi_client_ops; vi->client.dev = &pdev->dev; + if (vi->ops->vi_enable) + vi->ops->vi_enable(vi, true); + ret = host1x_client_register(&vi->client); if (ret < 0) { dev_err(&pdev->dev, @@ -2036,6 +1935,8 @@ static int tegra_vi_probe(struct platform_device *pdev) return 0; rpm_disable: + if (vi->ops->vi_enable) + vi->ops->vi_enable(vi, false); pm_runtime_disable(&pdev->dev); return ret; } @@ -2046,12 +1947,17 @@ static int tegra_vi_remove(struct platform_device *pdev) host1x_client_unregister(&vi->client); + if (vi->ops->vi_enable) + vi->ops->vi_enable(vi, false); pm_runtime_disable(&pdev->dev); return 0; } static const struct of_device_id tegra_vi_of_id_table[] = { +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + { .compatible = "nvidia,tegra20-vi", .data = &tegra20_vi_soc }, +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-vi", .data = &tegra210_vi_soc }, #endif diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h index a68e2c02c7b0..1e6a5caa7082 100644 --- a/drivers/staging/media/tegra-video/vi.h +++ b/drivers/staging/media/tegra-video/vi.h @@ -25,17 +25,11 @@ #define V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define TEGRA_MIN_WIDTH 32U -#define TEGRA_MAX_WIDTH 32768U -#define TEGRA_MIN_HEIGHT 32U -#define TEGRA_MAX_HEIGHT 32768U - #define TEGRA_DEF_WIDTH 1920 #define TEGRA_DEF_HEIGHT 1080 #define TEGRA_IMAGE_FORMAT_DEF 32 #define MAX_FORMAT_NUM 64 -#define SURFACE_ALIGN_BYTES 64 enum tegra_vi_pg_mode { TEGRA_VI_PG_DISABLED = 0, @@ -43,8 +37,17 @@ enum tegra_vi_pg_mode { TEGRA_VI_PG_PATCH, }; +struct tegra_vi; +struct tegra_vi_channel; + /** * struct tegra_vi_ops - Tegra VI operations + * @vi_enable: soc-specific operations needed to enable/disable the VI peripheral + * @channel_host1x_syncpt_init: initialize synchronization points + * @channel_host1x_syncpt_free: free all synchronization points + * @vi_fmt_align: modify `pix` to fit the hardware alignment + * requirements and fill image geometry + * @channel_queue_setup: additional operations at the end of vb2_ops::queue_setup * @vi_start_streaming: starts media pipeline, subdevice streaming, sets up * VI for capture and runs capture start and capture finish * kthreads for capturing frames to buffer and returns them back. @@ -52,6 +55,11 @@ enum tegra_vi_pg_mode { * back any queued buffers. */ struct tegra_vi_ops { + int (*vi_enable)(struct tegra_vi *vi, bool on); + int (*channel_host1x_syncpt_init)(struct tegra_vi_channel *chan); + void (*channel_host1x_syncpt_free)(struct tegra_vi_channel *chan); + void (*vi_fmt_align)(struct v4l2_pix_format *pix, unsigned int bpp); + void (*channel_queue_setup)(struct tegra_vi_channel *chan); int (*vi_start_streaming)(struct vb2_queue *vq, u32 count); void (*vi_stop_streaming)(struct vb2_queue *vq); }; @@ -61,18 +69,22 @@ struct tegra_vi_ops { * * @video_formats: supported video formats * @nformats: total video formats + * @default_video_format: default video format (pointer to a @video_formats item) * @ops: vi operations * @hw_revision: VI hw_revision * @vi_max_channels: supported max streaming channels * @vi_max_clk_hz: VI clock max frequency + * @has_h_v_flip: the chip can do H and V flip, and the driver implements it */ struct tegra_vi_soc { const struct tegra_video_format *video_formats; const unsigned int nformats; + const struct tegra_video_format *default_video_format; const struct tegra_vi_ops *ops; u32 hw_revision; unsigned int vi_max_channels; unsigned int vi_max_clk_hz; + bool has_h_v_flip:1; }; /** @@ -99,19 +111,6 @@ struct tegra_vi { }; /** - * struct tegra_vi_graph_entity - Entity in the video graph - * - * @asd: subdev asynchronous registration information - * @entity: media entity from the corresponding V4L2 subdev - * @subdev: V4L2 subdev - */ -struct tegra_vi_graph_entity { - struct v4l2_async_subdev asd; - struct media_entity *entity; - struct v4l2_subdev *subdev; -}; - -/** * struct tegra_vi_channel - Tegra video channel * * @list: list head for this entry @@ -122,11 +121,13 @@ struct tegra_vi_graph_entity { * @vi: Tegra video input device structure * @frame_start_sp: host1x syncpoint pointer to synchronize programmed capture * start condition with hardware frame start events through host1x - * syncpoint counters. + * syncpoint counters. (Tegra210) * @mw_ack_sp: host1x syncpoint pointer to synchronize programmed memory write * ack trigger condition with hardware memory write done at end of - * frame through host1x syncpoint counters. + * frame through host1x syncpoint counters (On Tegra20 used for the + * OUT_1 syncpt) * @sp_incr_lock: protects cpu syncpoint increment. + * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20) * * @kthread_start_capture: kthread to start capture of single frame when * vb buffer is available. This thread programs VI CSI hardware @@ -151,6 +152,12 @@ struct tegra_vi_graph_entity { * @queue: vb2 buffers queue * @sequence: V4L2 buffers sequence number * + * @addr_offset_u: U plane base address, relative to buffer base address (only for planar) + * @addr_offset_v: V plane base address, relative to buffer base address (only for planar) + * @start_offset: 1st Y byte to write, relative to buffer base address (for H/V flip) + * @start_offset_u: 1st U byte to write, relative to buffer base address (for H/V flip) + * @start_offset_v: 1st V byte to write, relative to buffer base address (for H/V flip) + * * @capture: list of queued buffers for capture * @start_lock: protects the capture queued list * @done: list of capture done queued buffers @@ -167,6 +174,9 @@ struct tegra_vi_graph_entity { * @tpg_fmts_bitmap: a bitmap for supported TPG formats * @pg_mode: test pattern generator mode (disabled/direct/patch) * @notifier: V4L2 asynchronous subdevs notifier + * + * @hflip: Horizontal flip is enabled + * @vflip: Vertical flip is enabled */ struct tegra_vi_channel { struct list_head list; @@ -180,6 +190,7 @@ struct tegra_vi_channel { struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX]; /* protects the cpu syncpoint increment */ spinlock_t sp_incr_lock[GANG_PORTS_MAX]; + u32 next_out_sp_idx; struct task_struct *kthread_start_capture; wait_queue_head_t start_wait; @@ -191,6 +202,12 @@ struct tegra_vi_channel { struct vb2_queue queue; u32 sequence; + unsigned int addr_offset_u; + unsigned int addr_offset_v; + unsigned int start_offset; + unsigned int start_offset_u; + unsigned int start_offset_v; + struct list_head capture; /* protects the capture queued list */ spinlock_t start_lock; @@ -210,6 +227,9 @@ struct tegra_vi_channel { enum tegra_vi_pg_mode pg_mode; struct v4l2_async_notifier notifier; + + bool hflip:1; + bool vflip:1; }; /** @@ -260,11 +280,11 @@ enum tegra_image_dt { /** * struct tegra_video_format - Tegra video format description * - * @img_dt: image data type - * @bit_width: format width in bits per component + * @img_dt: MIPI CSI-2 data type (for CSI-2 only) + * @bit_width: format width in bits per component (for CSI/Tegra210 only) * @code: media bus format code * @bpp: bytes per pixel (when stored in memory) - * @img_fmt: image format + * @img_fmt: image format (for CSI/Tegra210 only) * @fourcc: V4L2 pixel format FCC identifier */ struct tegra_video_format { @@ -276,6 +296,9 @@ struct tegra_video_format { u32 fourcc; }; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +extern const struct tegra_vi_soc tegra20_vi_soc; +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) extern const struct tegra_vi_soc tegra210_vi_soc; #endif diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c index d966b319553f..074ad0dc56ca 100644 --- a/drivers/staging/media/tegra-video/video.c +++ b/drivers/staging/media/tegra-video/video.c @@ -123,6 +123,10 @@ static int host1x_video_remove(struct host1x_device *dev) } static const struct of_device_id host1x_video_subdevs[] = { +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + { .compatible = "nvidia,tegra20-vip", }, + { .compatible = "nvidia,tegra20-vi", }, +#endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-csi", }, { .compatible = "nvidia,tegra210-vi", }, @@ -141,6 +145,7 @@ static struct host1x_driver host1x_video_driver = { static struct platform_driver * const drivers[] = { &tegra_csi_driver, + &tegra_vip_driver, &tegra_vi_driver, }; diff --git a/drivers/staging/media/tegra-video/video.h b/drivers/staging/media/tegra-video/video.h index fadaf2189dc9..7275affa6558 100644 --- a/drivers/staging/media/tegra-video/video.h +++ b/drivers/staging/media/tegra-video/video.h @@ -12,7 +12,6 @@ #include <media/v4l2-device.h> #include "vi.h" -#include "csi.h" struct tegra_video_device { struct v4l2_device v4l2_dev; @@ -25,5 +24,6 @@ int tegra_v4l2_nodes_setup_tpg(struct tegra_video_device *vid); void tegra_v4l2_nodes_cleanup_tpg(struct tegra_video_device *vid); extern struct platform_driver tegra_vi_driver; +extern struct platform_driver tegra_vip_driver; extern struct platform_driver tegra_csi_driver; #endif diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c new file mode 100644 index 000000000000..a1ab886acc18 --- /dev/null +++ b/drivers/staging/media/tegra-video/vip.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Parallel video capture module (VIP) for the Tegra VI. + * + * This file implements the VIP-specific infrastructure. + * + * Copyright (C) 2023 SKIDATA GmbH + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> + */ + +#include <linux/device.h> +#include <linux/host1x.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-fwnode.h> + +#include "vip.h" + +static inline struct tegra_vip *host1x_client_to_vip(struct host1x_client *client) +{ + return container_of(client, struct tegra_vip, client); +} + +static inline struct tegra_vip_channel *subdev_to_vip_channel(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct tegra_vip_channel, subdev); +} + +static inline struct tegra_vip *vip_channel_to_vip(struct tegra_vip_channel *chan) +{ + return container_of(chan, struct tegra_vip, chan); +} + +/* Find the previous subdev in the pipeline (i.e. the one connected to our sink pad) */ +static struct v4l2_subdev *tegra_vip_channel_get_prev_subdev(struct tegra_vip_channel *chan) +{ + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_first(&chan->pads[TEGRA_VIP_PAD_SINK]); + if (!remote_pad) + return NULL; + + return media_entity_to_v4l2_subdev(remote_pad->entity); +} + +static int tegra_vip_enable_stream(struct v4l2_subdev *subdev) +{ + struct tegra_vip_channel *vip_chan = subdev_to_vip_channel(subdev); + struct tegra_vip *vip = vip_channel_to_vip(vip_chan); + struct v4l2_subdev *prev_subdev = tegra_vip_channel_get_prev_subdev(vip_chan); + int err; + + err = pm_runtime_resume_and_get(vip->dev); + if (err) + return dev_err_probe(vip->dev, err, "failed to get runtime PM\n"); + + err = vip->soc->ops->vip_start_streaming(vip_chan); + if (err < 0) + goto err_start_streaming; + + err = v4l2_subdev_call(prev_subdev, video, s_stream, true); + if (err < 0 && err != -ENOIOCTLCMD) + goto err_prev_subdev_start_stream; + + return 0; + +err_prev_subdev_start_stream: +err_start_streaming: + pm_runtime_put(vip->dev); + return err; +} + +static int tegra_vip_disable_stream(struct v4l2_subdev *subdev) +{ + struct tegra_vip_channel *vip_chan = subdev_to_vip_channel(subdev); + struct tegra_vip *vip = vip_channel_to_vip(vip_chan); + struct v4l2_subdev *prev_subdev = tegra_vip_channel_get_prev_subdev(vip_chan); + + v4l2_subdev_call(prev_subdev, video, s_stream, false); + + pm_runtime_put(vip->dev); + + return 0; +} + +static int tegra_vip_s_stream(struct v4l2_subdev *subdev, int enable) +{ + int err; + + if (enable) + err = tegra_vip_enable_stream(subdev); + else + err = tegra_vip_disable_stream(subdev); + + return err; +} + +static const struct v4l2_subdev_video_ops tegra_vip_video_ops = { + .s_stream = tegra_vip_s_stream, +}; + +static const struct v4l2_subdev_ops tegra_vip_ops = { + .video = &tegra_vip_video_ops, +}; + +static int tegra_vip_channel_of_parse(struct tegra_vip *vip) +{ + struct device *dev = vip->dev; + struct device_node *np = dev->of_node; + struct v4l2_fwnode_endpoint v4l2_ep = { + .bus_type = V4L2_MBUS_PARALLEL + }; + struct fwnode_handle *fwh; + struct device_node *ep; + unsigned int num_pads; + int err; + + dev_dbg(dev, "Parsing %pOF", np); + + ep = of_graph_get_endpoint_by_regs(np, 0, 0); + if (!ep) { + err = -EINVAL; + dev_err_probe(dev, err, "%pOF: error getting endpoint node\n", np); + goto err_node_put; + } + + fwh = of_fwnode_handle(ep); + err = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep); + of_node_put(ep); + if (err) { + dev_err_probe(dev, err, "%pOF: failed to parse v4l2 endpoint\n", np); + goto err_node_put; + } + + num_pads = of_graph_get_endpoint_count(np); + if (num_pads != TEGRA_VIP_PADS_NUM) { + err = -EINVAL; + dev_err_probe(dev, err, "%pOF: need 2 pads, got %d\n", np, num_pads); + goto err_node_put; + } + + vip->chan.of_node = of_node_get(np); + vip->chan.pads[TEGRA_VIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + vip->chan.pads[TEGRA_VIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + return 0; + +err_node_put: + of_node_put(np); + return err; +} + +static int tegra_vip_channel_init(struct tegra_vip *vip) +{ + struct v4l2_subdev *subdev; + int err; + + subdev = &vip->chan.subdev; + v4l2_subdev_init(subdev, &tegra_vip_ops); + subdev->dev = vip->dev; + snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s", + kbasename(vip->chan.of_node->full_name)); + + v4l2_set_subdevdata(subdev, &vip->chan); + subdev->fwnode = of_fwnode_handle(vip->chan.of_node); + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + err = media_entity_pads_init(&subdev->entity, TEGRA_VIP_PADS_NUM, vip->chan.pads); + if (err) + return dev_err_probe(vip->dev, err, "failed to initialize media entity\n"); + + err = v4l2_async_register_subdev(subdev); + if (err) { + dev_err_probe(vip->dev, err, "failed to register subdev\n"); + goto err_register_subdev; + } + + return 0; + +err_register_subdev: + media_entity_cleanup(&subdev->entity); + return err; +} + +static int tegra_vip_init(struct host1x_client *client) +{ + struct tegra_vip *vip = host1x_client_to_vip(client); + int err; + + err = tegra_vip_channel_of_parse(vip); + if (err) + return err; + + err = tegra_vip_channel_init(vip); + if (err) + goto err_init; + + return 0; + +err_init: + of_node_put(vip->chan.of_node); + return err; +} + +static int tegra_vip_exit(struct host1x_client *client) +{ + struct tegra_vip *vip = host1x_client_to_vip(client); + struct v4l2_subdev *subdev = &vip->chan.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + of_node_put(vip->chan.of_node); + + return 0; +} + +static const struct host1x_client_ops vip_client_ops = { + .init = tegra_vip_init, + .exit = tegra_vip_exit, +}; + +static int tegra_vip_probe(struct platform_device *pdev) +{ + struct tegra_vip *vip; + int err; + + dev_dbg(&pdev->dev, "Probing VIP \"%s\" from %pOF\n", pdev->name, pdev->dev.of_node); + + vip = devm_kzalloc(&pdev->dev, sizeof(*vip), GFP_KERNEL); + if (!vip) + return -ENOMEM; + + vip->soc = of_device_get_match_data(&pdev->dev); + + vip->dev = &pdev->dev; + platform_set_drvdata(pdev, vip); + + /* initialize host1x interface */ + INIT_LIST_HEAD(&vip->client.list); + vip->client.ops = &vip_client_ops; + vip->client.dev = &pdev->dev; + + err = host1x_client_register(&vip->client); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to register host1x client\n"); + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static int tegra_vip_remove(struct platform_device *pdev) +{ + struct tegra_vip *vip = platform_get_drvdata(pdev); + + host1x_client_unregister(&vip->client); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +extern const struct tegra_vip_soc tegra20_vip_soc; +#endif + +static const struct of_device_id tegra_vip_of_id_table[] = { +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + { .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc }, +#endif + { } +}; +MODULE_DEVICE_TABLE(of, tegra_vip_of_id_table); + +struct platform_driver tegra_vip_driver = { + .driver = { + .name = "tegra-vip", + .of_match_table = tegra_vip_of_id_table, + }, + .probe = tegra_vip_probe, + .remove = tegra_vip_remove, +}; diff --git a/drivers/staging/media/tegra-video/vip.h b/drivers/staging/media/tegra-video/vip.h new file mode 100644 index 000000000000..32ceaaccbba2 --- /dev/null +++ b/drivers/staging/media/tegra-video/vip.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 SKIDATA GmbH + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> + */ + +#ifndef __TEGRA_VIP_H__ +#define __TEGRA_VIP_H__ + +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +enum { + TEGRA_VIP_PAD_SINK, + TEGRA_VIP_PAD_SOURCE, + TEGRA_VIP_PADS_NUM, +}; + +struct tegra_vip; + +/** + * struct tegra_vip_channel - Tegra VIP (parallel video capture) channel + * + * @subdev: V4L2 subdevice associated with this channel + * @pads: media pads for the subdevice entity + * @of_node: vip device tree node + */ +struct tegra_vip_channel { + struct v4l2_subdev subdev; + struct media_pad pads[TEGRA_VIP_PADS_NUM]; + struct device_node *of_node; +}; + +/** + * struct tegra_vip_ops - Tegra VIP operations + * + * @vip_start_streaming: programs vip hardware to enable streaming. + */ +struct tegra_vip_ops { + int (*vip_start_streaming)(struct tegra_vip_channel *vip_chan); +}; + +/** + * struct tegra_vip_soc - NVIDIA Tegra VIP SoC structure + * + * @ops: vip hardware operations + */ +struct tegra_vip_soc { + const struct tegra_vip_ops *ops; +}; + +/** + * struct tegra_vip - NVIDIA Tegra VIP device structure + * + * @dev: device struct + * @client: host1x_client struct + * @soc: pointer to SoC data structure + * @chan: the VIP channel + */ +struct tegra_vip { + struct device *dev; + struct host1x_client client; + const struct tegra_vip_soc *soc; + struct tegra_vip_channel chan; +}; + +#endif |