Pull media updates from Mauro Carvalho Chehab: - Removal of some old unused sensor drivers: ad9389b, m5mols, mt9m032, mt9t001, noon010pc30, s5k6aa, sr030pc30 and vs6624 - New i.MX8 image sensor interface driver - Some new RC keymaps - lots of cleanups at atomisp driver to make it support standard features present on other webcam drivers - the cx18 and saa7146 now uses VB2 - lots of cleanups and driver improvements * tag 'media/v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (460 commits) media: ov5670: Fix probe on ACPI media: nxp: imx8-isi: Remove 300ms sleep after enabling channel media: nxp: imx8-isi: Replace udelay() with fsleep() media: nxp: imx8-isi: Drop partial support for i.MX8QM and i.MX8QXP media: nxp: Add i.MX8 ISI driver media: dt-bindings: media: Add i.MX8 ISI DT bindings media: atomisp: gmin_platform: Add Lenovo Ideapad Miix 310 gmin_vars media: atomisp: gmin_platform: Make DMI quirks take precedence over the _DSM table media: atomisp: Remove struct atomisp_sub_device index field media: atomisp: Drop support for streaming from 2 sensors at once media: atomisp: Remove atomisp_try_fmt() call from atomisp_set_fmt() media: atomisp: Remove unused ATOM_ISP_MAX_WIDTH_TMP and ATOM_ISP_MAX_HEIGHT_TMP media: atomisp: Remove snr_mbus_fmt local var from atomisp_try_fmt() media: atomisp: Remove custom V4L2_CID_FMT_AUTO control media: atomisp: Remove continuous mode related code from atomisp_set_fmt() media: atomisp: Remove duplicate atomisp_[start|stop]_streaming() prototypes media: atomisp: gc0310: Switch over to ACPI powermanagement media: atomisp: gc0310: Use devm_kzalloc() for data struct media: atomisp: gc0310: Add runtime-pm support media: atomisp: gc0310: Delay power-on till streaming is started ...
Unified · Split
| @@ -55,6 +55,15 @@ Miscellaneous: · 54 unmodified lines | |||
| 55 | you can control the CEC line through this driver. This supports error | 55 | you can control the CEC line through this driver. This supports error |
| 56 | injection as well. | 56 | injection as well. |
| 57 | 57 | ||
| 58 | - cec-gpio and Allwinner A10 (or any other driver that uses the CEC pin | ||
| 59 | framework to drive the CEC pin directly): the CEC pin framework uses | ||
| 60 | high-resolution timers. These timers are affected by NTP daemons that | ||
| 61 | speed up or slow down the clock to sync with the official time. The | ||
| 62 | chronyd server will by default increase or decrease the clock by | ||
| 63 | 1/12th. This will cause the CEC timings to go out of spec. To fix this, | ||
| 64 | add a 'maxslewrate 40000' line to chronyd.conf. This limits the clock | ||
| 65 | frequency change to 1/25th, which keeps the CEC timings within spec. | ||
| 66 | |||
| 58 | 67 | ||
| 59 | Utilities | 68 | Utilities |
| 60 | ========= | 69 | ========= |
| @@ -296,69 +305,71 @@ broadcast messages twice to reduce the chance of them being lost. Specifically · 235 unmodified lines | |||
| 296 | Making a CEC debugger | 305 | Making a CEC debugger |
| 297 | ===================== | 306 | ===================== |
| 298 | 307 | ||
| 299 | By using a Raspberry Pi 2B/3/4 and some cheap components you can make | 308 | By using a Raspberry Pi 4B and some cheap components you can make |
| 300 | your own low-level CEC debugger. | 309 | your own low-level CEC debugger. |
| 301 | 310 | ||
| 302 | Here is a picture of my setup: | 311 | The critical component is one of these HDMI female-female passthrough connectors |
| 303 | 312 | (full soldering type 1): | |
| 304 | https://hverkuil.home.xs4all.nl/rpi3-cec.jpg | ||
| 305 | |||
| 306 | It's a Raspberry Pi 3 together with a breadboard and some breadboard wires: | ||
| 307 | |||
| 308 | http://www.dx.com/p/diy-40p-male-to-female-male-to-male-female-to-female-dupont-line-wire-3pcs-356089#.WYLOOXWGN7I | ||
| 309 | |||
| 310 | Finally on of these HDMI female-female passthrough connectors (full soldering type 1): | ||
| 311 | 313 | ||
| 312 | https://elabbay.myshopify.com/collections/camera/products/hdmi-af-af-v1a-hdmi-type-a-female-to-hdmi-type-a-female-pass-through-adapter-breakout-board?variant=45533926147 | 314 | https://elabbay.myshopify.com/collections/camera/products/hdmi-af-af-v1a-hdmi-type-a-female-to-hdmi-type-a-female-pass-through-adapter-breakout-board?variant=45533926147 |
| 313 | 315 | ||
| 314 | We've tested this and it works up to 4kp30 (297 MHz). The quality is not high | 316 | The video quality is variable and certainly not enough to pass-through 4kp60 |
| 315 | enough to pass-through 4kp60 (594 MHz). | 317 | (594 MHz) video. You might be able to support 4kp30, but more likely you will |
| 316 | 318 | be limited to 1080p60 (148.5 MHz). But for CEC testing that is fine. | |
| 317 | I also added an RTC and a breakout shield: | ||
| 318 | |||
| 319 | https://www.amazon.com/Makerfire%C2%AE-Raspberry-Module-DS1307-Battery/dp/B00ZOXWHK4 | ||
| 320 | 319 | ||
| 321 | https://www.dx.com/p/raspberry-pi-gpio-expansion-board-breadboard-easy-multiplexing-board-one-to-three-with-screw-for-raspberry-pi-2-3-b-b-2729992.html#.YGRCG0MzZ7I | 320 | You need a breadboard and some breadboard wires: |
| 322 | 321 | ||
| 323 | These two are not needed but they make life a bit easier. | 322 | http://www.dx.com/p/diy-40p-male-to-female-male-to-male-female-to-female-dupont-line-wire-3pcs-356089#.WYLOOXWGN7I |
| 324 | 323 | ||
| 325 | If you want to monitor the HPD line as well, then you need one of these | 324 | If you want to monitor the HPD and/or 5V lines as well, then you need one of |
| 326 | level shifters: | 325 | these 5V to 3.3V level shifters: |
| 327 | 326 | ||
| 328 | https://www.adafruit.com/product/757 | 327 | https://www.adafruit.com/product/757 |
| 329 | 328 | ||
| 330 | (This is just where I got these components, there are many other places you | 329 | (This is just where I got these components, there are many other places you |
| 331 | can get similar things). | 330 | can get similar things). |
| 332 | 331 | ||
| 332 | The ground pin of the HDMI connector needs to be connected to a ground | ||
| 333 | pin of the Raspberry Pi, of course. | ||
| 334 | |||
| 333 | The CEC pin of the HDMI connector needs to be connected to these pins: | 335 | The CEC pin of the HDMI connector needs to be connected to these pins: |
| 334 | CE0/IO8 and CE1/IO7 (pull-up GPIOs). The (optional) HPD pin of the HDMI | 336 | GPIO 6 and GPIO 7. The optional HPD pin of the HDMI connector should |
| 335 | connector should be connected (via a level shifter to convert the 5V | 337 | be connected via the level shifter to these pins: GPIO 23 and GPIO 12. |
| 336 | to 3.3V) to these pins: IO17 and IO27. The (optional) 5V pin of the HDMI | 338 | The optional 5V pin of the HDMI connector should be connected via the |
| 337 | connector should be connected (via a level shifter) to these pins: IO22 | 339 | level shifter to these pins: GPIO 25 and GPIO 22. Monitoring the HPD and |
| 338 | and IO24. Monitoring the HPD an 5V lines is not necessary, but it is helpful. | 340 | 5V lines is not necessary, but it is helpful. |
| 339 | 341 | ||
| 340 | This kernel patch will hook up the cec-gpio driver correctly to | 342 | This device tree addition in ``arch/arm/boot/dts/bcm2711-rpi-4-b.dts`` |
| 341 | e.g. ``arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts``:: | 343 | will hook up the cec-gpio driver correctly:: |
| 344 | |||
| 345 | cec@6 { | ||
| 346 | compatible = "cec-gpio"; | ||
| 347 | cec-gpios = <&gpio 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; | ||
| 348 | hpd-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>; | ||
| 349 | v5-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>; | ||
| 350 | }; | ||
| 342 | 351 | ||
| 343 | cec@7 { | 352 | cec@7 { |
| 344 | compatible = "cec-gpio"; | 353 | compatible = "cec-gpio"; |
| 345 | cec-gpios = <&gpio 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; | 354 | cec-gpios = <&gpio 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; |
| 346 | hpd-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; | 355 | hpd-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; |
| 347 | v5-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; | 356 | v5-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; |
| 348 | }; | 357 | }; |
| 349 | 358 | ||
| 350 | cec@8 { | 359 | If you haven't hooked up the HPD and/or 5V lines, then just delete those |
| 351 | compatible = "cec-gpio"; | 360 | lines. |
| 352 | cec-gpios = <&gpio 8 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; | ||
| 353 | hpd-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>; | ||
| 354 | v5-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>; | ||
| 355 | }; | ||
| 356 | 361 | ||
| 357 | This dts change will enable two cec GPIO devices: I typically use one to | 362 | This dts change will enable two cec GPIO devices: I typically use one to |
| 358 | send/receive CEC commands and the other to monitor. If you monitor using | 363 | send/receive CEC commands and the other to monitor. If you monitor using |
| 359 | an unconfigured CEC adapter then it will use GPIO interrupts which makes | 364 | an unconfigured CEC adapter then it will use GPIO interrupts which makes |
| 360 | monitoring very accurate. | 365 | monitoring very accurate. |
| 361 | 366 | ||
| 367 | If you just want to monitor traffic, then a single instance is sufficient. | ||
| 368 | The minimum configuration is one HDMI female-female passthrough connector | ||
| 369 | and two female-female breadboard wires: one for connecting the HDMI ground | ||
| 370 | pin to a ground pin on the Raspberry Pi, and the other to connect the HDMI | ||
| 371 | CEC pin to GPIO 6 on the Raspberry Pi. | ||
| 372 | |||
| 362 | The documentation on how to use the error injection is here: :ref:`cec_pin_error_inj`. | 373 | The documentation on how to use the error injection is here: :ref:`cec_pin_error_inj`. |
| 363 | 374 | ||
| 364 | ``cec-ctl --monitor-pin`` will do low-level CEC bus sniffing and analysis. | 375 | ``cec-ctl --monitor-pin`` will do low-level CEC bus sniffing and analysis. |
| @@ -72,17 +72,13 @@ imx319 Sony IMX319 sensor · 71 unmodified lines | |||
| 72 | imx334 Sony IMX334 sensor | 72 | imx334 Sony IMX334 sensor |
| 73 | imx355 Sony IMX355 sensor | 73 | imx355 Sony IMX355 sensor |
| 74 | imx412 Sony IMX412 sensor | 74 | imx412 Sony IMX412 sensor |
| 75 | m5mols Fujitsu M-5MOLS 8MP sensor | ||
| 76 | mt9m001 mt9m001 | 75 | mt9m001 mt9m001 |
| 77 | mt9m032 MT9M032 camera sensor | ||
| 78 | mt9m111 mt9m111, mt9m112 and mt9m131 | 76 | mt9m111 mt9m111, mt9m112 and mt9m131 |
| 79 | mt9p031 Aptina MT9P031 | 77 | mt9p031 Aptina MT9P031 |
| 80 | mt9t001 Aptina MT9T001 | ||
| 81 | mt9t112 Aptina MT9T111/MT9T112 | 78 | mt9t112 Aptina MT9T111/MT9T112 |
| 82 | mt9v011 Micron mt9v011 sensor | 79 | mt9v011 Micron mt9v011 sensor |
| 83 | mt9v032 Micron MT9V032 sensor | 80 | mt9v032 Micron MT9V032 sensor |
| 84 | mt9v111 Aptina MT9V111 sensor | 81 | mt9v111 Aptina MT9V111 sensor |
| 85 | noon010pc30 Siliconfile NOON010PC30 sensor | ||
| 86 | ov13858 OmniVision OV13858 sensor | 82 | ov13858 OmniVision OV13858 sensor |
| 87 | ov13b10 OmniVision OV13B10 sensor | 83 | ov13b10 OmniVision OV13B10 sensor |
| 88 | ov2640 OmniVision OV2640 sensor | 84 | ov2640 OmniVision OV2640 sensor |
| @@ -109,9 +105,6 @@ s5c73m3 Samsung S5C73M3 sensor · 20 unmodified lines | |||
| 109 | s5k4ecgx Samsung S5K4ECGX sensor | 105 | s5k4ecgx Samsung S5K4ECGX sensor |
| 110 | s5k5baf Samsung S5K5BAF sensor | 106 | s5k5baf Samsung S5K5BAF sensor |
| 111 | s5k6a3 Samsung S5K6A3 sensor | 107 | s5k6a3 Samsung S5K6A3 sensor |
| 112 | s5k6aa Samsung S5K6AAFX sensor | ||
| 113 | sr030pc30 Siliconfile SR030PC30 sensor | ||
| 114 | vs6624 ST VS6624 sensor | ||
| 115 | ============ ========================================================== | 108 | ============ ========================================================== |
| 116 | 109 | ||
| 117 | Flash devices | 110 | Flash devices |
| @@ -222,7 +215,6 @@ Video encoders · 104 unmodified lines | |||
| 222 | ============ ========================================================== | 215 | ============ ========================================================== |
| 223 | Driver Name | 216 | Driver Name |
| 224 | ============ ========================================================== | 217 | ============ ========================================================== |
| 225 | ad9389b Analog Devices AD9389B encoder | ||
| 226 | adv7170 Analog Devices ADV7170 video encoder | 218 | adv7170 Analog Devices ADV7170 video encoder |
| 227 | adv7175 Analog Devices ADV7175 video encoder | 219 | adv7175 Analog Devices ADV7175 video encoder |
| 228 | adv7343 ADV7343 video encoder | 220 | adv7343 ADV7343 video encoder |
| @@ -11,7 +11,7 @@ maintainers: · 10 unmodified lines | |||
| 11 | - Maxime Ripard <mripard@kernel.org> | 11 | - Maxime Ripard <mripard@kernel.org> |
| 12 | 12 | ||
| 13 | allOf: | 13 | allOf: |
| 14 | - $ref: "rc.yaml#" | 14 | - $ref: rc.yaml# |
| 15 | 15 | ||
| 16 | properties: | 16 | properties: |
| 17 | compatible: | 17 | compatible: |
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/allwinner,sun50i-h6-vpu-g2.yaml#" | 5 | $id: http://devicetree.org/schemas/media/allwinner,sun50i-h6-vpu-g2.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Hantro G2 VPU codec implemented on Allwinner H6 SoC | 8 | title: Hantro G2 VPU codec implemented on Allwinner H6 SoC |
| 9 | 9 | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | # Copyright 2020 BayLibre, SAS | 2 | # Copyright 2020 BayLibre, SAS |
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/amlogic,axg-ge2d.yaml#" | 5 | $id: http://devicetree.org/schemas/media/amlogic,axg-ge2d.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Amlogic GE2D Acceleration Unit | 8 | title: Amlogic GE2D Acceleration Unit |
| 9 | 9 | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | # Copyright 2019 BayLibre, SAS | 2 | # Copyright 2019 BayLibre, SAS |
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/amlogic,gx-vdec.yaml#" | 5 | $id: http://devicetree.org/schemas/media/amlogic,gx-vdec.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Amlogic Video Decoder | 8 | title: Amlogic Video Decoder |
| 9 | 9 | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/amlogic,meson-ir-tx.yaml#" | 5 | $id: http://devicetree.org/schemas/media/amlogic,meson-ir-tx.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Amlogic Meson IR transmitter | 8 | title: Amlogic Meson IR transmitter |
| 9 | 9 | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/amlogic,meson6-ir.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Amlogic Meson IR remote control receiver | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Neil Armstrong <neil.armstrong@linaro.org> | ||
| 11 | |||
| 12 | allOf: | ||
| 13 | - $ref: rc.yaml# | ||
| 14 | |||
| 15 | properties: | ||
| 16 | compatible: | ||
| 17 | oneOf: | ||
| 18 | - enum: | ||
| 19 | - amlogic,meson6-ir | ||
| 20 | - amlogic,meson8b-ir | ||
| 21 | - amlogic,meson-gxbb-ir | ||
| 22 | - items: | ||
| 23 | - const: amlogic,meson-gx-ir | ||
| 24 | - const: amlogic,meson-gxbb-ir | ||
| 25 | |||
| 26 | reg: | ||
| 27 | maxItems: 1 | ||
| 28 | |||
| 29 | interrupts: | ||
| 30 | maxItems: 1 | ||
| 31 | |||
| 32 | required: | ||
| 33 | - compatible | ||
| 34 | - reg | ||
| 35 | - interrupts | ||
| 36 | |||
| 37 | unevaluatedProperties: false | ||
| 38 | |||
| 39 | examples: | ||
| 40 | - | | ||
| 41 | #include <dt-bindings/interrupt-controller/irq.h> | ||
| 42 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 43 | ir-receiver@c8100480 { | ||
| 44 | compatible = "amlogic,meson6-ir"; | ||
| 45 | reg = <0xc8100480 0x20>; | ||
| 46 | interrupts = <GIC_SPI 15 IRQ_TYPE_EDGE_RISING>; | ||
| 47 | }; | ||
| @@ -1,16 +0,0 @@ | |||
| 1 | Exynos4x12/Exynos5 SoC series camera host interface (FIMC-LITE) | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | |||
| 5 | - compatible : should be one of: | ||
| 6 | "samsung,exynos4212-fimc-lite" for Exynos4212/4412 SoCs, | ||
| 7 | "samsung,exynos5250-fimc-lite" for Exynos5250 compatible | ||
| 8 | devices; | ||
| 9 | - reg : physical base address and size of the device memory mapped | ||
| 10 | registers; | ||
| 11 | - interrupts : should contain FIMC-LITE interrupt; | ||
| 12 | - clocks : FIMC LITE gate clock should be specified in this property. | ||
| 13 | - clock-names : should contain "flite" entry. | ||
| 14 | |||
| 15 | Each FIMC device should have an alias in the aliases node, in the form of | ||
| 16 | fimc-lite<n>, where <n> is an integer specifying the IP block instance. | ||
| @@ -1,50 +0,0 @@ | |||
| 1 | Exynos4x12 SoC series Imaging Subsystem (FIMC-IS) | ||
| 2 | |||
| 3 | The FIMC-IS is a subsystem for processing image signal from an image sensor. | ||
| 4 | The Exynos4x12 SoC series FIMC-IS V1.5 comprises of a dedicated ARM Cortex-A5 | ||
| 5 | processor, ISP, DRC and FD IP blocks and peripheral devices such as UART, I2C | ||
| 6 | and SPI bus controllers, PWM and ADC. | ||
| 7 | |||
| 8 | fimc-is node | ||
| 9 | ------------ | ||
| 10 | |||
| 11 | Required properties: | ||
| 12 | - compatible : should be "samsung,exynos4212-fimc-is" for Exynos4212 and | ||
| 13 | Exynos4412 SoCs; | ||
| 14 | - reg : physical base address and length of the registers set; | ||
| 15 | - interrupts : must contain two FIMC-IS interrupts, in order: ISP0, ISP1; | ||
| 16 | - clocks : list of clock specifiers, corresponding to entries in | ||
| 17 | clock-names property; | ||
| 18 | - clock-names : must contain "ppmuispx", "ppmuispx", "lite0", "lite1" | ||
| 19 | "mpll", "sysreg", "isp", "drc", "fd", "mcuisp", "gicisp", | ||
| 20 | "pwm_isp", "mcuctl_isp", "uart", "ispdiv0", "ispdiv1", | ||
| 21 | "mcuispdiv0", "mcuispdiv1", "aclk200", "div_aclk200", | ||
| 22 | "aclk400mcuisp", "div_aclk400mcuisp" entries, | ||
| 23 | matching entries in the clocks property. | ||
| 24 | pmu subnode | ||
| 25 | ----------- | ||
| 26 | |||
| 27 | Required properties: | ||
| 28 | - reg : must contain PMU physical base address and size of the register set. | ||
| 29 | |||
| 30 | The following are the FIMC-IS peripheral device nodes and can be specified | ||
| 31 | either standalone or as the fimc-is node child nodes. | ||
| 32 | |||
| 33 | i2c-isp (ISP I2C bus controller) nodes | ||
| 34 | ------------------------------------------ | ||
| 35 | |||
| 36 | Required properties: | ||
| 37 | |||
| 38 | - compatible : should be "samsung,exynos4212-i2c-isp" for Exynos4212 and | ||
| 39 | Exynos4412 SoCs; | ||
| 40 | - reg : physical base address and length of the registers set; | ||
| 41 | - clocks : must contain gate clock specifier for this controller; | ||
| 42 | - clock-names : must contain "i2c_isp" entry. | ||
| 43 | |||
| 44 | For the above nodes it is required to specify a pinctrl state named "default", | ||
| 45 | according to the pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt. | ||
| 46 | |||
| 47 | Device tree nodes of the image sensors' controlled directly by the FIMC-IS | ||
| 48 | firmware must be child nodes of their corresponding ISP I2C bus controller node. | ||
| 49 | The data link of these image sensors must be specified using the common video | ||
| 50 | interfaces bindings, defined in video-interfaces.txt. | ||
| @@ -23,6 +23,9 @@ properties: · 22 unmodified lines | |||
| 23 | description: autosuspend delay time in milliseconds | 23 | description: autosuspend delay time in milliseconds |
| 24 | $ref: /schemas/types.yaml#/definitions/uint32 | 24 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 25 | 25 | ||
| 26 | wakeup-source: | ||
| 27 | description: IR receiver can wake-up the system. | ||
| 28 | |||
| 26 | required: | 29 | required: |
| 27 | - compatible | 30 | - compatible |
| 28 | - gpios | 31 | - gpios |
| @@ -1,8 +1,8 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
| 2 | %YAML 1.2 | 2 | %YAML 1.2 |
| 3 | --- | 3 | --- |
| 4 | $id: "http://devicetree.org/schemas/media/i2c/chrontel,ch7322.yaml#" | 4 | $id: http://devicetree.org/schemas/media/i2c/chrontel,ch7322.yaml# |
| 5 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 6 | 6 | ||
| 7 | title: Chrontel HDMI-CEC Controller | 7 | title: Chrontel HDMI-CEC Controller |
| 8 | 8 | ||
| @@ -38,7 +38,7 @@ properties: · 37 unmodified lines | |||
| 38 | dongwoon,aac-mode: | 38 | dongwoon,aac-mode: |
| 39 | description: | 39 | description: |
| 40 | Indication of AAC mode select. | 40 | Indication of AAC mode select. |
| 41 | $ref: "/schemas/types.yaml#/definitions/uint32" | 41 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 42 | enum: | 42 | enum: |
| 43 | - 1 # AAC2 mode(operation time# 0.48 x Tvib) | 43 | - 1 # AAC2 mode(operation time# 0.48 x Tvib) |
| 44 | - 2 # AAC3 mode(operation time# 0.70 x Tvib) | 44 | - 2 # AAC3 mode(operation time# 0.70 x Tvib) |
| @@ -50,7 +50,7 @@ properties: · 5 unmodified lines | |||
| 50 | description: | 50 | description: |
| 51 | Number of AAC Timing count that controlled by one 6-bit period of | 51 | Number of AAC Timing count that controlled by one 6-bit period of |
| 52 | vibration register AACT[5:0], the unit of which is 100 us. | 52 | vibration register AACT[5:0], the unit of which is 100 us. |
| 53 | $ref: "/schemas/types.yaml#/definitions/uint32" | 53 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 54 | default: 0x20 | 54 | default: 0x20 |
| 55 | minimum: 0x00 | 55 | minimum: 0x00 |
| 56 | maximum: 0x3f | 56 | maximum: 0x3f |
| @@ -59,7 +59,7 @@ properties: · 2 unmodified lines | |||
| 59 | description: | 59 | description: |
| 60 | Indication of VCM internal clock dividing rate select, as one multiple | 60 | Indication of VCM internal clock dividing rate select, as one multiple |
| 61 | factor to calculate VCM ring periodic time Tvib. | 61 | factor to calculate VCM ring periodic time Tvib. |
| 62 | $ref: "/schemas/types.yaml#/definitions/uint32" | 62 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 63 | enum: | 63 | enum: |
| 64 | - 0 # Dividing Rate - 2 | 64 | - 0 # Dividing Rate - 2 |
| 65 | - 1 # Dividing Rate - 1 | 65 | - 1 # Dividing Rate - 1 |
| @@ -86,7 +86,7 @@ properties: · 85 unmodified lines | |||
| 86 | is 100000 micro volts | 86 | is 100000 micro volts |
| 87 | 87 | ||
| 88 | maxim,gpio-poc: | 88 | maxim,gpio-poc: |
| 89 | $ref: '/schemas/types.yaml#/definitions/uint32-array' | 89 | $ref: /schemas/types.yaml#/definitions/uint32-array |
| 90 | minItems: 2 | 90 | minItems: 2 |
| 91 | maxItems: 2 | 91 | maxItems: 2 |
| 92 | description: | | 92 | description: | |
| @@ -1,41 +0,0 @@ | |||
| 1 | * Omnivision OV2685 MIPI CSI-2 sensor | ||
| 2 | |||
| 3 | Required Properties: | ||
| 4 | - compatible: shall be "ovti,ov2685" | ||
| 5 | - clocks: reference to the xvclk input clock | ||
| 6 | - clock-names: shall be "xvclk" | ||
| 7 | - avdd-supply: Analog voltage supply, 2.8 volts | ||
| 8 | - dovdd-supply: Digital I/O voltage supply, 1.8 volts | ||
| 9 | - dvdd-supply: Digital core voltage supply, 1.8 volts | ||
| 10 | - reset-gpios: Low active reset gpio | ||
| 11 | |||
| 12 | The device node shall contain one 'port' child node with an | ||
| 13 | 'endpoint' subnode for its digital output video port, | ||
| 14 | in accordance with the video interface bindings defined in | ||
| 15 | Documentation/devicetree/bindings/media/video-interfaces.txt. | ||
| 16 | The endpoint optional property 'data-lanes' shall be "<1>". | ||
| 17 | |||
| 18 | Example: | ||
| 19 | &i2c7 { | ||
| 20 | ov2685: camera-sensor@3c { | ||
| 21 | compatible = "ovti,ov2685"; | ||
| 22 | reg = <0x3c>; | ||
| 23 | pinctrl-names = "default"; | ||
| 24 | pinctrl-0 = <&clk_24m_cam>; | ||
| 25 | |||
| 26 | clocks = <&cru SCLK_TESTCLKOUT1>; | ||
| 27 | clock-names = "xvclk"; | ||
| 28 | |||
| 29 | avdd-supply = <&pp2800_cam>; | ||
| 30 | dovdd-supply = <&pp1800>; | ||
| 31 | dvdd-supply = <&pp1800>; | ||
| 32 | reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>; | ||
| 33 | |||
| 34 | port { | ||
| 35 | ucam_out: endpoint { | ||
| 36 | remote-endpoint = <&mipi_in_ucam>; | ||
| 37 | data-lanes = <1>; | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | }; | ||
| @@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# · 7 unmodified lines | |||
| 8 | title: Omnivision OV8856 CMOS Sensor | 8 | title: Omnivision OV8856 CMOS Sensor |
| 9 | 9 | ||
| 10 | maintainers: | 10 | maintainers: |
| 11 | - Dongchun Zhu <dongchun.zhu@mediatek.com> | 11 | - Sakari Ailus <sakari.ailus@linux.intel.com> |
| 12 | 12 | ||
| 13 | description: |- | 13 | description: |- |
| 14 | The Omnivision OV8856 is a high performance, 1/4-inch, 8 megapixel, CMOS | 14 | The Omnivision OV8856 is a high performance, 1/4-inch, 8 megapixel, CMOS |
| @@ -88,7 +88,7 @@ properties: · 87 unmodified lines | |||
| 88 | properties: | 88 | properties: |
| 89 | link-frequencies: true | 89 | link-frequencies: true |
| 90 | ovti,mipi-clock-voltage: | 90 | ovti,mipi-clock-voltage: |
| 91 | $ref: "/schemas/types.yaml#/definitions/uint32" | 91 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 92 | description: | 92 | description: |
| 93 | Definition of MIPI clock voltage unit. This entry corresponds to | 93 | Definition of MIPI clock voltage unit. This entry corresponds to |
| 94 | the link speed defined by the 'link-frequencies' property. | 94 | the link speed defined by the 'link-frequencies' property. |
| @@ -0,0 +1,101 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/i2c/ovti,ov2685.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: OmniVision OV2685 Image Sensor | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Shunqian Zheng <zhengsq@rock-chips.com> | ||
| 11 | |||
| 12 | properties: | ||
| 13 | compatible: | ||
| 14 | const: ovti,ov2685 | ||
| 15 | |||
| 16 | reg: | ||
| 17 | maxItems: 1 | ||
| 18 | |||
| 19 | clocks: | ||
| 20 | items: | ||
| 21 | - description: XVCLK clock | ||
| 22 | |||
| 23 | clock-names: | ||
| 24 | items: | ||
| 25 | - const: xvclk | ||
| 26 | |||
| 27 | dvdd-supply: | ||
| 28 | description: Digital Domain Power Supply | ||
| 29 | |||
| 30 | avdd-supply: | ||
| 31 | description: Analog Domain Power Supply | ||
| 32 | |||
| 33 | dovdd-supply: | ||
| 34 | description: I/O Domain Power Supply | ||
| 35 | |||
| 36 | reset-gpios: | ||
| 37 | maxItems: 1 | ||
| 38 | description: Reset Pin GPIO Control (active low) | ||
| 39 | |||
| 40 | port: | ||
| 41 | description: MIPI CSI-2 transmitter port | ||
| 42 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 43 | additionalProperties: false | ||
| 44 | |||
| 45 | properties: | ||
| 46 | endpoint: | ||
| 47 | $ref: /schemas/media/video-interfaces.yaml# | ||
| 48 | unevaluatedProperties: false | ||
| 49 | |||
| 50 | properties: | ||
| 51 | data-lanes: | ||
| 52 | maxItems: 2 | ||
| 53 | |||
| 54 | required: | ||
| 55 | - data-lanes | ||
| 56 | |||
| 57 | required: | ||
| 58 | - compatible | ||
| 59 | - reg | ||
| 60 | - clocks | ||
| 61 | - clock-names | ||
| 62 | - dvdd-supply | ||
| 63 | - avdd-supply | ||
| 64 | - dovdd-supply | ||
| 65 | - port | ||
| 66 | |||
| 67 | additionalProperties: false | ||
| 68 | |||
| 69 | examples: | ||
| 70 | - | | ||
| 71 | #include <dt-bindings/clock/rk3399-cru.h> | ||
| 72 | #include <dt-bindings/gpio/gpio.h> | ||
| 73 | |||
| 74 | i2c { | ||
| 75 | #address-cells = <1>; | ||
| 76 | #size-cells = <0>; | ||
| 77 | |||
| 78 | ov2685: camera-sensor@3c { | ||
| 79 | compatible = "ovti,ov2685"; | ||
| 80 | reg = <0x3c>; | ||
| 81 | pinctrl-names = "default"; | ||
| 82 | pinctrl-0 = <&clk_24m_cam>; | ||
| 83 | |||
| 84 | clocks = <&cru SCLK_TESTCLKOUT1>; | ||
| 85 | clock-names = "xvclk"; | ||
| 86 | |||
| 87 | avdd-supply = <&pp2800_cam>; | ||
| 88 | dovdd-supply = <&pp1800>; | ||
| 89 | dvdd-supply = <&pp1800>; | ||
| 90 | reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>; | ||
| 91 | |||
| 92 | port { | ||
| 93 | ucam_out: endpoint { | ||
| 94 | remote-endpoint = <&mipi_in_ucam>; | ||
| 95 | data-lanes = <1>; | ||
| 96 | }; | ||
| 97 | }; | ||
| 98 | }; | ||
| 99 | }; | ||
| 100 | |||
| 101 | ... | ||
| @@ -0,0 +1,101 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/i2c/samsung,s5k5baf.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | |||
| 12 | properties: | ||
| 13 | compatible: | ||
| 14 | const: samsung,s5k5baf | ||
| 15 | |||
| 16 | reg: | ||
| 17 | maxItems: 1 | ||
| 18 | |||
| 19 | clocks: | ||
| 20 | maxItems: 1 | ||
| 21 | |||
| 22 | clock-names: | ||
| 23 | items: | ||
| 24 | - const: mclk | ||
| 25 | |||
| 26 | clock-frequency: | ||
| 27 | default: 24000000 | ||
| 28 | description: mclk clock frequency | ||
| 29 | |||
| 30 | rstn-gpios: | ||
| 31 | maxItems: 1 | ||
| 32 | description: RSTN pin | ||
| 33 | |||
| 34 | stbyn-gpios: | ||
| 35 | maxItems: 1 | ||
| 36 | description: STDBYN pin | ||
| 37 | |||
| 38 | vdda-supply: | ||
| 39 | description: Analog power supply 2.8V (2.6V to 3.0V) | ||
| 40 | |||
| 41 | vddio-supply: | ||
| 42 | description: I/O power supply 1.8V (1.65V to 1.95V) or 2.8V (2.5V to 3.1V) | ||
| 43 | |||
| 44 | vddreg-supply: | ||
| 45 | description: | ||
| 46 | Regulator input power supply 1.8V (1.7V to 1.9V) or 2.8V (2.6V to 3.0) | ||
| 47 | |||
| 48 | port: | ||
| 49 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 50 | additionalProperties: false | ||
| 51 | |||
| 52 | properties: | ||
| 53 | endpoint: | ||
| 54 | $ref: /schemas/media/video-interfaces.yaml# | ||
| 55 | unevaluatedProperties: false | ||
| 56 | |||
| 57 | properties: | ||
| 58 | data-lanes: | ||
| 59 | items: | ||
| 60 | - const: 1 | ||
| 61 | |||
| 62 | required: | ||
| 63 | - compatible | ||
| 64 | - clocks | ||
| 65 | - clock-names | ||
| 66 | - rstn-gpios | ||
| 67 | - stbyn-gpios | ||
| 68 | - vdda-supply | ||
| 69 | - vddio-supply | ||
| 70 | - vddreg-supply | ||
| 71 | |||
| 72 | additionalProperties: false | ||
| 73 | |||
| 74 | examples: | ||
| 75 | - | | ||
| 76 | #include <dt-bindings/gpio/gpio.h> | ||
| 77 | |||
| 78 | i2c { | ||
| 79 | #address-cells = <1>; | ||
| 80 | #size-cells = <0>; | ||
| 81 | |||
| 82 | sensor@2d { | ||
| 83 | compatible = "samsung,s5k5baf"; | ||
| 84 | reg = <0x2d>; | ||
| 85 | clocks = <&camera 0>; | ||
| 86 | clock-names = "mclk"; | ||
| 87 | clock-frequency = <24000000>; | ||
| 88 | rstn-gpios = <&gpl2 1 GPIO_ACTIVE_LOW>; | ||
| 89 | stbyn-gpios = <&gpl2 0 GPIO_ACTIVE_LOW>; | ||
| 90 | vdda-supply = <&cam_io_en_reg>; | ||
| 91 | vddio-supply = <&vtcam_reg>; | ||
| 92 | vddreg-supply = <&vt_core_15v_reg>; | ||
| 93 | |||
| 94 | port { | ||
| 95 | endpoint { | ||
| 96 | remote-endpoint = <&csis1_ep>; | ||
| 97 | data-lanes = <1>; | ||
| 98 | }; | ||
| 99 | }; | ||
| 100 | }; | ||
| 101 | }; | ||
| @@ -0,0 +1,98 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/i2c/samsung,s5k6a3.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5K6A3(YX) raw image sensor | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | |||
| 12 | description: | ||
| 13 | S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data | ||
| 14 | interfaces and CCI (I2C compatible) control bus. | ||
| 15 | |||
| 16 | properties: | ||
| 17 | compatible: | ||
| 18 | const: samsung,s5k6a3 | ||
| 19 | |||
| 20 | reg: | ||
| 21 | maxItems: 1 | ||
| 22 | |||
| 23 | clocks: | ||
| 24 | maxItems: 1 | ||
| 25 | |||
| 26 | clock-names: | ||
| 27 | items: | ||
| 28 | - const: extclk | ||
| 29 | |||
| 30 | clock-frequency: | ||
| 31 | default: 24000000 | ||
| 32 | description: extclk clock frequency | ||
| 33 | |||
| 34 | gpios: | ||
| 35 | maxItems: 1 | ||
| 36 | description: GPIO connected to the RESET pin | ||
| 37 | |||
| 38 | afvdd-supply: | ||
| 39 | description: AF (actuator) voltage supply | ||
| 40 | |||
| 41 | svdda-supply: | ||
| 42 | description: Core voltage supply | ||
| 43 | |||
| 44 | svddio-supply: | ||
| 45 | description: I/O voltage supply | ||
| 46 | |||
| 47 | port: | ||
| 48 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 49 | additionalProperties: false | ||
| 50 | |||
| 51 | properties: | ||
| 52 | endpoint: | ||
| 53 | $ref: /schemas/media/video-interfaces.yaml# | ||
| 54 | unevaluatedProperties: false | ||
| 55 | |||
| 56 | properties: | ||
| 57 | data-lanes: | ||
| 58 | items: | ||
| 59 | - const: 1 | ||
| 60 | |||
| 61 | required: | ||
| 62 | - compatible | ||
| 63 | - clocks | ||
| 64 | - clock-names | ||
| 65 | - gpios | ||
| 66 | - afvdd-supply | ||
| 67 | - svdda-supply | ||
| 68 | - svddio-supply | ||
| 69 | |||
| 70 | additionalProperties: false | ||
| 71 | |||
| 72 | examples: | ||
| 73 | - | | ||
| 74 | #include <dt-bindings/gpio/gpio.h> | ||
| 75 | |||
| 76 | i2c { | ||
| 77 | #address-cells = <1>; | ||
| 78 | #size-cells = <0>; | ||
| 79 | |||
| 80 | sensor@10 { | ||
| 81 | compatible = "samsung,s5k6a3"; | ||
| 82 | reg = <0x10>; | ||
| 83 | clock-frequency = <24000000>; | ||
| 84 | clocks = <&camera 1>; | ||
| 85 | clock-names = "extclk"; | ||
| 86 | gpios = <&gpm1 6 GPIO_ACTIVE_LOW>; | ||
| 87 | afvdd-supply = <&ldo19_reg>; | ||
| 88 | svdda-supply = <&cam_io_reg>; | ||
| 89 | svddio-supply = <&ldo19_reg>; | ||
| 90 | |||
| 91 | port { | ||
| 92 | endpoint { | ||
| 93 | remote-endpoint = <&csis1_ep>; | ||
| 94 | data-lanes = <1>; | ||
| 95 | }; | ||
| 96 | }; | ||
| 97 | }; | ||
| 98 | }; | ||
| @@ -12,15 +12,26 @@ maintainers: · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | description: |- | 13 | description: |- |
| 14 | The Sony IMX290 is a 1/2.8-Inch CMOS Solid-state image sensor with Square | 14 | The Sony IMX290 is a 1/2.8-Inch CMOS Solid-state image sensor with Square |
| 15 | Pixel for Color Cameras. It is programmable through I2C and 4-wire | 15 | Pixel, available in either mono or colour variants. It is programmable |
| 16 | interfaces. The sensor output is available via CMOS logic parallel SDR | 16 | through I2C and 4-wire interfaces. |
| 17 | output, Low voltage LVDS DDR output and CSI-2 serial data output. The CSI-2 | 17 | |
| 18 | bus is the default. No bindings have been defined for the other busses. | 18 | The sensor output is available via CMOS logic parallel SDR output, Low voltage |
| 19 | LVDS DDR output and CSI-2 serial data output. The CSI-2 bus is the default. | ||
| 20 | No bindings have been defined for the other busses. | ||
| 21 | |||
| 22 | imx290lqr is the full model identifier for the colour variant. "sony,imx290" | ||
| 23 | is treated the same as this as it was the original compatible string. | ||
| 24 | imx290llr is the mono version of the sensor. | ||
| 19 | 25 | ||
| 20 | properties: | 26 | properties: |
| 21 | compatible: | 27 | compatible: |
| 22 | enum: | 28 | oneOf: |
| 23 | - sony,imx290 | 29 | - enum: |
| 30 | - sony,imx290lqr # Colour | ||
| 31 | - sony,imx290llr # Monochrome | ||
| 32 | - sony,imx327lqr # Colour | ||
| 33 | - const: sony,imx290 | ||
| 34 | deprecated: true | ||
| 24 | 35 | ||
| 25 | reg: | 36 | reg: |
| 26 | maxItems: 1 | 37 | maxItems: 1 |
| @@ -101,7 +112,7 @@ examples: · 74 unmodified lines | |||
| 101 | #size-cells = <0>; | 112 | #size-cells = <0>; |
| 102 | 113 | ||
| 103 | imx290: camera-sensor@1a { | 114 | imx290: camera-sensor@1a { |
| 104 | compatible = "sony,imx290"; | 115 | compatible = "sony,imx290lqr"; |
| 105 | reg = <0x1a>; | 116 | reg = <0x1a>; |
| 106 | 117 | ||
| 107 | pinctrl-names = "default"; | 118 | pinctrl-names = "default"; |
| @@ -82,7 +82,7 @@ examples: · 81 unmodified lines | |||
| 82 | imx334: endpoint { | 82 | imx334: endpoint { |
| 83 | remote-endpoint = <&cam>; | 83 | remote-endpoint = <&cam>; |
| 84 | data-lanes = <1 2 3 4>; | 84 | data-lanes = <1 2 3 4>; |
| 85 | link-frequencies = /bits/ 64 <891000000>; | 85 | link-frequencies = /bits/ 64 <891000000 445500000>; |
| 86 | }; | 86 | }; |
| 87 | }; | 87 | }; |
| 88 | }; | 88 | }; |
| @@ -27,7 +27,7 @@ properties: · 26 unmodified lines | |||
| 27 | maxItems: 1 | 27 | maxItems: 1 |
| 28 | 28 | ||
| 29 | mediatek,gce-client-reg: | 29 | mediatek,gce-client-reg: |
| 30 | $ref: '/schemas/types.yaml#/definitions/phandle-array' | 30 | $ref: /schemas/types.yaml#/definitions/phandle-array |
| 31 | items: | 31 | items: |
| 32 | items: | 32 | items: |
| 33 | - description: phandle of GCE | 33 | - description: phandle of GCE |
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/mediatek,vcodec-subdev-decoder.yaml#" | 5 | $id: http://devicetree.org/schemas/media/mediatek,vcodec-subdev-decoder.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Mediatek Video Decode Accelerator With Multi Hardware | 8 | title: Mediatek Video Decode Accelerator With Multi Hardware |
| 9 | 9 | ||
| @@ -61,7 +61,10 @@ properties: · 51 unmodified lines | |||
| 61 | - mediatek,mt8195-vcodec-dec | 61 | - mediatek,mt8195-vcodec-dec |
| 62 | 62 | ||
| 63 | reg: | 63 | reg: |
| 64 | maxItems: 1 | 64 | minItems: 1 |
| 65 | items: | ||
| 66 | - description: VDEC_SYS register space | ||
| 67 | - description: VDEC_RACING_CTRL register space | ||
| 65 | 68 | ||
| 66 | iommus: | 69 | iommus: |
| 67 | minItems: 1 | 70 | minItems: 1 |
| @@ -76,11 +79,6 @@ properties: · 8 unmodified lines | |||
| 76 | The node of system control processor (SCP), using | 79 | The node of system control processor (SCP), using |
| 77 | the remoteproc & rpmsg framework. | 80 | the remoteproc & rpmsg framework. |
| 78 | 81 | ||
| 79 | dma-ranges: | ||
| 80 | maxItems: 1 | ||
| 81 | description: | | ||
| 82 | Describes the physical address space of IOMMU maps to memory. | ||
| 83 | |||
| 84 | "#address-cells": | 82 | "#address-cells": |
| 85 | const: 2 | 83 | const: 2 |
| 86 | 84 | ||
| @@ -91,17 +89,19 @@ properties: · 4 unmodified lines | |||
| 91 | 89 | ||
| 92 | # Required child node: | 90 | # Required child node: |
| 93 | patternProperties: | 91 | patternProperties: |
| 94 | '^vcodec-lat@[0-9a-f]+$': | 92 | '^video-codec@[0-9a-f]+$': |
| 95 | type: object | 93 | type: object |
| 96 | 94 | ||
| 97 | properties: | 95 | properties: |
| 98 | compatible: | 96 | compatible: |
| 99 | enum: | 97 | enum: |
| 98 | - mediatek,mtk-vcodec-core | ||
| 100 | - mediatek,mtk-vcodec-lat | 99 | - mediatek,mtk-vcodec-lat |
| 101 | - mediatek,mtk-vcodec-lat-soc | 100 | - mediatek,mtk-vcodec-lat-soc |
| 102 | 101 | ||
| 103 | reg: | 102 | reg: |
| 104 | maxItems: 1 | 103 | maxItems: 1 |
| 104 | description: VDEC_MISC register space | ||
| 105 | 105 | ||
| 106 | interrupts: | 106 | interrupts: |
| 107 | maxItems: 1 | 107 | maxItems: 1 |
| @@ -114,68 +114,13 @@ patternProperties: · 6 unmodified lines | |||
| 114 | Refer to bindings/iommu/mediatek,iommu.yaml. | 114 | Refer to bindings/iommu/mediatek,iommu.yaml. |
| 115 | 115 | ||
| 116 | clocks: | 116 | clocks: |
| 117 | minItems: 4 | ||
| 117 | maxItems: 5 | 118 | maxItems: 5 |
| 118 | 119 | ||
| 119 | clock-names: | 120 | clock-names: |
| 120 | items: | 121 | minItems: 4 |
| 121 | - const: sel | ||
| 122 | - const: soc-vdec | ||
| 123 | - const: soc-lat | ||
| 124 | - const: vdec | ||
| 125 | - const: top | ||
| 126 | |||
| 127 | assigned-clocks: | ||
| 128 | maxItems: 1 | ||
| 129 | |||
| 130 | assigned-clock-parents: | ||
| 131 | maxItems: 1 | ||
| 132 | |||
| 133 | power-domains: | ||
| 134 | maxItems: 1 | ||
| 135 | |||
| 136 | required: | ||
| 137 | - compatible | ||
| 138 | - reg | ||
| 139 | - iommus | ||
| 140 | - clocks | ||
| 141 | - clock-names | ||
| 142 | - assigned-clocks | ||
| 143 | - assigned-clock-parents | ||
| 144 | - power-domains | ||
| 145 | |||
| 146 | additionalProperties: false | ||
| 147 | |||
| 148 | '^vcodec-core@[0-9a-f]+$': | ||
| 149 | type: object | ||
| 150 | |||
| 151 | properties: | ||
| 152 | compatible: | ||
| 153 | const: mediatek,mtk-vcodec-core | ||
| 154 | |||
| 155 | reg: | ||
| 156 | maxItems: 1 | ||
| 157 | |||
| 158 | interrupts: | ||
| 159 | maxItems: 1 | ||
| 160 | |||
| 161 | iommus: | ||
| 162 | minItems: 1 | ||
| 163 | maxItems: 32 | ||
| 164 | description: | | ||
| 165 | List of the hardware port in respective IOMMU block for current Socs. | ||
| 166 | Refer to bindings/iommu/mediatek,iommu.yaml. | ||
| 167 | |||
| 168 | clocks: | ||
| 169 | maxItems: 5 | 122 | maxItems: 5 |
| 170 | 123 | ||
| 171 | clock-names: | ||
| 172 | items: | ||
| 173 | - const: sel | ||
| 174 | - const: soc-vdec | ||
| 175 | - const: soc-lat | ||
| 176 | - const: vdec | ||
| 177 | - const: top | ||
| 178 | |||
| 179 | assigned-clocks: | 124 | assigned-clocks: |
| 180 | maxItems: 1 | 125 | maxItems: 1 |
| 181 | 126 | ||
| @@ -188,7 +133,6 @@ patternProperties: · 6 unmodified lines | |||
| 188 | required: | 133 | required: |
| 189 | - compatible | 134 | - compatible |
| 190 | - reg | 135 | - reg |
| 191 | - interrupts | ||
| 192 | - iommus | 136 | - iommus |
| 193 | - clocks | 137 | - clocks |
| 194 | - clock-names | 138 | - clock-names |
| @@ -203,7 +147,6 @@ required: · 8 unmodified lines | |||
| 203 | - reg | 147 | - reg |
| 204 | - iommus | 148 | - iommus |
| 205 | - mediatek,scp | 149 | - mediatek,scp |
| 206 | - dma-ranges | ||
| 207 | - ranges | 150 | - ranges |
| 208 | 151 | ||
| 209 | if: | 152 | if: |
| @@ -211,12 +154,45 @@ if: · 1 unmodified line | |||
| 211 | compatible: | 154 | compatible: |
| 212 | contains: | 155 | contains: |
| 213 | enum: | 156 | enum: |
| 157 | - mediatek,mtk-vcodec-core | ||
| 214 | - mediatek,mtk-vcodec-lat | 158 | - mediatek,mtk-vcodec-lat |
| 215 | 159 | ||
| 216 | then: | 160 | then: |
| 217 | required: | 161 | required: |
| 218 | - interrupts | 162 | - interrupts |
| 219 | 163 | ||
| 164 | allOf: | ||
| 165 | - if: | ||
| 166 | properties: | ||
| 167 | compatible: | ||
| 168 | contains: | ||
| 169 | enum: | ||
| 170 | - mediatek,mt8192-vcodec-dec | ||
| 171 | then: | ||
| 172 | properties: | ||
| 173 | clock-names: | ||
| 174 | items: | ||
| 175 | - const: sel | ||
| 176 | - const: soc-vdec | ||
| 177 | - const: soc-lat | ||
| 178 | - const: vdec | ||
| 179 | - const: top | ||
| 180 | |||
| 181 | - if: | ||
| 182 | properties: | ||
| 183 | compatible: | ||
| 184 | contains: | ||
| 185 | enum: | ||
| 186 | - mediatek,mt8195-vcodec-dec | ||
| 187 | then: | ||
| 188 | properties: | ||
| 189 | clock-names: | ||
| 190 | items: | ||
| 191 | - const: sel | ||
| 192 | - const: vdec | ||
| 193 | - const: lat | ||
| 194 | - const: top | ||
| 195 | |||
| 220 | additionalProperties: false | 196 | additionalProperties: false |
| 221 | 197 | ||
| 222 | examples: | 198 | examples: |
| @@ -236,12 +212,11 @@ examples: · 13 unmodified lines | |||
| 236 | compatible = "mediatek,mt8192-vcodec-dec"; | 212 | compatible = "mediatek,mt8192-vcodec-dec"; |
| 237 | mediatek,scp = <&scp>; | 213 | mediatek,scp = <&scp>; |
| 238 | iommus = <&iommu0 M4U_PORT_L4_VDEC_MC_EXT>; | 214 | iommus = <&iommu0 M4U_PORT_L4_VDEC_MC_EXT>; |
| 239 | dma-ranges = <0x1 0x0 0x0 0x40000000 0x0 0xfff00000>; | ||
| 240 | #address-cells = <2>; | 215 | #address-cells = <2>; |
| 241 | #size-cells = <2>; | 216 | #size-cells = <2>; |
| 242 | ranges = <0 0 0 0x16000000 0 0x40000>; | 217 | ranges = <0 0 0 0x16000000 0 0x40000>; |
| 243 | reg = <0 0x16000000 0 0x1000>; /* VDEC_SYS */ | 218 | reg = <0 0x16000000 0 0x1000>; /* VDEC_SYS */ |
| 244 | vcodec-lat@10000 { | 219 | video-codec@10000 { |
| 245 | compatible = "mediatek,mtk-vcodec-lat"; | 220 | compatible = "mediatek,mtk-vcodec-lat"; |
| 246 | reg = <0 0x10000 0 0x800>; | 221 | reg = <0 0x10000 0 0x800>; |
| 247 | interrupts = <GIC_SPI 426 IRQ_TYPE_LEVEL_HIGH 0>; | 222 | interrupts = <GIC_SPI 426 IRQ_TYPE_LEVEL_HIGH 0>; |
| @@ -264,7 +239,7 @@ examples: · 16 unmodified lines | |||
| 264 | power-domains = <&spm MT8192_POWER_DOMAIN_VDEC>; | 239 | power-domains = <&spm MT8192_POWER_DOMAIN_VDEC>; |
| 265 | }; | 240 | }; |
| 266 | 241 | ||
| 267 | vcodec-core@25000 { | 242 | video-codec@25000 { |
| 268 | compatible = "mediatek,mtk-vcodec-core"; | 243 | compatible = "mediatek,mtk-vcodec-core"; |
| 269 | reg = <0 0x25000 0 0x1000>; | 244 | reg = <0 0x25000 0 0x1000>; |
| 270 | interrupts = <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH 0>; | 245 | interrupts = <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH 0>; |
| @@ -1,20 +0,0 @@ | |||
| 1 | * Amlogic Meson IR remote control receiver | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : depending on the platform this should be one of: | ||
| 5 | - "amlogic,meson6-ir" | ||
| 6 | - "amlogic,meson8b-ir" | ||
| 7 | - "amlogic,meson-gxbb-ir" | ||
| 8 | - reg : physical base address and length of the device registers | ||
| 9 | - interrupts : a single specifier for the interrupt from the device | ||
| 10 | |||
| 11 | Optional properties: | ||
| 12 | - linux,rc-map-name: see rc.txt file in the same directory. | ||
| 13 | |||
| 14 | Example: | ||
| 15 | |||
| 16 | ir-receiver@c8100480 { | ||
| 17 | compatible= "amlogic,meson6-ir"; | ||
| 18 | reg = <0xc8100480 0x20>; | ||
| 19 | interrupts = <0 15 1>; | ||
| 20 | }; | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/microchip,sama5d4-vdec.yaml#" | 5 | $id: http://devicetree.org/schemas/media/microchip,sama5d4-vdec.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Hantro G1 VPU codec implemented on Microchip SAMA5D4 SoCs | 8 | title: Hantro G1 VPU codec implemented on Microchip SAMA5D4 SoCs |
| 9 | 9 | ||
| @@ -0,0 +1,173 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/nxp,imx8-isi.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: i.MX8 Image Sensing Interface | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 11 | |||
| 12 | description: | | ||
| 13 | The Image Sensing Interface (ISI) combines image processing pipelines with | ||
| 14 | DMA engines to process and capture frames originating from a variety of | ||
| 15 | sources. The inputs to the ISI go through Pixel Link interfaces, and their | ||
| 16 | number and nature is SoC-dependent. They cover both capture interfaces (MIPI | ||
| 17 | CSI-2 RX, HDMI RX, ...) and display engine outputs for writeback support. | ||
| 18 | |||
| 19 | properties: | ||
| 20 | compatible: | ||
| 21 | enum: | ||
| 22 | - fsl,imx8mn-isi | ||
| 23 | - fsl,imx8mp-isi | ||
| 24 | |||
| 25 | reg: | ||
| 26 | maxItems: 1 | ||
| 27 | |||
| 28 | clocks: | ||
| 29 | items: | ||
| 30 | - description: The AXI clock | ||
| 31 | - description: The APB clock | ||
| 32 | # TODO: Check if the per-channel ipg_proc_clk clocks need to be specified | ||
| 33 | # as well, in case some SoCs have the ability to control them separately. | ||
| 34 | # This may be the case of the i.MX8[DQ]X(P) | ||
| 35 | |||
| 36 | clock-names: | ||
| 37 | items: | ||
| 38 | - const: axi | ||
| 39 | - const: apb | ||
| 40 | |||
| 41 | fsl,blk-ctrl: | ||
| 42 | $ref: /schemas/types.yaml#/definitions/phandle | ||
| 43 | description: | ||
| 44 | A phandle referencing the block control that contains the CSIS to ISI | ||
| 45 | gasket. | ||
| 46 | |||
| 47 | interrupts: | ||
| 48 | description: Processing pipeline interrupts, one per pipeline | ||
| 49 | minItems: 1 | ||
| 50 | maxItems: 2 | ||
| 51 | |||
| 52 | power-domains: | ||
| 53 | maxItems: 1 | ||
| 54 | |||
| 55 | ports: | ||
| 56 | $ref: /schemas/graph.yaml#/properties/ports | ||
| 57 | description: | | ||
| 58 | Ports represent the Pixel Link inputs to the ISI. Their number and | ||
| 59 | assignment are model-dependent. Each port shall have a single endpoint. | ||
| 60 | |||
| 61 | required: | ||
| 62 | - compatible | ||
| 63 | - reg | ||
| 64 | - interrupts | ||
| 65 | - clocks | ||
| 66 | - clock-names | ||
| 67 | - fsl,blk-ctrl | ||
| 68 | - ports | ||
| 69 | |||
| 70 | allOf: | ||
| 71 | - if: | ||
| 72 | properties: | ||
| 73 | compatible: | ||
| 74 | contains: | ||
| 75 | const: fsl,imx8mn-isi | ||
| 76 | then: | ||
| 77 | properties: | ||
| 78 | interrupts: | ||
| 79 | maxItems: 1 | ||
| 80 | ports: | ||
| 81 | properties: | ||
| 82 | port@0: | ||
| 83 | description: MIPI CSI-2 RX | ||
| 84 | required: | ||
| 85 | - port@0 | ||
| 86 | |||
| 87 | - if: | ||
| 88 | properties: | ||
| 89 | compatible: | ||
| 90 | contains: | ||
| 91 | const: fsl,imx8mp-isi | ||
| 92 | then: | ||
| 93 | properties: | ||
| 94 | interrupts: | ||
| 95 | maxItems: 2 | ||
| 96 | ports: | ||
| 97 | properties: | ||
| 98 | port@0: | ||
| 99 | description: MIPI CSI-2 RX 0 | ||
| 100 | port@1: | ||
| 101 | description: MIPI CSI-2 RX 1 | ||
| 102 | required: | ||
| 103 | - port@0 | ||
| 104 | - port@1 | ||
| 105 | |||
| 106 | additionalProperties: false | ||
| 107 | |||
| 108 | examples: | ||
| 109 | - | | ||
| 110 | #include <dt-bindings/clock/imx8mn-clock.h> | ||
| 111 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 112 | #include <dt-bindings/interrupt-controller/irq.h> | ||
| 113 | #include <dt-bindings/power/imx8mn-power.h> | ||
| 114 | |||
| 115 | isi@32e20000 { | ||
| 116 | compatible = "fsl,imx8mn-isi"; | ||
| 117 | reg = <0x32e20000 0x100>; | ||
| 118 | interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>; | ||
| 119 | clocks = <&clk IMX8MN_CLK_DISP_AXI_ROOT>, | ||
| 120 | <&clk IMX8MN_CLK_DISP_APB_ROOT>; | ||
| 121 | clock-names = "axi", "apb"; | ||
| 122 | fsl,blk-ctrl = <&disp_blk_ctrl>; | ||
| 123 | power-domains = <&disp_blk_ctrl IMX8MN_DISPBLK_PD_ISI>; | ||
| 124 | |||
| 125 | ports { | ||
| 126 | #address-cells = <1>; | ||
| 127 | #size-cells = <0>; | ||
| 128 | |||
| 129 | port@0 { | ||
| 130 | reg = <0>; | ||
| 131 | isi_in: endpoint { | ||
| 132 | remote-endpoint = <&mipi_csi_out>; | ||
| 133 | }; | ||
| 134 | }; | ||
| 135 | }; | ||
| 136 | }; | ||
| 137 | |||
| 138 | - | | ||
| 139 | #include <dt-bindings/clock/imx8mp-clock.h> | ||
| 140 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 141 | #include <dt-bindings/interrupt-controller/irq.h> | ||
| 142 | |||
| 143 | isi@32e00000 { | ||
| 144 | compatible = "fsl,imx8mp-isi"; | ||
| 145 | reg = <0x32e00000 0x4000>; | ||
| 146 | interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>, | ||
| 147 | <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; | ||
| 148 | clocks = <&clk IMX8MP_CLK_MEDIA_AXI_ROOT>, | ||
| 149 | <&clk IMX8MP_CLK_MEDIA_APB_ROOT>; | ||
| 150 | clock-names = "axi", "apb"; | ||
| 151 | fsl,blk-ctrl = <&media_blk_ctrl>; | ||
| 152 | power-domains = <&mediamix_pd>; | ||
| 153 | |||
| 154 | ports { | ||
| 155 | #address-cells = <1>; | ||
| 156 | #size-cells = <0>; | ||
| 157 | |||
| 158 | port@0 { | ||
| 159 | reg = <0>; | ||
| 160 | isi_in_0: endpoint { | ||
| 161 | remote-endpoint = <&mipi_csi_0_out>; | ||
| 162 | }; | ||
| 163 | }; | ||
| 164 | |||
| 165 | port@1 { | ||
| 166 | reg = <1>; | ||
| 167 | isi_in_1: endpoint { | ||
| 168 | remote-endpoint = <&mipi_csi_1_out>; | ||
| 169 | }; | ||
| 170 | }; | ||
| 171 | }; | ||
| 172 | }; | ||
| 173 | ... | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/nxp,imx8mq-vpu.yaml#" | 5 | $id: http://devicetree.org/schemas/media/nxp,imx8mq-vpu.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Hantro G1/G2 VPU codecs implemented on i.MX8M SoCs | 8 | title: Hantro G1/G2 VPU codecs implemented on i.MX8M SoCs |
| 9 | 9 | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,msm8916-camss.yaml#" | 5 | $id: http://devicetree.org/schemas/media/qcom,msm8916-camss.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Qualcomm CAMSS ISP | 8 | title: Qualcomm CAMSS ISP |
| 9 | 9 | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,msm8916-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,msm8916-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm MSM8916 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,msm8916-venus | 21 | const: qcom,msm8916-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | maxItems: 1 | 24 | maxItems: 1 |
| 29 | 25 | ||
| @@ -39,9 +35,6 @@ properties: · 9 unmodified lines | |||
| 39 | iommus: | 35 | iommus: |
| 40 | maxItems: 1 | 36 | maxItems: 1 |
| 41 | 37 | ||
| 42 | memory-region: | ||
| 43 | maxItems: 1 | ||
| 44 | |||
| 45 | video-decoder: | 38 | video-decoder: |
| 46 | type: object | 39 | type: object |
| 47 | 40 | ||
| @@ -66,57 +59,36 @@ properties: · 18 unmodified lines | |||
| 66 | 59 | ||
| 67 | additionalProperties: false | 60 | additionalProperties: false |
| 68 | 61 | ||
| 69 | video-firmware: | ||
| 70 | type: object | ||
| 71 | additionalProperties: false | ||
| 72 | |||
| 73 | description: | | ||
| 74 | Firmware subnode is needed when the platform does not | ||
| 75 | have TrustZone. | ||
| 76 | |||
| 77 | properties: | ||
| 78 | iommus: | ||
| 79 | maxItems: 1 | ||
| 80 | |||
| 81 | required: | ||
| 82 | - iommus | ||
| 83 | |||
| 84 | required: | 62 | required: |
| 85 | - compatible | 63 | - compatible |
| 86 | - reg | ||
| 87 | - interrupts | ||
| 88 | - power-domains | ||
| 89 | - clocks | ||
| 90 | - clock-names | ||
| 91 | - iommus | 64 | - iommus |
| 92 | - memory-region | ||
| 93 | - video-decoder | 65 | - video-decoder |
| 94 | - video-encoder | 66 | - video-encoder |
| 95 | 67 | ||
| 96 | additionalProperties: false | 68 | unevaluatedProperties: false |
| 97 | 69 | ||
| 98 | examples: | 70 | examples: |
| 99 | - | | 71 | - | |
| 100 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 72 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 101 | #include <dt-bindings/clock/qcom,gcc-msm8916.h> | 73 | #include <dt-bindings/clock/qcom,gcc-msm8916.h> |
| 102 | 74 | ||
| 103 | video-codec@1d00000 { | 75 | video-codec@1d00000 { |
| 104 | compatible = "qcom,msm8916-venus"; | 76 | compatible = "qcom,msm8916-venus"; |
| 105 | reg = <0x01d00000 0xff000>; | 77 | reg = <0x01d00000 0xff000>; |
| 106 | interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; | 78 | interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; |
| 107 | clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, | 79 | clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, |
| 108 | <&gcc GCC_VENUS0_AHB_CLK>, | 80 | <&gcc GCC_VENUS0_AHB_CLK>, |
| 109 | <&gcc GCC_VENUS0_AXI_CLK>; | 81 | <&gcc GCC_VENUS0_AXI_CLK>; |
| 110 | clock-names = "core", "iface", "bus"; | 82 | clock-names = "core", "iface", "bus"; |
| 111 | power-domains = <&gcc VENUS_GDSC>; | 83 | power-domains = <&gcc VENUS_GDSC>; |
| 112 | iommus = <&apps_iommu 5>; | 84 | iommus = <&apps_iommu 5>; |
| 113 | memory-region = <&venus_mem>; | 85 | memory-region = <&venus_mem>; |
| 114 | 86 | ||
| 115 | video-decoder { | 87 | video-decoder { |
| 116 | compatible = "venus-decoder"; | 88 | compatible = "venus-decoder"; |
| 117 | }; | 89 | }; |
| 118 | 90 | ||
| 119 | video-encoder { | 91 | video-encoder { |
| 120 | compatible = "venus-encoder"; | 92 | compatible = "venus-encoder"; |
| 121 | }; | ||
| 122 | }; | 93 | }; |
| 94 | }; | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,msm8996-camss.yaml#" | 5 | $id: http://devicetree.org/schemas/media/qcom,msm8996-camss.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Qualcomm CAMSS ISP | 8 | title: Qualcomm CAMSS ISP |
| 9 | 9 | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,msm8996-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,msm8996-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm MSM8996 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,msm8996-venus | 21 | const: qcom,msm8996-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | maxItems: 1 | 24 | maxItems: 1 |
| 29 | 25 | ||
| @@ -37,12 +33,17 @@ properties: · 7 unmodified lines | |||
| 37 | - const: bus | 33 | - const: bus |
| 38 | - const: mbus | 34 | - const: mbus |
| 39 | 35 | ||
| 36 | interconnects: | ||
| 37 | maxItems: 2 | ||
| 38 | |||
| 39 | interconnect-names: | ||
| 40 | items: | ||
| 41 | - const: video-mem | ||
| 42 | - const: cpu-cfg | ||
| 43 | |||
| 40 | iommus: | 44 | iommus: |
| 41 | maxItems: 20 | 45 | maxItems: 20 |
| 42 | 46 | ||
| 43 | memory-region: | ||
| 44 | maxItems: 1 | ||
| 45 | |||
| 46 | video-decoder: | 47 | video-decoder: |
| 47 | type: object | 48 | type: object |
| 48 | 49 | ||
| @@ -93,83 +94,62 @@ properties: · 44 unmodified lines | |||
| 93 | 94 | ||
| 94 | additionalProperties: false | 95 | additionalProperties: false |
| 95 | 96 | ||
| 96 | video-firmware: | ||
| 97 | type: object | ||
| 98 | additionalProperties: false | ||
| 99 | |||
| 100 | description: | | ||
| 101 | Firmware subnode is needed when the platform does not | ||
| 102 | have TrustZone. | ||
| 103 | |||
| 104 | properties: | ||
| 105 | iommus: | ||
| 106 | maxItems: 1 | ||
| 107 | |||
| 108 | required: | ||
| 109 | - iommus | ||
| 110 | |||
| 111 | required: | 97 | required: |
| 112 | - compatible | 98 | - compatible |
| 113 | - reg | ||
| 114 | - interrupts | ||
| 115 | - power-domains | ||
| 116 | - clocks | ||
| 117 | - clock-names | ||
| 118 | - iommus | 99 | - iommus |
| 119 | - memory-region | ||
| 120 | - video-decoder | 100 | - video-decoder |
| 121 | - video-encoder | 101 | - video-encoder |
| 122 | 102 | ||
| 123 | additionalProperties: false | 103 | unevaluatedProperties: false |
| 124 | 104 | ||
| 125 | examples: | 105 | examples: |
| 126 | - | | 106 | - | |
| 127 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 107 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 128 | #include <dt-bindings/clock/qcom,mmcc-msm8996.h> | 108 | #include <dt-bindings/clock/qcom,mmcc-msm8996.h> |
| 129 | 109 | ||
| 130 | video-codec@c00000 { | 110 | video-codec@c00000 { |
| 131 | compatible = "qcom,msm8996-venus"; | 111 | compatible = "qcom,msm8996-venus"; |
| 132 | reg = <0x00c00000 0xff000>; | 112 | reg = <0x00c00000 0xff000>; |
| 133 | interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>; | 113 | interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>; |
| 134 | clocks = <&mmcc VIDEO_CORE_CLK>, | 114 | clocks = <&mmcc VIDEO_CORE_CLK>, |
| 135 | <&mmcc VIDEO_AHB_CLK>, | 115 | <&mmcc VIDEO_AHB_CLK>, |
| 136 | <&mmcc VIDEO_AXI_CLK>, | 116 | <&mmcc VIDEO_AXI_CLK>, |
| 137 | <&mmcc VIDEO_MAXI_CLK>; | 117 | <&mmcc VIDEO_MAXI_CLK>; |
| 138 | clock-names = "core", "iface", "bus", "mbus"; | 118 | clock-names = "core", "iface", "bus", "mbus"; |
| 139 | power-domains = <&mmcc VENUS_GDSC>; | 119 | power-domains = <&mmcc VENUS_GDSC>; |
| 140 | iommus = <&venus_smmu 0x00>, | 120 | iommus = <&venus_smmu 0x00>, |
| 141 | <&venus_smmu 0x01>, | 121 | <&venus_smmu 0x01>, |
| 142 | <&venus_smmu 0x0a>, | 122 | <&venus_smmu 0x0a>, |
| 143 | <&venus_smmu 0x07>, | 123 | <&venus_smmu 0x07>, |
| 144 | <&venus_smmu 0x0e>, | 124 | <&venus_smmu 0x0e>, |
| 145 | <&venus_smmu 0x0f>, | 125 | <&venus_smmu 0x0f>, |
| 146 | <&venus_smmu 0x08>, | 126 | <&venus_smmu 0x08>, |
| 147 | <&venus_smmu 0x09>, | 127 | <&venus_smmu 0x09>, |
| 148 | <&venus_smmu 0x0b>, | 128 | <&venus_smmu 0x0b>, |
| 149 | <&venus_smmu 0x0c>, | 129 | <&venus_smmu 0x0c>, |
| 150 | <&venus_smmu 0x0d>, | 130 | <&venus_smmu 0x0d>, |
| 151 | <&venus_smmu 0x10>, | 131 | <&venus_smmu 0x10>, |
| 152 | <&venus_smmu 0x11>, | 132 | <&venus_smmu 0x11>, |
| 153 | <&venus_smmu 0x21>, | 133 | <&venus_smmu 0x21>, |
| 154 | <&venus_smmu 0x28>, | 134 | <&venus_smmu 0x28>, |
| 155 | <&venus_smmu 0x29>, | 135 | <&venus_smmu 0x29>, |
| 156 | <&venus_smmu 0x2b>, | 136 | <&venus_smmu 0x2b>, |
| 157 | <&venus_smmu 0x2c>, | 137 | <&venus_smmu 0x2c>, |
| 158 | <&venus_smmu 0x2d>, | 138 | <&venus_smmu 0x2d>, |
| 159 | <&venus_smmu 0x31>; | 139 | <&venus_smmu 0x31>; |
| 160 | memory-region = <&venus_mem>; | 140 | memory-region = <&venus_mem>; |
| 161 | 141 | ||
| 162 | video-decoder { | 142 | video-decoder { |
| 163 | compatible = "venus-decoder"; | 143 | compatible = "venus-decoder"; |
| 164 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; | 144 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; |
| 165 | clock-names = "core"; | 145 | clock-names = "core"; |
| 166 | power-domains = <&mmcc VENUS_CORE0_GDSC>; | 146 | power-domains = <&mmcc VENUS_CORE0_GDSC>; |
| 167 | }; | 147 | }; |
| 168 | 148 | ||
| 169 | video-encoder { | 149 | video-encoder { |
| 170 | compatible = "venus-encoder"; | 150 | compatible = "venus-encoder"; |
| 171 | clocks = <&mmcc VIDEO_SUBCORE1_CLK>; | 151 | clocks = <&mmcc VIDEO_SUBCORE1_CLK>; |
| 172 | clock-names = "core"; | 152 | clock-names = "core"; |
| 173 | power-domains = <&mmcc VENUS_CORE1_GDSC>; | 153 | power-domains = <&mmcc VENUS_CORE1_GDSC>; |
| 174 | }; | ||
| 175 | }; | 154 | }; |
| 155 | }; | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sc7180-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sc7180-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SC7180 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,sc7180-venus | 21 | const: qcom,sc7180-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | minItems: 2 | 24 | minItems: 2 |
| 29 | maxItems: 3 | 25 | maxItems: 3 |
| @@ -60,6 +56,10 @@ properties: · 30 unmodified lines | |||
| 60 | - const: video-mem | 56 | - const: video-mem |
| 61 | - const: cpu-cfg | 57 | - const: cpu-cfg |
| 62 | 58 | ||
| 59 | operating-points-v2: true | ||
| 60 | opp-table: | ||
| 61 | type: object | ||
| 62 | |||
| 63 | video-decoder: | 63 | video-decoder: |
| 64 | type: object | 64 | type: object |
| 65 | 65 | ||
| @@ -84,63 +84,42 @@ properties: · 18 unmodified lines | |||
| 84 | 84 | ||
| 85 | additionalProperties: false | 85 | additionalProperties: false |
| 86 | 86 | ||
| 87 | video-firmware: | ||
| 88 | type: object | ||
| 89 | additionalProperties: false | ||
| 90 | |||
| 91 | description: | | ||
| 92 | Firmware subnode is needed when the platform does not | ||
| 93 | have TrustZone. | ||
| 94 | |||
| 95 | properties: | ||
| 96 | iommus: | ||
| 97 | maxItems: 1 | ||
| 98 | |||
| 99 | required: | ||
| 100 | - iommus | ||
| 101 | |||
| 102 | required: | 87 | required: |
| 103 | - compatible | 88 | - compatible |
| 104 | - reg | ||
| 105 | - interrupts | ||
| 106 | - power-domains | ||
| 107 | - power-domain-names | 89 | - power-domain-names |
| 108 | - clocks | ||
| 109 | - clock-names | ||
| 110 | - iommus | 90 | - iommus |
| 111 | - memory-region | ||
| 112 | - video-decoder | 91 | - video-decoder |
| 113 | - video-encoder | 92 | - video-encoder |
| 114 | 93 | ||
| 115 | additionalProperties: false | 94 | unevaluatedProperties: false |
| 116 | 95 | ||
| 117 | examples: | 96 | examples: |
| 118 | - | | 97 | - | |
| 119 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 98 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 120 | #include <dt-bindings/clock/qcom,videocc-sc7180.h> | 99 | #include <dt-bindings/clock/qcom,videocc-sc7180.h> |
| 121 | 100 | ||
| 122 | venus: video-codec@aa00000 { | 101 | venus: video-codec@aa00000 { |
| 123 | compatible = "qcom,sc7180-venus"; | 102 | compatible = "qcom,sc7180-venus"; |
| 124 | reg = <0x0aa00000 0xff000>; | 103 | reg = <0x0aa00000 0xff000>; |
| 125 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; | 104 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; |
| 126 | power-domains = <&videocc VENUS_GDSC>, | 105 | power-domains = <&videocc VENUS_GDSC>, |
| 127 | <&videocc VCODEC0_GDSC>; | 106 | <&videocc VCODEC0_GDSC>; |
| 128 | power-domain-names = "venus", "vcodec0"; | 107 | power-domain-names = "venus", "vcodec0"; |
| 129 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, | 108 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, |
| 130 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, | 109 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, |
| 131 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, | 110 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, |
| 132 | <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, | 111 | <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, |
| 133 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; | 112 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; |
| 134 | clock-names = "core", "iface", "bus", | 113 | clock-names = "core", "iface", "bus", |
| 135 | "vcodec0_core", "vcodec0_bus"; | 114 | "vcodec0_core", "vcodec0_bus"; |
| 136 | iommus = <&apps_smmu 0x0c00 0x60>; | 115 | iommus = <&apps_smmu 0x0c00 0x60>; |
| 137 | memory-region = <&venus_mem>; | 116 | memory-region = <&venus_mem>; |
| 138 | 117 | ||
| 139 | video-decoder { | 118 | video-decoder { |
| 140 | compatible = "venus-decoder"; | 119 | compatible = "venus-decoder"; |
| 141 | }; | 120 | }; |
| 142 | 121 | ||
| 143 | video-encoder { | 122 | video-encoder { |
| 144 | compatible = "venus-encoder"; | 123 | compatible = "venus-encoder"; |
| 145 | }; | ||
| 146 | }; | 124 | }; |
| 125 | }; | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sc7280-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sc7280-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SC7280 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus Iris2 IP is a video encode and decode accelerator present | 13 | The Venus Iris2 IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,sc7280-venus | 21 | const: qcom,sc7280-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | minItems: 2 | 24 | minItems: 2 |
| 29 | maxItems: 3 | 25 | maxItems: 3 |
| @@ -49,9 +45,6 @@ properties: · 19 unmodified lines | |||
| 49 | iommus: | 45 | iommus: |
| 50 | maxItems: 2 | 46 | maxItems: 2 |
| 51 | 47 | ||
| 52 | memory-region: | ||
| 53 | maxItems: 1 | ||
| 54 | |||
| 55 | interconnects: | 48 | interconnects: |
| 56 | maxItems: 2 | 49 | maxItems: 2 |
| 57 | 50 | ||
| @@ -60,6 +53,10 @@ properties: · 2 unmodified lines | |||
| 60 | - const: cpu-cfg | 53 | - const: cpu-cfg |
| 61 | - const: video-mem | 54 | - const: video-mem |
| 62 | 55 | ||
| 56 | operating-points-v2: true | ||
| 57 | opp-table: | ||
| 58 | type: object | ||
| 59 | |||
| 63 | video-decoder: | 60 | video-decoder: |
| 64 | type: object | 61 | type: object |
| 65 | 62 | ||
| @@ -84,79 +81,58 @@ properties: · 18 unmodified lines | |||
| 84 | 81 | ||
| 85 | additionalProperties: false | 82 | additionalProperties: false |
| 86 | 83 | ||
| 87 | video-firmware: | ||
| 88 | type: object | ||
| 89 | additionalProperties: false | ||
| 90 | |||
| 91 | description: | | ||
| 92 | Firmware subnode is needed when the platform does not | ||
| 93 | have TrustZone. | ||
| 94 | |||
| 95 | properties: | ||
| 96 | iommus: | ||
| 97 | maxItems: 1 | ||
| 98 | |||
| 99 | required: | ||
| 100 | - iommus | ||
| 101 | |||
| 102 | required: | 84 | required: |
| 103 | - compatible | 85 | - compatible |
| 104 | - reg | ||
| 105 | - interrupts | ||
| 106 | - power-domains | ||
| 107 | - power-domain-names | 86 | - power-domain-names |
| 108 | - clocks | ||
| 109 | - clock-names | ||
| 110 | - iommus | 87 | - iommus |
| 111 | - memory-region | ||
| 112 | - video-decoder | 88 | - video-decoder |
| 113 | - video-encoder | 89 | - video-encoder |
| 114 | 90 | ||
| 115 | additionalProperties: false | 91 | unevaluatedProperties: false |
| 116 | 92 | ||
| 117 | examples: | 93 | examples: |
| 118 | - | | 94 | - | |
| 119 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 95 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 120 | #include <dt-bindings/clock/qcom,videocc-sc7280.h> | 96 | #include <dt-bindings/clock/qcom,videocc-sc7280.h> |
| 121 | #include <dt-bindings/interconnect/qcom,sc7280.h> | 97 | #include <dt-bindings/interconnect/qcom,sc7280.h> |
| 122 | #include <dt-bindings/power/qcom-rpmpd.h> | 98 | #include <dt-bindings/power/qcom-rpmpd.h> |
| 123 | 99 | ||
| 124 | venus: video-codec@aa00000 { | 100 | venus: video-codec@aa00000 { |
| 125 | compatible = "qcom,sc7280-venus"; | 101 | compatible = "qcom,sc7280-venus"; |
| 126 | reg = <0x0aa00000 0xd0600>; | 102 | reg = <0x0aa00000 0xd0600>; |
| 127 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; | 103 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; |
| 128 | 104 | ||
| 129 | clocks = <&videocc VIDEO_CC_MVSC_CORE_CLK>, | 105 | clocks = <&videocc VIDEO_CC_MVSC_CORE_CLK>, |
| 130 | <&videocc VIDEO_CC_MVSC_CTL_AXI_CLK>, | 106 | <&videocc VIDEO_CC_MVSC_CTL_AXI_CLK>, |
| 131 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, | 107 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, |
| 132 | <&videocc VIDEO_CC_MVS0_CORE_CLK>, | 108 | <&videocc VIDEO_CC_MVS0_CORE_CLK>, |
| 133 | <&videocc VIDEO_CC_MVS0_AXI_CLK>; | 109 | <&videocc VIDEO_CC_MVS0_AXI_CLK>; |
| 134 | clock-names = "core", "bus", "iface", | 110 | clock-names = "core", "bus", "iface", |
| 135 | "vcodec_core", "vcodec_bus"; | 111 | "vcodec_core", "vcodec_bus"; |
| 136 | 112 | ||
| 137 | power-domains = <&videocc MVSC_GDSC>, | 113 | power-domains = <&videocc MVSC_GDSC>, |
| 138 | <&videocc MVS0_GDSC>, | 114 | <&videocc MVS0_GDSC>, |
| 139 | <&rpmhpd SC7280_CX>; | 115 | <&rpmhpd SC7280_CX>; |
| 140 | power-domain-names = "venus", "vcodec0", "cx"; | 116 | power-domain-names = "venus", "vcodec0", "cx"; |
| 141 | 117 | ||
| 142 | interconnects = <&gem_noc MASTER_APPSS_PROC 0 &cnoc2 SLAVE_VENUS_CFG 0>, | 118 | interconnects = <&gem_noc MASTER_APPSS_PROC 0 &cnoc2 SLAVE_VENUS_CFG 0>, |
| 143 | <&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>; | 119 | <&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>; |
| 144 | interconnect-names = "cpu-cfg", "video-mem"; | 120 | interconnect-names = "cpu-cfg", "video-mem"; |
| 145 | 121 | ||
| 146 | iommus = <&apps_smmu 0x2180 0x20>, | 122 | iommus = <&apps_smmu 0x2180 0x20>, |
| 147 | <&apps_smmu 0x2184 0x20>; | 123 | <&apps_smmu 0x2184 0x20>; |
| 148 | 124 | ||
| 149 | memory-region = <&video_mem>; | 125 | memory-region = <&video_mem>; |
| 150 | 126 | ||
| 151 | video-decoder { | 127 | video-decoder { |
| 152 | compatible = "venus-decoder"; | 128 | compatible = "venus-decoder"; |
| 153 | }; | 129 | }; |
| 154 | 130 | ||
| 155 | video-encoder { | 131 | video-encoder { |
| 156 | compatible = "venus-encoder"; | 132 | compatible = "venus-encoder"; |
| 157 | }; | 133 | }; |
| 158 | 134 | ||
| 159 | video-firmware { | 135 | video-firmware { |
| 160 | iommus = <&apps_smmu 0x21a2 0x0>; | 136 | iommus = <&apps_smmu 0x21a2 0x0>; |
| 161 | }; | ||
| 162 | }; | 137 | }; |
| 138 | }; | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sdm660-camss.yaml#" | 5 | $id: http://devicetree.org/schemas/media/qcom,sdm660-camss.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Qualcomm CAMSS ISP | 8 | title: Qualcomm CAMSS ISP |
| 9 | 9 | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sdm660-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sdm660-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SDM660 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -15,13 +14,13 @@ description: | · 3 unmodified lines | |||
| 15 | The Venus IP is a video encode and decode accelerator present | 14 | The Venus IP is a video encode and decode accelerator present |
| 16 | on Qualcomm platforms | 15 | on Qualcomm platforms |
| 17 | 16 | ||
| 17 | allOf: | ||
| 18 | - $ref: qcom,venus-common.yaml# | ||
| 19 | |||
| 18 | properties: | 20 | properties: |
| 19 | compatible: | 21 | compatible: |
| 20 | const: qcom,sdm660-venus | 22 | const: qcom,sdm660-venus |
| 21 | 23 | ||
| 22 | reg: | ||
| 23 | maxItems: 1 | ||
| 24 | |||
| 25 | clocks: | 24 | clocks: |
| 26 | maxItems: 4 | 25 | maxItems: 4 |
| 27 | 26 | ||
| @@ -40,15 +39,9 @@ properties: · 12 unmodified lines | |||
| 40 | - const: cpu-cfg | 39 | - const: cpu-cfg |
| 41 | - const: video-mem | 40 | - const: video-mem |
| 42 | 41 | ||
| 43 | interrupts: | ||
| 44 | maxItems: 1 | ||
| 45 | |||
| 46 | iommus: | 42 | iommus: |
| 47 | maxItems: 20 | 43 | maxItems: 20 |
| 48 | 44 | ||
| 49 | memory-region: | ||
| 50 | maxItems: 1 | ||
| 51 | |||
| 52 | power-domains: | 45 | power-domains: |
| 53 | maxItems: 1 | 46 | maxItems: 1 |
| 54 | 47 | ||
| @@ -102,86 +95,65 @@ properties: · 47 unmodified lines | |||
| 102 | 95 | ||
| 103 | additionalProperties: false | 96 | additionalProperties: false |
| 104 | 97 | ||
| 105 | video-firmware: | ||
| 106 | type: object | ||
| 107 | additionalProperties: false | ||
| 108 | |||
| 109 | description: | | ||
| 110 | Firmware subnode is needed when the platform does not | ||
| 111 | have TrustZone. | ||
| 112 | |||
| 113 | properties: | ||
| 114 | iommus: | ||
| 115 | maxItems: 1 | ||
| 116 | |||
| 117 | required: | ||
| 118 | - iommus | ||
| 119 | |||
| 120 | required: | 98 | required: |
| 121 | - compatible | 99 | - compatible |
| 122 | - reg | ||
| 123 | - clocks | ||
| 124 | - clock-names | ||
| 125 | - interrupts | ||
| 126 | - iommus | 100 | - iommus |
| 127 | - memory-region | ||
| 128 | - power-domains | ||
| 129 | - video-decoder | 101 | - video-decoder |
| 130 | - video-encoder | 102 | - video-encoder |
| 131 | 103 | ||
| 132 | additionalProperties: false | 104 | unevaluatedProperties: false |
| 133 | 105 | ||
| 134 | examples: | 106 | examples: |
| 135 | - | | 107 | - | |
| 136 | #include <dt-bindings/clock/qcom,mmcc-sdm660.h> | 108 | #include <dt-bindings/clock/qcom,mmcc-sdm660.h> |
| 137 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 109 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 138 | 110 | ||
| 139 | video-codec@cc00000 { | 111 | video-codec@cc00000 { |
| 140 | compatible = "qcom,sdm660-venus"; | 112 | compatible = "qcom,sdm660-venus"; |
| 141 | reg = <0x0cc00000 0xff000>; | 113 | reg = <0x0cc00000 0xff000>; |
| 142 | clocks = <&mmcc VIDEO_CORE_CLK>, | 114 | clocks = <&mmcc VIDEO_CORE_CLK>, |
| 143 | <&mmcc VIDEO_AHB_CLK>, | 115 | <&mmcc VIDEO_AHB_CLK>, |
| 144 | <&mmcc VIDEO_AXI_CLK>, | 116 | <&mmcc VIDEO_AXI_CLK>, |
| 145 | <&mmcc THROTTLE_VIDEO_AXI_CLK>; | 117 | <&mmcc THROTTLE_VIDEO_AXI_CLK>; |
| 146 | clock-names = "core", "iface", "bus", "bus_throttle"; | 118 | clock-names = "core", "iface", "bus", "bus_throttle"; |
| 147 | interconnects = <&gnoc 0 &mnoc 13>, | 119 | interconnects = <&gnoc 0 &mnoc 13>, |
| 148 | <&mnoc 4 &bimc 5>; | 120 | <&mnoc 4 &bimc 5>; |
| 149 | interconnect-names = "cpu-cfg", "video-mem"; | 121 | interconnect-names = "cpu-cfg", "video-mem"; |
| 150 | interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>; | 122 | interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>; |
| 151 | iommus = <&mmss_smmu 0x400>, | 123 | iommus = <&mmss_smmu 0x400>, |
| 152 | <&mmss_smmu 0x401>, | 124 | <&mmss_smmu 0x401>, |
| 153 | <&mmss_smmu 0x40a>, | 125 | <&mmss_smmu 0x40a>, |
| 154 | <&mmss_smmu 0x407>, | 126 | <&mmss_smmu 0x407>, |
| 155 | <&mmss_smmu 0x40e>, | 127 | <&mmss_smmu 0x40e>, |
| 156 | <&mmss_smmu 0x40f>, | 128 | <&mmss_smmu 0x40f>, |
| 157 | <&mmss_smmu 0x408>, | 129 | <&mmss_smmu 0x408>, |
| 158 | <&mmss_smmu 0x409>, | 130 | <&mmss_smmu 0x409>, |
| 159 | <&mmss_smmu 0x40b>, | 131 | <&mmss_smmu 0x40b>, |
| 160 | <&mmss_smmu 0x40c>, | 132 | <&mmss_smmu 0x40c>, |
| 161 | <&mmss_smmu 0x40d>, | 133 | <&mmss_smmu 0x40d>, |
| 162 | <&mmss_smmu 0x410>, | 134 | <&mmss_smmu 0x410>, |
| 163 | <&mmss_smmu 0x421>, | 135 | <&mmss_smmu 0x421>, |
| 164 | <&mmss_smmu 0x428>, | 136 | <&mmss_smmu 0x428>, |
| 165 | <&mmss_smmu 0x429>, | 137 | <&mmss_smmu 0x429>, |
| 166 | <&mmss_smmu 0x42b>, | 138 | <&mmss_smmu 0x42b>, |
| 167 | <&mmss_smmu 0x42c>, | 139 | <&mmss_smmu 0x42c>, |
| 168 | <&mmss_smmu 0x42d>, | 140 | <&mmss_smmu 0x42d>, |
| 169 | <&mmss_smmu 0x411>, | 141 | <&mmss_smmu 0x411>, |
| 170 | <&mmss_smmu 0x431>; | 142 | <&mmss_smmu 0x431>; |
| 171 | memory-region = <&venus_region>; | 143 | memory-region = <&venus_region>; |
| 172 | power-domains = <&mmcc VENUS_GDSC>; | 144 | power-domains = <&mmcc VENUS_GDSC>; |
| 173 | 145 | ||
| 174 | video-decoder { | 146 | video-decoder { |
| 175 | compatible = "venus-decoder"; | 147 | compatible = "venus-decoder"; |
| 176 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; | 148 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; |
| 177 | clock-names = "vcodec0_core"; | 149 | clock-names = "vcodec0_core"; |
| 178 | power-domains = <&mmcc VENUS_CORE0_GDSC>; | 150 | power-domains = <&mmcc VENUS_CORE0_GDSC>; |
| 179 | }; | 151 | }; |
| 180 | 152 | ||
| 181 | video-encoder { | 153 | video-encoder { |
| 182 | compatible = "venus-encoder"; | 154 | compatible = "venus-encoder"; |
| 183 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; | 155 | clocks = <&mmcc VIDEO_SUBCORE0_CLK>; |
| 184 | clock-names = "vcodec0_core"; | 156 | clock-names = "vcodec0_core"; |
| 185 | power-domains = <&mmcc VENUS_CORE0_GDSC>; | 157 | power-domains = <&mmcc VENUS_CORE0_GDSC>; |
| 186 | }; | ||
| 187 | }; | 158 | }; |
| 159 | }; | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sdm845-camss.yaml#" | 5 | $id: http://devicetree.org/schemas/media/qcom,sdm845-camss.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Qualcomm CAMSS ISP | 8 | title: Qualcomm CAMSS ISP |
| 9 | 9 | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sdm845-venus-v2.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sdm845-venus-v2.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SDM845 Venus v2 video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,sdm845-venus-v2 | 21 | const: qcom,sdm845-venus-v2 |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | minItems: 3 | 24 | minItems: 3 |
| 29 | maxItems: 4 | 25 | maxItems: 4 |
| @@ -52,8 +48,9 @@ properties: · 22 unmodified lines | |||
| 52 | iommus: | 48 | iommus: |
| 53 | maxItems: 2 | 49 | maxItems: 2 |
| 54 | 50 | ||
| 55 | memory-region: | 51 | operating-points-v2: true |
| 56 | maxItems: 1 | 52 | opp-table: |
| 53 | type: object | ||
| 57 | 54 | ||
| 58 | video-core0: | 55 | video-core0: |
| 59 | type: object | 56 | type: object |
| @@ -79,68 +76,47 @@ properties: · 19 unmodified lines | |||
| 79 | 76 | ||
| 80 | additionalProperties: false | 77 | additionalProperties: false |
| 81 | 78 | ||
| 82 | video-firmware: | ||
| 83 | type: object | ||
| 84 | additionalProperties: false | ||
| 85 | |||
| 86 | description: | | ||
| 87 | Firmware subnode is needed when the platform does not | ||
| 88 | have TrustZone. | ||
| 89 | |||
| 90 | properties: | ||
| 91 | iommus: | ||
| 92 | maxItems: 1 | ||
| 93 | |||
| 94 | required: | ||
| 95 | - iommus | ||
| 96 | |||
| 97 | required: | 79 | required: |
| 98 | - compatible | 80 | - compatible |
| 99 | - reg | ||
| 100 | - interrupts | ||
| 101 | - power-domains | ||
| 102 | - power-domain-names | 81 | - power-domain-names |
| 103 | - clocks | ||
| 104 | - clock-names | ||
| 105 | - iommus | 82 | - iommus |
| 106 | - memory-region | ||
| 107 | - video-core0 | 83 | - video-core0 |
| 108 | - video-core1 | 84 | - video-core1 |
| 109 | 85 | ||
| 110 | additionalProperties: false | 86 | unevaluatedProperties: false |
| 111 | 87 | ||
| 112 | examples: | 88 | examples: |
| 113 | - | | 89 | - | |
| 114 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 90 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 115 | #include <dt-bindings/clock/qcom,videocc-sdm845.h> | 91 | #include <dt-bindings/clock/qcom,videocc-sdm845.h> |
| 116 | 92 | ||
| 117 | video-codec@aa00000 { | 93 | video-codec@aa00000 { |
| 118 | compatible = "qcom,sdm845-venus-v2"; | 94 | compatible = "qcom,sdm845-venus-v2"; |
| 119 | reg = <0x0aa00000 0xff000>; | 95 | reg = <0x0aa00000 0xff000>; |
| 120 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; | 96 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; |
| 121 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, | 97 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, |
| 122 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, | 98 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, |
| 123 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, | 99 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, |
| 124 | <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, | 100 | <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, |
| 125 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>, | 101 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>, |
| 126 | <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, | 102 | <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, |
| 127 | <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; | 103 | <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; |
| 128 | clock-names = "core", "iface", "bus", | 104 | clock-names = "core", "iface", "bus", |
| 129 | "vcodec0_core", "vcodec0_bus", | 105 | "vcodec0_core", "vcodec0_bus", |
| 130 | "vcodec1_core", "vcodec1_bus"; | 106 | "vcodec1_core", "vcodec1_bus"; |
| 131 | power-domains = <&videocc VENUS_GDSC>, | 107 | power-domains = <&videocc VENUS_GDSC>, |
| 132 | <&videocc VCODEC0_GDSC>, | 108 | <&videocc VCODEC0_GDSC>, |
| 133 | <&videocc VCODEC1_GDSC>; | 109 | <&videocc VCODEC1_GDSC>; |
| 134 | power-domain-names = "venus", "vcodec0", "vcodec1"; | 110 | power-domain-names = "venus", "vcodec0", "vcodec1"; |
| 135 | iommus = <&apps_smmu 0x10a0 0x8>, | 111 | iommus = <&apps_smmu 0x10a0 0x8>, |
| 136 | <&apps_smmu 0x10b0 0x0>; | 112 | <&apps_smmu 0x10b0 0x0>; |
| 137 | memory-region = <&venus_mem>; | 113 | memory-region = <&venus_mem>; |
| 138 | 114 | ||
| 139 | video-core0 { | 115 | video-core0 { |
| 140 | compatible = "venus-decoder"; | 116 | compatible = "venus-decoder"; |
| 141 | }; | 117 | }; |
| 142 | 118 | ||
| 143 | video-core1 { | 119 | video-core1 { |
| 144 | compatible = "venus-encoder"; | 120 | compatible = "venus-encoder"; |
| 145 | }; | ||
| 146 | }; | 121 | }; |
| 122 | }; | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sdm845-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sdm845-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SDM845 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,sdm845-venus | 21 | const: qcom,sdm845-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | maxItems: 1 | 24 | maxItems: 1 |
| 29 | 25 | ||
| @@ -39,9 +35,6 @@ properties: · 9 unmodified lines | |||
| 39 | iommus: | 35 | iommus: |
| 40 | maxItems: 2 | 36 | maxItems: 2 |
| 41 | 37 | ||
| 42 | memory-region: | ||
| 43 | maxItems: 1 | ||
| 44 | |||
| 45 | video-core0: | 38 | video-core0: |
| 46 | type: object | 39 | type: object |
| 47 | 40 | ||
| @@ -94,66 +87,45 @@ properties: · 46 unmodified lines | |||
| 94 | 87 | ||
| 95 | additionalProperties: false | 88 | additionalProperties: false |
| 96 | 89 | ||
| 97 | video-firmware: | ||
| 98 | type: object | ||
| 99 | additionalProperties: false | ||
| 100 | |||
| 101 | description: | | ||
| 102 | Firmware subnode is needed when the platform does not | ||
| 103 | have TrustZone. | ||
| 104 | |||
| 105 | properties: | ||
| 106 | iommus: | ||
| 107 | maxItems: 1 | ||
| 108 | |||
| 109 | required: | ||
| 110 | - iommus | ||
| 111 | |||
| 112 | required: | 90 | required: |
| 113 | - compatible | 91 | - compatible |
| 114 | - reg | ||
| 115 | - interrupts | ||
| 116 | - power-domains | ||
| 117 | - clocks | ||
| 118 | - clock-names | ||
| 119 | - iommus | 92 | - iommus |
| 120 | - memory-region | ||
| 121 | - video-core0 | 93 | - video-core0 |
| 122 | - video-core1 | 94 | - video-core1 |
| 123 | 95 | ||
| 124 | additionalProperties: false | 96 | unevaluatedProperties: false |
| 125 | 97 | ||
| 126 | examples: | 98 | examples: |
| 127 | - | | 99 | - | |
| 128 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 100 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 129 | #include <dt-bindings/clock/qcom,videocc-sdm845.h> | 101 | #include <dt-bindings/clock/qcom,videocc-sdm845.h> |
| 130 | 102 | ||
| 131 | video-codec@aa00000 { | 103 | video-codec@aa00000 { |
| 132 | compatible = "qcom,sdm845-venus"; | 104 | compatible = "qcom,sdm845-venus"; |
| 133 | reg = <0x0aa00000 0xff000>; | 105 | reg = <0x0aa00000 0xff000>; |
| 134 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; | 106 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; |
| 135 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, | 107 | clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, |
| 136 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, | 108 | <&videocc VIDEO_CC_VENUS_AHB_CLK>, |
| 137 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>; | 109 | <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>; |
| 138 | clock-names = "core", "iface", "bus"; | 110 | clock-names = "core", "iface", "bus"; |
| 139 | power-domains = <&videocc VENUS_GDSC>; | 111 | power-domains = <&videocc VENUS_GDSC>; |
| 140 | iommus = <&apps_smmu 0x10a0 0x8>, | 112 | iommus = <&apps_smmu 0x10a0 0x8>, |
| 141 | <&apps_smmu 0x10b0 0x0>; | 113 | <&apps_smmu 0x10b0 0x0>; |
| 142 | memory-region = <&venus_mem>; | 114 | memory-region = <&venus_mem>; |
| 143 | 115 | ||
| 144 | video-core0 { | 116 | video-core0 { |
| 145 | compatible = "venus-decoder"; | 117 | compatible = "venus-decoder"; |
| 146 | clocks = <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, | 118 | clocks = <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, |
| 147 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; | 119 | <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; |
| 148 | clock-names = "core", "bus"; | 120 | clock-names = "core", "bus"; |
| 149 | power-domains = <&videocc VCODEC0_GDSC>; | 121 | power-domains = <&videocc VCODEC0_GDSC>; |
| 150 | }; | 122 | }; |
| 151 | 123 | ||
| 152 | video-core1 { | 124 | video-core1 { |
| 153 | compatible = "venus-encoder"; | 125 | compatible = "venus-encoder"; |
| 154 | clocks = <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, | 126 | clocks = <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, |
| 155 | <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; | 127 | <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; |
| 156 | clock-names = "core", "bus"; | 128 | clock-names = "core", "bus"; |
| 157 | power-domains = <&videocc VCODEC1_GDSC>; | 129 | power-domains = <&videocc VCODEC1_GDSC>; |
| 158 | }; | ||
| 159 | }; | 130 | }; |
| 131 | }; | ||
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sm8250-camss.yaml#" | 5 | $id: http://devicetree.org/schemas/media/qcom,sm8250-camss.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Qualcomm CAMSS ISP | 8 | title: Qualcomm CAMSS ISP |
| 9 | 9 | ||
| @@ -1,11 +1,10 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
| 2 | |||
| 3 | %YAML 1.2 | 2 | %YAML 1.2 |
| 4 | --- | 3 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/qcom,sm8250-venus.yaml#" | 4 | $id: http://devicetree.org/schemas/media/qcom,sm8250-venus.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 6 | ||
| 8 | title: Qualcomm Venus video encode and decode accelerators | 7 | title: Qualcomm SM8250 Venus video encode and decode accelerators |
| 9 | 8 | ||
| 10 | maintainers: | 9 | maintainers: |
| 11 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> | 10 | - Stanimir Varbanov <stanimir.varbanov@linaro.org> |
| @@ -14,16 +13,13 @@ description: | · 2 unmodified lines | |||
| 14 | The Venus IP is a video encode and decode accelerator present | 13 | The Venus IP is a video encode and decode accelerator present |
| 15 | on Qualcomm platforms | 14 | on Qualcomm platforms |
| 16 | 15 | ||
| 16 | allOf: | ||
| 17 | - $ref: qcom,venus-common.yaml# | ||
| 18 | |||
| 17 | properties: | 19 | properties: |
| 18 | compatible: | 20 | compatible: |
| 19 | const: qcom,sm8250-venus | 21 | const: qcom,sm8250-venus |
| 20 | 22 | ||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | interrupts: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | power-domains: | 23 | power-domains: |
| 28 | minItems: 2 | 24 | minItems: 2 |
| 29 | maxItems: 3 | 25 | maxItems: 3 |
| @@ -47,9 +43,6 @@ properties: · 17 unmodified lines | |||
| 47 | iommus: | 43 | iommus: |
| 48 | maxItems: 1 | 44 | maxItems: 1 |
| 49 | 45 | ||
| 50 | memory-region: | ||
| 51 | maxItems: 1 | ||
| 52 | |||
| 53 | interconnects: | 46 | interconnects: |
| 54 | maxItems: 2 | 47 | maxItems: 2 |
| 55 | 48 | ||
| @@ -58,6 +51,10 @@ properties: · 2 unmodified lines | |||
| 58 | - const: cpu-cfg | 51 | - const: cpu-cfg |
| 59 | - const: video-mem | 52 | - const: video-mem |
| 60 | 53 | ||
| 54 | operating-points-v2: true | ||
| 55 | opp-table: | ||
| 56 | type: object | ||
| 57 | |||
| 61 | resets: | 58 | resets: |
| 62 | maxItems: 2 | 59 | maxItems: 2 |
| 63 | 60 | ||
| @@ -90,78 +87,57 @@ properties: · 26 unmodified lines | |||
| 90 | 87 | ||
| 91 | additionalProperties: false | 88 | additionalProperties: false |
| 92 | 89 | ||
| 93 | video-firmware: | ||
| 94 | type: object | ||
| 95 | additionalProperties: false | ||
| 96 | |||
| 97 | description: | | ||
| 98 | Firmware subnode is needed when the platform does not | ||
| 99 | have TrustZone. | ||
| 100 | |||
| 101 | properties: | ||
| 102 | iommus: | ||
| 103 | maxItems: 1 | ||
| 104 | |||
| 105 | required: | ||
| 106 | - iommus | ||
| 107 | |||
| 108 | required: | 90 | required: |
| 109 | - compatible | 91 | - compatible |
| 110 | - reg | ||
| 111 | - interrupts | ||
| 112 | - power-domains | ||
| 113 | - power-domain-names | 92 | - power-domain-names |
| 114 | - clocks | ||
| 115 | - clock-names | ||
| 116 | - interconnects | 93 | - interconnects |
| 117 | - interconnect-names | 94 | - interconnect-names |
| 118 | - iommus | 95 | - iommus |
| 119 | - memory-region | ||
| 120 | - resets | 96 | - resets |
| 121 | - reset-names | 97 | - reset-names |
| 122 | - video-decoder | 98 | - video-decoder |
| 123 | - video-encoder | 99 | - video-encoder |
| 124 | 100 | ||
| 125 | additionalProperties: false | 101 | unevaluatedProperties: false |
| 126 | 102 | ||
| 127 | examples: | 103 | examples: |
| 128 | - | | 104 | - | |
| 129 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 105 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
| 130 | #include <dt-bindings/clock/qcom,videocc-sm8250.h> | 106 | #include <dt-bindings/clock/qcom,videocc-sm8250.h> |
| 131 | #include <dt-bindings/interconnect/qcom,sm8250.h> | 107 | #include <dt-bindings/interconnect/qcom,sm8250.h> |
| 132 | #include <dt-bindings/clock/qcom,gcc-sm8250.h> | 108 | #include <dt-bindings/clock/qcom,gcc-sm8250.h> |
| 133 | #include <dt-bindings/power/qcom-rpmpd.h> | 109 | #include <dt-bindings/power/qcom-rpmpd.h> |
| 134 | 110 | ||
| 135 | venus: video-codec@aa00000 { | 111 | venus: video-codec@aa00000 { |
| 136 | compatible = "qcom,sm8250-venus"; | 112 | compatible = "qcom,sm8250-venus"; |
| 137 | reg = <0x0aa00000 0xff000>; | 113 | reg = <0x0aa00000 0xff000>; |
| 138 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; | 114 | interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; |
| 139 | power-domains = <&videocc MVS0C_GDSC>, | 115 | power-domains = <&videocc MVS0C_GDSC>, |
| 140 | <&videocc MVS0_GDSC>, | 116 | <&videocc MVS0_GDSC>, |
| 141 | <&rpmhpd SM8250_MX>; | 117 | <&rpmhpd SM8250_MX>; |
| 142 | power-domain-names = "venus", "vcodec0", "mx"; | 118 | power-domain-names = "venus", "vcodec0", "mx"; |
| 143 | 119 | ||
| 144 | clocks = <&gcc GCC_VIDEO_AXI0_CLK>, | 120 | clocks = <&gcc GCC_VIDEO_AXI0_CLK>, |
| 145 | <&videocc VIDEO_CC_MVS0C_CLK>, | 121 | <&videocc VIDEO_CC_MVS0C_CLK>, |
| 146 | <&videocc VIDEO_CC_MVS0_CLK>; | 122 | <&videocc VIDEO_CC_MVS0_CLK>; |
| 147 | clock-names = "iface", "core", "vcodec0_core"; | 123 | clock-names = "iface", "core", "vcodec0_core"; |
| 148 | 124 | ||
| 149 | interconnects = <&gem_noc MASTER_AMPSS_M0 &config_noc SLAVE_VENUS_CFG>, | 125 | interconnects = <&gem_noc MASTER_AMPSS_M0 &config_noc SLAVE_VENUS_CFG>, |
| 150 | <&mmss_noc MASTER_VIDEO_P0 &mc_virt SLAVE_EBI_CH0>; | 126 | <&mmss_noc MASTER_VIDEO_P0 &mc_virt SLAVE_EBI_CH0>; |
| 151 | interconnect-names = "cpu-cfg", "video-mem"; | 127 | interconnect-names = "cpu-cfg", "video-mem"; |
| 152 | 128 | ||
| 153 | iommus = <&apps_smmu 0x2100 0x0400>; | 129 | iommus = <&apps_smmu 0x2100 0x0400>; |
| 154 | memory-region = <&video_mem>; | 130 | memory-region = <&video_mem>; |
| 155 | 131 | ||
| 156 | resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>, | 132 | resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>, |
| 157 | <&videocc VIDEO_CC_MVS0C_CLK_ARES>; | 133 | <&videocc VIDEO_CC_MVS0C_CLK_ARES>; |
| 158 | reset-names = "bus", "core"; | 134 | reset-names = "bus", "core"; |
| 159 | 135 | ||
| 160 | video-decoder { | 136 | video-decoder { |
| 161 | compatible = "venus-decoder"; | 137 | compatible = "venus-decoder"; |
| 162 | }; | 138 | }; |
| 163 | 139 | ||
| 164 | video-encoder { | 140 | video-encoder { |
| 165 | compatible = "venus-encoder"; | 141 | compatible = "venus-encoder"; |
| 166 | }; | ||
| 167 | }; | 142 | }; |
| 143 | }; | ||
| @@ -0,0 +1,73 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/qcom,venus-common.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Qualcomm SoC Venus Video Encode and Decode Accelerators Common Properties | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Stanimir Varbanov <stanimir.k.varbanov@gmail.com> | ||
| 11 | - Vikash Garodia <quic_vgarodia@quicinc.com> | ||
| 12 | |||
| 13 | description: | | ||
| 14 | The Venus IP is a video encode and decode accelerator present | ||
| 15 | on Qualcomm platforms | ||
| 16 | |||
| 17 | properties: | ||
| 18 | reg: | ||
| 19 | maxItems: 1 | ||
| 20 | |||
| 21 | clocks: | ||
| 22 | minItems: 3 | ||
| 23 | maxItems: 7 | ||
| 24 | |||
| 25 | clock-names: | ||
| 26 | minItems: 3 | ||
| 27 | maxItems: 7 | ||
| 28 | |||
| 29 | firmware-name: | ||
| 30 | maxItems: 1 | ||
| 31 | |||
| 32 | interrupts: | ||
| 33 | maxItems: 1 | ||
| 34 | |||
| 35 | iommus: | ||
| 36 | minItems: 1 | ||
| 37 | maxItems: 20 | ||
| 38 | |||
| 39 | memory-region: | ||
| 40 | maxItems: 1 | ||
| 41 | |||
| 42 | power-domains: | ||
| 43 | minItems: 1 | ||
| 44 | maxItems: 4 | ||
| 45 | |||
| 46 | power-domain-names: | ||
| 47 | minItems: 1 | ||
| 48 | maxItems: 4 | ||
| 49 | |||
| 50 | video-firmware: | ||
| 51 | type: object | ||
| 52 | additionalProperties: false | ||
| 53 | |||
| 54 | description: | | ||
| 55 | Firmware subnode is needed when the platform does not | ||
| 56 | have TrustZone. | ||
| 57 | |||
| 58 | properties: | ||
| 59 | iommus: | ||
| 60 | maxItems: 1 | ||
| 61 | |||
| 62 | required: | ||
| 63 | - iommus | ||
| 64 | |||
| 65 | required: | ||
| 66 | - reg | ||
| 67 | - clocks | ||
| 68 | - clock-names | ||
| 69 | - interrupts | ||
| 70 | - memory-region | ||
| 71 | - power-domains | ||
| 72 | |||
| 73 | additionalProperties: true | ||
| @@ -18,7 +18,7 @@ properties: · 17 unmodified lines | |||
| 18 | description: | 18 | description: |
| 19 | Specifies the scancode/key mapping table defined in-kernel for | 19 | Specifies the scancode/key mapping table defined in-kernel for |
| 20 | the remote controller. | 20 | the remote controller. |
| 21 | $ref: '/schemas/types.yaml#/definitions/string' | 21 | $ref: /schemas/types.yaml#/definitions/string |
| 22 | enum: | 22 | enum: |
| 23 | - rc-adstech-dvb-t-pci | 23 | - rc-adstech-dvb-t-pci |
| 24 | - rc-alink-dtu-m | 24 | - rc-alink-dtu-m |
| @@ -39,6 +39,7 @@ properties: · 14 unmodified lines | |||
| 39 | - rc-avertv-303 | 39 | - rc-avertv-303 |
| 40 | - rc-azurewave-ad-tu700 | 40 | - rc-azurewave-ad-tu700 |
| 41 | - rc-beelink-gs1 | 41 | - rc-beelink-gs1 |
| 42 | - rc-beelink-mxiii | ||
| 42 | - rc-behold | 43 | - rc-behold |
| 43 | - rc-behold-columbus | 44 | - rc-behold-columbus |
| 44 | - rc-budget-ci-old | 45 | - rc-budget-ci-old |
| @@ -55,6 +56,7 @@ properties: · 10 unmodified lines | |||
| 55 | - rc-dm1105-nec | 56 | - rc-dm1105-nec |
| 56 | - rc-dntv-live-dvb-t | 57 | - rc-dntv-live-dvb-t |
| 57 | - rc-dntv-live-dvbt-pro | 58 | - rc-dntv-live-dvbt-pro |
| 59 | - rc-dreambox | ||
| 58 | - rc-dtt200u | 60 | - rc-dtt200u |
| 59 | - rc-dvbsky | 61 | - rc-dvbsky |
| 60 | - rc-dvico-mce | 62 | - rc-dvico-mce |
| @@ -31,6 +31,7 @@ properties: · 30 unmodified lines | |||
| 31 | - renesas,r8a77980-csi2 # R-Car V3H | 31 | - renesas,r8a77980-csi2 # R-Car V3H |
| 32 | - renesas,r8a77990-csi2 # R-Car E3 | 32 | - renesas,r8a77990-csi2 # R-Car E3 |
| 33 | - renesas,r8a779a0-csi2 # R-Car V3U | 33 | - renesas,r8a779a0-csi2 # R-Car V3U |
| 34 | - renesas,r8a779g0-csi2 # R-Car V4H | ||
| 34 | 35 | ||
| 35 | reg: | 36 | reg: |
| 36 | maxItems: 1 | 37 | maxItems: 1 |
| @@ -21,15 +21,22 @@ description: | · 20 unmodified lines | |||
| 21 | 21 | ||
| 22 | properties: | 22 | properties: |
| 23 | compatible: | 23 | compatible: |
| 24 | enum: | 24 | oneOf: |
| 25 | - renesas,fcpv # FCP for VSP | 25 | - enum: |
| 26 | - renesas,fcpf # FCP for FDP | 26 | - renesas,fcpv # FCP for VSP |
| 27 | - renesas,fcpf # FCP for FDP | ||
| 28 | - items: | ||
| 29 | - enum: | ||
| 30 | - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} | ||
| 31 | - renesas,r9a07g054-fcpvd # RZ/V2L | ||
| 32 | - const: renesas,fcpv # Generic FCP for VSP fallback | ||
| 27 | 33 | ||
| 28 | reg: | 34 | reg: |
| 29 | maxItems: 1 | 35 | maxItems: 1 |
| 30 | 36 | ||
| 31 | clocks: | 37 | clocks: true |
| 32 | maxItems: 1 | 38 | |
| 39 | clock-names: true | ||
| 33 | 40 | ||
| 34 | iommus: | 41 | iommus: |
| 35 | maxItems: 1 | 42 | maxItems: 1 |
| @@ -49,6 +56,34 @@ required: · 13 unmodified lines | |||
| 49 | 56 | ||
| 50 | additionalProperties: false | 57 | additionalProperties: false |
| 51 | 58 | ||
| 59 | allOf: | ||
| 60 | - if: | ||
| 61 | properties: | ||
| 62 | compatible: | ||
| 63 | contains: | ||
| 64 | enum: | ||
| 65 | - renesas,r9a07g044-fcpvd | ||
| 66 | - renesas,r9a07g054-fcpvd | ||
| 67 | then: | ||
| 68 | properties: | ||
| 69 | clocks: | ||
| 70 | items: | ||
| 71 | - description: Main clock | ||
| 72 | - description: Register access clock | ||
| 73 | - description: Video clock | ||
| 74 | clock-names: | ||
| 75 | items: | ||
| 76 | - const: aclk | ||
| 77 | - const: pclk | ||
| 78 | - const: vclk | ||
| 79 | required: | ||
| 80 | - clock-names | ||
| 81 | else: | ||
| 82 | properties: | ||
| 83 | clocks: | ||
| 84 | maxItems: 1 | ||
| 85 | clock-names: false | ||
| 86 | |||
| 52 | examples: | 87 | examples: |
| 53 | # R8A7795 (R-Car H3) FCP for VSP-D1 | 88 | # R8A7795 (R-Car H3) FCP for VSP-D1 |
| 54 | - | | 89 | - | |
| @@ -21,6 +21,7 @@ properties: · 20 unmodified lines | |||
| 21 | items: | 21 | items: |
| 22 | - enum: | 22 | - enum: |
| 23 | - renesas,r8a779a0-isp # V3U | 23 | - renesas,r8a779a0-isp # V3U |
| 24 | - renesas,r8a779g0-isp # V4H | ||
| 24 | reg: | 25 | reg: |
| 25 | maxItems: 1 | 26 | maxItems: 1 |
| 26 | 27 | ||
| @@ -53,6 +53,7 @@ properties: · 52 unmodified lines | |||
| 53 | - renesas,vin-r8a77990 # R-Car E3 | 53 | - renesas,vin-r8a77990 # R-Car E3 |
| 54 | - renesas,vin-r8a77995 # R-Car D3 | 54 | - renesas,vin-r8a77995 # R-Car D3 |
| 55 | - renesas,vin-r8a779a0 # R-Car V3U | 55 | - renesas,vin-r8a779a0 # R-Car V3U |
| 56 | - renesas,vin-r8a779g0 # R-Car V4H | ||
| 56 | 57 | ||
| 57 | reg: | 58 | reg: |
| 58 | maxItems: 1 | 59 | maxItems: 1 |
| @@ -16,10 +16,15 @@ description: · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | properties: | 17 | properties: |
| 18 | compatible: | 18 | compatible: |
| 19 | enum: | 19 | oneOf: |
| 20 | - renesas,r9a07g044-vsp2 # RZ/G2L | 20 | - enum: |
| 21 | - renesas,vsp1 # R-Car Gen2 and RZ/G1 | 21 | - renesas,r9a07g044-vsp2 # RZ/G2L |
| 22 | - renesas,vsp2 # R-Car Gen3 and RZ/G2 | 22 | - renesas,vsp1 # R-Car Gen2 and RZ/G1 |
| 23 | - renesas,vsp2 # R-Car Gen3 and RZ/G2 | ||
| 24 | - items: | ||
| 25 | - enum: | ||
| 26 | - renesas,r9a07g054-vsp2 # RZ/V2L | ||
| 27 | - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback | ||
| 23 | 28 | ||
| 24 | reg: | 29 | reg: |
| 25 | maxItems: 1 | 30 | maxItems: 1 |
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/rockchip,rk3568-vepu.yaml#" | 5 | $id: http://devicetree.org/schemas/media/rockchip,rk3568-vepu.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Hantro G1 VPU encoders implemented on Rockchip SoCs | 8 | title: Hantro G1 VPU encoders implemented on Rockchip SoCs |
| 9 | 9 | ||
| @@ -212,12 +212,19 @@ examples: · 211 unmodified lines | |||
| 212 | compatible = "ovti,ov2685"; | 212 | compatible = "ovti,ov2685"; |
| 213 | reg = <0x3c>; | 213 | reg = <0x3c>; |
| 214 | 214 | ||
| 215 | port { | 215 | clocks = <&cru SCLK_TESTCLKOUT1>; |
| 216 | ucam_out: endpoint { | 216 | clock-names = "xvclk"; |
| 217 | remote-endpoint = <&mipi_in_ucam>; | 217 | |
| 218 | data-lanes = <1>; | 218 | avdd-supply = <&pp2800_cam>; |
| 219 | }; | 219 | dovdd-supply = <&pp1800>; |
| 220 | }; | 220 | dvdd-supply = <&pp1800>; |
| 221 | |||
| 222 | port { | ||
| 223 | ucam_out: endpoint { | ||
| 224 | remote-endpoint = <&mipi_in_ucam>; | ||
| 225 | data-lanes = <1>; | ||
| 226 | }; | ||
| 227 | }; | ||
| 221 | }; | 228 | }; |
| 222 | }; | 229 | }; |
| 223 | }; | 230 | }; |
| @@ -2,8 +2,8 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | %YAML 1.2 | 3 | %YAML 1.2 |
| 4 | --- | 4 | --- |
| 5 | $id: "http://devicetree.org/schemas/media/rockchip-vpu.yaml#" | 5 | $id: http://devicetree.org/schemas/media/rockchip-vpu.yaml# |
| 6 | $schema: "http://devicetree.org/meta-schemas/core.yaml#" | 6 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 7 | 7 | ||
| 8 | title: Hantro G1 VPU codecs implemented on Rockchip SoCs | 8 | title: Hantro G1 VPU codecs implemented on Rockchip SoCs |
| 9 | 9 | ||
| @@ -0,0 +1,170 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,exynos4210-csis.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5P/Exynos SoC series MIPI CSI-2 receiver (MIPI CSIS) | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | properties: | ||
| 14 | compatible: | ||
| 15 | enum: | ||
| 16 | - samsung,s5pv210-csis | ||
| 17 | - samsung,exynos4210-csis | ||
| 18 | - samsung,exynos4212-csis | ||
| 19 | - samsung,exynos5250-csis | ||
| 20 | |||
| 21 | reg: | ||
| 22 | maxItems: 1 | ||
| 23 | |||
| 24 | '#address-cells': | ||
| 25 | const: 1 | ||
| 26 | |||
| 27 | '#size-cells': | ||
| 28 | const: 0 | ||
| 29 | |||
| 30 | bus-width: | ||
| 31 | $ref: /schemas/types.yaml#/definitions/uint32 | ||
| 32 | enum: [2, 4] | ||
| 33 | description: | ||
| 34 | Number of data lines supported. | ||
| 35 | |||
| 36 | clocks: | ||
| 37 | maxItems: 2 | ||
| 38 | |||
| 39 | clock-names: | ||
| 40 | items: | ||
| 41 | - const: csis | ||
| 42 | - const: sclk_csis | ||
| 43 | |||
| 44 | clock-frequency: | ||
| 45 | default: 166000000 | ||
| 46 | description: | ||
| 47 | The IP's main (system bus) clock frequency in Hz. | ||
| 48 | |||
| 49 | interrupts: | ||
| 50 | maxItems: 1 | ||
| 51 | |||
| 52 | phys: | ||
| 53 | maxItems: 1 | ||
| 54 | |||
| 55 | phy-names: | ||
| 56 | items: | ||
| 57 | - const: csis | ||
| 58 | |||
| 59 | power-domains: | ||
| 60 | maxItems: 1 | ||
| 61 | |||
| 62 | vddio-supply: | ||
| 63 | description: MIPI CSIS I/O and PLL voltage supply (e.g. 1.8V). | ||
| 64 | |||
| 65 | vddcore-supply: | ||
| 66 | description: MIPI CSIS Core voltage supply (e.g. 1.1V). | ||
| 67 | |||
| 68 | patternProperties: | ||
| 69 | "^port@[34]$": | ||
| 70 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 71 | additionalProperties: false | ||
| 72 | description: | ||
| 73 | Camera input port. | ||
| 74 | |||
| 75 | properties: | ||
| 76 | reg: | ||
| 77 | enum: [3, 4] | ||
| 78 | |||
| 79 | endpoint: | ||
| 80 | $ref: video-interfaces.yaml# | ||
| 81 | unevaluatedProperties: false | ||
| 82 | |||
| 83 | properties: | ||
| 84 | data-lanes: | ||
| 85 | minItems: 1 | ||
| 86 | maxItems: 4 | ||
| 87 | |||
| 88 | samsung,csis-hs-settle: | ||
| 89 | $ref: /schemas/types.yaml#/definitions/uint32 | ||
| 90 | description: Differential receiver (HS-RX) settle time. | ||
| 91 | |||
| 92 | samsung,csis-wclk: | ||
| 93 | type: boolean | ||
| 94 | description: | ||
| 95 | CSI-2 wrapper clock selection. If this property is present external clock | ||
| 96 | from CMU will be used, or the bus clock if it's not specified. | ||
| 97 | |||
| 98 | required: | ||
| 99 | - data-lanes | ||
| 100 | |||
| 101 | required: | ||
| 102 | - reg | ||
| 103 | |||
| 104 | required: | ||
| 105 | - compatible | ||
| 106 | - reg | ||
| 107 | - bus-width | ||
| 108 | - clocks | ||
| 109 | - clock-names | ||
| 110 | - interrupts | ||
| 111 | - vddio-supply | ||
| 112 | - vddcore-supply | ||
| 113 | |||
| 114 | anyOf: | ||
| 115 | - required: | ||
| 116 | - port@3 | ||
| 117 | - required: | ||
| 118 | - port@4 | ||
| 119 | |||
| 120 | allOf: | ||
| 121 | - if: | ||
| 122 | required: | ||
| 123 | - samsung,isp-wb | ||
| 124 | then: | ||
| 125 | required: | ||
| 126 | - samsung,sysreg | ||
| 127 | |||
| 128 | additionalProperties: false | ||
| 129 | |||
| 130 | examples: | ||
| 131 | - | | ||
| 132 | #include <dt-bindings/clock/exynos4.h> | ||
| 133 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 134 | |||
| 135 | csis@11890000 { | ||
| 136 | compatible = "samsung,exynos4210-csis"; | ||
| 137 | reg = <0x11890000 0x4000>; | ||
| 138 | clocks = <&clock CLK_CSIS1>, | ||
| 139 | <&clock CLK_SCLK_CSIS1>; | ||
| 140 | clock-names = "csis", "sclk_csis"; | ||
| 141 | assigned-clocks = <&clock CLK_MOUT_CSIS1>, | ||
| 142 | <&clock CLK_SCLK_CSIS1>; | ||
| 143 | assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; | ||
| 144 | assigned-clock-rates = <0>, <176000000>; | ||
| 145 | |||
| 146 | interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>; | ||
| 147 | |||
| 148 | bus-width = <2>; | ||
| 149 | power-domains = <&pd_cam>; | ||
| 150 | phys = <&mipi_phy 2>; | ||
| 151 | phy-names = "csis"; | ||
| 152 | |||
| 153 | vddcore-supply = <&ldo8_reg>; | ||
| 154 | vddio-supply = <&ldo10_reg>; | ||
| 155 | |||
| 156 | #address-cells = <1>; | ||
| 157 | #size-cells = <0>; | ||
| 158 | |||
| 159 | /* Camera D (4) MIPI CSI-2 (CSIS1) */ | ||
| 160 | port@4 { | ||
| 161 | reg = <4>; | ||
| 162 | |||
| 163 | endpoint { | ||
| 164 | remote-endpoint = <&is_s5k6a3_ep>; | ||
| 165 | data-lanes = <1>; | ||
| 166 | samsung,csis-hs-settle = <18>; | ||
| 167 | samsung,csis-wclk; | ||
| 168 | }; | ||
| 169 | }; | ||
| 170 | }; | ||
| @@ -0,0 +1,152 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,exynos4210-fimc.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5P/Exynos SoC Fully Integrated Mobile Camera | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | description: | ||
| 14 | Each FIMC device should have an alias in the aliases node, in the form of | ||
| 15 | fimc<n>, where <n> is an integer specifying the IP block instance. | ||
| 16 | |||
| 17 | properties: | ||
| 18 | compatible: | ||
| 19 | enum: | ||
| 20 | - samsung,exynos4210-fimc | ||
| 21 | - samsung,exynos4212-fimc | ||
| 22 | - samsung,s5pv210-fimc | ||
| 23 | |||
| 24 | reg: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | clocks: | ||
| 28 | maxItems: 2 | ||
| 29 | |||
| 30 | clock-names: | ||
| 31 | items: | ||
| 32 | - const: fimc | ||
| 33 | - const: sclk_fimc | ||
| 34 | |||
| 35 | clock-frequency: | ||
| 36 | description: | ||
| 37 | Maximum FIMC local clock (LCLK) frequency. | ||
| 38 | |||
| 39 | interrupts: | ||
| 40 | maxItems: 1 | ||
| 41 | |||
| 42 | iommus: | ||
| 43 | maxItems: 1 | ||
| 44 | |||
| 45 | power-domains: | ||
| 46 | maxItems: 1 | ||
| 47 | |||
| 48 | samsung,cam-if: | ||
| 49 | type: boolean | ||
| 50 | description: | ||
| 51 | The FIMC IP block includes the camera input interface. | ||
| 52 | |||
| 53 | samsung,isp-wb: | ||
| 54 | type: boolean | ||
| 55 | description: | | ||
| 56 | The FIMC IP block has the ISP writeback input. | ||
| 57 | |||
| 58 | samsung,lcd-wb: | ||
| 59 | type: boolean | ||
| 60 | description: | | ||
| 61 | The FIMC IP block has the LCD writeback input. | ||
| 62 | |||
| 63 | samsung,mainscaler-ext: | ||
| 64 | type: boolean | ||
| 65 | description: | ||
| 66 | FIMC IP supports extended image size and has CIEXTEN register. | ||
| 67 | |||
| 68 | samsung,min-pix-alignment: | ||
| 69 | $ref: /schemas/types.yaml#/definitions/uint32-array | ||
| 70 | items: | ||
| 71 | - description: Minimum supported image height alignment. | ||
| 72 | - description: Horizontal image offset. | ||
| 73 | description: | ||
| 74 | The values are in pixels and default is <2 1>. | ||
| 75 | |||
| 76 | samsung,min-pix-sizes: | ||
| 77 | $ref: /schemas/types.yaml#/definitions/uint32-array | ||
| 78 | maxItems: 2 | ||
| 79 | description: | | ||
| 80 | An array specyfing minimum image size in pixels at the FIMC input and | ||
| 81 | output DMA, in the first and second cell respectively. Default value | ||
| 82 | is <16 16>. | ||
| 83 | |||
| 84 | samsung,pix-limits: | ||
| 85 | $ref: /schemas/types.yaml#/definitions/uint32-array | ||
| 86 | maxItems: 4 | ||
| 87 | description: | | ||
| 88 | An array of maximum supported image sizes in pixels, for details refer to | ||
| 89 | Table 2-1 in the S5PV210 SoC User Manual. The meaning of each cell is as | ||
| 90 | follows: | ||
| 91 | 0 - scaler input horizontal size | ||
| 92 | 1 - input horizontal size for the scaler bypassed | ||
| 93 | 2 - REAL_WIDTH without input rotation | ||
| 94 | 3 - REAL_HEIGHT with input rotation | ||
| 95 | |||
| 96 | samsung,rotators: | ||
| 97 | $ref: /schemas/types.yaml#/definitions/uint32 | ||
| 98 | default: 0x11 | ||
| 99 | description: | | ||
| 100 | A bitmask specifying whether this IP has the input and the output | ||
| 101 | rotator. Bits 4 and 0 correspond to input and output rotator | ||
| 102 | respectively. If a rotator is present its corresponding bit should be | ||
| 103 | set. | ||
| 104 | |||
| 105 | samsung,sysreg: | ||
| 106 | $ref: /schemas/types.yaml#/definitions/phandle | ||
| 107 | description: | ||
| 108 | System Registers (SYSREG) node. | ||
| 109 | |||
| 110 | required: | ||
| 111 | - compatible | ||
| 112 | - reg | ||
| 113 | - clocks | ||
| 114 | - clock-names | ||
| 115 | - samsung,pix-limits | ||
| 116 | |||
| 117 | allOf: | ||
| 118 | - if: | ||
| 119 | required: | ||
| 120 | - samsung,isp-wb | ||
| 121 | then: | ||
| 122 | required: | ||
| 123 | - samsung,sysreg | ||
| 124 | |||
| 125 | additionalProperties: false | ||
| 126 | |||
| 127 | examples: | ||
| 128 | - | | ||
| 129 | #include <dt-bindings/clock/exynos4.h> | ||
| 130 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 131 | |||
| 132 | fimc@11800000 { | ||
| 133 | compatible = "samsung,exynos4212-fimc"; | ||
| 134 | reg = <0x11800000 0x1000>; | ||
| 135 | clocks = <&clock CLK_FIMC0>, | ||
| 136 | <&clock CLK_SCLK_FIMC0>; | ||
| 137 | clock-names = "fimc", "sclk_fimc"; | ||
| 138 | interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; | ||
| 139 | iommus = <&sysmmu_fimc0>; | ||
| 140 | power-domains = <&pd_cam>; | ||
| 141 | samsung,sysreg = <&sys_reg>; | ||
| 142 | |||
| 143 | samsung,pix-limits = <4224 8192 1920 4224>; | ||
| 144 | samsung,mainscaler-ext; | ||
| 145 | samsung,isp-wb; | ||
| 146 | samsung,cam-if; | ||
| 147 | |||
| 148 | assigned-clocks = <&clock CLK_MOUT_FIMC0>, | ||
| 149 | <&clock CLK_SCLK_FIMC0>; | ||
| 150 | assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; | ||
| 151 | assigned-clock-rates = <0>, <176000000>; | ||
| 152 | }; | ||
| @@ -0,0 +1,220 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,exynos4212-fimc-is.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung Exynos4212/4412 SoC Imaging Subsystem (FIMC-IS) | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | description: | ||
| 14 | The FIMC-IS is a subsystem for processing image signal from an image sensor. | ||
| 15 | The Exynos4x12 SoC series FIMC-IS V1.5 comprises of a dedicated ARM Cortex-A5 | ||
| 16 | processor, ISP, DRC and FD IP blocks and peripheral devices such as UART, I2C | ||
| 17 | and SPI bus controllers, PWM and ADC. | ||
| 18 | |||
| 19 | properties: | ||
| 20 | compatible: | ||
| 21 | enum: | ||
| 22 | - samsung,exynos4212-fimc-is | ||
| 23 | |||
| 24 | reg: | ||
| 25 | maxItems: 1 | ||
| 26 | |||
| 27 | ranges: true | ||
| 28 | |||
| 29 | '#address-cells': | ||
| 30 | const: 1 | ||
| 31 | |||
| 32 | '#size-cells': | ||
| 33 | const: 1 | ||
| 34 | |||
| 35 | clocks: | ||
| 36 | maxItems: 21 | ||
| 37 | |||
| 38 | clock-names: | ||
| 39 | items: | ||
| 40 | - const: lite0 | ||
| 41 | - const: lite1 | ||
| 42 | - const: ppmuispx | ||
| 43 | - const: ppmuispmx | ||
| 44 | - const: isp | ||
| 45 | - const: drc | ||
| 46 | - const: fd | ||
| 47 | - const: mcuisp | ||
| 48 | - const: gicisp | ||
| 49 | - const: mcuctl_isp | ||
| 50 | - const: pwm_isp | ||
| 51 | - const: ispdiv0 | ||
| 52 | - const: ispdiv1 | ||
| 53 | - const: mcuispdiv0 | ||
| 54 | - const: mcuispdiv1 | ||
| 55 | - const: mpll | ||
| 56 | - const: aclk200 | ||
| 57 | - const: aclk400mcuisp | ||
| 58 | - const: div_aclk200 | ||
| 59 | - const: div_aclk400mcuisp | ||
| 60 | - const: uart | ||
| 61 | |||
| 62 | interrupts: | ||
| 63 | maxItems: 2 | ||
| 64 | |||
| 65 | iommus: | ||
| 66 | maxItems: 4 | ||
| 67 | |||
| 68 | iommu-names: | ||
| 69 | items: | ||
| 70 | - const: isp | ||
| 71 | - const: drc | ||
| 72 | - const: fd | ||
| 73 | - const: mcuctl | ||
| 74 | |||
| 75 | power-domains: | ||
| 76 | maxItems: 1 | ||
| 77 | |||
| 78 | patternProperties: | ||
| 79 | "^pmu@[0-9a-f]+$": | ||
| 80 | type: object | ||
| 81 | additionalProperties: false | ||
| 82 | description: | ||
| 83 | Node representing the SoC's Power Management Unit (duplicated with the | ||
| 84 | correct PMU node in the SoC). | ||
| 85 | |||
| 86 | properties: | ||
| 87 | reg: | ||
| 88 | maxItems: 1 | ||
| 89 | |||
| 90 | required: | ||
| 91 | - reg | ||
| 92 | |||
| 93 | "^i2c-isp@[0-9a-f]+$": | ||
| 94 | type: object | ||
| 95 | $ref: /schemas/i2c/i2c-controller.yaml# | ||
| 96 | unevaluatedProperties: false | ||
| 97 | description: | ||
| 98 | ISP I2C bus controller | ||
| 99 | |||
| 100 | properties: | ||
| 101 | compatible: | ||
| 102 | const: samsung,exynos4212-i2c-isp | ||
| 103 | |||
| 104 | reg: | ||
| 105 | maxItems: 1 | ||
| 106 | |||
| 107 | |||
| 108 | clocks: | ||
| 109 | maxItems: 1 | ||
| 110 | |||
| 111 | clock-names: | ||
| 112 | items: | ||
| 113 | - const: i2c_isp | ||
| 114 | |||
| 115 | pinctrl-0: true | ||
| 116 | pinctrl-names: | ||
| 117 | items: | ||
| 118 | - const: default | ||
| 119 | |||
| 120 | required: | ||
| 121 | - compatible | ||
| 122 | - reg | ||
| 123 | - clocks | ||
| 124 | - clock-names | ||
| 125 | |||
| 126 | required: | ||
| 127 | - compatible | ||
| 128 | - reg | ||
| 129 | - '#address-cells' | ||
| 130 | - clocks | ||
| 131 | - clock-names | ||
| 132 | - interrupts | ||
| 133 | - ranges | ||
| 134 | - '#size-cells' | ||
| 135 | |||
| 136 | additionalProperties: false | ||
| 137 | |||
| 138 | examples: | ||
| 139 | - | | ||
| 140 | #include <dt-bindings/clock/exynos4.h> | ||
| 141 | #include <dt-bindings/gpio/gpio.h> | ||
| 142 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 143 | |||
| 144 | fimc-is@12000000 { | ||
| 145 | compatible = "samsung,exynos4212-fimc-is"; | ||
| 146 | reg = <0x12000000 0x260000>; | ||
| 147 | interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, | ||
| 148 | <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>; | ||
| 149 | clocks = <&isp_clock CLK_ISP_FIMC_LITE0>, | ||
| 150 | <&isp_clock CLK_ISP_FIMC_LITE1>, | ||
| 151 | <&isp_clock CLK_ISP_PPMUISPX>, | ||
| 152 | <&isp_clock CLK_ISP_PPMUISPMX>, | ||
| 153 | <&isp_clock CLK_ISP_FIMC_ISP>, | ||
| 154 | <&isp_clock CLK_ISP_FIMC_DRC>, | ||
| 155 | <&isp_clock CLK_ISP_FIMC_FD>, | ||
| 156 | <&isp_clock CLK_ISP_MCUISP>, | ||
| 157 | <&isp_clock CLK_ISP_GICISP>, | ||
| 158 | <&isp_clock CLK_ISP_MCUCTL_ISP>, | ||
| 159 | <&isp_clock CLK_ISP_PWM_ISP>, | ||
| 160 | <&isp_clock CLK_ISP_DIV_ISP0>, | ||
| 161 | <&isp_clock CLK_ISP_DIV_ISP1>, | ||
| 162 | <&isp_clock CLK_ISP_DIV_MCUISP0>, | ||
| 163 | <&isp_clock CLK_ISP_DIV_MCUISP1>, | ||
| 164 | <&clock CLK_MOUT_MPLL_USER_T>, | ||
| 165 | <&clock CLK_ACLK200>, | ||
| 166 | <&clock CLK_ACLK400_MCUISP>, | ||
| 167 | <&clock CLK_DIV_ACLK200>, | ||
| 168 | <&clock CLK_DIV_ACLK400_MCUISP>, | ||
| 169 | <&clock CLK_UART_ISP_SCLK>; | ||
| 170 | clock-names = "lite0", "lite1", "ppmuispx", | ||
| 171 | "ppmuispmx", "isp", | ||
| 172 | "drc", "fd", "mcuisp", | ||
| 173 | "gicisp", "mcuctl_isp", "pwm_isp", | ||
| 174 | "ispdiv0", "ispdiv1", "mcuispdiv0", | ||
| 175 | "mcuispdiv1", "mpll", "aclk200", | ||
| 176 | "aclk400mcuisp", "div_aclk200", | ||
| 177 | "div_aclk400mcuisp", "uart"; | ||
| 178 | iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, | ||
| 179 | <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; | ||
| 180 | iommu-names = "isp", "drc", "fd", "mcuctl"; | ||
| 181 | power-domains = <&pd_isp>; | ||
| 182 | |||
| 183 | #address-cells = <1>; | ||
| 184 | #size-cells = <1>; | ||
| 185 | ranges; | ||
| 186 | |||
| 187 | pmu@10020000 { | ||
| 188 | reg = <0x10020000 0x3000>; | ||
| 189 | }; | ||
| 190 | |||
| 191 | i2c-isp@12140000 { | ||
| 192 | compatible = "samsung,exynos4212-i2c-isp"; | ||
| 193 | reg = <0x12140000 0x100>; | ||
| 194 | clocks = <&isp_clock CLK_ISP_I2C1_ISP>; | ||
| 195 | clock-names = "i2c_isp"; | ||
| 196 | pinctrl-0 = <&fimc_is_i2c1>; | ||
| 197 | pinctrl-names = "default"; | ||
| 198 | #address-cells = <1>; | ||
| 199 | #size-cells = <0>; | ||
| 200 | |||
| 201 | image-sensor@10 { | ||
| 202 | compatible = "samsung,s5k6a3"; | ||
| 203 | reg = <0x10>; | ||
| 204 | svdda-supply = <&cam_io_reg>; | ||
| 205 | svddio-supply = <&ldo19_reg>; | ||
| 206 | afvdd-supply = <&ldo19_reg>; | ||
| 207 | clock-frequency = <24000000>; | ||
| 208 | clocks = <&camera 1>; | ||
| 209 | clock-names = "extclk"; | ||
| 210 | gpios = <&gpm1 6 GPIO_ACTIVE_LOW>; | ||
| 211 | |||
| 212 | port { | ||
| 213 | endpoint { | ||
| 214 | remote-endpoint = <&csis1_ep>; | ||
| 215 | data-lanes = <1>; | ||
| 216 | }; | ||
| 217 | }; | ||
| 218 | }; | ||
| 219 | }; | ||
| 220 | }; | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,exynos4212-fimc-lite.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung Exynos SoC series camera host interface (FIMC-LITE) | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | description: | ||
| 14 | Each FIMC device should have an alias in the aliases node, in the form of | ||
| 15 | fimc-lite<n>, where <n> is an integer specifying the IP block instance. | ||
| 16 | |||
| 17 | properties: | ||
| 18 | compatible: | ||
| 19 | enum: | ||
| 20 | - samsung,exynos4212-fimc-lite | ||
| 21 | - samsung,exynos5250-fimc-lite | ||
| 22 | |||
| 23 | reg: | ||
| 24 | maxItems: 1 | ||
| 25 | |||
| 26 | clocks: | ||
| 27 | maxItems: 1 | ||
| 28 | |||
| 29 | clock-names: | ||
| 30 | items: | ||
| 31 | - const: flite | ||
| 32 | |||
| 33 | interrupts: | ||
| 34 | maxItems: 1 | ||
| 35 | |||
| 36 | iommus: | ||
| 37 | maxItems: 1 | ||
| 38 | |||
| 39 | power-domains: | ||
| 40 | maxItems: 1 | ||
| 41 | |||
| 42 | required: | ||
| 43 | - compatible | ||
| 44 | - reg | ||
| 45 | - clocks | ||
| 46 | - clock-names | ||
| 47 | - interrupts | ||
| 48 | |||
| 49 | additionalProperties: false | ||
| 50 | |||
| 51 | examples: | ||
| 52 | - | | ||
| 53 | #include <dt-bindings/clock/exynos4.h> | ||
| 54 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 55 | fimc-lite@12390000 { | ||
| 56 | compatible = "samsung,exynos4212-fimc-lite"; | ||
| 57 | reg = <0x12390000 0x1000>; | ||
| 58 | clocks = <&isp_clock CLK_ISP_FIMC_LITE0>; | ||
| 59 | clock-names = "flite"; | ||
| 60 | interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; | ||
| 61 | power-domains = <&pd_isp>; | ||
| 62 | iommus = <&sysmmu_fimc_lite0>; | ||
| 63 | }; | ||
| @@ -0,0 +1,279 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,fimc.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5P/Exynos SoC Camera Subsystem (FIMC) | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | description: | | ||
| 14 | The S5P/Exynos SoC Camera subsystem comprises of multiple sub-devices | ||
| 15 | represented by separate device tree nodes. Currently this includes: Fully | ||
| 16 | Integrated Mobile Camera (FIMC, in the S5P SoCs series known as CAMIF), MIPI | ||
| 17 | CSIS, FIMC-LITE and FIMC-IS (ISP). | ||
| 18 | |||
| 19 | properties: | ||
| 20 | compatible: | ||
| 21 | const: samsung,fimc | ||
| 22 | |||
| 23 | ranges: true | ||
| 24 | |||
| 25 | '#address-cells': | ||
| 26 | const: 1 | ||
| 27 | |||
| 28 | '#size-cells': | ||
| 29 | const: 1 | ||
| 30 | |||
| 31 | '#clock-cells': | ||
| 32 | const: 1 | ||
| 33 | description: | | ||
| 34 | The clock specifier cell stores an index of a clock: 0, 1 for | ||
| 35 | CAM_A_CLKOUT, CAM_B_CLKOUT clocks respectively. | ||
| 36 | |||
| 37 | clocks: | ||
| 38 | minItems: 2 | ||
| 39 | maxItems: 4 | ||
| 40 | |||
| 41 | clock-names: | ||
| 42 | minItems: 2 | ||
| 43 | items: | ||
| 44 | - const: sclk_cam0 | ||
| 45 | - const: sclk_cam1 | ||
| 46 | - const: pxl_async0 | ||
| 47 | - const: pxl_async1 | ||
| 48 | |||
| 49 | clock-output-names: | ||
| 50 | maxItems: 2 | ||
| 51 | |||
| 52 | parallel-ports: | ||
| 53 | $ref: /schemas/graph.yaml#/properties/ports | ||
| 54 | description: | ||
| 55 | Active parallel video input ports. | ||
| 56 | |||
| 57 | patternProperties: | ||
| 58 | "^port@[01]$": | ||
| 59 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 60 | description: | ||
| 61 | Camera A and camera B inputs. | ||
| 62 | |||
| 63 | properties: | ||
| 64 | endpoint: | ||
| 65 | $ref: /schemas/media/video-interfaces.yaml# | ||
| 66 | unevaluatedProperties: false | ||
| 67 | |||
| 68 | pinctrl-names: | ||
| 69 | minItems: 1 | ||
| 70 | items: | ||
| 71 | - const: default | ||
| 72 | - const: idle | ||
| 73 | - const: active_a | ||
| 74 | - const: active_b | ||
| 75 | |||
| 76 | patternProperties: | ||
| 77 | "^csis@[0-9a-f]+$": | ||
| 78 | type: object | ||
| 79 | $ref: samsung,exynos4210-csis.yaml# | ||
| 80 | description: MIPI CSI-2 receiver. | ||
| 81 | |||
| 82 | "^fimc@[0-9a-f]+$": | ||
| 83 | type: object | ||
| 84 | $ref: samsung,exynos4210-fimc.yaml# | ||
| 85 | description: Fully Integrated Mobile Camera. | ||
| 86 | |||
| 87 | "^fimc-is@[0-9a-f]+$": | ||
| 88 | type: object | ||
| 89 | $ref: samsung,exynos4212-fimc-is.yaml# | ||
| 90 | description: Imaging Subsystem (FIMC-IS). | ||
| 91 | |||
| 92 | "^fimc-lite@[0-9a-f]+$": | ||
| 93 | type: object | ||
| 94 | $ref: samsung,exynos4212-fimc-lite.yaml# | ||
| 95 | description: Camera host interface (FIMC-LITE). | ||
| 96 | |||
| 97 | required: | ||
| 98 | - compatible | ||
| 99 | - '#address-cells' | ||
| 100 | - '#clock-cells' | ||
| 101 | - clocks | ||
| 102 | - clock-names | ||
| 103 | - clock-output-names | ||
| 104 | - ranges | ||
| 105 | - '#size-cells' | ||
| 106 | |||
| 107 | additionalProperties: false | ||
| 108 | |||
| 109 | examples: | ||
| 110 | - | | ||
| 111 | #include <dt-bindings/clock/exynos4.h> | ||
| 112 | #include <dt-bindings/gpio/gpio.h> | ||
| 113 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 114 | |||
| 115 | camera@11800000 { | ||
| 116 | compatible = "samsung,fimc"; | ||
| 117 | #clock-cells = <1>; | ||
| 118 | #address-cells = <1>; | ||
| 119 | #size-cells = <1>; | ||
| 120 | ranges = <0x0 0x0 0x18000000>; | ||
| 121 | |||
| 122 | clocks = <&clock CLK_SCLK_CAM0>, <&clock CLK_SCLK_CAM1>, | ||
| 123 | <&clock CLK_PIXELASYNCM0>, <&clock CLK_PIXELASYNCM1>; | ||
| 124 | clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", "pxl_async1"; | ||
| 125 | clock-output-names = "cam_a_clkout", "cam_b_clkout"; | ||
| 126 | |||
| 127 | assigned-clocks = <&clock CLK_MOUT_CAM0>, | ||
| 128 | <&clock CLK_MOUT_CAM1>; | ||
| 129 | assigned-clock-parents = <&clock CLK_XUSBXTI>, | ||
| 130 | <&clock CLK_XUSBXTI>; | ||
| 131 | |||
| 132 | pinctrl-0 = <&cam_port_a_clk_active &cam_port_b_clk_active>; | ||
| 133 | pinctrl-names = "default"; | ||
| 134 | |||
| 135 | fimc@11800000 { | ||
| 136 | compatible = "samsung,exynos4212-fimc"; | ||
| 137 | reg = <0x11800000 0x1000>; | ||
| 138 | interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; | ||
| 139 | clocks = <&clock CLK_FIMC0>, | ||
| 140 | <&clock CLK_SCLK_FIMC0>; | ||
| 141 | clock-names = "fimc", "sclk_fimc"; | ||
| 142 | power-domains = <&pd_cam>; | ||
| 143 | samsung,sysreg = <&sys_reg>; | ||
| 144 | iommus = <&sysmmu_fimc0>; | ||
| 145 | |||
| 146 | samsung,pix-limits = <4224 8192 1920 4224>; | ||
| 147 | samsung,mainscaler-ext; | ||
| 148 | samsung,isp-wb; | ||
| 149 | samsung,cam-if; | ||
| 150 | }; | ||
| 151 | |||
| 152 | /* ... FIMC 1-3 */ | ||
| 153 | |||
| 154 | csis@11880000 { | ||
| 155 | compatible = "samsung,exynos4210-csis"; | ||
| 156 | reg = <0x11880000 0x4000>; | ||
| 157 | interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>; | ||
| 158 | clocks = <&clock CLK_CSIS0>, | ||
| 159 | <&clock CLK_SCLK_CSIS0>; | ||
| 160 | clock-names = "csis", "sclk_csis"; | ||
| 161 | assigned-clocks = <&clock CLK_MOUT_CSIS0>, | ||
| 162 | <&clock CLK_SCLK_CSIS0>; | ||
| 163 | assigned-clock-parents = <&clock CLK_MOUT_MPLL_USER_T>; | ||
| 164 | assigned-clock-rates = <0>, <176000000>; | ||
| 165 | |||
| 166 | bus-width = <4>; | ||
| 167 | power-domains = <&pd_cam>; | ||
| 168 | phys = <&mipi_phy 0>; | ||
| 169 | phy-names = "csis"; | ||
| 170 | #address-cells = <1>; | ||
| 171 | #size-cells = <0>; | ||
| 172 | |||
| 173 | vddcore-supply = <&ldo8_reg>; | ||
| 174 | vddio-supply = <&ldo10_reg>; | ||
| 175 | |||
| 176 | /* Camera C (3) MIPI CSI-2 (CSIS0) */ | ||
| 177 | port@3 { | ||
| 178 | reg = <3>; | ||
| 179 | endpoint { | ||
| 180 | remote-endpoint = <&s5c73m3_ep>; | ||
| 181 | data-lanes = <1 2 3 4>; | ||
| 182 | samsung,csis-hs-settle = <12>; | ||
| 183 | }; | ||
| 184 | }; | ||
| 185 | }; | ||
| 186 | |||
| 187 | /* ... CSIS 1 */ | ||
| 188 | |||
| 189 | fimc-lite@12390000 { | ||
| 190 | compatible = "samsung,exynos4212-fimc-lite"; | ||
| 191 | reg = <0x12390000 0x1000>; | ||
| 192 | interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; | ||
| 193 | power-domains = <&pd_isp>; | ||
| 194 | clocks = <&isp_clock CLK_ISP_FIMC_LITE0>; | ||
| 195 | clock-names = "flite"; | ||
| 196 | iommus = <&sysmmu_fimc_lite0>; | ||
| 197 | }; | ||
| 198 | |||
| 199 | /* ... FIMC-LITE 1 */ | ||
| 200 | |||
| 201 | fimc-is@12000000 { | ||
| 202 | compatible = "samsung,exynos4212-fimc-is"; | ||
| 203 | reg = <0x12000000 0x260000>; | ||
| 204 | interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, | ||
| 205 | <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>; | ||
| 206 | clocks = <&isp_clock CLK_ISP_FIMC_LITE0>, | ||
| 207 | <&isp_clock CLK_ISP_FIMC_LITE1>, | ||
| 208 | <&isp_clock CLK_ISP_PPMUISPX>, | ||
| 209 | <&isp_clock CLK_ISP_PPMUISPMX>, | ||
| 210 | <&isp_clock CLK_ISP_FIMC_ISP>, | ||
| 211 | <&isp_clock CLK_ISP_FIMC_DRC>, | ||
| 212 | <&isp_clock CLK_ISP_FIMC_FD>, | ||
| 213 | <&isp_clock CLK_ISP_MCUISP>, | ||
| 214 | <&isp_clock CLK_ISP_GICISP>, | ||
| 215 | <&isp_clock CLK_ISP_MCUCTL_ISP>, | ||
| 216 | <&isp_clock CLK_ISP_PWM_ISP>, | ||
| 217 | <&isp_clock CLK_ISP_DIV_ISP0>, | ||
| 218 | <&isp_clock CLK_ISP_DIV_ISP1>, | ||
| 219 | <&isp_clock CLK_ISP_DIV_MCUISP0>, | ||
| 220 | <&isp_clock CLK_ISP_DIV_MCUISP1>, | ||
| 221 | <&clock CLK_MOUT_MPLL_USER_T>, | ||
| 222 | <&clock CLK_ACLK200>, | ||
| 223 | <&clock CLK_ACLK400_MCUISP>, | ||
| 224 | <&clock CLK_DIV_ACLK200>, | ||
| 225 | <&clock CLK_DIV_ACLK400_MCUISP>, | ||
| 226 | <&clock CLK_UART_ISP_SCLK>; | ||
| 227 | clock-names = "lite0", "lite1", "ppmuispx", | ||
| 228 | "ppmuispmx", "isp", | ||
| 229 | "drc", "fd", "mcuisp", | ||
| 230 | "gicisp", "mcuctl_isp", "pwm_isp", | ||
| 231 | "ispdiv0", "ispdiv1", "mcuispdiv0", | ||
| 232 | "mcuispdiv1", "mpll", "aclk200", | ||
| 233 | "aclk400mcuisp", "div_aclk200", | ||
| 234 | "div_aclk400mcuisp", "uart"; | ||
| 235 | iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, | ||
| 236 | <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; | ||
| 237 | iommu-names = "isp", "drc", "fd", "mcuctl"; | ||
| 238 | power-domains = <&pd_isp>; | ||
| 239 | |||
| 240 | #address-cells = <1>; | ||
| 241 | #size-cells = <1>; | ||
| 242 | ranges; | ||
| 243 | |||
| 244 | pmu@10020000 { | ||
| 245 | reg = <0x10020000 0x3000>; | ||
| 246 | }; | ||
| 247 | |||
| 248 | i2c-isp@12140000 { | ||
| 249 | compatible = "samsung,exynos4212-i2c-isp"; | ||
| 250 | reg = <0x12140000 0x100>; | ||
| 251 | clocks = <&isp_clock CLK_ISP_I2C1_ISP>; | ||
| 252 | clock-names = "i2c_isp"; | ||
| 253 | pinctrl-0 = <&fimc_is_i2c1>; | ||
| 254 | pinctrl-names = "default"; | ||
| 255 | #address-cells = <1>; | ||
| 256 | #size-cells = <0>; | ||
| 257 | |||
| 258 | image-sensor@10 { | ||
| 259 | compatible = "samsung,s5k6a3"; | ||
| 260 | reg = <0x10>; | ||
| 261 | svdda-supply = <&cam_io_reg>; | ||
| 262 | svddio-supply = <&ldo19_reg>; | ||
| 263 | afvdd-supply = <&ldo19_reg>; | ||
| 264 | clock-frequency = <24000000>; | ||
| 265 | /* CAM_B_CLKOUT */ | ||
| 266 | clocks = <&camera 1>; | ||
| 267 | clock-names = "extclk"; | ||
| 268 | gpios = <&gpm1 6 GPIO_ACTIVE_LOW>; | ||
| 269 | |||
| 270 | port { | ||
| 271 | endpoint { | ||
| 272 | remote-endpoint = <&csis1_ep>; | ||
| 273 | data-lanes = <1>; | ||
| 274 | }; | ||
| 275 | }; | ||
| 276 | }; | ||
| 277 | }; | ||
| 278 | }; | ||
| 279 | }; | ||
| @@ -0,0 +1,165 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/samsung,s5c73m3.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Samsung S5C73M3 8Mp camera ISP | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> | ||
| 11 | - Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 12 | |||
| 13 | description: | ||
| 14 | The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) | ||
| 15 | video data busses. The I2C bus is the main control bus and additionally the | ||
| 16 | SPI bus is used, mostly for transferring the firmware to and from the | ||
| 17 | device. Two slave device nodes corresponding to these control bus | ||
| 18 | interfaces are required and should be placed under respective bus | ||
| 19 | controller nodes. | ||
| 20 | |||
| 21 | properties: | ||
| 22 | compatible: | ||
| 23 | const: samsung,s5c73m3 | ||
| 24 | |||
| 25 | reg: | ||
| 26 | maxItems: 1 | ||
| 27 | |||
| 28 | clocks: | ||
| 29 | maxItems: 1 | ||
| 30 | |||
| 31 | clock-names: | ||
| 32 | items: | ||
| 33 | - const: cis_extclk | ||
| 34 | |||
| 35 | clock-frequency: | ||
| 36 | default: 24000000 | ||
| 37 | description: cis_extclk clock frequency. | ||
| 38 | |||
| 39 | standby-gpios: | ||
| 40 | maxItems: 1 | ||
| 41 | description: STANDBY pin. | ||
| 42 | |||
| 43 | vdda-supply: | ||
| 44 | description: Analog power supply (1.2V). | ||
| 45 | |||
| 46 | vdd-af-supply: | ||
| 47 | description: lens power supply (2.8V). | ||
| 48 | |||
| 49 | vddio-cis-supply: | ||
| 50 | description: CIS I/O power supply (1.2V to 1.8V). | ||
| 51 | |||
| 52 | vddio-host-supply: | ||
| 53 | description: Host I/O power supply (1.8V to 2.8V). | ||
| 54 | |||
| 55 | vdd-int-supply: | ||
| 56 | description: Digital power supply (1.2V). | ||
| 57 | |||
| 58 | vdd-reg-supply: | ||
| 59 | description: Regulator input power supply (2.8V). | ||
| 60 | |||
| 61 | xshutdown-gpios: | ||
| 62 | maxItems: 1 | ||
| 63 | description: XSHUTDOWN pin. | ||
| 64 | |||
| 65 | port: | ||
| 66 | $ref: /schemas/graph.yaml#/$defs/port-base | ||
| 67 | additionalProperties: false | ||
| 68 | |||
| 69 | properties: | ||
| 70 | endpoint: | ||
| 71 | $ref: /schemas/media/video-interfaces.yaml# | ||
| 72 | unevaluatedProperties: false | ||
| 73 | |||
| 74 | properties: | ||
| 75 | data-lanes: | ||
| 76 | items: | ||
| 77 | - const: 1 | ||
| 78 | - const: 2 | ||
| 79 | - const: 3 | ||
| 80 | - const: 4 | ||
| 81 | |||
| 82 | required: | ||
| 83 | - compatible | ||
| 84 | - reg | ||
| 85 | |||
| 86 | allOf: | ||
| 87 | - $ref: /schemas/spi/spi-peripheral-props.yaml# | ||
| 88 | - if: | ||
| 89 | required: | ||
| 90 | - spi-max-frequency | ||
| 91 | then: | ||
| 92 | properties: | ||
| 93 | # The SPI node is simplified firmware-transfer interface only | ||
| 94 | clocks: false | ||
| 95 | clock-names: false | ||
| 96 | standby-gpios: false | ||
| 97 | vdda-supply: false | ||
| 98 | vdd-af-supply: false | ||
| 99 | vddio-cis-supply: false | ||
| 100 | vddio-host-supply: false | ||
| 101 | vdd-int-supply: false | ||
| 102 | vdd-reg-supply: false | ||
| 103 | xshutdown-gpios: false | ||
| 104 | port: false | ||
| 105 | else: | ||
| 106 | required: | ||
| 107 | - clocks | ||
| 108 | - clock-names | ||
| 109 | - standby-gpios | ||
| 110 | - vdda-supply | ||
| 111 | - vdd-af-supply | ||
| 112 | - vddio-cis-supply | ||
| 113 | - vddio-host-supply | ||
| 114 | - vdd-int-supply | ||
| 115 | - vdd-reg-supply | ||
| 116 | - xshutdown-gpios | ||
| 117 | - port | ||
| 118 | |||
| 119 | unevaluatedProperties: false | ||
| 120 | |||
| 121 | examples: | ||
| 122 | - | | ||
| 123 | #include <dt-bindings/gpio/gpio.h> | ||
| 124 | |||
| 125 | i2c { | ||
| 126 | #address-cells = <1>; | ||
| 127 | #size-cells = <0>; | ||
| 128 | |||
| 129 | image-sensor@3c { | ||
| 130 | compatible = "samsung,s5c73m3"; | ||
| 131 | reg = <0x3c>; | ||
| 132 | clock-frequency = <24000000>; | ||
| 133 | clocks = <&camera 0>; | ||
| 134 | clock-names = "cis_extclk"; | ||
| 135 | standby-gpios = <&gpm0 6 GPIO_ACTIVE_LOW>; | ||
| 136 | vdda-supply = <&cam_vdda_reg>; | ||
| 137 | vdd-af-supply = <&cam_af_reg>; | ||
| 138 | vddio-cis-supply = <&ldo9_reg>; | ||
| 139 | vddio-host-supply = <&ldo18_reg>; | ||
| 140 | vdd-int-supply = <&buck9_reg>; | ||
| 141 | vdd-reg-supply = <&cam_io_reg>; | ||
| 142 | xshutdown-gpios = <&gpf1 3 GPIO_ACTIVE_LOW>; /* ISP_RESET */ | ||
| 143 | |||
| 144 | port { | ||
| 145 | s5c73m3_ep: endpoint { | ||
| 146 | remote-endpoint = <&csis0_ep>; | ||
| 147 | data-lanes = <1 2 3 4>; | ||
| 148 | }; | ||
| 149 | }; | ||
| 150 | }; | ||
| 151 | }; | ||
| 152 | |||
| 153 | spi { | ||
| 154 | #address-cells = <1>; | ||
| 155 | #size-cells = <0>; | ||
| 156 | |||
| 157 | image-sensor@0 { | ||
| 158 | compatible = "samsung,s5c73m3"; | ||
| 159 | reg = <0>; | ||
| 160 | spi-max-frequency = <50000000>; | ||
| 161 | controller-data { | ||
| 162 | samsung,spi-feedback-delay = <2>; | ||
| 163 | }; | ||
| 164 | }; | ||
| 165 | }; | ||
| @@ -1,209 +0,0 @@ | |||
| 1 | Samsung S5P/Exynos SoC Camera Subsystem (FIMC) | ||
| 2 | ---------------------------------------------- | ||
| 3 | |||
| 4 | The S5P/Exynos SoC Camera subsystem comprises of multiple sub-devices | ||
| 5 | represented by separate device tree nodes. Currently this includes: FIMC (in | ||
| 6 | the S5P SoCs series known as CAMIF), MIPI CSIS, FIMC-LITE and FIMC-IS (ISP). | ||
| 7 | |||
| 8 | The sub-subdevices are defined as child nodes of the common 'camera' node which | ||
| 9 | also includes common properties of the whole subsystem not really specific to | ||
| 10 | any single sub-device, like common camera port pins or the CAMCLK clock outputs | ||
| 11 | for external image sensors attached to an SoC. | ||
| 12 | |||
| 13 | Common 'camera' node | ||
| 14 | -------------------- | ||
| 15 | |||
| 16 | Required properties: | ||
| 17 | |||
| 18 | - compatible: must be "samsung,fimc", "simple-bus" | ||
| 19 | - clocks: list of clock specifiers, corresponding to entries in | ||
| 20 | the clock-names property; | ||
| 21 | - clock-names : must contain "sclk_cam0", "sclk_cam1", "pxl_async0", | ||
| 22 | "pxl_async1" entries, matching entries in the clocks property. | ||
| 23 | |||
| 24 | - #clock-cells: from the common clock bindings (../clock/clock-bindings.txt), | ||
| 25 | must be 1. A clock provider is associated with the 'camera' node and it should | ||
| 26 | be referenced by external sensors that use clocks provided by the SoC on | ||
| 27 | CAM_*_CLKOUT pins. The clock specifier cell stores an index of a clock. | ||
| 28 | The indices are 0, 1 for CAM_A_CLKOUT, CAM_B_CLKOUT clocks respectively. | ||
| 29 | |||
| 30 | - clock-output-names: from the common clock bindings, should contain names of | ||
| 31 | clocks registered by the camera subsystem corresponding to CAM_A_CLKOUT, | ||
| 32 | CAM_B_CLKOUT output clocks respectively. | ||
| 33 | |||
| 34 | The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used | ||
| 35 | to define a required pinctrl state named "default" and optional pinctrl states: | ||
| 36 | "idle", "active-a", active-b". These optional states can be used to switch the | ||
| 37 | camera port pinmux at runtime. The "idle" state should configure both the camera | ||
| 38 | ports A and B into high impedance state, especially the CAMCLK clock output | ||
| 39 | should be inactive. For the "active-a" state the camera port A must be activated | ||
| 40 | and the port B deactivated and for the state "active-b" it should be the other | ||
| 41 | way around. | ||
| 42 | |||
| 43 | The 'camera' node must include at least one 'fimc' child node. | ||
| 44 | |||
| 45 | |||
| 46 | 'fimc' device nodes | ||
| 47 | ------------------- | ||
| 48 | |||
| 49 | Required properties: | ||
| 50 | |||
| 51 | - compatible: "samsung,s5pv210-fimc" for S5PV210, "samsung,exynos4210-fimc" | ||
| 52 | for Exynos4210 and "samsung,exynos4212-fimc" for Exynos4x12 SoCs; | ||
| 53 | - reg: physical base address and length of the registers set for the device; | ||
| 54 | - interrupts: should contain FIMC interrupt; | ||
| 55 | - clocks: list of clock specifiers, must contain an entry for each required | ||
| 56 | entry in clock-names; | ||
| 57 | - clock-names: must contain "fimc", "sclk_fimc" entries. | ||
| 58 | - samsung,pix-limits: an array of maximum supported image sizes in pixels, for | ||
| 59 | details refer to Table 2-1 in the S5PV210 SoC User Manual; The meaning of | ||
| 60 | each cell is as follows: | ||
| 61 | 0 - scaler input horizontal size, | ||
| 62 | 1 - input horizontal size for the scaler bypassed, | ||
| 63 | 2 - REAL_WIDTH without input rotation, | ||
| 64 | 3 - REAL_HEIGHT with input rotation, | ||
| 65 | - samsung,sysreg: a phandle to the SYSREG node. | ||
| 66 | |||
| 67 | Each FIMC device should have an alias in the aliases node, in the form of | ||
| 68 | fimc<n>, where <n> is an integer specifying the IP block instance. | ||
| 69 | |||
| 70 | Optional properties: | ||
| 71 | |||
| 72 | - clock-frequency: maximum FIMC local clock (LCLK) frequency; | ||
| 73 | - samsung,min-pix-sizes: an array specyfing minimum image size in pixels at | ||
| 74 | the FIMC input and output DMA, in the first and second cell respectively. | ||
| 75 | Default value when this property is not present is <16 16>; | ||
| 76 | - samsung,min-pix-alignment: minimum supported image height alignment (first | ||
| 77 | cell) and the horizontal image offset (second cell). The values are in pixels | ||
| 78 | and default to <2 1> when this property is not present; | ||
| 79 | - samsung,mainscaler-ext: a boolean property indicating whether the FIMC IP | ||
| 80 | supports extended image size and has CIEXTEN register; | ||
| 81 | - samsung,rotators: a bitmask specifying whether this IP has the input and | ||
| 82 | the output rotator. Bits 4 and 0 correspond to input and output rotator | ||
| 83 | respectively. If a rotator is present its corresponding bit should be set. | ||
| 84 | Default value when this property is not specified is 0x11. | ||
| 85 | - samsung,cam-if: a bolean property indicating whether the IP block includes | ||
| 86 | the camera input interface. | ||
| 87 | - samsung,isp-wb: this property must be present if the IP block has the ISP | ||
| 88 | writeback input. | ||
| 89 | - samsung,lcd-wb: this property must be present if the IP block has the LCD | ||
| 90 | writeback input. | ||
| 91 | |||
| 92 | |||
| 93 | 'parallel-ports' node | ||
| 94 | --------------------- | ||
| 95 | |||
| 96 | This node should contain child 'port' nodes specifying active parallel video | ||
| 97 | input ports. It includes camera A and camera B inputs. 'reg' property in the | ||
| 98 | port nodes specifies data input - 1, 2 indicates input A, B respectively. | ||
| 99 | |||
| 100 | Optional properties | ||
| 101 | |||
| 102 | - samsung,camclk-out (deprecated) : specifies clock output for remote sensor, | ||
| 103 | 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT; | ||
| 104 | |||
| 105 | Image sensor nodes | ||
| 106 | ------------------ | ||
| 107 | |||
| 108 | The sensor device nodes should be added to their control bus controller (e.g. | ||
| 109 | I2C0) nodes and linked to a port node in the csis or the parallel-ports node, | ||
| 110 | using the common video interfaces bindings, defined in video-interfaces.txt. | ||
| 111 | |||
| 112 | Example: | ||
| 113 | |||
| 114 | aliases { | ||
| 115 | fimc0 = &fimc_0; | ||
| 116 | }; | ||
| 117 | |||
| 118 | /* Parallel bus IF sensor */ | ||
| 119 | i2c_0: i2c@13860000 { | ||
| 120 | s5k6aa: sensor@3c { | ||
| 121 | compatible = "samsung,s5k6aafx"; | ||
| 122 | reg = <0x3c>; | ||
| 123 | vddio-supply = <...>; | ||
| 124 | |||
| 125 | clock-frequency = <24000000>; | ||
| 126 | clocks = <&camera 1>; | ||
| 127 | clock-names = "mclk"; | ||
| 128 | |||
| 129 | port { | ||
| 130 | s5k6aa_ep: endpoint { | ||
| 131 | remote-endpoint = <&fimc0_ep>; | ||
| 132 | bus-width = <8>; | ||
| 133 | hsync-active = <0>; | ||
| 134 | vsync-active = <1>; | ||
| 135 | pclk-sample = <1>; | ||
| 136 | }; | ||
| 137 | }; | ||
| 138 | }; | ||
| 139 | |||
| 140 | /* MIPI CSI-2 bus IF sensor */ | ||
| 141 | s5c73m3: sensor@1a { | ||
| 142 | compatible = "samsung,s5c73m3"; | ||
| 143 | reg = <0x1a>; | ||
| 144 | vddio-supply = <...>; | ||
| 145 | |||
| 146 | clock-frequency = <24000000>; | ||
| 147 | clocks = <&camera 0>; | ||
| 148 | clock-names = "mclk"; | ||
| 149 | |||
| 150 | port { | ||
| 151 | s5c73m3_1: endpoint { | ||
| 152 | data-lanes = <1 2 3 4>; | ||
| 153 | remote-endpoint = <&csis0_ep>; | ||
| 154 | }; | ||
| 155 | }; | ||
| 156 | }; | ||
| 157 | }; | ||
| 158 | |||
| 159 | camera { | ||
| 160 | compatible = "samsung,fimc", "simple-bus"; | ||
| 161 | clocks = <&clock 132>, <&clock 133>, <&clock 351>, | ||
| 162 | <&clock 352>; | ||
| 163 | clock-names = "sclk_cam0", "sclk_cam1", "pxl_async0", | ||
| 164 | "pxl_async1"; | ||
| 165 | #clock-cells = <1>; | ||
| 166 | clock-output-names = "cam_a_clkout", "cam_b_clkout"; | ||
| 167 | pinctrl-names = "default"; | ||
| 168 | pinctrl-0 = <&cam_port_a_clk_active>; | ||
| 169 | #address-cells = <1>; | ||
| 170 | #size-cells = <1>; | ||
| 171 | |||
| 172 | /* parallel camera ports */ | ||
| 173 | parallel-ports { | ||
| 174 | /* camera A input */ | ||
| 175 | port@1 { | ||
| 176 | reg = <1>; | ||
| 177 | fimc0_ep: endpoint { | ||
| 178 | remote-endpoint = <&s5k6aa_ep>; | ||
| 179 | bus-width = <8>; | ||
| 180 | hsync-active = <0>; | ||
| 181 | vsync-active = <1>; | ||
| 182 | pclk-sample = <1>; | ||
| 183 | }; | ||
| 184 | }; | ||
| 185 | }; | ||
| 186 | |||
| 187 | fimc_0: fimc@11800000 { | ||
| 188 | compatible = "samsung,exynos4210-fimc"; | ||
| 189 | reg = <0x11800000 0x1000>; | ||
| 190 | interrupts = <0 85 0>; | ||
| 191 | }; | ||
| 192 | |||
| 193 | csis_0: csis@11880000 { | ||
| 194 | compatible = "samsung,exynos4210-csis"; | ||
| 195 | reg = <0x11880000 0x1000>; | ||
| 196 | interrupts = <0 78 0>; | ||
| 197 | /* camera C input */ | ||
| 198 | port@3 { | ||
| 199 | reg = <3>; | ||
| 200 | csis0_ep: endpoint { | ||
| 201 | remote-endpoint = <&s5c73m3_ep>; | ||
| 202 | data-lanes = <1 2 3 4>; | ||
| 203 | samsung,csis-hs-settle = <12>; | ||
| 204 | }; | ||
| 205 | }; | ||
| 206 | }; | ||
| 207 | }; | ||
| 208 | |||
| 209 | The MIPI-CSIS device binding is defined in samsung-mipi-csis.txt. | ||
| @@ -1,81 +0,0 @@ | |||
| 1 | Samsung S5P/Exynos SoC series MIPI CSI-2 receiver (MIPI CSIS) | ||
| 2 | ------------------------------------------------------------- | ||
| 3 | |||
| 4 | Required properties: | ||
| 5 | |||
| 6 | - compatible : "samsung,s5pv210-csis" for S5PV210 (S5PC110), | ||
| 7 | "samsung,exynos4210-csis" for Exynos4210 (S5PC210), | ||
| 8 | "samsung,exynos4212-csis" for Exynos4212/Exynos4412, | ||
| 9 | "samsung,exynos5250-csis" for Exynos5250; | ||
| 10 | - reg : offset and length of the register set for the device; | ||
| 11 | - interrupts : should contain MIPI CSIS interrupt; the format of the | ||
| 12 | interrupt specifier depends on the interrupt controller; | ||
| 13 | - bus-width : maximum number of data lanes supported (SoC specific); | ||
| 14 | - vddio-supply : MIPI CSIS I/O and PLL voltage supply (e.g. 1.8V); | ||
| 15 | - vddcore-supply : MIPI CSIS Core voltage supply (e.g. 1.1V); | ||
| 16 | - clocks : list of clock specifiers, corresponding to entries in | ||
| 17 | clock-names property; | ||
| 18 | - clock-names : must contain "csis", "sclk_csis" entries, matching entries | ||
| 19 | in the clocks property. | ||
| 20 | |||
| 21 | Optional properties: | ||
| 22 | |||
| 23 | - clock-frequency : The IP's main (system bus) clock frequency in Hz, default | ||
| 24 | value when this property is not specified is 166 MHz; | ||
| 25 | - samsung,csis-wclk : CSI-2 wrapper clock selection. If this property is present | ||
| 26 | external clock from CMU will be used, or the bus clock if | ||
| 27 | if it's not specified. | ||
| 28 | |||
| 29 | The device node should contain one 'port' child node with one child 'endpoint' | ||
| 30 | node, according to the bindings defined in Documentation/devicetree/bindings/ | ||
| 31 | media/video-interfaces.txt. The following are properties specific to those nodes. | ||
| 32 | |||
| 33 | port node | ||
| 34 | --------- | ||
| 35 | |||
| 36 | - reg : (required) must be 3 for camera C input (CSIS0) or 4 for | ||
| 37 | camera D input (CSIS1); | ||
| 38 | |||
| 39 | endpoint node | ||
| 40 | ------------- | ||
| 41 | |||
| 42 | - data-lanes : (required) an array specifying active physical MIPI-CSI2 | ||
| 43 | data input lanes and their mapping to logical lanes; the | ||
| 44 | array's content is unused, only its length is meaningful; | ||
| 45 | |||
| 46 | - samsung,csis-hs-settle : (optional) differential receiver (HS-RX) settle time; | ||
| 47 | |||
| 48 | |||
| 49 | Example: | ||
| 50 | |||
| 51 | reg0: regulator@0 { | ||
| 52 | }; | ||
| 53 | |||
| 54 | reg1: regulator@1 { | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* SoC properties */ | ||
| 58 | |||
| 59 | csis_0: csis@11880000 { | ||
| 60 | compatible = "samsung,exynos4210-csis"; | ||
| 61 | reg = <0x11880000 0x1000>; | ||
| 62 | interrupts = <0 78 0>; | ||
| 63 | #address-cells = <1>; | ||
| 64 | #size-cells = <0>; | ||
| 65 | }; | ||
| 66 | |||
| 67 | /* Board properties */ | ||
| 68 | |||
| 69 | csis_0: csis@11880000 { | ||
| 70 | clock-frequency = <166000000>; | ||
| 71 | vddio-supply = <®0>; | ||
| 72 | vddcore-supply = <®1>; | ||
| 73 | port { | ||
| 74 | reg = <3>; /* 3 - CSIS0, 4 - CSIS1 */ | ||
| 75 | csis0_ep: endpoint { | ||
| 76 | remote-endpoint = <...>; | ||
| 77 | data-lanes = <1>, <2>; | ||
| 78 | samsung,csis-hs-settle = <12>; | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | }; | ||
| @@ -1,97 +0,0 @@ | |||
| 1 | Samsung S5C73M3 8Mp camera ISP | ||
| 2 | ------------------------------ | ||
| 3 | |||
| 4 | The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video | ||
| 5 | data busses. The I2C bus is the main control bus and additionally the SPI bus | ||
| 6 | is used, mostly for transferring the firmware to and from the device. Two | ||
| 7 | slave device nodes corresponding to these control bus interfaces are required | ||
| 8 | and should be placed under respective bus controller nodes. | ||
| 9 | |||
| 10 | I2C slave device node | ||
| 11 | --------------------- | ||
| 12 | |||
| 13 | Required properties: | ||
| 14 | |||
| 15 | - compatible : "samsung,s5c73m3"; | ||
| 16 | - reg : I2C slave address of the sensor; | ||
| 17 | - vdd-int-supply : digital power supply (1.2V); | ||
| 18 | - vdda-supply : analog power supply (1.2V); | ||
| 19 | - vdd-reg-supply : regulator input power supply (2.8V); | ||
| 20 | - vddio-host-supply : host I/O power supply (1.8V to 2.8V); | ||
| 21 | - vddio-cis-supply : CIS I/O power supply (1.2V to 1.8V); | ||
| 22 | - vdd-af-supply : lens power supply (2.8V); | ||
| 23 | - xshutdown-gpios : specifier of GPIO connected to the XSHUTDOWN pin; | ||
| 24 | - standby-gpios : specifier of GPIO connected to the STANDBY pin; | ||
| 25 | - clocks : should contain list of phandle and clock specifier pairs | ||
| 26 | according to common clock bindings for the clocks described | ||
| 27 | in the clock-names property; | ||
| 28 | - clock-names : should contain "cis_extclk" entry for the CIS_EXTCLK clock; | ||
| 29 | |||
| 30 | Optional properties: | ||
| 31 | |||
| 32 | - clock-frequency : the frequency at which the "cis_extclk" clock should be | ||
| 33 | configured to operate, in Hz; if this property is not | ||
| 34 | specified default 24 MHz value will be used. | ||
| 35 | |||
| 36 | The common video interfaces bindings (see video-interfaces.txt) should be used | ||
| 37 | to specify link from the S5C73M3 to an external image data receiver. The S5C73M3 | ||
| 38 | device node should contain one 'port' child node with an 'endpoint' subnode for | ||
| 39 | this purpose. The data link from a raw image sensor to the S5C73M3 can be | ||
| 40 | similarly specified, but it is optional since the S5C73M3 ISP and a raw image | ||
| 41 | sensor are usually inseparable and form a hybrid module. | ||
| 42 | |||
| 43 | Following properties are valid for the endpoint node(s): | ||
| 44 | |||
| 45 | endpoint subnode | ||
| 46 | ---------------- | ||
| 47 | |||
| 48 | - data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in | ||
| 49 | video-interfaces.txt. This sensor doesn't support data lane remapping | ||
| 50 | and physical lane indexes in subsequent elements of the array should | ||
| 51 | be only consecutive ascending values. | ||
| 52 | |||
| 53 | SPI device node | ||
| 54 | --------------- | ||
| 55 | |||
| 56 | Required properties: | ||
| 57 | |||
| 58 | - compatible : "samsung,s5c73m3"; | ||
| 59 | |||
| 60 | For more details see description of the SPI busses bindings | ||
| 61 | (../spi/spi-bus.txt) and bindings of a specific bus controller. | ||
| 62 | |||
| 63 | Example: | ||
| 64 | |||
| 65 | i2c@138a000000 { | ||
| 66 | ... | ||
| 67 | s5c73m3@3c { | ||
| 68 | compatible = "samsung,s5c73m3"; | ||
| 69 | reg = <0x3c>; | ||
| 70 | vdd-int-supply = <&buck9_reg>; | ||
| 71 | vdda-supply = <&ldo17_reg>; | ||
| 72 | vdd-reg-supply = <&cam_io_reg>; | ||
| 73 | vddio-host-supply = <&ldo18_reg>; | ||
| 74 | vddio-cis-supply = <&ldo9_reg>; | ||
| 75 | vdd-af-supply = <&cam_af_reg>; | ||
| 76 | clock-frequency = <24000000>; | ||
| 77 | clocks = <&clk 0>; | ||
| 78 | clock-names = "cis_extclk"; | ||
| 79 | xshutdown-gpios = <&gpf1 3 1>; | ||
| 80 | standby-gpios = <&gpm0 1 1>; | ||
| 81 | port { | ||
| 82 | s5c73m3_ep: endpoint { | ||
| 83 | remote-endpoint = <&csis0_ep>; | ||
| 84 | data-lanes = <1 2 3 4>; | ||
| 85 | }; | ||
| 86 | }; | ||
| 87 | }; | ||
| 88 | }; | ||
| 89 | |||
| 90 | spi@1392000 { | ||
| 91 | ... | ||
| 92 | s5c73m3_spi: s5c73m3@0 { | ||
| 93 | compatible = "samsung,s5c73m3"; | ||
| 94 | reg = <0>; | ||
| 95 | ... | ||
| 96 | }; | ||
| 97 | }; | ||
| @@ -1,58 +0,0 @@ | |||
| 1 | Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP | ||
| 2 | -------------------------------------------------------------------- | ||
| 3 | |||
| 4 | Required properties: | ||
| 5 | |||
| 6 | - compatible : "samsung,s5k5baf"; | ||
| 7 | - reg : I2C slave address of the sensor; | ||
| 8 | - vdda-supply : analog power supply 2.8V (2.6V to 3.0V); | ||
| 9 | - vddreg-supply : regulator input power supply 1.8V (1.7V to 1.9V) | ||
| 10 | or 2.8V (2.6V to 3.0); | ||
| 11 | - vddio-supply : I/O power supply 1.8V (1.65V to 1.95V) | ||
| 12 | or 2.8V (2.5V to 3.1V); | ||
| 13 | - stbyn-gpios : GPIO connected to STDBYN pin; | ||
| 14 | - rstn-gpios : GPIO connected to RSTN pin; | ||
| 15 | - clocks : list of phandle and clock specifier pairs | ||
| 16 | according to common clock bindings for the | ||
| 17 | clocks described in clock-names; | ||
| 18 | - clock-names : should include "mclk" for the sensor's master clock; | ||
| 19 | |||
| 20 | Optional properties: | ||
| 21 | |||
| 22 | - clock-frequency : the frequency at which the "mclk" clock should be | ||
| 23 | configured to operate, in Hz; if this property is not | ||
| 24 | specified default 24 MHz value will be used. | ||
| 25 | |||
| 26 | The device node should contain one 'port' child node with one child 'endpoint' | ||
| 27 | node, according to the bindings defined in Documentation/devicetree/bindings/ | ||
| 28 | media/video-interfaces.txt. The following are properties specific to those | ||
| 29 | nodes. | ||
| 30 | |||
| 31 | endpoint node | ||
| 32 | ------------- | ||
| 33 | |||
| 34 | - data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in | ||
| 35 | video-interfaces.txt. If present it should be <1> - the device | ||
| 36 | supports only one data lane without re-mapping. | ||
| 37 | |||
| 38 | Example: | ||
| 39 | |||
| 40 | s5k5bafx@2d { | ||
| 41 | compatible = "samsung,s5k5baf"; | ||
| 42 | reg = <0x2d>; | ||
| 43 | vdda-supply = <&cam_io_en_reg>; | ||
| 44 | vddreg-supply = <&vt_core_15v_reg>; | ||
| 45 | vddio-supply = <&vtcam_reg>; | ||
| 46 | stbyn-gpios = <&gpl2 0 1>; | ||
| 47 | rstn-gpios = <&gpl2 1 1>; | ||
| 48 | clock-names = "mclk"; | ||
| 49 | clocks = <&clock_cam 0>; | ||
| 50 | clock-frequency = <24000000>; | ||
| 51 | |||
| 52 | port { | ||
| 53 | s5k5bafx_ep: endpoint { | ||
| 54 | remote-endpoint = <&csis1_ep>; | ||
| 55 | data-lanes = <1>; | ||
| 56 | }; | ||
| 57 | }; | ||
| 58 | }; | ||
| @@ -1,33 +0,0 @@ | |||
| 1 | Samsung S5K6A3(YX) raw image sensor | ||
| 2 | --------------------------------- | ||
| 3 | |||
| 4 | S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces | ||
| 5 | and CCI (I2C compatible) control bus. | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | |||
| 9 | - compatible : "samsung,s5k6a3"; | ||
| 10 | - reg : I2C slave address of the sensor; | ||
| 11 | - svdda-supply : core voltage supply; | ||
| 12 | - svddio-supply : I/O voltage supply; | ||
| 13 | - afvdd-supply : AF (actuator) voltage supply; | ||
| 14 | - gpios : specifier of a GPIO connected to the RESET pin; | ||
| 15 | - clocks : should contain list of phandle and clock specifier pairs | ||
| 16 | according to common clock bindings for the clocks described | ||
| 17 | in the clock-names property; | ||
| 18 | - clock-names : should contain "extclk" entry for the sensor's EXTCLK clock; | ||
| 19 | |||
| 20 | Optional properties: | ||
| 21 | |||
| 22 | - clock-frequency : the frequency at which the "extclk" clock should be | ||
| 23 | configured to operate, in Hz; if this property is not | ||
| 24 | specified default 24 MHz value will be used. | ||
| 25 | |||
| 26 | The common video interfaces bindings (see video-interfaces.txt) should be | ||
| 27 | used to specify link to the image data receiver. The S5K6A3(YX) device | ||
| 28 | node should contain one 'port' child node with an 'endpoint' subnode. | ||
| 29 | |||
| 30 | Following properties are valid for the endpoint node: | ||
| 31 | |||
| 32 | - data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in | ||
| 33 | video-interfaces.txt. The sensor supports only one data lane. | ||
| @@ -1,26 +0,0 @@ | |||
| 1 | * Silicon Labs FM Radio receiver | ||
| 2 | |||
| 3 | The Silicon Labs Si470x is family of FM radio receivers with receive power scan | ||
| 4 | supporting 76-108 MHz, programmable through an I2C interface. | ||
| 5 | Some of them includes an RDS encoder. | ||
| 6 | |||
| 7 | Required Properties: | ||
| 8 | - compatible: Should contain "silabs,si470x" | ||
| 9 | - reg: the I2C address of the device | ||
| 10 | |||
| 11 | Optional Properties: | ||
| 12 | - interrupts : The interrupt number | ||
| 13 | - reset-gpios: GPIO specifier for the chips reset line | ||
| 14 | |||
| 15 | Example: | ||
| 16 | |||
| 17 | &i2c2 { | ||
| 18 | si470x@63 { | ||
| 19 | compatible = "silabs,si470x"; | ||
| 20 | reg = <0x63>; | ||
| 21 | |||
| 22 | interrupt-parent = <&gpj2>; | ||
| 23 | interrupts = <4 IRQ_TYPE_EDGE_FALLING>; | ||
| 24 | reset-gpios = <&gpj2 5 GPIO_ACTIVE_HIGH>; | ||
| 25 | }; | ||
| 26 | }; | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/silabs,si470x.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: Silicon Labs Si470x FM Radio Receiver | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Hans Verkuil <hverkuil@xs4all.nl> | ||
| 11 | - Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com> | ||
| 12 | |||
| 13 | properties: | ||
| 14 | compatible: | ||
| 15 | const: silabs,si470x | ||
| 16 | |||
| 17 | reg: | ||
| 18 | maxItems: 1 | ||
| 19 | |||
| 20 | interrupts: | ||
| 21 | maxItems: 1 | ||
| 22 | |||
| 23 | reset-gpios: | ||
| 24 | maxItems: 1 | ||
| 25 | |||
| 26 | required: | ||
| 27 | - compatible | ||
| 28 | - reg | ||
| 29 | |||
| 30 | additionalProperties: false | ||
| 31 | |||
| 32 | examples: | ||
| 33 | - | | ||
| 34 | #include <dt-bindings/gpio/gpio.h> | ||
| 35 | #include <dt-bindings/interrupt-controller/irq.h> | ||
| 36 | |||
| 37 | i2c { | ||
| 38 | #address-cells = <1>; | ||
| 39 | #size-cells = <0>; | ||
| 40 | |||
| 41 | fmradio@10 { | ||
| 42 | compatible = "silabs,si470x"; | ||
| 43 | reg = <0x10>; | ||
| 44 | interrupt-parent = <&gpj2>; | ||
| 45 | interrupts = <4 IRQ_TYPE_EDGE_FALLING>; | ||
| 46 | reset-gpios = <&gpj2 5 GPIO_ACTIVE_HIGH>; | ||
| 47 | }; | ||
| 48 | }; | ||
| @@ -1,53 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
| 2 | %YAML 1.2 | ||
| 3 | --- | ||
| 4 | $id: http://devicetree.org/schemas/media/st,stm32-cec.yaml# | ||
| 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# | ||
| 6 | |||
| 7 | title: STMicroelectronics STM32 CEC | ||
| 8 | |||
| 9 | maintainers: | ||
| 10 | - Yannick Fertre <yannick.fertre@foss.st.com> | ||
| 11 | |||
| 12 | properties: | ||
| 13 | compatible: | ||
| 14 | const: st,stm32-cec | ||
| 15 | |||
| 16 | reg: | ||
| 17 | maxItems: 1 | ||
| 18 | |||
| 19 | interrupts: | ||
| 20 | maxItems: 1 | ||
| 21 | |||
| 22 | clocks: | ||
| 23 | items: | ||
| 24 | - description: Module Clock | ||
| 25 | - description: Bus Clock | ||
| 26 | |||
| 27 | clock-names: | ||
| 28 | items: | ||
| 29 | - const: cec | ||
| 30 | - const: hdmi-cec | ||
| 31 | |||
| 32 | required: | ||
| 33 | - compatible | ||
| 34 | - reg | ||
| 35 | - interrupts | ||
| 36 | - clocks | ||
| 37 | - clock-names | ||
| 38 | |||
| 39 | additionalProperties: false | ||
| 40 | |||
| 41 | examples: | ||
| 42 | - | | ||
| 43 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
| 44 | #include <dt-bindings/clock/stm32mp1-clks.h> | ||
| 45 | cec: cec@40006c00 { | ||
| 46 | compatible = "st,stm32-cec"; | ||
| 47 | reg = <0x40006c00 0x400>; | ||
| 48 | interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>; | ||
| 49 | clocks = <&rcc CEC_K>, <&clk_lse>; | ||
| 50 | clock-names = "cec", "hdmi-cec"; | ||
| 51 | }; | ||
| 52 | |||
| 53 | ... | ||
| @@ -47,7 +47,7 @@ properties: · 46 unmodified lines | |||
| 47 | maxItems: 1 | 47 | maxItems: 1 |
| 48 | 48 | ||
| 49 | ti,camerrx-control: | 49 | ti,camerrx-control: |
| 50 | $ref: "/schemas/types.yaml#/definitions/phandle-array" | 50 | $ref: /schemas/types.yaml#/definitions/phandle-array |
| 51 | items: | 51 | items: |
| 52 | - items: | 52 | - items: |
| 53 | - description: phandle to device control module | 53 | - description: phandle to device control module |
| @@ -56,6 +56,28 @@ analogue data is never read from the pixel matrix that are outside the · 55 unmodified lines | |||
| 56 | configured selection rectangle that designates crop. The difference has an | 56 | configured selection rectangle that designates crop. The difference has an |
| 57 | effect in device timing and likely also in power consumption. | 57 | effect in device timing and likely also in power consumption. |
| 58 | 58 | ||
| 59 | CCS static data | ||
| 60 | --------------- | ||
| 61 | |||
| 62 | The MIPI CCS driver supports CCS static data for all compliant devices, | ||
| 63 | including not just those compliant with CCS 1.1 but also CCS 1.0 and SMIA(++). | ||
| 64 | For CCS the file names are formed as | ||
| 65 | |||
| 66 | ccs/ccs-sensor-vvvv-mmmm-rrrr.fw (sensor) and | ||
| 67 | ccs/ccs-module-vvvv-mmmm-rrrr.fw (module). | ||
| 68 | |||
| 69 | For SMIA++ compliant devices the corresponding file names are | ||
| 70 | |||
| 71 | ccs/smiapp-sensor-vv-mmmm-rr.fw (sensor) and | ||
| 72 | ccs/smiapp-module-vv-mmmm-rrrr.fw (module). | ||
| 73 | |||
| 74 | For SMIA (non-++) compliant devices the static data file name is | ||
| 75 | |||
| 76 | ccs/smia-sensor-vv-mmmm-rr.fw (sensor). | ||
| 77 | |||
| 78 | vvvv or vv denotes MIPI and SMIA manufacturer IDs respectively, mmmm model ID | ||
| 79 | and rrrr or rr revision number. | ||
| 80 | |||
| 59 | Register definition generator | 81 | Register definition generator |
| 60 | ----------------------------- | 82 | ----------------------------- |
| 61 | 83 | ||
| @@ -67,6 +67,7 @@ ioctls must be supported by all video overlay devices. · 66 unmodified lines | |||
| 67 | Setup | 67 | Setup |
| 68 | ===== | 68 | ===== |
| 69 | 69 | ||
| 70 | *Note: support for this has been removed.* | ||
| 70 | Before overlay can commence applications must program the driver with | 71 | Before overlay can commence applications must program the driver with |
| 71 | frame buffer parameters, namely the address and size of the frame buffer | 72 | frame buffer parameters, namely the address and size of the frame buffer |
| 72 | and the image format, for example RGB 5:6:5. The | 73 | and the image format, for example RGB 5:6:5. The |
| @@ -92,11 +93,13 @@ A driver may support any (or none) of five clipping/blending methods: · 19 unmodified lines | |||
| 92 | 1. Chroma-keying displays the overlaid image only where pixels in the | 93 | 1. Chroma-keying displays the overlaid image only where pixels in the |
| 93 | primary graphics surface assume a certain color. | 94 | primary graphics surface assume a certain color. |
| 94 | 95 | ||
| 95 | 2. A bitmap can be specified where each bit corresponds to a pixel in | 96 | 2. *Note: support for this has been removed.* |
| 97 | A bitmap can be specified where each bit corresponds to a pixel in | ||
| 96 | the overlaid image. When the bit is set, the corresponding video | 98 | the overlaid image. When the bit is set, the corresponding video |
| 97 | pixel is displayed, otherwise a pixel of the graphics surface. | 99 | pixel is displayed, otherwise a pixel of the graphics surface. |
| 98 | 100 | ||
| 99 | 3. A list of clipping rectangles can be specified. In these regions *no* | 101 | 3. *Note: support for this has been removed.* |
| 102 | A list of clipping rectangles can be specified. In these regions *no* | ||
| 100 | video is displayed, so the graphics surface can be seen here. | 103 | video is displayed, so the graphics surface can be seen here. |
| 101 | 104 | ||
| 102 | 4. The framebuffer has an alpha channel that can be used to clip or | 105 | 4. The framebuffer has an alpha channel that can be used to clip or |
| @@ -185,6 +188,7 @@ struct v4l2_window · 82 unmodified lines | |||
| 185 | be 0xRRGGBB on a little endian, 0xBBGGRR on a big endian host. | 188 | be 0xRRGGBB on a little endian, 0xBBGGRR on a big endian host. |
| 186 | 189 | ||
| 187 | ``struct v4l2_clip * clips`` | 190 | ``struct v4l2_clip * clips`` |
| 191 | *Note: support for this has been removed.* | ||
| 188 | When chroma-keying has *not* been negotiated and | 192 | When chroma-keying has *not* been negotiated and |
| 189 | :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` indicated this capability, | 193 | :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` indicated this capability, |
| 190 | applications can set this field to point to an array of clipping | 194 | applications can set this field to point to an array of clipping |
| @@ -201,6 +205,7 @@ struct v4l2_window · 10 unmodified lines | |||
| 201 | are undefined. | 205 | are undefined. |
| 202 | 206 | ||
| 203 | ``__u32 clipcount`` | 207 | ``__u32 clipcount`` |
| 208 | *Note: support for this has been removed.* | ||
| 204 | When the application set the ``clips`` field, this field must | 209 | When the application set the ``clips`` field, this field must |
| 205 | contain the number of clipping rectangles in the list. When clip | 210 | contain the number of clipping rectangles in the list. When clip |
| 206 | lists are not supported the driver ignores this field, its contents | 211 | lists are not supported the driver ignores this field, its contents |
| @@ -208,6 +213,7 @@ struct v4l2_window · 1 unmodified line | |||
| 208 | supported but no clipping is desired this field must be set to zero. | 213 | supported but no clipping is desired this field must be set to zero. |
| 209 | 214 | ||
| 210 | ``void * bitmap`` | 215 | ``void * bitmap`` |
| 216 | *Note: support for this has been removed.* | ||
| 211 | When chroma-keying has *not* been negotiated and | 217 | When chroma-keying has *not* been negotiated and |
| 212 | :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` indicated this capability, | 218 | :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` indicated this capability, |
| 213 | applications can set this field to point to a clipping bit mask. | 219 | applications can set this field to point to a clipping bit mask. |
| @@ -88,6 +88,11 @@ Compressed Formats · 87 unmodified lines | |||
| 88 | - ``V4L2_PIX_FMT_H263`` | 88 | - ``V4L2_PIX_FMT_H263`` |
| 89 | - 'H263' | 89 | - 'H263' |
| 90 | - H263 video elementary stream. | 90 | - H263 video elementary stream. |
| 91 | * .. _V4L2-PIX-FMT-SPK: | ||
| 92 | |||
| 93 | - ``V4L2_PIX_FMT_SPK`` | ||
| 94 | - 'SPK0' | ||
| 95 | - Sorenson Spark is an implementation of H.263 for use in Flash Video and Adobe Flash files | ||
| 91 | * .. _V4L2-PIX-FMT-MPEG1: | 96 | * .. _V4L2-PIX-FMT-MPEG1: |
| 92 | 97 | ||
| 93 | - ``V4L2_PIX_FMT_MPEG1`` | 98 | - ``V4L2_PIX_FMT_MPEG1`` |
| @@ -232,6 +237,26 @@ Compressed Formats · 138 unmodified lines | |||
| 232 | Metadata associated with the frame to decode is required to be passed | 237 | Metadata associated with the frame to decode is required to be passed |
| 233 | through the ``V4L2_CID_STATELESS_FWHT_PARAMS`` control. | 238 | through the ``V4L2_CID_STATELESS_FWHT_PARAMS`` control. |
| 234 | See the :ref:`associated Codec Control ID <codec-stateless-fwht>`. | 239 | See the :ref:`associated Codec Control ID <codec-stateless-fwht>`. |
| 240 | * .. _V4L2-PIX-FMT-RV30: | ||
| 241 | |||
| 242 | - ``V4L2_PIX_FMT_RV30`` | ||
| 243 | - 'RV30' | ||
| 244 | - RealVideo, or also spelled as Real Video, is a suite of | ||
| 245 | proprietary video compression formats developed by | ||
| 246 | RealNetworks - the specific format changes with the version. | ||
| 247 | RealVideo codecs are identified by four-character codes. | ||
| 248 | RV30 corresponds to RealVideo 8, suspected to be based | ||
| 249 | largely on an early draft of H.264 | ||
| 250 | * .. _V4L2-PIX-FMT-RV40: | ||
| 251 | |||
| 252 | - ``V4L2_PIX_FMT_RV40`` | ||
| 253 | - 'RV40' | ||
| 254 | - RV40 represents RealVideo 9 and RealVideo 10. | ||
| 255 | RealVideo 9, suspected to be based on H.264. | ||
| 256 | RealVideo 10, aka RV9 EHQ, This refers to an improved encoder | ||
| 257 | for the RV9 format that is fully backwards compatible with | ||
| 258 | RV9 players - the format and decoder did not change, only | ||
| 259 | the encoder did. As a result, it uses the same FourCC. | ||
| 235 | 260 | ||
| 236 | .. raw:: latex | 261 | .. raw:: latex |
| 237 | 262 | ||
| @@ -257,6 +257,34 @@ the second byte and Y'\ :sub:`7-0` in the third byte. · 256 unmodified lines | |||
| 257 | - The padding bits contain undefined values that must be ignored by all | 257 | - The padding bits contain undefined values that must be ignored by all |
| 258 | applications and drivers. | 258 | applications and drivers. |
| 259 | 259 | ||
| 260 | The next table lists the packed YUV 4:4:4 formats with 12 bits per component. | ||
| 261 | Expand the bits per component to 16 bits, data in the high bits, zeros in the low bits, | ||
| 262 | arranged in little endian order, storing 1 pixel in 6 bytes. | ||
| 263 | |||
| 264 | .. flat-table:: Packed YUV 4:4:4 Image Formats (12bpc) | ||
| 265 | :header-rows: 1 | ||
| 266 | :stub-columns: 0 | ||
| 267 | |||
| 268 | * - Identifier | ||
| 269 | - Code | ||
| 270 | - Byte 1-0 | ||
| 271 | - Byte 3-2 | ||
| 272 | - Byte 5-4 | ||
| 273 | - Byte 7-6 | ||
| 274 | - Byte 9-8 | ||
| 275 | - Byte 11-10 | ||
| 276 | |||
| 277 | * .. _V4L2-PIX-FMT-YUV48-12: | ||
| 278 | |||
| 279 | - ``V4L2_PIX_FMT_YUV48_12`` | ||
| 280 | - 'Y312' | ||
| 281 | |||
| 282 | - Y'\ :sub:`0` | ||
| 283 | - Cb\ :sub:`0` | ||
| 284 | - Cr\ :sub:`0` | ||
| 285 | - Y'\ :sub:`1` | ||
| 286 | - Cb\ :sub:`1` | ||
| 287 | - Cr\ :sub:`1` | ||
| 260 | 288 | ||
| 261 | 4:2:2 Subsampling | 289 | 4:2:2 Subsampling |
| 262 | ================= | 290 | ================= |
| @@ -953,6 +953,48 @@ number of bits for each component. · 952 unmodified lines | |||
| 953 | 953 | ||
| 954 | \endgroup | 954 | \endgroup |
| 955 | 955 | ||
| 956 | 12 Bits Per Component | ||
| 957 | ============================== | ||
| 958 | |||
| 959 | These formats store an RGB triplet in six or eight bytes, with 12 bits per component. | ||
| 960 | Expand the bits per component to 16 bits, data in the high bits, zeros in the low bits, | ||
| 961 | arranged in little endian order. | ||
| 962 | |||
| 963 | .. raw:: latex | ||
| 964 | |||
| 965 | \small | ||
| 966 | |||
| 967 | .. flat-table:: RGB Formats With 12 Bits Per Component | ||
| 968 | :header-rows: 1 | ||
| 969 | |||
| 970 | * - Identifier | ||
| 971 | - Code | ||
| 972 | - Byte 1-0 | ||
| 973 | - Byte 3-2 | ||
| 974 | - Byte 5-4 | ||
| 975 | - Byte 7-6 | ||
| 976 | * .. _V4L2-PIX-FMT-BGR48-12: | ||
| 977 | |||
| 978 | - ``V4L2_PIX_FMT_BGR48_12`` | ||
| 979 | - 'B312' | ||
| 980 | |||
| 981 | - B\ :sub:`15-4` | ||
| 982 | - G\ :sub:`15-4` | ||
| 983 | - R\ :sub:`15-4` | ||
| 984 | - | ||
| 985 | * .. _V4L2-PIX-FMT-ABGR64-12: | ||
| 986 | |||
| 987 | - ``V4L2_PIX_FMT_ABGR64_12`` | ||
| 988 | - 'B412' | ||
| 989 | |||
| 990 | - B\ :sub:`15-4` | ||
| 991 | - G\ :sub:`15-4` | ||
| 992 | - R\ :sub:`15-4` | ||
| 993 | - A\ :sub:`15-4` | ||
| 994 | |||
| 995 | .. raw:: latex | ||
| 996 | |||
| 997 | \normalsize | ||
| 956 | 998 | ||
| 957 | Deprecated RGB Formats | 999 | Deprecated RGB Formats |
| 958 | ====================== | 1000 | ====================== |
| @@ -103,6 +103,17 @@ are often referred to as greyscale formats. · 102 unmodified lines | |||
| 103 | - ... | 103 | - ... |
| 104 | - ... | 104 | - ... |
| 105 | 105 | ||
| 106 | * .. _V4L2-PIX-FMT-Y012: | ||
| 107 | |||
| 108 | - ``V4L2_PIX_FMT_Y012`` | ||
| 109 | - 'Y012' | ||
| 110 | |||
| 111 | - Y'\ :sub:`0`\ [3:0] `0000` | ||
| 112 | - Y'\ :sub:`0`\ [11:4] | ||
| 113 | - ... | ||
| 114 | - ... | ||
| 115 | - ... | ||
| 116 | |||
| 106 | * .. _V4L2-PIX-FMT-Y14: | 117 | * .. _V4L2-PIX-FMT-Y14: |
| 107 | 118 | ||
| 108 | - ``V4L2_PIX_FMT_Y14`` | 119 | - ``V4L2_PIX_FMT_Y14`` |
| @@ -146,3 +157,7 @@ are often referred to as greyscale formats. · 37 unmodified lines | |||
| 146 | than 16 bits. For example, 10 bits per pixel uses values in the range 0 to | 157 | than 16 bits. For example, 10 bits per pixel uses values in the range 0 to |
| 147 | 1023. For the IPU3_Y10 format 25 pixels are packed into 32 bytes, which | 158 | 1023. For the IPU3_Y10 format 25 pixels are packed into 32 bytes, which |
| 148 | leaves the 6 most significant bits of the last byte padded with 0. | 159 | leaves the 6 most significant bits of the last byte padded with 0. |
| 160 | |||
| 161 | For Y012 and Y12 formats, Y012 places its data in the 12 high bits, with | ||
| 162 | padding zeros in the 4 low bits, in contrast to the Y12 format, which has | ||
| 163 | its padding located in the most significant bits of the 16 bit word. | ||
| @@ -123,6 +123,20 @@ All components are stored with the same number of bits per component. · 122 unmodified lines | |||
| 123 | - Cb, Cr | 123 | - Cb, Cr |
| 124 | - Yes | 124 | - Yes |
| 125 | - 4x4 tiles | 125 | - 4x4 tiles |
| 126 | * - V4L2_PIX_FMT_P012 | ||
| 127 | - 'P012' | ||
| 128 | - 12 | ||
| 129 | - 4:2:0 | ||
| 130 | - Cb, Cr | ||
| 131 | - Yes | ||
| 132 | - Linear | ||
| 133 | * - V4L2_PIX_FMT_P012M | ||
| 134 | - 'PM12' | ||
| 135 | - 12 | ||
| 136 | - 4:2:0 | ||
| 137 | - Cb, Cr | ||
| 138 | - No | ||
| 139 | - Linear | ||
| 126 | * - V4L2_PIX_FMT_NV16 | 140 | * - V4L2_PIX_FMT_NV16 |
| 127 | - 'NV16' | 141 | - 'NV16' |
| 128 | - 8 | 142 | - 8 |
| @@ -586,6 +600,86 @@ Data in the 10 high bits, zeros in the 6 low bits, arranged in little endian ord · 457 unmodified lines | |||
| 586 | - Cb\ :sub:`11` | 600 | - Cb\ :sub:`11` |
| 587 | - Cr\ :sub:`11` | 601 | - Cr\ :sub:`11` |
| 588 | 602 | ||
| 603 | .. _V4L2-PIX-FMT-P012: | ||
| 604 | .. _V4L2-PIX-FMT-P012M: | ||
| 605 | |||
| 606 | P012 and P012M | ||
| 607 | -------------- | ||
| 608 | |||
| 609 | P012 is like NV12 with 12 bits per component, expanded to 16 bits. | ||
| 610 | Data in the 12 high bits, zeros in the 4 low bits, arranged in little endian order. | ||
| 611 | |||
| 612 | .. flat-table:: Sample 4x4 P012 Image | ||
| 613 | :header-rows: 0 | ||
| 614 | :stub-columns: 0 | ||
| 615 | |||
| 616 | * - start + 0: | ||
| 617 | - Y'\ :sub:`00` | ||
| 618 | - Y'\ :sub:`01` | ||
| 619 | - Y'\ :sub:`02` | ||
| 620 | - Y'\ :sub:`03` | ||
| 621 | * - start + 8: | ||
| 622 | - Y'\ :sub:`10` | ||
| 623 | - Y'\ :sub:`11` | ||
| 624 | - Y'\ :sub:`12` | ||
| 625 | - Y'\ :sub:`13` | ||
| 626 | * - start + 16: | ||
| 627 | - Y'\ :sub:`20` | ||
| 628 | - Y'\ :sub:`21` | ||
| 629 | - Y'\ :sub:`22` | ||
| 630 | - Y'\ :sub:`23` | ||
| 631 | * - start + 24: | ||
| 632 | - Y'\ :sub:`30` | ||
| 633 | - Y'\ :sub:`31` | ||
| 634 | - Y'\ :sub:`32` | ||
| 635 | - Y'\ :sub:`33` | ||
| 636 | * - start + 32: | ||
| 637 | - Cb\ :sub:`00` | ||
| 638 | - Cr\ :sub:`00` | ||
| 639 | - Cb\ :sub:`01` | ||
| 640 | - Cr\ :sub:`01` | ||
| 641 | * - start + 40: | ||
| 642 | - Cb\ :sub:`10` | ||
| 643 | - Cr\ :sub:`10` | ||
| 644 | - Cb\ :sub:`11` | ||
| 645 | - Cr\ :sub:`11` | ||
| 646 | |||
| 647 | .. flat-table:: Sample 4x4 P012M Image | ||
| 648 | :header-rows: 0 | ||
| 649 | :stub-columns: 0 | ||
| 650 | |||
| 651 | * - start0 + 0: | ||
| 652 | - Y'\ :sub:`00` | ||
| 653 | - Y'\ :sub:`01` | ||
| 654 | - Y'\ :sub:`02` | ||
| 655 | - Y'\ :sub:`03` | ||
| 656 | * - start0 + 8: | ||
| 657 | - Y'\ :sub:`10` | ||
| 658 | - Y'\ :sub:`11` | ||
| 659 | - Y'\ :sub:`12` | ||
| 660 | - Y'\ :sub:`13` | ||
| 661 | * - start0 + 16: | ||
| 662 | - Y'\ :sub:`20` | ||
| 663 | - Y'\ :sub:`21` | ||
| 664 | - Y'\ :sub:`22` | ||
| 665 | - Y'\ :sub:`23` | ||
| 666 | * - start0 + 24: | ||
| 667 | - Y'\ :sub:`30` | ||
| 668 | - Y'\ :sub:`31` | ||
| 669 | - Y'\ :sub:`32` | ||
| 670 | - Y'\ :sub:`33` | ||
| 671 | * - | ||
| 672 | * - start1 + 0: | ||
| 673 | - Cb\ :sub:`00` | ||
| 674 | - Cr\ :sub:`00` | ||
| 675 | - Cb\ :sub:`01` | ||
| 676 | - Cr\ :sub:`01` | ||
| 677 | * - start1 + 8: | ||
| 678 | - Cb\ :sub:`10` | ||
| 679 | - Cr\ :sub:`10` | ||
| 680 | - Cb\ :sub:`11` | ||
| 681 | - Cr\ :sub:`11` | ||
| 682 | |||
| 589 | 683 | ||
| 590 | Fully Planar YUV Formats | 684 | Fully Planar YUV Formats |
| 591 | ======================== | 685 | ======================== |
| @@ -72,6 +72,7 @@ Function Reference · 71 unmodified lines | |||
| 72 | vidioc-subdev-g-frame-interval | 72 | vidioc-subdev-g-frame-interval |
| 73 | vidioc-subdev-g-routing | 73 | vidioc-subdev-g-routing |
| 74 | vidioc-subdev-g-selection | 74 | vidioc-subdev-g-selection |
| 75 | vidioc-subdev-g-client-cap | ||
| 75 | vidioc-subdev-querycap | 76 | vidioc-subdev-querycap |
| 76 | vidioc-subscribe-event | 77 | vidioc-subscribe-event |
| 77 | func-mmap | 78 | func-mmap |
| @@ -185,6 +185,16 @@ still cause this situation. · 184 unmodified lines | |||
| 185 | - ``p_u32`` | 185 | - ``p_u32`` |
| 186 | - A pointer to a matrix control of unsigned 32-bit values. Valid if | 186 | - A pointer to a matrix control of unsigned 32-bit values. Valid if |
| 187 | this control is of type ``V4L2_CTRL_TYPE_U32``. | 187 | this control is of type ``V4L2_CTRL_TYPE_U32``. |
| 188 | * - __u32 * | ||
| 189 | - ``p_s32`` | ||
| 190 | - A pointer to a matrix control of signed 32-bit values. Valid if | ||
| 191 | this control is of type ``V4L2_CTRL_TYPE_INTEGER`` and | ||
| 192 | ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set. | ||
| 193 | * - __u32 * | ||
| 194 | - ``p_s64`` | ||
| 195 | - A pointer to a matrix control of signed 64-bit values. Valid if | ||
| 196 | this control is of type ``V4L2_CTRL_TYPE_INTEGER64`` and | ||
| 197 | ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set. | ||
| 188 | * - struct :c:type:`v4l2_area` * | 198 | * - struct :c:type:`v4l2_area` * |
| 189 | - ``p_area`` | 199 | - ``p_area`` |
| 190 | - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is | 200 | - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is |
| @@ -49,6 +49,9 @@ of a graphics card. A non-destructive overlay blends video images into a · 48 unmodified lines | |||
| 49 | VGA signal or graphics into a video signal. *Video Output Overlays* are | 49 | VGA signal or graphics into a video signal. *Video Output Overlays* are |
| 50 | always non-destructive. | 50 | always non-destructive. |
| 51 | 51 | ||
| 52 | Destructive overlay support has been removed: with modern GPUs and CPUs | ||
| 53 | this is no longer needed, and it was always a very dangerous feature. | ||
| 54 | |||
| 52 | To get the current parameters applications call the :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` | 55 | To get the current parameters applications call the :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` |
| 53 | ioctl with a pointer to a struct :c:type:`v4l2_framebuffer` | 56 | ioctl with a pointer to a struct :c:type:`v4l2_framebuffer` |
| 54 | structure. The driver fills all fields of the structure or returns an | 57 | structure. The driver fills all fields of the structure or returns an |
| @@ -63,18 +66,12 @@ this structure, the driver prepares for the overlay and returns the · 8 unmodified lines | |||
| 63 | framebuffer parameters as :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` does, or it returns an error | 66 | framebuffer parameters as :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` does, or it returns an error |
| 64 | code. | 67 | code. |
| 65 | 68 | ||
| 66 | To set the parameters for a *non-destructive Video Overlay*, | 69 | To set the parameters for a *Video Capture Overlay* |
| 67 | applications must initialize the ``flags`` field, the ``fmt`` | 70 | applications must initialize the ``flags`` field, the ``fmt`` |
| 68 | substructure, and call :ref:`VIDIOC_S_FBUF <VIDIOC_G_FBUF>`. Again the driver prepares for | 71 | substructure, and call :ref:`VIDIOC_S_FBUF <VIDIOC_G_FBUF>`. Again the driver prepares for |
| 69 | the overlay and returns the framebuffer parameters as :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` | 72 | the overlay and returns the framebuffer parameters as :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>` |
| 70 | does, or it returns an error code. | 73 | does, or it returns an error code. |
| 71 | 74 | ||
| 72 | For a *destructive Video Overlay* applications must additionally provide | ||
| 73 | a ``base`` address. Setting up a DMA to a random memory location can | ||
| 74 | jeopardize the system security, its stability or even damage the | ||
| 75 | hardware, therefore only the superuser can set the parameters for a | ||
| 76 | destructive video overlay. | ||
| 77 | |||
| 78 | .. tabularcolumns:: |p{3.5cm}|p{3.5cm}|p{3.5cm}|p{6.6cm}| | 75 | .. tabularcolumns:: |p{3.5cm}|p{3.5cm}|p{3.5cm}|p{6.6cm}| |
| 79 | 76 | ||
| 80 | .. c:type:: v4l2_framebuffer | 77 | .. c:type:: v4l2_framebuffer |
| @@ -100,17 +97,14 @@ destructive video overlay. · 19 unmodified lines | |||
| 100 | - ``base`` | 97 | - ``base`` |
| 101 | - | 98 | - |
| 102 | - Physical base address of the framebuffer, that is the address of | 99 | - Physical base address of the framebuffer, that is the address of |
| 103 | the pixel in the top left corner of the framebuffer. [#f1]_ | 100 | the pixel in the top left corner of the framebuffer. |
| 104 | * - | 101 | For :ref:`VIDIOC_S_FBUF <VIDIOC_G_FBUF>` this field is no longer supported |
| 105 | - | 102 | and the kernel will always set this to NULL. |
| 106 | - | 103 | For *Video Output Overlays* |
| 107 | - This field is irrelevant to *non-destructive Video Overlays*. For | 104 | the driver will return a valid base address, so applications can |
| 108 | *destructive Video Overlays* applications must provide a base | ||
| 109 | address. The driver may accept only base addresses which are a | ||
| 110 | multiple of two, four or eight bytes. For *Video Output Overlays* | ||
| 111 | the driver must return a valid base address, so applications can | ||
| 112 | find the corresponding Linux framebuffer device (see | 105 | find the corresponding Linux framebuffer device (see |
| 113 | :ref:`osd`). | 106 | :ref:`osd`). For *Video Capture Overlays* this field will always be |
| 107 | NULL. | ||
| 114 | * - struct | 108 | * - struct |
| 115 | - ``fmt`` | 109 | - ``fmt`` |
| 116 | - | 110 | - |
| @@ -136,8 +130,7 @@ destructive video overlay. · 19 unmodified lines | |||
| 136 | * - | 130 | * - |
| 137 | - | 131 | - |
| 138 | - | 132 | - |
| 139 | - For *destructive Video Overlays* applications must initialize this | 133 | - For *Video Output Overlays* the driver must return a valid |
| 140 | field. For *Video Output Overlays* the driver must return a valid | ||
| 141 | format. | 134 | format. |
| 142 | * - | 135 | * - |
| 143 | - | 136 | - |
| @@ -165,13 +158,6 @@ destructive video overlay. · 21 unmodified lines | |||
| 165 | 158 | ||
| 166 | This field is irrelevant to *non-destructive Video Overlays*. | 159 | This field is irrelevant to *non-destructive Video Overlays*. |
| 167 | 160 | ||
| 168 | For *destructive Video Overlays* both applications and drivers can | ||
| 169 | set this field to request padding bytes at the end of each line. | ||
| 170 | Drivers however may ignore the requested value, returning | ||
| 171 | ``width`` times bytes-per-pixel or a larger value required by the | ||
| 172 | hardware. That implies applications can just set this field to | ||
| 173 | zero to get a reasonable default. | ||
| 174 | |||
| 175 | For *Video Output Overlays* the driver must return a valid value. | 161 | For *Video Output Overlays* the driver must return a valid value. |
| 176 | 162 | ||
| 177 | Video hardware may access padding bytes, therefore they must | 163 | Video hardware may access padding bytes, therefore they must |
| @@ -190,9 +176,8 @@ destructive video overlay. · 12 unmodified lines | |||
| 190 | * - | 176 | * - |
| 191 | - __u32 | 177 | - __u32 |
| 192 | - ``sizeimage`` | 178 | - ``sizeimage`` |
| 193 | - This field is irrelevant to *non-destructive Video Overlays*. For | 179 | - This field is irrelevant to *non-destructive Video Overlays*. |
| 194 | *destructive Video Overlays* applications must initialize this | 180 | For *Video Output Overlays* the driver must return a valid |
| 195 | field. For *Video Output Overlays* the driver must return a valid | ||
| 196 | format. | 181 | format. |
| 197 | 182 | ||
| 198 | Together with ``base`` it defines the framebuffer memory | 183 | Together with ``base`` it defines the framebuffer memory |
| @@ -232,9 +217,11 @@ destructive video overlay. · 33 unmodified lines | |||
| 232 | * - ``V4L2_FBUF_CAP_LIST_CLIPPING`` | 217 | * - ``V4L2_FBUF_CAP_LIST_CLIPPING`` |
| 233 | - 0x0004 | 218 | - 0x0004 |
| 234 | - The device supports clipping using a list of clip rectangles. | 219 | - The device supports clipping using a list of clip rectangles. |
| 220 | Note that this is no longer supported. | ||
| 235 | * - ``V4L2_FBUF_CAP_BITMAP_CLIPPING`` | 221 | * - ``V4L2_FBUF_CAP_BITMAP_CLIPPING`` |
| 236 | - 0x0008 | 222 | - 0x0008 |
| 237 | - The device supports clipping using a bit mask. | 223 | - The device supports clipping using a bit mask. |
| 224 | Note that this is no longer supported. | ||
| 238 | * - ``V4L2_FBUF_CAP_LOCAL_ALPHA`` | 225 | * - ``V4L2_FBUF_CAP_LOCAL_ALPHA`` |
| 239 | - 0x0010 | 226 | - 0x0010 |
| 240 | - The device supports clipping/blending using the alpha channel of | 227 | - The device supports clipping/blending using the alpha channel of |
| @@ -342,10 +329,3 @@ EPERM · 101 unmodified lines | |||
| 342 | 329 | ||
| 343 | EINVAL | 330 | EINVAL |
| 344 | The :ref:`VIDIOC_S_FBUF <VIDIOC_G_FBUF>` parameters are unsuitable. | 331 | The :ref:`VIDIOC_S_FBUF <VIDIOC_G_FBUF>` parameters are unsuitable. |
| 345 | |||
| 346 | .. [#f1] | ||
| 347 | A physical base address may not suit all platforms. GK notes in | ||
| 348 | theory we should pass something like PCI device + memory region + | ||
| 349 | offset instead. If you encounter problems please discuss on the | ||
| 350 | linux-media mailing list: | ||
| 351 | `https://linuxtv.org/lists.php <https://linuxtv.org/lists.php>`__. | ||
| @@ -31,18 +31,30 @@ Arguments · 30 unmodified lines | |||
| 31 | Description | 31 | Description |
| 32 | =========== | 32 | =========== |
| 33 | 33 | ||
| 34 | This ioctl allows applications to enumerate all frame sizes supported by | 34 | This ioctl allows applications to access the enumeration of frame sizes |
| 35 | a sub-device on the given pad for the given media bus format. Supported | 35 | supported by a sub-device on the specified pad |
| 36 | formats can be retrieved with the | 36 | for the specified media bus format. |
| 37 | Supported formats can be retrieved with the | ||
| 37 | :ref:`VIDIOC_SUBDEV_ENUM_MBUS_CODE` | 38 | :ref:`VIDIOC_SUBDEV_ENUM_MBUS_CODE` |
| 38 | ioctl. | 39 | ioctl. |
| 39 | 40 | ||
| 40 | To enumerate frame sizes applications initialize the ``pad``, ``which`` | 41 | The enumerations are defined by the driver, and indexed using the ``index`` field |
| 41 | , ``code`` and ``index`` fields of the struct | 42 | of the struct :c:type:`v4l2_subdev_frame_size_enum`. |
| 42 | :c:type:`v4l2_subdev_mbus_code_enum` and | 43 | Each pair of ``pad`` and ``code`` correspond to a separate enumeration. |
| 43 | call the :ref:`VIDIOC_SUBDEV_ENUM_FRAME_SIZE` ioctl with a pointer to the | 44 | Each enumeration starts with the ``index`` of 0, and |
| 44 | structure. Drivers fill the minimum and maximum frame sizes or return an | 45 | the lowest invalid index marks the end of the enumeration. |
| 45 | EINVAL error code if one of the input parameters is invalid. | 46 | |
| 47 | Therefore, to enumerate frame sizes allowed on the specified pad | ||
| 48 | and using the specified mbus format, initialize the | ||
| 49 | ``pad``, ``which``, and ``code`` fields to desired values, | ||
| 50 | and set ``index`` to 0. | ||
| 51 | Then call the :ref:`VIDIOC_SUBDEV_ENUM_FRAME_SIZE` ioctl with a pointer to the | ||
| 52 | structure. | ||
| 53 | |||
| 54 | A successful call will return with minimum and maximum frame sizes filled in. | ||
| 55 | Repeat with increasing ``index`` until ``EINVAL`` is received. | ||
| 56 | ``EINVAL`` means that either no more entries are available in the enumeration, | ||
| 57 | or that an input parameter was invalid. | ||
| 46 | 58 | ||
| 47 | Sub-devices that only support discrete frame sizes (such as most | 59 | Sub-devices that only support discrete frame sizes (such as most |
| 48 | sensors) will return one or more frame sizes with identical minimum and | 60 | sensors) will return one or more frame sizes with identical minimum and |
| @@ -72,26 +84,28 @@ information about try formats. · 23 unmodified lines | |||
| 72 | 84 | ||
| 73 | * - __u32 | 85 | * - __u32 |
| 74 | - ``index`` | 86 | - ``index`` |
| 75 | - Number of the format in the enumeration, set by the application. | 87 | - Index of the frame size in the enumeration belonging to the given pad |
| 88 | and format. Filled in by the application. | ||
| 76 | * - __u32 | 89 | * - __u32 |
| 77 | - ``pad`` | 90 | - ``pad`` |
| 78 | - Pad number as reported by the media controller API. | 91 | - Pad number as reported by the media controller API. |
| 92 | Filled in by the application. | ||
| 79 | * - __u32 | 93 | * - __u32 |
| 80 | - ``code`` | 94 | - ``code`` |
| 81 | - The media bus format code, as defined in | 95 | - The media bus format code, as defined in |
| 82 | :ref:`v4l2-mbus-format`. | 96 | :ref:`v4l2-mbus-format`. Filled in by the application. |
| 83 | * - __u32 | 97 | * - __u32 |
| 84 | - ``min_width`` | 98 | - ``min_width`` |
| 85 | - Minimum frame width, in pixels. | 99 | - Minimum frame width, in pixels. Filled in by the driver. |
| 86 | * - __u32 | 100 | * - __u32 |
| 87 | - ``max_width`` | 101 | - ``max_width`` |
| 88 | - Maximum frame width, in pixels. | 102 | - Maximum frame width, in pixels. Filled in by the driver. |
| 89 | * - __u32 | 103 | * - __u32 |
| 90 | - ``min_height`` | 104 | - ``min_height`` |
| 91 | - Minimum frame height, in pixels. | 105 | - Minimum frame height, in pixels. Filled in by the driver. |
| 92 | * - __u32 | 106 | * - __u32 |
| 93 | - ``max_height`` | 107 | - ``max_height`` |
| 94 | - Maximum frame height, in pixels. | 108 | - Maximum frame height, in pixels. Filled in by the driver. |
| 95 | * - __u32 | 109 | * - __u32 |
| 96 | - ``which`` | 110 | - ``which`` |
| 97 | - Frame sizes to be enumerated, from enum | 111 | - Frame sizes to be enumerated, from enum |
| @@ -31,15 +31,28 @@ Arguments · 30 unmodified lines | |||
| 31 | Description | 31 | Description |
| 32 | =========== | 32 | =========== |
| 33 | 33 | ||
| 34 | To enumerate media bus formats available at a given sub-device pad | 34 | This call is used by the application to access the enumeration |
| 35 | applications initialize the ``pad``, ``which`` and ``index`` fields of | 35 | of media bus formats for the selected pad. |
| 36 | struct | 36 | |
| 37 | :c:type:`v4l2_subdev_mbus_code_enum` and | 37 | The enumerations are defined by the driver, and indexed using the ``index`` field |
| 38 | call the :ref:`VIDIOC_SUBDEV_ENUM_MBUS_CODE` ioctl with a pointer to this | 38 | of struct :c:type:`v4l2_subdev_mbus_code_enum`. |
| 39 | structure. Drivers fill the rest of the structure or return an ``EINVAL`` | 39 | Each enumeration starts with the ``index`` of 0, and |
| 40 | error code if either the ``pad`` or ``index`` are invalid. All media bus | 40 | the lowest invalid index marks the end of enumeration. |
| 41 | formats are enumerable by beginning at index zero and incrementing by | 41 | |
| 42 | one until ``EINVAL`` is returned. | 42 | Therefore, to enumerate media bus formats available at a given sub-device pad, |
| 43 | initialize the ``pad``, and ``which`` fields to desired values, | ||
| 44 | and set ``index`` to 0. | ||
| 45 | Then call the :ref:`VIDIOC_SUBDEV_ENUM_MBUS_CODE` ioctl | ||
| 46 | with a pointer to this structure. | ||
| 47 | |||
| 48 | A successful call will return with the ``code`` field filled in | ||
| 49 | with a mbus code value. | ||
| 50 | Repeat with increasing ``index`` until ``EINVAL`` is received. | ||
| 51 | ``EINVAL`` means that either ``pad`` is invalid, | ||
| 52 | or that there are no more codes available at this pad. | ||
| 53 | |||
| 54 | The driver must not return the same value of ``code`` for different indices | ||
| 55 | at the same pad. | ||
| 43 | 56 | ||
| 44 | Available media bus formats may depend on the current 'try' formats at | 57 | Available media bus formats may depend on the current 'try' formats at |
| 45 | other pads of the sub-device, as well as on the current active links. | 58 | other pads of the sub-device, as well as on the current active links. |
| @@ -57,14 +70,16 @@ information about the try formats. · 11 unmodified lines | |||
| 57 | 70 | ||
| 58 | * - __u32 | 71 | * - __u32 |
| 59 | - ``pad`` | 72 | - ``pad`` |
| 60 | - Pad number as reported by the media controller API. | 73 | - Pad number as reported by the media controller API. Filled in by the |
| 74 | application. | ||
| 61 | * - __u32 | 75 | * - __u32 |
| 62 | - ``index`` | 76 | - ``index`` |
| 63 | - Number of the format in the enumeration, set by the application. | 77 | - Index of the mbus code in the enumeration belonging to the given pad. |
| 78 | Filled in by the application. | ||
| 64 | * - __u32 | 79 | * - __u32 |
| 65 | - ``code`` | 80 | - ``code`` |
| 66 | - The media bus format code, as defined in | 81 | - The media bus format code, as defined in |
| 67 | :ref:`v4l2-mbus-format`. | 82 | :ref:`v4l2-mbus-format`. Filled in by the driver. |
| 68 | * - __u32 | 83 | * - __u32 |
| 69 | - ``which`` | 84 | - ``which`` |
| 70 | - Media bus format codes to be enumerated, from enum | 85 | - Media bus format codes to be enumerated, from enum |
| @@ -0,0 +1,83 @@ | |||
| 1 | .. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later | ||
| 2 | .. c:namespace:: V4L | ||
| 3 | |||
| 4 | .. _VIDIOC_SUBDEV_G_CLIENT_CAP: | ||
| 5 | |||
| 6 | ************************************************************ | ||
| 7 | ioctl VIDIOC_SUBDEV_G_CLIENT_CAP, VIDIOC_SUBDEV_S_CLIENT_CAP | ||
| 8 | ************************************************************ | ||
| 9 | |||
| 10 | Name | ||
| 11 | ==== | ||
| 12 | |||
| 13 | VIDIOC_SUBDEV_G_CLIENT_CAP - VIDIOC_SUBDEV_S_CLIENT_CAP - Get or set client | ||
| 14 | capabilities. | ||
| 15 | |||
| 16 | Synopsis | ||
| 17 | ======== | ||
| 18 | |||
| 19 | .. c:macro:: VIDIOC_SUBDEV_G_CLIENT_CAP | ||
| 20 | |||
| 21 | ``int ioctl(int fd, VIDIOC_SUBDEV_G_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)`` | ||
| 22 | |||
| 23 | .. c:macro:: VIDIOC_SUBDEV_S_CLIENT_CAP | ||
| 24 | |||
| 25 | ``int ioctl(int fd, VIDIOC_SUBDEV_S_CLIENT_CAP, struct v4l2_subdev_client_capability *argp)`` | ||
| 26 | |||
| 27 | Arguments | ||
| 28 | ========= | ||
| 29 | |||
| 30 | ``fd`` | ||
| 31 | File descriptor returned by :ref:`open() <func-open>`. | ||
| 32 | |||
| 33 | ``argp`` | ||
| 34 | Pointer to struct :c:type:`v4l2_subdev_client_capability`. | ||
| 35 | |||
| 36 | Description | ||
| 37 | =========== | ||
| 38 | |||
| 39 | These ioctls are used to get and set the client (the application using the | ||
| 40 | subdevice ioctls) capabilities. The client capabilities are stored in the file | ||
| 41 | handle of the opened subdev device node, and the client must set the | ||
| 42 | capabilities for each opened subdev separately. | ||
| 43 | |||
| 44 | By default no client capabilities are set when a subdev device node is opened. | ||
| 45 | |||
| 46 | The purpose of the client capabilities are to inform the kernel of the behavior | ||
| 47 | of the client, mainly related to maintaining compatibility with different | ||
| 48 | kernel and userspace versions. | ||
| 49 | |||
| 50 | The ``VIDIOC_SUBDEV_G_CLIENT_CAP`` ioctl returns the current client capabilities | ||
| 51 | associated with the file handle ``fd``. | ||
| 52 | |||
| 53 | The ``VIDIOC_SUBDEV_S_CLIENT_CAP`` ioctl sets client capabilities for the file | ||
| 54 | handle ``fd``. The new capabilities fully replace the current capabilities, the | ||
| 55 | ioctl can therefore also be used to remove capabilities that have previously | ||
| 56 | been set. | ||
| 57 | |||
| 58 | ``VIDIOC_SUBDEV_S_CLIENT_CAP`` modifies the struct | ||
| 59 | :c:type:`v4l2_subdev_client_capability` to reflect the capabilities that have | ||
| 60 | been accepted. A common case for the kernel not accepting a capability is that | ||
| 61 | the kernel is older than the headers the userspace uses, and thus the capability | ||
| 62 | is unknown to the kernel. | ||
| 63 | |||
| 64 | .. flat-table:: Client Capabilities | ||
| 65 | :header-rows: 1 | ||
| 66 | |||
| 67 | * - Capability | ||
| 68 | - Description | ||
| 69 | * - ``V4L2_SUBDEV_CLIENT_CAP_STREAMS`` | ||
| 70 | - The client is aware of streams. Setting this flag enables the use | ||
| 71 | of 'stream' fields (referring to the stream number) with various | ||
| 72 | ioctls. If this is not set (which is the default), the 'stream' fields | ||
| 73 | will be forced to 0 by the kernel. | ||
| 74 | |||
| 75 | Return Value | ||
| 76 | ============ | ||
| 77 | |||
| 78 | On success 0 is returned, on error -1 and the ``errno`` variable is set | ||
| 79 | appropriately. The generic error codes are described at the | ||
| 80 | :ref:`Generic Error Codes <gen-errors>` chapter. | ||
| 81 | |||
| 82 | ENOIOCTLCMD | ||
| 83 | The kernel does not support this ioctl. | ||
| @@ -1227,12 +1227,6 @@ F: Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml · 1226 unmodified lines | |||
| 1227 | F: drivers/iio/addac/ad74413r.c | 1227 | F: drivers/iio/addac/ad74413r.c |
| 1228 | F: include/dt-bindings/iio/addac/adi,ad74413r.h | 1228 | F: include/dt-bindings/iio/addac/adi,ad74413r.h |
| 1229 | 1229 | ||
| 1230 | ANALOG DEVICES INC AD9389B DRIVER | ||
| 1231 | M: Hans Verkuil <hverkuil-cisco@xs4all.nl> | ||
| 1232 | L: linux-media@vger.kernel.org | ||
| 1233 | S: Maintained | ||
| 1234 | F: drivers/media/i2c/ad9389b* | ||
| 1235 | |||
| 1236 | ANALOG DEVICES INC ADA4250 DRIVER | 1230 | ANALOG DEVICES INC ADA4250 DRIVER |
| 1237 | M: Antoniu Miclaus <antoniu.miclaus@analog.com> | 1231 | M: Antoniu Miclaus <antoniu.miclaus@analog.com> |
| 1238 | L: linux-iio@vger.kernel.org | 1232 | L: linux-iio@vger.kernel.org |
| @@ -8441,14 +8435,6 @@ L: platform-driver-x86@vger.kernel.org · 7202 unmodified lines | |||
| 8441 | S: Maintained | 8435 | S: Maintained |
| 8442 | F: drivers/platform/x86/fujitsu-laptop.c | 8436 | F: drivers/platform/x86/fujitsu-laptop.c |
| 8443 | 8437 | ||
| 8444 | FUJITSU M-5MO LS CAMERA ISP DRIVER | ||
| 8445 | M: Kyungmin Park <kyungmin.park@samsung.com> | ||
| 8446 | M: Heungjun Kim <riverful.kim@samsung.com> | ||
| 8447 | L: linux-media@vger.kernel.org | ||
| 8448 | S: Maintained | ||
| 8449 | F: drivers/media/i2c/m5mols/ | ||
| 8450 | F: include/media/i2c/m5mols.h | ||
| 8451 | |||
| 8452 | FUJITSU TABLET EXTRAS | 8438 | FUJITSU TABLET EXTRAS |
| 8453 | M: Robert Gerlach <khnz@gmx.de> | 8439 | M: Robert Gerlach <khnz@gmx.de> |
| 8454 | L: platform-driver-x86@vger.kernel.org | 8440 | L: platform-driver-x86@vger.kernel.org |
| @@ -14206,14 +14192,6 @@ L: linux-mtd@lists.infradead.org · 5751 unmodified lines | |||
| 14206 | S: Maintained | 14192 | S: Maintained |
| 14207 | F: drivers/mtd/devices/docg3* | 14193 | F: drivers/mtd/devices/docg3* |
| 14208 | 14194 | ||
| 14209 | MT9M032 APTINA SENSOR DRIVER | ||
| 14210 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 14211 | L: linux-media@vger.kernel.org | ||
| 14212 | S: Maintained | ||
| 14213 | T: git git://linuxtv.org/media_tree.git | ||
| 14214 | F: drivers/media/i2c/mt9m032.c | ||
| 14215 | F: include/media/i2c/mt9m032.h | ||
| 14216 | |||
| 14217 | MT9P031 APTINA CAMERA SENSOR | 14195 | MT9P031 APTINA CAMERA SENSOR |
| 14218 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 14196 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| 14219 | L: linux-media@vger.kernel.org | 14197 | L: linux-media@vger.kernel.org |
| @@ -14223,14 +14201,6 @@ F: Documentation/devicetree/bindings/media/i2c/aptina,mt9p031.yaml · 3 unmodified lines | |||
| 14223 | F: drivers/media/i2c/mt9p031.c | 14201 | F: drivers/media/i2c/mt9p031.c |
| 14224 | F: include/media/i2c/mt9p031.h | 14202 | F: include/media/i2c/mt9p031.h |
| 14225 | 14203 | ||
| 14226 | MT9T001 APTINA CAMERA SENSOR | ||
| 14227 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 14228 | L: linux-media@vger.kernel.org | ||
| 14229 | S: Maintained | ||
| 14230 | T: git git://linuxtv.org/media_tree.git | ||
| 14231 | F: drivers/media/i2c/mt9t001.c | ||
| 14232 | F: include/media/i2c/mt9t001.h | ||
| 14233 | |||
| 14234 | MT9T112 APTINA CAMERA SENSOR | 14204 | MT9T112 APTINA CAMERA SENSOR |
| 14235 | M: Jacopo Mondi <jacopo@jmondi.org> | 14205 | M: Jacopo Mondi <jacopo@jmondi.org> |
| 14236 | L: linux-media@vger.kernel.org | 14206 | L: linux-media@vger.kernel.org |
| @@ -14983,6 +14953,13 @@ F: Documentation/devicetree/bindings/clock/imx* · 746 unmodified lines | |||
| 14983 | F: drivers/clk/imx/ | 14953 | F: drivers/clk/imx/ |
| 14984 | F: include/dt-bindings/clock/imx* | 14954 | F: include/dt-bindings/clock/imx* |
| 14985 | 14955 | ||
| 14956 | NXP i.MX 8M ISI DRIVER | ||
| 14957 | M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 14958 | L: linux-media@vger.kernel.org | ||
| 14959 | S: Maintained | ||
| 14960 | F: Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml | ||
| 14961 | F: drivers/media/platform/nxp/imx8-isi/ | ||
| 14962 | |||
| 14986 | NXP i.MX 8MQ DCSS DRIVER | 14963 | NXP i.MX 8MQ DCSS DRIVER |
| 14987 | M: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com> | 14964 | M: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com> |
| 14988 | R: Lucas Stach <l.stach@pengutronix.de> | 14965 | R: Lucas Stach <l.stach@pengutronix.de> |
| @@ -15410,6 +15387,7 @@ M: Shunqian Zheng <zhengsq@rock-chips.com> · 421 unmodified lines | |||
| 15410 | L: linux-media@vger.kernel.org | 15387 | L: linux-media@vger.kernel.org |
| 15411 | S: Maintained | 15388 | S: Maintained |
| 15412 | T: git git://linuxtv.org/media_tree.git | 15389 | T: git git://linuxtv.org/media_tree.git |
| 15390 | F: Documentation/devicetree/bindings/media/i2c/ovti,ov2685.yaml | ||
| 15413 | F: drivers/media/i2c/ov2685.c | 15391 | F: drivers/media/i2c/ov2685.c |
| 15414 | 15392 | ||
| 15415 | OMNIVISION OV2740 SENSOR DRIVER | 15393 | OMNIVISION OV2740 SENSOR DRIVER |
| @@ -15501,7 +15479,7 @@ F: Documentation/devicetree/bindings/media/i2c/ov7740.txt · 85 unmodified lines | |||
| 15501 | F: drivers/media/i2c/ov7740.c | 15479 | F: drivers/media/i2c/ov7740.c |
| 15502 | 15480 | ||
| 15503 | OMNIVISION OV8856 SENSOR DRIVER | 15481 | OMNIVISION OV8856 SENSOR DRIVER |
| 15504 | M: Dongchun Zhu <dongchun.zhu@mediatek.com> | 15482 | M: Sakari Ailus <sakari.ailus@linux.intel.com> |
| 15505 | L: linux-media@vger.kernel.org | 15483 | L: linux-media@vger.kernel.org |
| 15506 | S: Maintained | 15484 | S: Maintained |
| 15507 | T: git git://linuxtv.org/media_tree.git | 15485 | T: git git://linuxtv.org/media_tree.git |
| @@ -17260,6 +17238,7 @@ F: drivers/net/wwan/qcom_bam_dmux.c · 1752 unmodified lines | |||
| 17260 | QUALCOMM CAMERA SUBSYSTEM DRIVER | 17238 | QUALCOMM CAMERA SUBSYSTEM DRIVER |
| 17261 | M: Robert Foss <rfoss@kernel.org> | 17239 | M: Robert Foss <rfoss@kernel.org> |
| 17262 | M: Todor Tomov <todor.too@gmail.com> | 17240 | M: Todor Tomov <todor.too@gmail.com> |
| 17241 | M: Bryan O'Donoghue <bryan.odonoghue@linaro.org> | ||
| 17263 | L: linux-media@vger.kernel.org | 17242 | L: linux-media@vger.kernel.org |
| 17264 | S: Maintained | 17243 | S: Maintained |
| 17265 | F: Documentation/admin-guide/media/qcom_camss.rst | 17244 | F: Documentation/admin-guide/media/qcom_camss.rst |
| @@ -18536,6 +18515,7 @@ M: Sylwester Nawrocki <s.nawrocki@samsung.com> · 1270 unmodified lines | |||
| 18536 | M: Andrzej Hajda <andrzej.hajda@intel.com> | 18515 | M: Andrzej Hajda <andrzej.hajda@intel.com> |
| 18537 | L: linux-media@vger.kernel.org | 18516 | L: linux-media@vger.kernel.org |
| 18538 | S: Supported | 18517 | S: Supported |
| 18518 | F: Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml | ||
| 18539 | F: drivers/media/i2c/s5c73m3/* | 18519 | F: drivers/media/i2c/s5c73m3/* |
| 18540 | 18520 | ||
| 18541 | SAMSUNG S5K5BAF CAMERA DRIVER | 18521 | SAMSUNG S5K5BAF CAMERA DRIVER |
| @@ -18560,6 +18540,11 @@ M: Sylwester Nawrocki <s.nawrocki@samsung.com> · 18 unmodified lines | |||
| 18560 | L: linux-media@vger.kernel.org | 18540 | L: linux-media@vger.kernel.org |
| 18561 | S: Supported | 18541 | S: Supported |
| 18562 | Q: https://patchwork.linuxtv.org/project/linux-media/list/ | 18542 | Q: https://patchwork.linuxtv.org/project/linux-media/list/ |
| 18543 | F: Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml | ||
| 18544 | F: Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml | ||
| 18545 | F: Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml | ||
| 18546 | F: Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml | ||
| 18547 | F: Documentation/devicetree/bindings/media/samsung,fimc.yaml | ||
| 18563 | F: drivers/media/platform/samsung/exynos4-is/ | 18548 | F: drivers/media/platform/samsung/exynos4-is/ |
| 18564 | 18549 | ||
| 18565 | SAMSUNG SOC CLOCK DRIVERS | 18550 | SAMSUNG SOC CLOCK DRIVERS |
| @@ -19038,6 +19023,7 @@ L: linux-media@vger.kernel.org · 472 unmodified lines | |||
| 19038 | S: Odd Fixes | 19023 | S: Odd Fixes |
| 19039 | W: https://linuxtv.org | 19024 | W: https://linuxtv.org |
| 19040 | T: git git://linuxtv.org/media_tree.git | 19025 | T: git git://linuxtv.org/media_tree.git |
| 19026 | F: Documentation/devicetree/bindings/media/silabs,si470x.yaml | ||
| 19041 | F: drivers/media/radio/si470x/radio-si470x-i2c.c | 19027 | F: drivers/media/radio/si470x/radio-si470x-i2c.c |
| 19042 | 19028 | ||
| 19043 | SI470X FM RADIO RECEIVER USB DRIVER | 19029 | SI470X FM RADIO RECEIVER USB DRIVER |
| @@ -1052,6 +1052,7 @@ void cec_received_msg_ts(struct cec_adapter *adap, · 1051 unmodified lines | |||
| 1052 | u8 cmd = msg->msg[1]; | 1052 | u8 cmd = msg->msg[1]; |
| 1053 | bool is_reply = false; | 1053 | bool is_reply = false; |
| 1054 | bool valid_la = true; | 1054 | bool valid_la = true; |
| 1055 | bool monitor_valid_la = true; | ||
| 1055 | u8 min_len = 0; | 1056 | u8 min_len = 0; |
| 1056 | 1057 | ||
| 1057 | if (WARN_ON(!msg->len || msg->len > CEC_MAX_MSG_SIZE)) | 1058 | if (WARN_ON(!msg->len || msg->len > CEC_MAX_MSG_SIZE)) |
| @@ -1093,8 +1094,10 @@ void cec_received_msg_ts(struct cec_adapter *adap, · 35 unmodified lines | |||
| 1093 | adap->last_initiator = 0xff; | 1094 | adap->last_initiator = 0xff; |
| 1094 | 1095 | ||
| 1095 | /* Check if this message was for us (directed or broadcast). */ | 1096 | /* Check if this message was for us (directed or broadcast). */ |
| 1096 | if (!cec_msg_is_broadcast(msg)) | 1097 | if (!cec_msg_is_broadcast(msg)) { |
| 1097 | valid_la = cec_has_log_addr(adap, msg_dest); | 1098 | valid_la = cec_has_log_addr(adap, msg_dest); |
| 1099 | monitor_valid_la = valid_la; | ||
| 1100 | } | ||
| 1098 | 1101 | ||
| 1099 | /* | 1102 | /* |
| 1100 | * Check if the length is not too short or if the message is a | 1103 | * Check if the length is not too short or if the message is a |
| @@ -1227,7 +1230,7 @@ void cec_received_msg_ts(struct cec_adapter *adap, · 126 unmodified lines | |||
| 1227 | mutex_unlock(&adap->lock); | 1230 | mutex_unlock(&adap->lock); |
| 1228 | 1231 | ||
| 1229 | /* Pass the message on to any monitoring filehandles */ | 1232 | /* Pass the message on to any monitoring filehandles */ |
| 1230 | cec_queue_msg_monitor(adap, msg, valid_la); | 1233 | cec_queue_msg_monitor(adap, msg, monitor_valid_la); |
| 1231 | 1234 | ||
| 1232 | /* We're done if it is not for us or a poll message */ | 1235 | /* We're done if it is not for us or a poll message */ |
| 1233 | if (!valid_la || msg->len <= 1) | 1236 | if (!valid_la || msg->len <= 1) |
| @@ -269,13 +269,12 @@ del_adap: · 268 unmodified lines | |||
| 269 | return ret; | 269 | return ret; |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | static int cec_gpio_remove(struct platform_device *pdev) | 272 | static void cec_gpio_remove(struct platform_device *pdev) |
| 273 | { | 273 | { |
| 274 | struct cec_gpio *cec = platform_get_drvdata(pdev); | 274 | struct cec_gpio *cec = platform_get_drvdata(pdev); |
| 275 | 275 | ||
| 276 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); | 276 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); |
| 277 | cec_unregister_adapter(cec->adap); | 277 | cec_unregister_adapter(cec->adap); |
| 278 | return 0; | ||
| 279 | } | 278 | } |
| 280 | 279 | ||
| 281 | static const struct of_device_id cec_gpio_match[] = { | 280 | static const struct of_device_id cec_gpio_match[] = { |
| @@ -288,7 +287,7 @@ MODULE_DEVICE_TABLE(of, cec_gpio_match); · 6 unmodified lines | |||
| 288 | 287 | ||
| 289 | static struct platform_driver cec_gpio_pdrv = { | 288 | static struct platform_driver cec_gpio_pdrv = { |
| 290 | .probe = cec_gpio_probe, | 289 | .probe = cec_gpio_probe, |
| 291 | .remove = cec_gpio_remove, | 290 | .remove_new = cec_gpio_remove, |
| 292 | .driver = { | 291 | .driver = { |
| 293 | .name = "cec-gpio", | 292 | .name = "cec-gpio", |
| 294 | .of_match_table = cec_gpio_match, | 293 | .of_match_table = cec_gpio_match, |
| @@ -225,6 +225,12 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { · 224 unmodified lines | |||
| 225 | { "Google", "Kinox", "0000:00:02.0", "Port B" }, | 225 | { "Google", "Kinox", "0000:00:02.0", "Port B" }, |
| 226 | /* Google Kuldax */ | 226 | /* Google Kuldax */ |
| 227 | { "Google", "Kuldax", "0000:00:02.0", "Port B" }, | 227 | { "Google", "Kuldax", "0000:00:02.0", "Port B" }, |
| 228 | /* Google Aurash */ | ||
| 229 | { "Google", "Aurash", "0000:00:02.0", "Port B" }, | ||
| 230 | /* Google Gladios */ | ||
| 231 | { "Google", "Gladios", "0000:00:02.0", "Port B" }, | ||
| 232 | /* Google Lisbon */ | ||
| 233 | { "Google", "Lisbon", "0000:00:02.0", "Port B" }, | ||
| 228 | }; | 234 | }; |
| 229 | 235 | ||
| 230 | static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, | 236 | static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, |
| @@ -326,31 +332,31 @@ out_probe_adapter: · 95 unmodified lines | |||
| 326 | return ret; | 332 | return ret; |
| 327 | } | 333 | } |
| 328 | 334 | ||
| 329 | static int cros_ec_cec_remove(struct platform_device *pdev) | 335 | static void cros_ec_cec_remove(struct platform_device *pdev) |
| 330 | { | 336 | { |
| 331 | struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev); | 337 | struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev); |
| 332 | struct device *dev = &pdev->dev; | 338 | struct device *dev = &pdev->dev; |
| 333 | int ret; | 339 | int ret; |
| 334 | 340 | ||
| 341 | /* | ||
| 342 | * blocking_notifier_chain_unregister() only fails if the notifier isn't | ||
| 343 | * in the list. We know it was added to it by .probe(), so there should | ||
| 344 | * be no need for error checking. Be cautious and still check. | ||
| 345 | */ | ||
| 335 | ret = blocking_notifier_chain_unregister( | 346 | ret = blocking_notifier_chain_unregister( |
| 336 | &cros_ec_cec->cros_ec->event_notifier, | 347 | &cros_ec_cec->cros_ec->event_notifier, |
| 337 | &cros_ec_cec->notifier); | 348 | &cros_ec_cec->notifier); |
| 338 | 349 | if (ret) | |
| 339 | if (ret) { | ||
| 340 | dev_err(dev, "failed to unregister notifier\n"); | 350 | dev_err(dev, "failed to unregister notifier\n"); |
| 341 | return ret; | ||
| 342 | } | ||
| 343 | 351 | ||
| 344 | cec_notifier_cec_adap_unregister(cros_ec_cec->notify, | 352 | cec_notifier_cec_adap_unregister(cros_ec_cec->notify, |
| 345 | cros_ec_cec->adap); | 353 | cros_ec_cec->adap); |
| 346 | cec_unregister_adapter(cros_ec_cec->adap); | 354 | cec_unregister_adapter(cros_ec_cec->adap); |
| 347 | |||
| 348 | return 0; | ||
| 349 | } | 355 | } |
| 350 | 356 | ||
| 351 | static struct platform_driver cros_ec_cec_driver = { | 357 | static struct platform_driver cros_ec_cec_driver = { |
| 352 | .probe = cros_ec_cec_probe, | 358 | .probe = cros_ec_cec_probe, |
| 353 | .remove = cros_ec_cec_remove, | 359 | .remove_new = cros_ec_cec_remove, |
| 354 | .driver = { | 360 | .driver = { |
| 355 | .name = DRV_NAME, | 361 | .name = DRV_NAME, |
| 356 | .pm = &cros_ec_cec_pm_ops, | 362 | .pm = &cros_ec_cec_pm_ops, |
| @@ -744,7 +744,7 @@ out_probe_adapter: · 743 unmodified lines | |||
| 744 | return ret; | 744 | return ret; |
| 745 | } | 745 | } |
| 746 | 746 | ||
| 747 | static int meson_ao_cec_g12a_remove(struct platform_device *pdev) | 747 | static void meson_ao_cec_g12a_remove(struct platform_device *pdev) |
| 748 | { | 748 | { |
| 749 | struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev); | 749 | struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev); |
| 750 | 750 | ||
| @@ -753,8 +753,6 @@ static int meson_ao_cec_g12a_remove(struct platform_device *pdev) · 2 unmodified lines | |||
| 753 | cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); | 753 | cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); |
| 754 | 754 | ||
| 755 | cec_unregister_adapter(ao_cec->adap); | 755 | cec_unregister_adapter(ao_cec->adap); |
| 756 | |||
| 757 | return 0; | ||
| 758 | } | 756 | } |
| 759 | 757 | ||
| 760 | static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = { | 758 | static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = { |
| @@ -780,7 +778,7 @@ MODULE_DEVICE_TABLE(of, meson_ao_cec_g12a_of_match); · 19 unmodified lines | |||
| 780 | 778 | ||
| 781 | static struct platform_driver meson_ao_cec_g12a_driver = { | 779 | static struct platform_driver meson_ao_cec_g12a_driver = { |
| 782 | .probe = meson_ao_cec_g12a_probe, | 780 | .probe = meson_ao_cec_g12a_probe, |
| 783 | .remove = meson_ao_cec_g12a_remove, | 781 | .remove_new = meson_ao_cec_g12a_remove, |
| 784 | .driver = { | 782 | .driver = { |
| 785 | .name = "meson-ao-cec-g12a", | 783 | .name = "meson-ao-cec-g12a", |
| 786 | .of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match), | 784 | .of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match), |
| @@ -696,7 +696,7 @@ out_probe_adapter: · 695 unmodified lines | |||
| 696 | return ret; | 696 | return ret; |
| 697 | } | 697 | } |
| 698 | 698 | ||
| 699 | static int meson_ao_cec_remove(struct platform_device *pdev) | 699 | static void meson_ao_cec_remove(struct platform_device *pdev) |
| 700 | { | 700 | { |
| 701 | struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev); | 701 | struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev); |
| 702 | 702 | ||
| @@ -704,8 +704,6 @@ static int meson_ao_cec_remove(struct platform_device *pdev) · 1 unmodified line | |||
| 704 | 704 | ||
| 705 | cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); | 705 | cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); |
| 706 | cec_unregister_adapter(ao_cec->adap); | 706 | cec_unregister_adapter(ao_cec->adap); |
| 707 | |||
| 708 | return 0; | ||
| 709 | } | 707 | } |
| 710 | 708 | ||
| 711 | static const struct of_device_id meson_ao_cec_of_match[] = { | 709 | static const struct of_device_id meson_ao_cec_of_match[] = { |
| @@ -716,7 +714,7 @@ MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match); · 4 unmodified lines | |||
| 716 | 714 | ||
| 717 | static struct platform_driver meson_ao_cec_driver = { | 715 | static struct platform_driver meson_ao_cec_driver = { |
| 718 | .probe = meson_ao_cec_probe, | 716 | .probe = meson_ao_cec_probe, |
| 719 | .remove = meson_ao_cec_remove, | 717 | .remove_new = meson_ao_cec_remove, |
| 720 | .driver = { | 718 | .driver = { |
| 721 | .name = "meson-ao-cec", | 719 | .name = "meson-ao-cec", |
| 722 | .of_match_table = of_match_ptr(meson_ao_cec_of_match), | 720 | .of_match_table = of_match_ptr(meson_ao_cec_of_match), |
| @@ -249,14 +249,13 @@ err_delete_adapter: · 248 unmodified lines | |||
| 249 | return ret; | 249 | return ret; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | static int s5p_cec_remove(struct platform_device *pdev) | 252 | static void s5p_cec_remove(struct platform_device *pdev) |
| 253 | { | 253 | { |
| 254 | struct s5p_cec_dev *cec = platform_get_drvdata(pdev); | 254 | struct s5p_cec_dev *cec = platform_get_drvdata(pdev); |
| 255 | 255 | ||
| 256 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); | 256 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); |
| 257 | cec_unregister_adapter(cec->adap); | 257 | cec_unregister_adapter(cec->adap); |
| 258 | pm_runtime_disable(&pdev->dev); | 258 | pm_runtime_disable(&pdev->dev); |
| 259 | return 0; | ||
| 260 | } | 259 | } |
| 261 | 260 | ||
| 262 | static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev) | 261 | static int __maybe_unused s5p_cec_runtime_suspend(struct device *dev) |
| @@ -295,7 +294,7 @@ MODULE_DEVICE_TABLE(of, s5p_cec_match); · 32 unmodified lines | |||
| 295 | 294 | ||
| 296 | static struct platform_driver s5p_cec_pdrv = { | 295 | static struct platform_driver s5p_cec_pdrv = { |
| 297 | .probe = s5p_cec_probe, | 296 | .probe = s5p_cec_probe, |
| 298 | .remove = s5p_cec_remove, | 297 | .remove_new = s5p_cec_remove, |
| 299 | .driver = { | 298 | .driver = { |
| 300 | .name = CEC_NAME, | 299 | .name = CEC_NAME, |
| 301 | .of_match_table = s5p_cec_match, | 300 | .of_match_table = s5p_cec_match, |
| @@ -668,7 +668,7 @@ err: · 667 unmodified lines | |||
| 668 | return ret; | 668 | return ret; |
| 669 | } | 669 | } |
| 670 | 670 | ||
| 671 | static int secocec_remove(struct platform_device *pdev) | 671 | static void secocec_remove(struct platform_device *pdev) |
| 672 | { | 672 | { |
| 673 | struct secocec_data *secocec = platform_get_drvdata(pdev); | 673 | struct secocec_data *secocec = platform_get_drvdata(pdev); |
| 674 | u16 val; | 674 | u16 val; |
| @@ -686,8 +686,6 @@ static int secocec_remove(struct platform_device *pdev) · 11 unmodified lines | |||
| 686 | release_region(BRA_SMB_BASE_ADDR, 7); | 686 | release_region(BRA_SMB_BASE_ADDR, 7); |
| 687 | 687 | ||
| 688 | dev_dbg(&pdev->dev, "CEC device removed\n"); | 688 | dev_dbg(&pdev->dev, "CEC device removed\n"); |
| 689 | |||
| 690 | return 0; | ||
| 691 | } | 689 | } |
| 692 | 690 | ||
| 693 | #ifdef CONFIG_PM_SLEEP | 691 | #ifdef CONFIG_PM_SLEEP |
| @@ -780,7 +778,7 @@ static struct platform_driver secocec_driver = { · 86 unmodified lines | |||
| 780 | .pm = SECOCEC_PM_OPS, | 778 | .pm = SECOCEC_PM_OPS, |
| 781 | }, | 779 | }, |
| 782 | .probe = secocec_probe, | 780 | .probe = secocec_probe, |
| 783 | .remove = secocec_remove, | 781 | .remove_new = secocec_remove, |
| 784 | }; | 782 | }; |
| 785 | 783 | ||
| 786 | module_platform_driver(secocec_driver); | 784 | module_platform_driver(secocec_driver); |
| @@ -364,14 +364,12 @@ err_delete_adapter: · 363 unmodified lines | |||
| 364 | return ret; | 364 | return ret; |
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | static int stih_cec_remove(struct platform_device *pdev) | 367 | static void stih_cec_remove(struct platform_device *pdev) |
| 368 | { | 368 | { |
| 369 | struct stih_cec *cec = platform_get_drvdata(pdev); | 369 | struct stih_cec *cec = platform_get_drvdata(pdev); |
| 370 | 370 | ||
| 371 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); | 371 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); |
| 372 | cec_unregister_adapter(cec->adap); | 372 | cec_unregister_adapter(cec->adap); |
| 373 | |||
| 374 | return 0; | ||
| 375 | } | 373 | } |
| 376 | 374 | ||
| 377 | static const struct of_device_id stih_cec_match[] = { | 375 | static const struct of_device_id stih_cec_match[] = { |
| @@ -384,7 +382,7 @@ MODULE_DEVICE_TABLE(of, stih_cec_match); · 6 unmodified lines | |||
| 384 | 382 | ||
| 385 | static struct platform_driver stih_cec_pdrv = { | 383 | static struct platform_driver stih_cec_pdrv = { |
| 386 | .probe = stih_cec_probe, | 384 | .probe = stih_cec_probe, |
| 387 | .remove = stih_cec_remove, | 385 | .remove_new = stih_cec_remove, |
| 388 | .driver = { | 386 | .driver = { |
| 389 | .name = CEC_NAME, | 387 | .name = CEC_NAME, |
| 390 | .of_match_table = stih_cec_match, | 388 | .of_match_table = stih_cec_match, |
| @@ -344,7 +344,7 @@ err_unprepare_cec_clk: · 343 unmodified lines | |||
| 344 | return ret; | 344 | return ret; |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | static int stm32_cec_remove(struct platform_device *pdev) | 347 | static void stm32_cec_remove(struct platform_device *pdev) |
| 348 | { | 348 | { |
| 349 | struct stm32_cec *cec = platform_get_drvdata(pdev); | 349 | struct stm32_cec *cec = platform_get_drvdata(pdev); |
| 350 | 350 | ||
| @@ -352,8 +352,6 @@ static int stm32_cec_remove(struct platform_device *pdev) · 1 unmodified line | |||
| 352 | clk_unprepare(cec->clk_hdmi_cec); | 352 | clk_unprepare(cec->clk_hdmi_cec); |
| 353 | 353 | ||
| 354 | cec_unregister_adapter(cec->adap); | 354 | cec_unregister_adapter(cec->adap); |
| 355 | |||
| 356 | return 0; | ||
| 357 | } | 355 | } |
| 358 | 356 | ||
| 359 | static const struct of_device_id stm32_cec_of_match[] = { | 357 | static const struct of_device_id stm32_cec_of_match[] = { |
| @@ -364,7 +362,7 @@ MODULE_DEVICE_TABLE(of, stm32_cec_of_match); · 4 unmodified lines | |||
| 364 | 362 | ||
| 365 | static struct platform_driver stm32_cec_driver = { | 363 | static struct platform_driver stm32_cec_driver = { |
| 366 | .probe = stm32_cec_probe, | 364 | .probe = stm32_cec_probe, |
| 367 | .remove = stm32_cec_remove, | 365 | .remove_new = stm32_cec_remove, |
| 368 | .driver = { | 366 | .driver = { |
| 369 | .name = CEC_NAME, | 367 | .name = CEC_NAME, |
| 370 | .of_match_table = stm32_cec_of_match, | 368 | .of_match_table = stm32_cec_of_match, |
| @@ -421,7 +421,7 @@ err_clk: · 420 unmodified lines | |||
| 421 | return ret; | 421 | return ret; |
| 422 | } | 422 | } |
| 423 | 423 | ||
| 424 | static int tegra_cec_remove(struct platform_device *pdev) | 424 | static void tegra_cec_remove(struct platform_device *pdev) |
| 425 | { | 425 | { |
| 426 | struct tegra_cec *cec = platform_get_drvdata(pdev); | 426 | struct tegra_cec *cec = platform_get_drvdata(pdev); |
| 427 | 427 | ||
| @@ -429,8 +429,6 @@ static int tegra_cec_remove(struct platform_device *pdev) · 1 unmodified line | |||
| 429 | 429 | ||
| 430 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); | 430 | cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); |
| 431 | cec_unregister_adapter(cec->adap); | 431 | cec_unregister_adapter(cec->adap); |
| 432 | |||
| 433 | return 0; | ||
| 434 | } | 432 | } |
| 435 | 433 | ||
| 436 | #ifdef CONFIG_PM | 434 | #ifdef CONFIG_PM |
| @@ -467,7 +465,7 @@ static struct platform_driver tegra_cec_driver = { · 30 unmodified lines | |||
| 467 | .of_match_table = of_match_ptr(tegra_cec_of_match), | 465 | .of_match_table = of_match_ptr(tegra_cec_of_match), |
| 468 | }, | 466 | }, |
| 469 | .probe = tegra_cec_probe, | 467 | .probe = tegra_cec_probe, |
| 470 | .remove = tegra_cec_remove, | 468 | .remove_new = tegra_cec_remove, |
| 471 | 469 | ||
| 472 | #ifdef CONFIG_PM | 470 | #ifdef CONFIG_PM |
| 473 | .suspend = tegra_cec_suspend, | 471 | .suspend = tegra_cec_suspend, |
| @@ -1,29 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | */ | ||
| 4 | struct btcx_riscmem { | ||
| 5 | unsigned int size; | ||
| 6 | __le32 *cpu; | ||
| 7 | __le32 *jmp; | ||
| 8 | dma_addr_t dma; | ||
| 9 | }; | ||
| 10 | |||
| 11 | struct btcx_skiplist { | ||
| 12 | int start; | ||
| 13 | int end; | ||
| 14 | }; | ||
| 15 | |||
| 16 | int btcx_riscmem_alloc(struct pci_dev *pci, | ||
| 17 | struct btcx_riscmem *risc, | ||
| 18 | unsigned int size); | ||
| 19 | void btcx_riscmem_free(struct pci_dev *pci, | ||
| 20 | struct btcx_riscmem *risc); | ||
| 21 | |||
| 22 | int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, | ||
| 23 | struct v4l2_clip *clips, unsigned int n); | ||
| 24 | int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, | ||
| 25 | unsigned int n, int mask); | ||
| 26 | void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); | ||
| 27 | void btcx_calc_skips(int line, int width, int *maxy, | ||
| 28 | struct btcx_skiplist *skips, unsigned int *nskips, | ||
| 29 | const struct v4l2_clip *clips, unsigned int nclips); | ||
| @@ -6,5 +6,5 @@ config VIDEO_SAA7146 · 5 unmodified lines | |||
| 6 | config VIDEO_SAA7146_VV | 6 | config VIDEO_SAA7146_VV |
| 7 | tristate | 7 | tristate |
| 8 | depends on VIDEO_DEV | 8 | depends on VIDEO_DEV |
| 9 | select VIDEOBUF_DMA_SG | 9 | select VIDEOBUF2_DMA_SG |
| 10 | select VIDEO_SAA7146 | 10 | select VIDEO_SAA7146 |
| @@ -37,7 +37,8 @@ void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) · 36 unmodified lines | |||
| 37 | { | 37 | { |
| 38 | u32 value = 0; | 38 | u32 value = 0; |
| 39 | 39 | ||
| 40 | BUG_ON(port > 3); | 40 | if (WARN_ON(port > 3)) |
| 41 | return; | ||
| 41 | 42 | ||
| 42 | value = saa7146_read(dev, GPIO_CTRL); | 43 | value = saa7146_read(dev, GPIO_CTRL); |
| 43 | value &= ~(0xff << (8*port)); | 44 | value &= ~(0xff << (8*port)); |
| @@ -148,7 +149,8 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) · 104 unmodified lines | |||
| 148 | pg = vmalloc_to_page(virt); | 149 | pg = vmalloc_to_page(virt); |
| 149 | if (NULL == pg) | 150 | if (NULL == pg) |
| 150 | goto err; | 151 | goto err; |
| 151 | BUG_ON(PageHighMem(pg)); | 152 | if (WARN_ON(PageHighMem(pg))) |
| 153 | return NULL; | ||
| 152 | sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); | 154 | sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); |
| 153 | } | 155 | } |
| 154 | return sglist; | 156 | return sglist; |
| @@ -233,46 +235,32 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) · 78 unmodified lines | |||
| 233 | } | 235 | } |
| 234 | 236 | ||
| 235 | int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, | 237 | int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, |
| 236 | struct scatterlist *list, int sglen ) | 238 | struct scatterlist *list, int sglen) |
| 237 | { | 239 | { |
| 240 | struct sg_dma_page_iter dma_iter; | ||
| 238 | __le32 *ptr, fill; | 241 | __le32 *ptr, fill; |
| 239 | int nr_pages = 0; | 242 | int nr_pages = 0; |
| 240 | int i,p; | 243 | int i; |
| 241 | 244 | ||
| 242 | BUG_ON(0 == sglen); | 245 | if (WARN_ON(!sglen) || |
| 243 | BUG_ON(list->offset > PAGE_SIZE); | 246 | WARN_ON(list->offset > PAGE_SIZE)) |
| 247 | return -EIO; | ||
| 244 | 248 | ||
| 245 | /* if we have a user buffer, the first page may not be | 249 | /* if we have a user buffer, the first page may not be |
| 246 | aligned to a page boundary. */ | 250 | aligned to a page boundary. */ |
| 247 | pt->offset = list->offset; | 251 | pt->offset = list->offset; |
| 248 | 252 | ||
| 249 | ptr = pt->cpu; | 253 | ptr = pt->cpu; |
| 250 | for (i = 0; i < sglen; i++, list++) { | 254 | for_each_sg_dma_page(list, &dma_iter, sglen, 0) { |
| 251 | /* | 255 | *ptr++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter)); |
| 252 | pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n", | 256 | nr_pages++; |
| 253 | i, sg_dma_address(list), sg_dma_len(list), | ||
| 254 | list->offset); | ||
| 255 | */ | ||
| 256 | for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr++) { | ||
| 257 | *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096); | ||
| 258 | nr_pages++; | ||
| 259 | } | ||
| 260 | } | 257 | } |
| 261 | 258 | ||
| 262 | 259 | ||
| 263 | /* safety; fill the page table up with the last valid page */ | 260 | /* safety; fill the page table up with the last valid page */ |
| 264 | fill = *(ptr-1); | 261 | fill = *(ptr-1); |
| 265 | for(i=nr_pages;i<1024;i++) { | 262 | for (i = nr_pages; i < 1024; i++) |
| 266 | *ptr++ = fill; | 263 | *ptr++ = fill; |
| 267 | } | ||
| 268 | |||
| 269 | /* | ||
| 270 | ptr = pt->cpu; | ||
| 271 | pr_debug("offset: %d\n", pt->offset); | ||
| 272 | for(i=0;i<5;i++) { | ||
| 273 | pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]); | ||
| 274 | } | ||
| 275 | */ | ||
| 276 | return 0; | 264 | return 0; |
| 277 | } | 265 | } |
| 278 | 266 | ||
| @@ -7,12 +7,11 @@ · 6 unmodified lines | |||
| 7 | /****************************************************************************/ | 7 | /****************************************************************************/ |
| 8 | /* resource management functions, shamelessly stolen from saa7134 driver */ | 8 | /* resource management functions, shamelessly stolen from saa7134 driver */ |
| 9 | 9 | ||
| 10 | int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) | 10 | int saa7146_res_get(struct saa7146_dev *dev, unsigned int bit) |
| 11 | { | 11 | { |
| 12 | struct saa7146_dev *dev = fh->dev; | ||
| 13 | struct saa7146_vv *vv = dev->vv_data; | 12 | struct saa7146_vv *vv = dev->vv_data; |
| 14 | 13 | ||
| 15 | if (fh->resources & bit) { | 14 | if (vv->resources & bit) { |
| 16 | DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n", | 15 | DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n", |
| 17 | bit, vv->resources); | 16 | bit, vv->resources); |
| 18 | /* have it already allocated */ | 17 | /* have it already allocated */ |
| @@ -27,41 +26,22 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) · 8 unmodified lines | |||
| 27 | return 0; | 26 | return 0; |
| 28 | } | 27 | } |
| 29 | /* it's free, grab it */ | 28 | /* it's free, grab it */ |
| 30 | fh->resources |= bit; | ||
| 31 | vv->resources |= bit; | 29 | vv->resources |= bit; |
| 32 | DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources); | 30 | DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources); |
| 33 | return 1; | 31 | return 1; |
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) | 34 | void saa7146_res_free(struct saa7146_dev *dev, unsigned int bits) |
| 37 | { | 35 | { |
| 38 | struct saa7146_dev *dev = fh->dev; | ||
| 39 | struct saa7146_vv *vv = dev->vv_data; | 36 | struct saa7146_vv *vv = dev->vv_data; |
| 40 | 37 | ||
| 41 | BUG_ON((fh->resources & bits) != bits); | 38 | WARN_ON((vv->resources & bits) != bits); |
| 42 | 39 | ||
| 43 | fh->resources &= ~bits; | ||
| 44 | vv->resources &= ~bits; | 40 | vv->resources &= ~bits; |
| 45 | DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources); | 41 | DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources); |
| 46 | } | 42 | } |
| 47 | 43 | ||
| 48 | 44 | ||
| 49 | /********************************************************************************/ | ||
| 50 | /* common dma functions */ | ||
| 51 | |||
| 52 | void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, | ||
| 53 | struct saa7146_buf *buf) | ||
| 54 | { | ||
| 55 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
| 56 | DEB_EE("dev:%p, buf:%p\n", dev, buf); | ||
| 57 | |||
| 58 | videobuf_waiton(q, &buf->vb, 0, 0); | ||
| 59 | videobuf_dma_unmap(q->dev, dma); | ||
| 60 | videobuf_dma_free(dma); | ||
| 61 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | ||
| 62 | } | ||
| 63 | |||
| 64 | |||
| 65 | /********************************************************************************/ | 45 | /********************************************************************************/ |
| 66 | /* common buffer functions */ | 46 | /* common buffer functions */ |
| 67 | 47 | ||
| @@ -72,15 +52,15 @@ int saa7146_buffer_queue(struct saa7146_dev *dev, · 4 unmodified lines | |||
| 72 | assert_spin_locked(&dev->slock); | 52 | assert_spin_locked(&dev->slock); |
| 73 | DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf); | 53 | DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf); |
| 74 | 54 | ||
| 75 | BUG_ON(!q); | 55 | if (WARN_ON(!q)) |
| 56 | return -EIO; | ||
| 76 | 57 | ||
| 77 | if (NULL == q->curr) { | 58 | if (NULL == q->curr) { |
| 78 | q->curr = buf; | 59 | q->curr = buf; |
| 79 | DEB_D("immediately activating buffer %p\n", buf); | 60 | DEB_D("immediately activating buffer %p\n", buf); |
| 80 | buf->activate(dev,buf,NULL); | 61 | buf->activate(dev,buf,NULL); |
| 81 | } else { | 62 | } else { |
| 82 | list_add_tail(&buf->vb.queue,&q->queue); | 63 | list_add_tail(&buf->list, &q->queue); |
| 83 | buf->vb.state = VIDEOBUF_QUEUED; | ||
| 84 | DEB_D("adding buffer %p to queue. (active buffer present)\n", | 64 | DEB_D("adding buffer %p to queue. (active buffer present)\n", |
| 85 | buf); | 65 | buf); |
| 86 | } | 66 | } |
| @@ -91,21 +71,31 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, · 4 unmodified lines | |||
| 91 | struct saa7146_dmaqueue *q, | 71 | struct saa7146_dmaqueue *q, |
| 92 | int state) | 72 | int state) |
| 93 | { | 73 | { |
| 74 | struct saa7146_vv *vv = dev->vv_data; | ||
| 75 | struct saa7146_buf *buf = q->curr; | ||
| 76 | |||
| 94 | assert_spin_locked(&dev->slock); | 77 | assert_spin_locked(&dev->slock); |
| 95 | DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); | 78 | DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); |
| 96 | DEB_EE("q->curr:%p\n", q->curr); | 79 | DEB_EE("q->curr:%p\n", q->curr); |
| 97 | 80 | ||
| 98 | /* finish current buffer */ | 81 | /* finish current buffer */ |
| 99 | if (NULL == q->curr) { | 82 | if (!buf) { |
| 100 | DEB_D("aiii. no current buffer\n"); | 83 | DEB_D("aiii. no current buffer\n"); |
| 101 | return; | 84 | return; |
| 102 | } | 85 | } |
| 103 | 86 | ||
| 104 | q->curr->vb.state = state; | ||
| 105 | q->curr->vb.ts = ktime_get_ns(); | ||
| 106 | wake_up(&q->curr->vb.done); | ||
| 107 | |||
| 108 | q->curr = NULL; | 87 | q->curr = NULL; |
| 88 | buf->vb.vb2_buf.timestamp = ktime_get_ns(); | ||
| 89 | if (vv->video_fmt.field == V4L2_FIELD_ALTERNATE) | ||
| 90 | buf->vb.field = vv->last_field; | ||
| 91 | else if (vv->video_fmt.field == V4L2_FIELD_ANY) | ||
| 92 | buf->vb.field = (vv->video_fmt.height > vv->standard->v_max_out / 2) | ||
| 93 | ? V4L2_FIELD_INTERLACED | ||
| 94 | : V4L2_FIELD_BOTTOM; | ||
| 95 | else | ||
| 96 | buf->vb.field = vv->video_fmt.field; | ||
| 97 | buf->vb.sequence = vv->seqnr++; | ||
| 98 | vb2_buffer_done(&buf->vb.vb2_buf, state); | ||
| 109 | } | 99 | } |
| 110 | 100 | ||
| 111 | void saa7146_buffer_next(struct saa7146_dev *dev, | 101 | void saa7146_buffer_next(struct saa7146_dev *dev, |
| @@ -113,17 +103,18 @@ void saa7146_buffer_next(struct saa7146_dev *dev, · 1 unmodified line | |||
| 113 | { | 103 | { |
| 114 | struct saa7146_buf *buf,*next = NULL; | 104 | struct saa7146_buf *buf,*next = NULL; |
| 115 | 105 | ||
| 116 | BUG_ON(!q); | 106 | if (WARN_ON(!q)) |
| 107 | return; | ||
| 117 | 108 | ||
| 118 | DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi); | 109 | DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi); |
| 119 | 110 | ||
| 120 | assert_spin_locked(&dev->slock); | 111 | assert_spin_locked(&dev->slock); |
| 121 | if (!list_empty(&q->queue)) { | 112 | if (!list_empty(&q->queue)) { |
| 122 | /* activate next one from queue */ | 113 | /* activate next one from queue */ |
| 123 | buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue); | 114 | buf = list_entry(q->queue.next, struct saa7146_buf, list); |
| 124 | list_del(&buf->vb.queue); | 115 | list_del(&buf->list); |
| 125 | if (!list_empty(&q->queue)) | 116 | if (!list_empty(&q->queue)) |
| 126 | next = list_entry(q->queue.next,struct saa7146_buf, vb.queue); | 117 | next = list_entry(q->queue.next, struct saa7146_buf, list); |
| 127 | q->curr = buf; | 118 | q->curr = buf; |
| 128 | DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n", | 119 | DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n", |
| 129 | buf, q->queue.prev, q->queue.next); | 120 | buf, q->queue.prev, q->queue.next); |
| @@ -171,7 +162,7 @@ void saa7146_buffer_timeout(struct timer_list *t) · 41 unmodified lines | |||
| 171 | spin_lock_irqsave(&dev->slock,flags); | 162 | spin_lock_irqsave(&dev->slock,flags); |
| 172 | if (q->curr) { | 163 | if (q->curr) { |
| 173 | DEB_D("timeout on %p\n", q->curr); | 164 | DEB_D("timeout on %p\n", q->curr); |
| 174 | saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR); | 165 | saa7146_buffer_finish(dev, q, VB2_BUF_STATE_ERROR); |
| 175 | } | 166 | } |
| 176 | 167 | ||
| 177 | /* we don't restart the transfer here like other drivers do. when | 168 | /* we don't restart the transfer here like other drivers do. when |
| @@ -180,256 +171,39 @@ void saa7146_buffer_timeout(struct timer_list *t) · 2 unmodified lines | |||
| 180 | we mess up our capture logic. if a timeout occurs on another buffer, | 171 | we mess up our capture logic. if a timeout occurs on another buffer, |
| 181 | then something is seriously broken before, so no need to buffer the | 172 | then something is seriously broken before, so no need to buffer the |
| 182 | next capture IMHO... */ | 173 | next capture IMHO... */ |
| 183 | /* | 174 | |
| 184 | saa7146_buffer_next(dev,q); | 175 | saa7146_buffer_next(dev, q, 0); |
| 185 | */ | 176 | |
| 186 | spin_unlock_irqrestore(&dev->slock,flags); | 177 | spin_unlock_irqrestore(&dev->slock,flags); |
| 187 | } | 178 | } |
| 188 | 179 | ||
| 189 | /********************************************************************************/ | 180 | /********************************************************************************/ |
| 190 | /* file operations */ | 181 | /* file operations */ |
| 191 | 182 | ||
| 192 | static int fops_open(struct file *file) | 183 | static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) |
| 193 | { | 184 | { |
| 194 | struct video_device *vdev = video_devdata(file); | 185 | struct video_device *vdev = video_devdata(file); |
| 195 | struct saa7146_dev *dev = video_drvdata(file); | 186 | struct saa7146_dev *dev = video_drvdata(file); |
| 196 | struct saa7146_fh *fh = NULL; | 187 | int ret; |
| 197 | int result = 0; | ||
| 198 | |||
| 199 | DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); | ||
| 200 | |||
| 201 | if (mutex_lock_interruptible(vdev->lock)) | ||
| 202 | return -ERESTARTSYS; | ||
| 203 | |||
| 204 | DEB_D("using: %p\n", dev); | ||
| 205 | |||
| 206 | /* check if an extension is registered */ | ||
| 207 | if( NULL == dev->ext ) { | ||
| 208 | DEB_S("no extension registered for this device\n"); | ||
| 209 | result = -ENODEV; | ||
| 210 | goto out; | ||
| 211 | } | ||
| 212 | |||
| 213 | /* allocate per open data */ | ||
| 214 | fh = kzalloc(sizeof(*fh),GFP_KERNEL); | ||
| 215 | if (NULL == fh) { | ||
| 216 | DEB_S("cannot allocate memory for per open data\n"); | ||
| 217 | result = -ENOMEM; | ||
| 218 | goto out; | ||
| 219 | } | ||
| 220 | |||
| 221 | v4l2_fh_init(&fh->fh, vdev); | ||
| 222 | |||
| 223 | file->private_data = &fh->fh; | ||
| 224 | fh->dev = dev; | ||
| 225 | |||
| 226 | if (vdev->vfl_type == VFL_TYPE_VBI) { | ||
| 227 | DEB_S("initializing vbi...\n"); | ||
| 228 | if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) | ||
| 229 | result = saa7146_vbi_uops.open(dev,file); | ||
| 230 | if (dev->ext_vv_data->vbi_fops.open) | ||
| 231 | dev->ext_vv_data->vbi_fops.open(file); | ||
| 232 | } else { | ||
| 233 | DEB_S("initializing video...\n"); | ||
| 234 | result = saa7146_video_uops.open(dev,file); | ||
| 235 | } | ||
| 236 | |||
| 237 | if (0 != result) { | ||
| 238 | goto out; | ||
| 239 | } | ||
| 240 | |||
| 241 | if( 0 == try_module_get(dev->ext->module)) { | ||
| 242 | result = -EINVAL; | ||
| 243 | goto out; | ||
| 244 | } | ||
| 245 | |||
| 246 | result = 0; | ||
| 247 | v4l2_fh_add(&fh->fh); | ||
| 248 | out: | ||
| 249 | if (fh && result != 0) { | ||
| 250 | kfree(fh); | ||
| 251 | file->private_data = NULL; | ||
| 252 | } | ||
| 253 | mutex_unlock(vdev->lock); | ||
| 254 | return result; | ||
| 255 | } | ||
| 256 | |||
| 257 | static int fops_release(struct file *file) | ||
| 258 | { | ||
| 259 | struct video_device *vdev = video_devdata(file); | ||
| 260 | struct saa7146_fh *fh = file->private_data; | ||
| 261 | struct saa7146_dev *dev = fh->dev; | ||
| 262 | |||
| 263 | DEB_EE("file:%p\n", file); | ||
| 264 | |||
| 265 | mutex_lock(vdev->lock); | ||
| 266 | |||
| 267 | if (vdev->vfl_type == VFL_TYPE_VBI) { | ||
| 268 | if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) | ||
| 269 | saa7146_vbi_uops.release(dev,file); | ||
| 270 | if (dev->ext_vv_data->vbi_fops.release) | ||
| 271 | dev->ext_vv_data->vbi_fops.release(file); | ||
| 272 | } else { | ||
| 273 | saa7146_video_uops.release(dev,file); | ||
| 274 | } | ||
| 275 | |||
| 276 | v4l2_fh_del(&fh->fh); | ||
| 277 | v4l2_fh_exit(&fh->fh); | ||
| 278 | module_put(dev->ext->module); | ||
| 279 | file->private_data = NULL; | ||
| 280 | kfree(fh); | ||
| 281 | |||
| 282 | mutex_unlock(vdev->lock); | ||
| 283 | |||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int fops_mmap(struct file *file, struct vm_area_struct * vma) | ||
| 288 | { | ||
| 289 | struct video_device *vdev = video_devdata(file); | ||
| 290 | struct saa7146_fh *fh = file->private_data; | ||
| 291 | struct videobuf_queue *q; | ||
| 292 | int res; | ||
| 293 | |||
| 294 | switch (vdev->vfl_type) { | ||
| 295 | case VFL_TYPE_VIDEO: { | ||
| 296 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", | ||
| 297 | file, vma); | ||
| 298 | q = &fh->video_q; | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | case VFL_TYPE_VBI: { | ||
| 302 | DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", | ||
| 303 | file, vma); | ||
| 304 | if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) | ||
| 305 | return -ENODEV; | ||
| 306 | q = &fh->vbi_q; | ||
| 307 | break; | ||
| 308 | } | ||
| 309 | default: | ||
| 310 | BUG(); | ||
| 311 | } | ||
| 312 | 188 | ||
| 189 | if (vdev->vfl_type != VFL_TYPE_VBI || !dev->ext_vv_data->vbi_fops.write) | ||
| 190 | return -EINVAL; | ||
| 313 | if (mutex_lock_interruptible(vdev->lock)) | 191 | if (mutex_lock_interruptible(vdev->lock)) |
| 314 | return -ERESTARTSYS; | 192 | return -ERESTARTSYS; |
| 315 | res = videobuf_mmap_mapper(q, vma); | 193 | ret = dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); |
| 316 | mutex_unlock(vdev->lock); | 194 | mutex_unlock(vdev->lock); |
| 317 | return res; | 195 | return ret; |
| 318 | } | ||
| 319 | |||
| 320 | static __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait) | ||
| 321 | { | ||
| 322 | struct video_device *vdev = video_devdata(file); | ||
| 323 | struct saa7146_fh *fh = file->private_data; | ||
| 324 | struct videobuf_buffer *buf = NULL; | ||
| 325 | struct videobuf_queue *q; | ||
| 326 | __poll_t res = v4l2_ctrl_poll(file, wait); | ||
| 327 | |||
| 328 | DEB_EE("file:%p, poll:%p\n", file, wait); | ||
| 329 | |||
| 330 | if (vdev->vfl_type == VFL_TYPE_VBI) { | ||
| 331 | if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) | ||
| 332 | return res | EPOLLOUT | EPOLLWRNORM; | ||
| 333 | if( 0 == fh->vbi_q.streaming ) | ||
| 334 | return res | videobuf_poll_stream(file, &fh->vbi_q, wait); | ||
| 335 | q = &fh->vbi_q; | ||
| 336 | } else { | ||
| 337 | DEB_D("using video queue\n"); | ||
| 338 | q = &fh->video_q; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (!list_empty(&q->stream)) | ||
| 342 | buf = list_entry(q->stream.next, struct videobuf_buffer, stream); | ||
| 343 | |||
| 344 | if (!buf) { | ||
| 345 | DEB_D("buf == NULL!\n"); | ||
| 346 | return res | EPOLLERR; | ||
| 347 | } | ||
| 348 | |||
| 349 | poll_wait(file, &buf->done, wait); | ||
| 350 | if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { | ||
| 351 | DEB_D("poll succeeded!\n"); | ||
| 352 | return res | EPOLLIN | EPOLLRDNORM; | ||
| 353 | } | ||
| 354 | |||
| 355 | DEB_D("nothing to poll for, buf->state:%d\n", buf->state); | ||
| 356 | return res; | ||
| 357 | } | ||
| 358 | |||
| 359 | static __poll_t fops_poll(struct file *file, struct poll_table_struct *wait) | ||
| 360 | { | ||
| 361 | struct video_device *vdev = video_devdata(file); | ||
| 362 | __poll_t res; | ||
| 363 | |||
| 364 | mutex_lock(vdev->lock); | ||
| 365 | res = __fops_poll(file, wait); | ||
| 366 | mutex_unlock(vdev->lock); | ||
| 367 | return res; | ||
| 368 | } | ||
| 369 | |||
| 370 | static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
| 371 | { | ||
| 372 | struct video_device *vdev = video_devdata(file); | ||
| 373 | struct saa7146_fh *fh = file->private_data; | ||
| 374 | int ret; | ||
| 375 | |||
| 376 | switch (vdev->vfl_type) { | ||
| 377 | case VFL_TYPE_VIDEO: | ||
| 378 | /* | ||
| 379 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", | ||
| 380 | file, data, (unsigned long)count); | ||
| 381 | */ | ||
| 382 | return saa7146_video_uops.read(file,data,count,ppos); | ||
| 383 | case VFL_TYPE_VBI: | ||
| 384 | /* | ||
| 385 | DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", | ||
| 386 | file, data, (unsigned long)count); | ||
| 387 | */ | ||
| 388 | if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) { | ||
| 389 | if (mutex_lock_interruptible(vdev->lock)) | ||
| 390 | return -ERESTARTSYS; | ||
| 391 | ret = saa7146_vbi_uops.read(file, data, count, ppos); | ||
| 392 | mutex_unlock(vdev->lock); | ||
| 393 | return ret; | ||
| 394 | } | ||
| 395 | return -EINVAL; | ||
| 396 | default: | ||
| 397 | BUG(); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) | ||
| 402 | { | ||
| 403 | struct video_device *vdev = video_devdata(file); | ||
| 404 | struct saa7146_fh *fh = file->private_data; | ||
| 405 | int ret; | ||
| 406 | |||
| 407 | switch (vdev->vfl_type) { | ||
| 408 | case VFL_TYPE_VIDEO: | ||
| 409 | return -EINVAL; | ||
| 410 | case VFL_TYPE_VBI: | ||
| 411 | if (fh->dev->ext_vv_data->vbi_fops.write) { | ||
| 412 | if (mutex_lock_interruptible(vdev->lock)) | ||
| 413 | return -ERESTARTSYS; | ||
| 414 | ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); | ||
| 415 | mutex_unlock(vdev->lock); | ||
| 416 | return ret; | ||
| 417 | } | ||
| 418 | return -EINVAL; | ||
| 419 | default: | ||
| 420 | BUG(); | ||
| 421 | } | ||
| 422 | } | 196 | } |
| 423 | 197 | ||
| 424 | static const struct v4l2_file_operations video_fops = | 198 | static const struct v4l2_file_operations video_fops = |
| 425 | { | 199 | { |
| 426 | .owner = THIS_MODULE, | 200 | .owner = THIS_MODULE, |
| 427 | .open = fops_open, | 201 | .open = v4l2_fh_open, |
| 428 | .release = fops_release, | 202 | .release = vb2_fop_release, |
| 429 | .read = fops_read, | 203 | .read = vb2_fop_read, |
| 430 | .write = fops_write, | 204 | .write = fops_write, |
| 431 | .poll = fops_poll, | 205 | .poll = vb2_fop_poll, |
| 432 | .mmap = fops_mmap, | 206 | .mmap = vb2_fop_mmap, |
| 433 | .unlocked_ioctl = video_ioctl2, | 207 | .unlocked_ioctl = video_ioctl2, |
| 434 | }; | 208 | }; |
| 435 | 209 | ||
| @@ -516,33 +290,15 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) · 80 unmodified lines | |||
| 516 | configuration data) */ | 290 | configuration data) */ |
| 517 | dev->ext_vv_data = ext_vv; | 291 | dev->ext_vv_data = ext_vv; |
| 518 | 292 | ||
| 519 | vv->d_clipping.cpu_addr = | ||
| 520 | dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, | ||
| 521 | &vv->d_clipping.dma_handle, GFP_KERNEL); | ||
| 522 | if( NULL == vv->d_clipping.cpu_addr ) { | ||
| 523 | ERR("out of memory. aborting.\n"); | ||
| 524 | kfree(vv); | ||
| 525 | v4l2_ctrl_handler_free(hdl); | ||
| 526 | v4l2_device_unregister(&dev->v4l2_dev); | ||
| 527 | return -ENOMEM; | ||
| 528 | } | ||
| 529 | |||
| 530 | saa7146_video_uops.init(dev,vv); | 293 | saa7146_video_uops.init(dev,vv); |
| 531 | if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) | 294 | if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) |
| 532 | saa7146_vbi_uops.init(dev,vv); | 295 | saa7146_vbi_uops.init(dev,vv); |
| 533 | 296 | ||
| 534 | vv->ov_fb.fmt.width = vv->standard->h_max_out; | ||
| 535 | vv->ov_fb.fmt.height = vv->standard->v_max_out; | ||
| 536 | vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; | ||
| 537 | vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width; | ||
| 538 | vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height; | ||
| 539 | vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; | ||
| 540 | |||
| 541 | fmt = &vv->video_fmt; | 297 | fmt = &vv->video_fmt; |
| 542 | fmt->width = 384; | 298 | fmt->width = 384; |
| 543 | fmt->height = 288; | 299 | fmt->height = 288; |
| 544 | fmt->pixelformat = V4L2_PIX_FMT_BGR24; | 300 | fmt->pixelformat = V4L2_PIX_FMT_BGR24; |
| 545 | fmt->field = V4L2_FIELD_ANY; | 301 | fmt->field = V4L2_FIELD_INTERLACED; |
| 546 | fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; | 302 | fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; |
| 547 | fmt->bytesperline = 3 * fmt->width; | 303 | fmt->bytesperline = 3 * fmt->width; |
| 548 | fmt->sizeimage = fmt->bytesperline * fmt->height; | 304 | fmt->sizeimage = fmt->bytesperline * fmt->height; |
| @@ -561,8 +317,6 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) · 12 unmodified lines | |||
| 561 | 317 | ||
| 562 | timer_setup(&vv->vbi_read_timeout, NULL, 0); | 318 | timer_setup(&vv->vbi_read_timeout, NULL, 0); |
| 563 | 319 | ||
| 564 | vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; | ||
| 565 | vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; | ||
| 566 | dev->vv_data = vv; | 320 | dev->vv_data = vv; |
| 567 | dev->vv_callback = &vv_callback; | 321 | dev->vv_callback = &vv_callback; |
| 568 | 322 | ||
| @@ -577,8 +331,6 @@ int saa7146_vv_release(struct saa7146_dev* dev) · 8 unmodified lines | |||
| 577 | DEB_EE("dev:%p\n", dev); | 331 | DEB_EE("dev:%p\n", dev); |
| 578 | 332 | ||
| 579 | v4l2_device_unregister(&dev->v4l2_dev); | 333 | v4l2_device_unregister(&dev->v4l2_dev); |
| 580 | dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, | ||
| 581 | vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); | ||
| 582 | v4l2_ctrl_handler_free(&dev->ctrl_handler); | 334 | v4l2_ctrl_handler_free(&dev->ctrl_handler); |
| 583 | kfree(vv); | 335 | kfree(vv); |
| 584 | dev->vv_data = NULL; | 336 | dev->vv_data = NULL; |
| @@ -591,16 +343,20 @@ EXPORT_SYMBOL_GPL(saa7146_vv_release); · 6 unmodified lines | |||
| 591 | int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, | 343 | int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, |
| 592 | char *name, int type) | 344 | char *name, int type) |
| 593 | { | 345 | { |
| 346 | struct vb2_queue *q; | ||
| 594 | int err; | 347 | int err; |
| 595 | int i; | 348 | int i; |
| 596 | 349 | ||
| 597 | DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); | 350 | DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); |
| 598 | 351 | ||
| 599 | vfd->fops = &video_fops; | 352 | vfd->fops = &video_fops; |
| 600 | if (type == VFL_TYPE_VIDEO) | 353 | if (type == VFL_TYPE_VIDEO) { |
| 601 | vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; | 354 | vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; |
| 602 | else | 355 | q = &dev->vv_data->video_dmaq.q; |
| 356 | } else { | ||
| 603 | vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; | 357 | vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; |
| 358 | q = &dev->vv_data->vbi_dmaq.q; | ||
| 359 | } | ||
| 604 | vfd->release = video_device_release_empty; | 360 | vfd->release = video_device_release_empty; |
| 605 | vfd->lock = &dev->v4l2_lock; | 361 | vfd->lock = &dev->v4l2_lock; |
| 606 | vfd->v4l2_dev = &dev->v4l2_dev; | 362 | vfd->v4l2_dev = &dev->v4l2_dev; |
| @@ -608,15 +364,36 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, · 1 unmodified line | |||
| 608 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) | 364 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) |
| 609 | vfd->tvnorms |= dev->ext_vv_data->stds[i].id; | 365 | vfd->tvnorms |= dev->ext_vv_data->stds[i].id; |
| 610 | strscpy(vfd->name, name, sizeof(vfd->name)); | 366 | strscpy(vfd->name, name, sizeof(vfd->name)); |
| 611 | vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | | 367 | vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | |
| 612 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; | 368 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; |
| 613 | vfd->device_caps |= dev->ext_vv_data->capabilities; | 369 | vfd->device_caps |= dev->ext_vv_data->capabilities; |
| 614 | if (type == VFL_TYPE_VIDEO) | 370 | if (type == VFL_TYPE_VIDEO) { |
| 615 | vfd->device_caps &= | 371 | vfd->device_caps &= |
| 616 | ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); | 372 | ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); |
| 617 | else | 373 | } else if (vfd->device_caps & V4L2_CAP_SLICED_VBI_OUTPUT) { |
| 618 | vfd->device_caps &= | 374 | vfd->vfl_dir = VFL_DIR_TX; |
| 619 | ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); | 375 | vfd->device_caps &= ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | |
| 376 | V4L2_CAP_AUDIO | V4L2_CAP_TUNER); | ||
| 377 | } else { | ||
| 378 | vfd->device_caps &= ~V4L2_CAP_VIDEO_CAPTURE; | ||
| 379 | } | ||
| 380 | |||
| 381 | q->type = type == VFL_TYPE_VIDEO ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VBI_CAPTURE; | ||
| 382 | q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | ||
| 383 | q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; | ||
| 384 | q->ops = type == VFL_TYPE_VIDEO ? &video_qops : &vbi_qops; | ||
| 385 | q->mem_ops = &vb2_dma_sg_memops; | ||
| 386 | q->drv_priv = dev; | ||
| 387 | q->gfp_flags = __GFP_DMA32; | ||
| 388 | q->buf_struct_size = sizeof(struct saa7146_buf); | ||
| 389 | q->lock = &dev->v4l2_lock; | ||
| 390 | q->min_buffers_needed = 2; | ||
| 391 | q->dev = &dev->pci->dev; | ||
| 392 | err = vb2_queue_init(q); | ||
| 393 | if (err) | ||
| 394 | return err; | ||
| 395 | vfd->queue = q; | ||
| 396 | |||
| 620 | video_set_drvdata(vfd, dev); | 397 | video_set_drvdata(vfd, dev); |
| 621 | 398 | ||
| 622 | err = video_register_device(vfd, type, -1); | 399 | err = video_register_device(vfd, type, -1); |
| @@ -306,221 +306,6 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field · 305 unmodified lines | |||
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | /* simple bubble-sort algorithm with duplicate elimination */ | 308 | /* simple bubble-sort algorithm with duplicate elimination */ |
| 309 | static int sort_and_eliminate(u32* values, int* count) | ||
| 310 | { | ||
| 311 | int low = 0, high = 0, top = 0; | ||
| 312 | int cur = 0, next = 0; | ||
| 313 | |||
| 314 | /* sanity checks */ | ||
| 315 | if( (0 > *count) || (NULL == values) ) { | ||
| 316 | return -EINVAL; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* bubble sort the first @count items of the array @values */ | ||
| 320 | for( top = *count; top > 0; top--) { | ||
| 321 | for( low = 0, high = 1; high < top; low++, high++) { | ||
| 322 | if( values[low] > values[high] ) | ||
| 323 | swap(values[low], values[high]); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | /* remove duplicate items */ | ||
| 328 | for( cur = 0, next = 1; next < *count; next++) { | ||
| 329 | if( values[cur] != values[next]) | ||
| 330 | values[++cur] = values[next]; | ||
| 331 | } | ||
| 332 | |||
| 333 | *count = cur + 1; | ||
| 334 | |||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh, | ||
| 339 | struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) | ||
| 340 | { | ||
| 341 | struct saa7146_vv *vv = dev->vv_data; | ||
| 342 | __le32 *clipping = vv->d_clipping.cpu_addr; | ||
| 343 | |||
| 344 | int width = vv->ov.win.w.width; | ||
| 345 | int height = vv->ov.win.w.height; | ||
| 346 | int clipcount = vv->ov.nclips; | ||
| 347 | |||
| 348 | u32 line_list[32]; | ||
| 349 | u32 pixel_list[32]; | ||
| 350 | int numdwords = 0; | ||
| 351 | |||
| 352 | int i = 0, j = 0; | ||
| 353 | int cnt_line = 0, cnt_pixel = 0; | ||
| 354 | |||
| 355 | int x[32], y[32], w[32], h[32]; | ||
| 356 | |||
| 357 | /* clear out memory */ | ||
| 358 | memset(&line_list[0], 0x00, sizeof(u32)*32); | ||
| 359 | memset(&pixel_list[0], 0x00, sizeof(u32)*32); | ||
| 360 | memset(clipping, 0x00, SAA7146_CLIPPING_MEM); | ||
| 361 | |||
| 362 | /* fill the line and pixel-lists */ | ||
| 363 | for(i = 0; i < clipcount; i++) { | ||
| 364 | int l = 0, r = 0, t = 0, b = 0; | ||
| 365 | |||
| 366 | x[i] = vv->ov.clips[i].c.left; | ||
| 367 | y[i] = vv->ov.clips[i].c.top; | ||
| 368 | w[i] = vv->ov.clips[i].c.width; | ||
| 369 | h[i] = vv->ov.clips[i].c.height; | ||
| 370 | |||
| 371 | if( w[i] < 0) { | ||
| 372 | x[i] += w[i]; w[i] = -w[i]; | ||
| 373 | } | ||
| 374 | if( h[i] < 0) { | ||
| 375 | y[i] += h[i]; h[i] = -h[i]; | ||
| 376 | } | ||
| 377 | if( x[i] < 0) { | ||
| 378 | w[i] += x[i]; x[i] = 0; | ||
| 379 | } | ||
| 380 | if( y[i] < 0) { | ||
| 381 | h[i] += y[i]; y[i] = 0; | ||
| 382 | } | ||
| 383 | if( 0 != vv->vflip ) { | ||
| 384 | y[i] = height - y[i] - h[i]; | ||
| 385 | } | ||
| 386 | |||
| 387 | l = x[i]; | ||
| 388 | r = x[i]+w[i]; | ||
| 389 | t = y[i]; | ||
| 390 | b = y[i]+h[i]; | ||
| 391 | |||
| 392 | /* insert left/right coordinates */ | ||
| 393 | pixel_list[ 2*i ] = min_t(int, l, width); | ||
| 394 | pixel_list[(2*i)+1] = min_t(int, r, width); | ||
| 395 | /* insert top/bottom coordinates */ | ||
| 396 | line_list[ 2*i ] = min_t(int, t, height); | ||
| 397 | line_list[(2*i)+1] = min_t(int, b, height); | ||
| 398 | } | ||
| 399 | |||
| 400 | /* sort and eliminate lists */ | ||
| 401 | cnt_line = cnt_pixel = 2*clipcount; | ||
| 402 | sort_and_eliminate( &pixel_list[0], &cnt_pixel ); | ||
| 403 | sort_and_eliminate( &line_list[0], &cnt_line ); | ||
| 404 | |||
| 405 | /* calculate the number of used u32s */ | ||
| 406 | numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2; | ||
| 407 | numdwords = max_t(int, 4, numdwords); | ||
| 408 | numdwords = min_t(int, 64, numdwords); | ||
| 409 | |||
| 410 | /* fill up cliptable */ | ||
| 411 | for(i = 0; i < cnt_pixel; i++) { | ||
| 412 | clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16); | ||
| 413 | } | ||
| 414 | for(i = 0; i < cnt_line; i++) { | ||
| 415 | clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16); | ||
| 416 | } | ||
| 417 | |||
| 418 | /* fill up cliptable with the display infos */ | ||
| 419 | for(j = 0; j < clipcount; j++) { | ||
| 420 | |||
| 421 | for(i = 0; i < cnt_pixel; i++) { | ||
| 422 | |||
| 423 | if( x[j] < 0) | ||
| 424 | x[j] = 0; | ||
| 425 | |||
| 426 | if( pixel_list[i] < (x[j] + w[j])) { | ||
| 427 | |||
| 428 | if ( pixel_list[i] >= x[j] ) { | ||
| 429 | clipping[2*i] |= cpu_to_le32(1 << j); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | } | ||
| 433 | for(i = 0; i < cnt_line; i++) { | ||
| 434 | |||
| 435 | if( y[j] < 0) | ||
| 436 | y[j] = 0; | ||
| 437 | |||
| 438 | if( line_list[i] < (y[j] + h[j]) ) { | ||
| 439 | |||
| 440 | if( line_list[i] >= y[j] ) { | ||
| 441 | clipping[(2*i)+1] |= cpu_to_le32(1 << j); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | /* adjust arbitration control register */ | ||
| 448 | *arbtr_ctrl &= 0xffff00ff; | ||
| 449 | *arbtr_ctrl |= 0x00001c00; | ||
| 450 | |||
| 451 | vdma2->base_even = vv->d_clipping.dma_handle; | ||
| 452 | vdma2->base_odd = vv->d_clipping.dma_handle; | ||
| 453 | vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords)); | ||
| 454 | vdma2->base_page = 0x04; | ||
| 455 | vdma2->pitch = 0x00; | ||
| 456 | vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) ); | ||
| 457 | |||
| 458 | /* set clipping-mode. this depends on the field(s) used */ | ||
| 459 | *clip_format &= 0xfffffff7; | ||
| 460 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
| 461 | *clip_format |= 0x00000008; | ||
| 462 | } else { | ||
| 463 | *clip_format |= 0x00000000; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | /* disable clipping */ | ||
| 468 | static void saa7146_disable_clipping(struct saa7146_dev *dev) | ||
| 469 | { | ||
| 470 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
| 471 | |||
| 472 | /* mask out relevant bits (=lower word)*/ | ||
| 473 | clip_format &= MASK_W1; | ||
| 474 | |||
| 475 | /* upload clipping-registers*/ | ||
| 476 | saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); | ||
| 477 | saa7146_write(dev, MC2, (MASK_05 | MASK_21)); | ||
| 478 | |||
| 479 | /* disable video dma2 */ | ||
| 480 | saa7146_write(dev, MC1, MASK_21); | ||
| 481 | } | ||
| 482 | |||
| 483 | static void saa7146_set_clipping_rect(struct saa7146_fh *fh) | ||
| 484 | { | ||
| 485 | struct saa7146_dev *dev = fh->dev; | ||
| 486 | struct saa7146_vv *vv = dev->vv_data; | ||
| 487 | enum v4l2_field field = vv->ov.win.field; | ||
| 488 | struct saa7146_video_dma vdma2; | ||
| 489 | u32 clip_format; | ||
| 490 | u32 arbtr_ctrl; | ||
| 491 | |||
| 492 | /* check clipcount, disable clipping if clipcount == 0*/ | ||
| 493 | if (vv->ov.nclips == 0) { | ||
| 494 | saa7146_disable_clipping(dev); | ||
| 495 | return; | ||
| 496 | } | ||
| 497 | |||
| 498 | clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
| 499 | arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); | ||
| 500 | |||
| 501 | calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field); | ||
| 502 | |||
| 503 | /* set clipping format */ | ||
| 504 | clip_format &= 0xffff0008; | ||
| 505 | clip_format |= (SAA7146_CLIPPING_RECT << 4); | ||
| 506 | |||
| 507 | /* prepare video dma2 */ | ||
| 508 | saa7146_write(dev, BASE_EVEN2, vdma2.base_even); | ||
| 509 | saa7146_write(dev, BASE_ODD2, vdma2.base_odd); | ||
| 510 | saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr); | ||
| 511 | saa7146_write(dev, BASE_PAGE2, vdma2.base_page); | ||
| 512 | saa7146_write(dev, PITCH2, vdma2.pitch); | ||
| 513 | saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte); | ||
| 514 | |||
| 515 | /* prepare the rest */ | ||
| 516 | saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); | ||
| 517 | saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); | ||
| 518 | |||
| 519 | /* upload clip_control-register, clipping-registers, enable video dma2 */ | ||
| 520 | saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19)); | ||
| 521 | saa7146_write(dev, MC1, (MASK_05 | MASK_21)); | ||
| 522 | } | ||
| 523 | |||
| 524 | static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) | 309 | static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) |
| 525 | { | 310 | { |
| 526 | struct saa7146_vv *vv = dev->vv_data; | 311 | struct saa7146_vv *vv = dev->vv_data; |
| @@ -556,62 +341,6 @@ static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, e · 29 unmodified lines | |||
| 556 | saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); | 341 | saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); |
| 557 | } | 342 | } |
| 558 | 343 | ||
| 559 | /* calculate the new memory offsets for a desired position */ | ||
| 560 | static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat) | ||
| 561 | { | ||
| 562 | struct saa7146_vv *vv = dev->vv_data; | ||
| 563 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat); | ||
| 564 | |||
| 565 | int b_depth = vv->ov_fmt->depth; | ||
| 566 | int b_bpl = vv->ov_fb.fmt.bytesperline; | ||
| 567 | /* The unsigned long cast is to remove a 64-bit compile warning since | ||
| 568 | it looks like a 64-bit address is cast to a 32-bit value, even | ||
| 569 | though the base pointer is really a 32-bit physical address that | ||
| 570 | goes into a 32-bit DMA register. | ||
| 571 | FIXME: might not work on some 64-bit platforms, but see the FIXME | ||
| 572 | in struct v4l2_framebuffer (videodev2.h) for that. | ||
| 573 | */ | ||
| 574 | u32 base = (u32)(unsigned long)vv->ov_fb.base; | ||
| 575 | |||
| 576 | struct saa7146_video_dma vdma1; | ||
| 577 | |||
| 578 | /* calculate memory offsets for picture, look if we shall top-down-flip */ | ||
| 579 | vdma1.pitch = 2*b_bpl; | ||
| 580 | if ( 0 == vv->vflip ) { | ||
| 581 | vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); | ||
| 582 | vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2); | ||
| 583 | vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2)); | ||
| 584 | } | ||
| 585 | else { | ||
| 586 | vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); | ||
| 587 | vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2); | ||
| 588 | vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2)); | ||
| 589 | } | ||
| 590 | |||
| 591 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
| 592 | } else if (field == V4L2_FIELD_ALTERNATE) { | ||
| 593 | /* fixme */ | ||
| 594 | vdma1.base_odd = vdma1.prot_addr; | ||
| 595 | vdma1.pitch /= 2; | ||
| 596 | } else if (field == V4L2_FIELD_TOP) { | ||
| 597 | vdma1.base_odd = vdma1.prot_addr; | ||
| 598 | vdma1.pitch /= 2; | ||
| 599 | } else if (field == V4L2_FIELD_BOTTOM) { | ||
| 600 | vdma1.base_odd = vdma1.base_even; | ||
| 601 | vdma1.base_even = vdma1.prot_addr; | ||
| 602 | vdma1.pitch /= 2; | ||
| 603 | } | ||
| 604 | |||
| 605 | if ( 0 != vv->vflip ) { | ||
| 606 | vdma1.pitch *= -1; | ||
| 607 | } | ||
| 608 | |||
| 609 | vdma1.base_page = sfmt->swap; | ||
| 610 | vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels; | ||
| 611 | |||
| 612 | saa7146_write_out_dma(dev, 1, &vdma1); | ||
| 613 | } | ||
| 614 | |||
| 615 | static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) | 344 | static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) |
| 616 | { | 345 | { |
| 617 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | 346 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); |
| @@ -645,30 +374,6 @@ void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sy · 27 unmodified lines | |||
| 645 | } | 374 | } |
| 646 | EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); | 375 | EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); |
| 647 | 376 | ||
| 648 | int saa7146_enable_overlay(struct saa7146_fh *fh) | ||
| 649 | { | ||
| 650 | struct saa7146_dev *dev = fh->dev; | ||
| 651 | struct saa7146_vv *vv = dev->vv_data; | ||
| 652 | |||
| 653 | saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); | ||
| 654 | saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); | ||
| 655 | saa7146_set_output_format(dev, vv->ov_fmt->trans); | ||
| 656 | saa7146_set_clipping_rect(fh); | ||
| 657 | |||
| 658 | /* enable video dma1 */ | ||
| 659 | saa7146_write(dev, MC1, (MASK_06 | MASK_22)); | ||
| 660 | return 0; | ||
| 661 | } | ||
| 662 | |||
| 663 | void saa7146_disable_overlay(struct saa7146_fh *fh) | ||
| 664 | { | ||
| 665 | struct saa7146_dev *dev = fh->dev; | ||
| 666 | |||
| 667 | /* disable clipping + video dma1 */ | ||
| 668 | saa7146_disable_clipping(dev); | ||
| 669 | saa7146_write(dev, MC1, MASK_22); | ||
| 670 | } | ||
| 671 | |||
| 672 | void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) | 377 | void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) |
| 673 | { | 378 | { |
| 674 | int where = 0; | 379 | int where = 0; |
| @@ -702,14 +407,14 @@ void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_vi · 27 unmodified lines | |||
| 702 | static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) | 407 | static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) |
| 703 | { | 408 | { |
| 704 | struct saa7146_vv *vv = dev->vv_data; | 409 | struct saa7146_vv *vv = dev->vv_data; |
| 410 | struct v4l2_pix_format *pix = &vv->video_fmt; | ||
| 705 | struct saa7146_video_dma vdma1; | 411 | struct saa7146_video_dma vdma1; |
| 412 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat); | ||
| 706 | 413 | ||
| 707 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); | 414 | int width = pix->width; |
| 708 | 415 | int height = pix->height; | |
| 709 | int width = buf->fmt->width; | 416 | int bytesperline = pix->bytesperline; |
| 710 | int height = buf->fmt->height; | 417 | enum v4l2_field field = pix->field; |
| 711 | int bytesperline = buf->fmt->bytesperline; | ||
| 712 | enum v4l2_field field = buf->fmt->field; | ||
| 713 | 418 | ||
| 714 | int depth = sfmt->depth; | 419 | int depth = sfmt->depth; |
| 715 | 420 | ||
| @@ -764,8 +469,9 @@ static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa71 · 48 unmodified lines | |||
| 764 | 469 | ||
| 765 | static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) | 470 | static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) |
| 766 | { | 471 | { |
| 767 | int height = buf->fmt->height; | 472 | struct v4l2_pix_format *pix = &vv->video_fmt; |
| 768 | int width = buf->fmt->width; | 473 | int height = pix->height; |
| 474 | int width = pix->width; | ||
| 769 | 475 | ||
| 770 | vdma2->pitch = width; | 476 | vdma2->pitch = width; |
| 771 | vdma3->pitch = width; | 477 | vdma3->pitch = width; |
| @@ -795,8 +501,9 @@ static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struc · 23 unmodified lines | |||
| 795 | 501 | ||
| 796 | static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) | 502 | static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) |
| 797 | { | 503 | { |
| 798 | int height = buf->fmt->height; | 504 | struct v4l2_pix_format *pix = &vv->video_fmt; |
| 799 | int width = buf->fmt->width; | 505 | int height = pix->height; |
| 506 | int width = pix->width; | ||
| 800 | 507 | ||
| 801 | vdma2->pitch = width/2; | 508 | vdma2->pitch = width/2; |
| 802 | vdma3->pitch = width/2; | 509 | vdma3->pitch = width/2; |
| @@ -825,19 +532,20 @@ static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struc · 22 unmodified lines | |||
| 825 | static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) | 532 | static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) |
| 826 | { | 533 | { |
| 827 | struct saa7146_vv *vv = dev->vv_data; | 534 | struct saa7146_vv *vv = dev->vv_data; |
| 535 | struct v4l2_pix_format *pix = &vv->video_fmt; | ||
| 828 | struct saa7146_video_dma vdma1; | 536 | struct saa7146_video_dma vdma1; |
| 829 | struct saa7146_video_dma vdma2; | 537 | struct saa7146_video_dma vdma2; |
| 830 | struct saa7146_video_dma vdma3; | 538 | struct saa7146_video_dma vdma3; |
| 539 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat); | ||
| 831 | 540 | ||
| 832 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); | 541 | int width = pix->width; |
| 542 | int height = pix->height; | ||
| 543 | enum v4l2_field field = pix->field; | ||
| 833 | 544 | ||
| 834 | int width = buf->fmt->width; | 545 | if (WARN_ON(!buf->pt[0].dma) || |
| 835 | int height = buf->fmt->height; | 546 | WARN_ON(!buf->pt[1].dma) || |
| 836 | enum v4l2_field field = buf->fmt->field; | 547 | WARN_ON(!buf->pt[2].dma)) |
| 837 | 548 | return -1; | |
| 838 | BUG_ON(0 == buf->pt[0].dma); | ||
| 839 | BUG_ON(0 == buf->pt[1].dma); | ||
| 840 | BUG_ON(0 == buf->pt[2].dma); | ||
| 841 | 549 | ||
| 842 | DEB_CAP("[size=%dx%d,fields=%s]\n", | 550 | DEB_CAP("[size=%dx%d,fields=%s]\n", |
| 843 | width, height, v4l2_field_names[field]); | 551 | width, height, v4l2_field_names[field]); |
| @@ -994,10 +702,27 @@ static void program_capture_engine(struct saa7146_dev *dev, int planar) · 150 unmodified lines | |||
| 994 | WRITE_RPS0(CMD_STOP); | 702 | WRITE_RPS0(CMD_STOP); |
| 995 | } | 703 | } |
| 996 | 704 | ||
| 705 | /* disable clipping */ | ||
| 706 | static void saa7146_disable_clipping(struct saa7146_dev *dev) | ||
| 707 | { | ||
| 708 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
| 709 | |||
| 710 | /* mask out relevant bits (=lower word)*/ | ||
| 711 | clip_format &= MASK_W1; | ||
| 712 | |||
| 713 | /* upload clipping-registers*/ | ||
| 714 | saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format); | ||
| 715 | saa7146_write(dev, MC2, (MASK_05 | MASK_21)); | ||
| 716 | |||
| 717 | /* disable video dma2 */ | ||
| 718 | saa7146_write(dev, MC1, MASK_21); | ||
| 719 | } | ||
| 720 | |||
| 997 | void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) | 721 | void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) |
| 998 | { | 722 | { |
| 999 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); | ||
| 1000 | struct saa7146_vv *vv = dev->vv_data; | 723 | struct saa7146_vv *vv = dev->vv_data; |
| 724 | struct v4l2_pix_format *pix = &vv->video_fmt; | ||
| 725 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat); | ||
| 1001 | u32 vdma1_prot_addr; | 726 | u32 vdma1_prot_addr; |
| 1002 | 727 | ||
| 1003 | DEB_CAP("buf:%p, next:%p\n", buf, next); | 728 | DEB_CAP("buf:%p, next:%p\n", buf, next); |
| @@ -1009,7 +734,7 @@ void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struc · 5 unmodified lines | |||
| 1009 | saa7146_write(dev, MC2, MASK_27 ); | 734 | saa7146_write(dev, MC2, MASK_27 ); |
| 1010 | } | 735 | } |
| 1011 | 736 | ||
| 1012 | saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field); | 737 | saa7146_set_window(dev, pix->width, pix->height, pix->field); |
| 1013 | saa7146_set_output_format(dev, sfmt->trans); | 738 | saa7146_set_output_format(dev, sfmt->trans); |
| 1014 | saa7146_disable_clipping(dev); | 739 | saa7146_disable_clipping(dev); |
| 1015 | 740 | ||
| @@ -207,7 +207,6 @@ static int buffer_activate(struct saa7146_dev *dev, · 206 unmodified lines | |||
| 207 | struct saa7146_buf *next) | 207 | struct saa7146_buf *next) |
| 208 | { | 208 | { |
| 209 | struct saa7146_vv *vv = dev->vv_data; | 209 | struct saa7146_vv *vv = dev->vv_data; |
| 210 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
| 211 | 210 | ||
| 212 | DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); | 211 | DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); |
| 213 | saa7146_set_vbi_capture(dev,buf,next); | 212 | saa7146_set_vbi_capture(dev,buf,next); |
| @@ -216,114 +215,101 @@ static int buffer_activate(struct saa7146_dev *dev, · 2 unmodified lines | |||
| 216 | return 0; | 215 | return 0; |
| 217 | } | 216 | } |
| 218 | 217 | ||
| 219 | static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) | 218 | /* ------------------------------------------------------------------ */ |
| 220 | { | ||
| 221 | struct file *file = q->priv_data; | ||
| 222 | struct saa7146_fh *fh = file->private_data; | ||
| 223 | struct saa7146_dev *dev = fh->dev; | ||
| 224 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
| 225 | 219 | ||
| 226 | int err = 0; | 220 | static int queue_setup(struct vb2_queue *q, |
| 227 | int lines, llength, size; | 221 | unsigned int *num_buffers, unsigned int *num_planes, |
| 222 | unsigned int sizes[], struct device *alloc_devs[]) | ||
| 223 | { | ||
| 224 | unsigned int size = 16 * 2 * vbi_pixel_to_capture; | ||
| 228 | 225 | ||
| 229 | lines = 16 * 2 ; /* 2 fields */ | 226 | if (*num_planes) |
| 230 | llength = vbi_pixel_to_capture; | 227 | return sizes[0] < size ? -EINVAL : 0; |
| 231 | size = lines * llength; | 228 | *num_planes = 1; |
| 229 | sizes[0] = size; | ||
| 232 | 230 | ||
| 233 | DEB_VBI("vb:%p\n", vb); | 231 | return 0; |
| 232 | } | ||
| 234 | 233 | ||
| 235 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { | 234 | static void buf_queue(struct vb2_buffer *vb) |
| 236 | DEB_VBI("size mismatch\n"); | 235 | { |
| 237 | return -EINVAL; | 236 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
| 238 | } | 237 | struct vb2_queue *vq = vb->vb2_queue; |
| 238 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); | ||
| 239 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); | ||
| 240 | unsigned long flags; | ||
| 239 | 241 | ||
| 240 | if (buf->vb.size != size) | 242 | spin_lock_irqsave(&dev->slock, flags); |
| 241 | saa7146_dma_free(dev,q,buf); | ||
| 242 | 243 | ||
| 243 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | 244 | saa7146_buffer_queue(dev, &dev->vv_data->vbi_dmaq, buf); |
| 244 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | 245 | spin_unlock_irqrestore(&dev->slock, flags); |
| 246 | } | ||
| 245 | 247 | ||
| 246 | buf->vb.width = llength; | 248 | static int buf_init(struct vb2_buffer *vb) |
| 247 | buf->vb.height = lines; | 249 | { |
| 248 | buf->vb.size = size; | 250 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
| 249 | buf->vb.field = field; // FIXME: check this | 251 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
| 252 | struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); | ||
| 253 | struct scatterlist *list = sgt->sgl; | ||
| 254 | int length = sgt->nents; | ||
| 255 | struct vb2_queue *vq = vb->vb2_queue; | ||
| 256 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); | ||
| 257 | int ret; | ||
| 250 | 258 | ||
| 251 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | ||
| 252 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | ||
| 253 | |||
| 254 | err = videobuf_iolock(q,&buf->vb, NULL); | ||
| 255 | if (err) | ||
| 256 | goto oops; | ||
| 257 | err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], | ||
| 258 | dma->sglist, dma->sglen); | ||
| 259 | if (0 != err) | ||
| 260 | return err; | ||
| 261 | } | ||
| 262 | buf->vb.state = VIDEOBUF_PREPARED; | ||
| 263 | buf->activate = buffer_activate; | 259 | buf->activate = buffer_activate; |
| 264 | 260 | ||
| 265 | return 0; | 261 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); |
| 266 | |||
| 267 | oops: | ||
| 268 | DEB_VBI("error out\n"); | ||
| 269 | saa7146_dma_free(dev,q,buf); | ||
| 270 | 262 | ||
| 271 | return err; | 263 | ret = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], |
| 264 | list, length); | ||
| 265 | if (ret) | ||
| 266 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | ||
| 267 | return ret; | ||
| 272 | } | 268 | } |
| 273 | 269 | ||
| 274 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | 270 | static int buf_prepare(struct vb2_buffer *vb) |
| 275 | { | 271 | { |
| 276 | int llength,lines; | 272 | unsigned int size = 16 * 2 * vbi_pixel_to_capture; |
| 277 | |||
| 278 | lines = 16 * 2 ; /* 2 fields */ | ||
| 279 | llength = vbi_pixel_to_capture; | ||
| 280 | |||
| 281 | *size = lines * llength; | ||
| 282 | *count = 2; | ||
| 283 | |||
| 284 | DEB_VBI("count:%d, size:%d\n", *count, *size); | ||
| 285 | 273 | ||
| 274 | if (vb2_plane_size(vb, 0) < size) | ||
| 275 | return -EINVAL; | ||
| 276 | vb2_set_plane_payload(vb, 0, size); | ||
| 286 | return 0; | 277 | return 0; |
| 287 | } | 278 | } |
| 288 | 279 | ||
| 289 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | 280 | static void buf_cleanup(struct vb2_buffer *vb) |
| 290 | { | 281 | { |
| 291 | struct file *file = q->priv_data; | 282 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
| 292 | struct saa7146_fh *fh = file->private_data; | 283 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
| 293 | struct saa7146_dev *dev = fh->dev; | 284 | struct vb2_queue *vq = vb->vb2_queue; |
| 294 | struct saa7146_vv *vv = dev->vv_data; | 285 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); |
| 295 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
| 296 | 286 | ||
| 297 | DEB_VBI("vb:%p\n", vb); | 287 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); |
| 298 | saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); | ||
| 299 | } | 288 | } |
| 300 | 289 | ||
| 301 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | 290 | static void return_buffers(struct vb2_queue *q, int state) |
| 302 | { | 291 | { |
| 303 | struct file *file = q->priv_data; | 292 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 304 | struct saa7146_fh *fh = file->private_data; | 293 | struct saa7146_dmaqueue *dq = &dev->vv_data->vbi_dmaq; |
| 305 | struct saa7146_dev *dev = fh->dev; | 294 | struct saa7146_buf *buf; |
| 306 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | 295 | |
| 307 | 296 | if (dq->curr) { | |
| 308 | DEB_VBI("vb:%p\n", vb); | 297 | buf = dq->curr; |
| 309 | saa7146_dma_free(dev,q,buf); | 298 | dq->curr = NULL; |
| 299 | vb2_buffer_done(&buf->vb.vb2_buf, state); | ||
| 300 | } | ||
| 301 | while (!list_empty(&dq->queue)) { | ||
| 302 | buf = list_entry(dq->queue.next, struct saa7146_buf, list); | ||
| 303 | list_del(&buf->list); | ||
| 304 | vb2_buffer_done(&buf->vb.vb2_buf, state); | ||
| 305 | } | ||
| 310 | } | 306 | } |
| 311 | 307 | ||
| 312 | static const struct videobuf_queue_ops vbi_qops = { | 308 | static void vbi_stop(struct saa7146_dev *dev) |
| 313 | .buf_setup = buffer_setup, | ||
| 314 | .buf_prepare = buffer_prepare, | ||
| 315 | .buf_queue = buffer_queue, | ||
| 316 | .buf_release = buffer_release, | ||
| 317 | }; | ||
| 318 | |||
| 319 | /* ------------------------------------------------------------------ */ | ||
| 320 | |||
| 321 | static void vbi_stop(struct saa7146_fh *fh, struct file *file) | ||
| 322 | { | 309 | { |
| 323 | struct saa7146_dev *dev = fh->dev; | ||
| 324 | struct saa7146_vv *vv = dev->vv_data; | 310 | struct saa7146_vv *vv = dev->vv_data; |
| 325 | unsigned long flags; | 311 | unsigned long flags; |
| 326 | DEB_VBI("dev:%p, fh:%p\n", dev, fh); | 312 | DEB_VBI("dev:%p\n", dev); |
| 327 | 313 | ||
| 328 | spin_lock_irqsave(&dev->slock,flags); | 314 | spin_lock_irqsave(&dev->slock,flags); |
| 329 | 315 | ||
| @@ -336,13 +322,6 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) · 6 unmodified lines | |||
| 336 | /* shut down dma 3 transfers */ | 322 | /* shut down dma 3 transfers */ |
| 337 | saa7146_write(dev, MC1, MASK_20); | 323 | saa7146_write(dev, MC1, MASK_20); |
| 338 | 324 | ||
| 339 | if (vv->vbi_dmaq.curr) | ||
| 340 | saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); | ||
| 341 | |||
| 342 | videobuf_queue_cancel(&fh->vbi_q); | ||
| 343 | |||
| 344 | vv->vbi_streaming = NULL; | ||
| 345 | |||
| 346 | del_timer(&vv->vbi_dmaq.timeout); | 325 | del_timer(&vv->vbi_dmaq.timeout); |
| 347 | del_timer(&vv->vbi_read_timeout); | 326 | del_timer(&vv->vbi_read_timeout); |
| 348 | 327 | ||
| @@ -352,38 +331,22 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) · 3 unmodified lines | |||
| 352 | static void vbi_read_timeout(struct timer_list *t) | 331 | static void vbi_read_timeout(struct timer_list *t) |
| 353 | { | 332 | { |
| 354 | struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); | 333 | struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); |
| 355 | struct file *file = vv->vbi_read_timeout_file; | 334 | struct saa7146_dev *dev = vv->vbi_dmaq.dev; |
| 356 | struct saa7146_fh *fh = file->private_data; | ||
| 357 | struct saa7146_dev *dev = fh->dev; | ||
| 358 | |||
| 359 | DEB_VBI("dev:%p, fh:%p\n", dev, fh); | ||
| 360 | 335 | ||
| 361 | vbi_stop(fh, file); | ||
| 362 | } | ||
| 363 | |||
| 364 | static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | ||
| 365 | { | ||
| 366 | DEB_VBI("dev:%p\n", dev); | 336 | DEB_VBI("dev:%p\n", dev); |
| 367 | 337 | ||
| 368 | INIT_LIST_HEAD(&vv->vbi_dmaq.queue); | 338 | vbi_stop(dev); |
| 369 | |||
| 370 | timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); | ||
| 371 | vv->vbi_dmaq.dev = dev; | ||
| 372 | |||
| 373 | init_waitqueue_head(&vv->vbi_wq); | ||
| 374 | } | 339 | } |
| 375 | 340 | ||
| 376 | static int vbi_open(struct saa7146_dev *dev, struct file *file) | 341 | static int vbi_begin(struct saa7146_dev *dev) |
| 377 | { | 342 | { |
| 378 | struct saa7146_fh *fh = file->private_data; | 343 | struct saa7146_vv *vv = dev->vv_data; |
| 379 | struct saa7146_vv *vv = fh->dev->vv_data; | ||
| 380 | |||
| 381 | u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); | 344 | u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); |
| 382 | int ret = 0; | 345 | int ret = 0; |
| 383 | 346 | ||
| 384 | DEB_VBI("dev:%p, fh:%p\n", dev, fh); | 347 | DEB_VBI("dev:%p\n", dev); |
| 385 | 348 | ||
| 386 | ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); | 349 | ret = saa7146_res_get(dev, RESOURCE_DMA3_BRS); |
| 387 | if (0 == ret) { | 350 | if (0 == ret) { |
| 388 | DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); | 351 | DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); |
| 389 | return -EBUSY; | 352 | return -EBUSY; |
| @@ -395,15 +358,7 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) · 5 unmodified lines | |||
| 395 | saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); | 358 | saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); |
| 396 | saa7146_write(dev, MC2, (MASK_04|MASK_20)); | 359 | saa7146_write(dev, MC2, (MASK_04|MASK_20)); |
| 397 | 360 | ||
| 398 | videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, | ||
| 399 | &dev->pci->dev, &dev->slock, | ||
| 400 | V4L2_BUF_TYPE_VBI_CAPTURE, | ||
| 401 | V4L2_FIELD_SEQ_TB, // FIXME: does this really work? | ||
| 402 | sizeof(struct saa7146_buf), | ||
| 403 | file, &dev->v4l2_lock); | ||
| 404 | |||
| 405 | vv->vbi_read_timeout.function = vbi_read_timeout; | 361 | vv->vbi_read_timeout.function = vbi_read_timeout; |
| 406 | vv->vbi_read_timeout_file = file; | ||
| 407 | 362 | ||
| 408 | /* initialize the brs */ | 363 | /* initialize the brs */ |
| 409 | if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { | 364 | if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { |
| @@ -422,16 +377,52 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) · 12 unmodified lines | |||
| 422 | return 0; | 377 | return 0; |
| 423 | } | 378 | } |
| 424 | 379 | ||
| 425 | static void vbi_close(struct saa7146_dev *dev, struct file *file) | 380 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
| 426 | { | 381 | { |
| 427 | struct saa7146_fh *fh = file->private_data; | 382 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 428 | struct saa7146_vv *vv = dev->vv_data; | 383 | int ret; |
| 429 | DEB_VBI("dev:%p, fh:%p\n", dev, fh); | 384 | |
| 385 | if (!vb2_is_streaming(&dev->vv_data->vbi_dmaq.q)) | ||
| 386 | dev->vv_data->seqnr = 0; | ||
| 387 | ret = vbi_begin(dev); | ||
| 388 | if (ret) | ||
| 389 | return_buffers(q, VB2_BUF_STATE_QUEUED); | ||
| 390 | return ret; | ||
| 391 | } | ||
| 430 | 392 | ||
| 431 | if( fh == vv->vbi_streaming ) { | 393 | static void stop_streaming(struct vb2_queue *q) |
| 432 | vbi_stop(fh, file); | 394 | { |
| 433 | } | 395 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 434 | saa7146_res_free(fh, RESOURCE_DMA3_BRS); | 396 | |
| 397 | vbi_stop(dev); | ||
| 398 | return_buffers(q, VB2_BUF_STATE_ERROR); | ||
| 399 | saa7146_res_free(dev, RESOURCE_DMA3_BRS); | ||
| 400 | } | ||
| 401 | |||
| 402 | const struct vb2_ops vbi_qops = { | ||
| 403 | .queue_setup = queue_setup, | ||
| 404 | .buf_queue = buf_queue, | ||
| 405 | .buf_init = buf_init, | ||
| 406 | .buf_prepare = buf_prepare, | ||
| 407 | .buf_cleanup = buf_cleanup, | ||
| 408 | .start_streaming = start_streaming, | ||
| 409 | .stop_streaming = stop_streaming, | ||
| 410 | .wait_prepare = vb2_ops_wait_prepare, | ||
| 411 | .wait_finish = vb2_ops_wait_finish, | ||
| 412 | }; | ||
| 413 | |||
| 414 | /* ------------------------------------------------------------------ */ | ||
| 415 | |||
| 416 | static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | ||
| 417 | { | ||
| 418 | DEB_VBI("dev:%p\n", dev); | ||
| 419 | |||
| 420 | INIT_LIST_HEAD(&vv->vbi_dmaq.queue); | ||
| 421 | |||
| 422 | timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); | ||
| 423 | vv->vbi_dmaq.dev = dev; | ||
| 424 | |||
| 425 | init_waitqueue_head(&vv->vbi_wq); | ||
| 435 | } | 426 | } |
| 436 | 427 | ||
| 437 | static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) | 428 | static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) |
| @@ -441,10 +432,7 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) · 3 unmodified lines | |||
| 441 | 432 | ||
| 442 | if (vv->vbi_dmaq.curr) { | 433 | if (vv->vbi_dmaq.curr) { |
| 443 | DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); | 434 | DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); |
| 444 | /* this must be += 2, one count for each field */ | 435 | saa7146_buffer_finish(dev, &vv->vbi_dmaq, VB2_BUF_STATE_DONE); |
| 445 | vv->vbi_fieldcount+=2; | ||
| 446 | vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; | ||
| 447 | saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); | ||
| 448 | } else { | 436 | } else { |
| 449 | DEB_VBI("dev:%p\n", dev); | 437 | DEB_VBI("dev:%p\n", dev); |
| 450 | } | 438 | } |
| @@ -453,46 +441,7 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) · 2 unmodified lines | |||
| 453 | spin_unlock(&dev->slock); | 441 | spin_unlock(&dev->slock); |
| 454 | } | 442 | } |
| 455 | 443 | ||
| 456 | static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
| 457 | { | ||
| 458 | struct saa7146_fh *fh = file->private_data; | ||
| 459 | struct saa7146_dev *dev = fh->dev; | ||
| 460 | struct saa7146_vv *vv = dev->vv_data; | ||
| 461 | ssize_t ret = 0; | ||
| 462 | |||
| 463 | DEB_VBI("dev:%p, fh:%p\n", dev, fh); | ||
| 464 | |||
| 465 | if( NULL == vv->vbi_streaming ) { | ||
| 466 | // fixme: check if dma3 is available | ||
| 467 | // fixme: activate vbi engine here if necessary. (really?) | ||
| 468 | vv->vbi_streaming = fh; | ||
| 469 | } | ||
| 470 | |||
| 471 | if( fh != vv->vbi_streaming ) { | ||
| 472 | DEB_VBI("open %p is already using vbi capture\n", | ||
| 473 | vv->vbi_streaming); | ||
| 474 | return -EBUSY; | ||
| 475 | } | ||
| 476 | |||
| 477 | mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); | ||
| 478 | ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, | ||
| 479 | file->f_flags & O_NONBLOCK); | ||
| 480 | /* | ||
| 481 | printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); | ||
| 482 | printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); | ||
| 483 | printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); | ||
| 484 | printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); | ||
| 485 | printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); | ||
| 486 | printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); | ||
| 487 | printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); | ||
| 488 | */ | ||
| 489 | return ret; | ||
| 490 | } | ||
| 491 | |||
| 492 | const struct saa7146_use_ops saa7146_vbi_uops = { | 444 | const struct saa7146_use_ops saa7146_vbi_uops = { |
| 493 | .init = vbi_init, | 445 | .init = vbi_init, |
| 494 | .open = vbi_open, | ||
| 495 | .release = vbi_close, | ||
| 496 | .irq_done = vbi_irq_done, | 446 | .irq_done = vbi_irq_done, |
| 497 | .read = vbi_read, | ||
| 498 | }; | 447 | }; |
| @@ -6,17 +6,6 @@ · 5 unmodified lines | |||
| 6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
| 7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
| 8 | 8 | ||
| 9 | static int max_memory = 32; | ||
| 10 | |||
| 11 | module_param(max_memory, int, 0644); | ||
| 12 | MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); | ||
| 13 | |||
| 14 | #define IS_CAPTURE_ACTIVE(fh) \ | ||
| 15 | (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) | ||
| 16 | |||
| 17 | #define IS_OVERLAY_ACTIVE(fh) \ | ||
| 18 | (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) | ||
| 19 | |||
| 20 | /* format descriptions for capture and preview */ | 9 | /* format descriptions for capture and preview */ |
| 21 | static struct saa7146_format formats[] = { | 10 | static struct saa7146_format formats[] = { |
| 22 | { | 11 | { |
| @@ -54,7 +43,7 @@ static struct saa7146_format formats[] = { · 31 unmodified lines | |||
| 54 | .pixelformat = V4L2_PIX_FMT_YUV422P, | 43 | .pixelformat = V4L2_PIX_FMT_YUV422P, |
| 55 | .trans = YUV422_DECOMPOSED, | 44 | .trans = YUV422_DECOMPOSED, |
| 56 | .depth = 16, | 45 | .depth = 16, |
| 57 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | 46 | .flags = FORMAT_IS_PLANAR, |
| 58 | }, { | 47 | }, { |
| 59 | .pixelformat = V4L2_PIX_FMT_YVU420, | 48 | .pixelformat = V4L2_PIX_FMT_YVU420, |
| 60 | .trans = YUV420_DECOMPOSED, | 49 | .trans = YUV420_DECOMPOSED, |
| @@ -91,115 +80,18 @@ struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fou · 30 unmodified lines | |||
| 91 | return NULL; | 80 | return NULL; |
| 92 | } | 81 | } |
| 93 | 82 | ||
| 94 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f); | ||
| 95 | |||
| 96 | int saa7146_start_preview(struct saa7146_fh *fh) | ||
| 97 | { | ||
| 98 | struct saa7146_dev *dev = fh->dev; | ||
| 99 | struct saa7146_vv *vv = dev->vv_data; | ||
| 100 | struct v4l2_format fmt; | ||
| 101 | int ret = 0, err = 0; | ||
| 102 | |||
| 103 | DEB_EE("dev:%p, fh:%p\n", dev, fh); | ||
| 104 | |||
| 105 | /* check if we have overlay information */ | ||
| 106 | if (vv->ov.fh == NULL) { | ||
| 107 | DEB_D("no overlay data available. try S_FMT first.\n"); | ||
| 108 | return -EAGAIN; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* check if streaming capture is running */ | ||
| 112 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
| 113 | DEB_D("streaming capture is active\n"); | ||
| 114 | return -EBUSY; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* check if overlay is running */ | ||
| 118 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
| 119 | if (vv->video_fh == fh) { | ||
| 120 | DEB_D("overlay is already active\n"); | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | DEB_D("overlay is already active in another open\n"); | ||
| 124 | return -EBUSY; | ||
| 125 | } | ||
| 126 | |||
| 127 | if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { | ||
| 128 | DEB_D("cannot get necessary overlay resources\n"); | ||
| 129 | return -EBUSY; | ||
| 130 | } | ||
| 131 | |||
| 132 | fmt.fmt.win = vv->ov.win; | ||
| 133 | err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); | ||
| 134 | if (0 != err) { | ||
| 135 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
| 136 | return -EBUSY; | ||
| 137 | } | ||
| 138 | vv->ov.win = fmt.fmt.win; | ||
| 139 | |||
| 140 | DEB_D("%dx%d+%d+%d 0x%08x field=%s\n", | ||
| 141 | vv->ov.win.w.width, vv->ov.win.w.height, | ||
| 142 | vv->ov.win.w.left, vv->ov.win.w.top, | ||
| 143 | vv->ov_fmt->pixelformat, v4l2_field_names[vv->ov.win.field]); | ||
| 144 | |||
| 145 | if (0 != (ret = saa7146_enable_overlay(fh))) { | ||
| 146 | DEB_D("enabling overlay failed: %d\n", ret); | ||
| 147 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | vv->video_status = STATUS_OVERLAY; | ||
| 152 | vv->video_fh = fh; | ||
| 153 | |||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | EXPORT_SYMBOL_GPL(saa7146_start_preview); | ||
| 157 | |||
| 158 | int saa7146_stop_preview(struct saa7146_fh *fh) | ||
| 159 | { | ||
| 160 | struct saa7146_dev *dev = fh->dev; | ||
| 161 | struct saa7146_vv *vv = dev->vv_data; | ||
| 162 | |||
| 163 | DEB_EE("dev:%p, fh:%p\n", dev, fh); | ||
| 164 | |||
| 165 | /* check if streaming capture is running */ | ||
| 166 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
| 167 | DEB_D("streaming capture is active\n"); | ||
| 168 | return -EBUSY; | ||
| 169 | } | ||
| 170 | |||
| 171 | /* check if overlay is running at all */ | ||
| 172 | if ((vv->video_status & STATUS_OVERLAY) == 0) { | ||
| 173 | DEB_D("no active overlay\n"); | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (vv->video_fh != fh) { | ||
| 178 | DEB_D("overlay is active, but in another open\n"); | ||
| 179 | return -EBUSY; | ||
| 180 | } | ||
| 181 | |||
| 182 | vv->video_status = 0; | ||
| 183 | vv->video_fh = NULL; | ||
| 184 | |||
| 185 | saa7146_disable_overlay(fh); | ||
| 186 | |||
| 187 | saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
| 188 | |||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | EXPORT_SYMBOL_GPL(saa7146_stop_preview); | ||
| 192 | |||
| 193 | /********************************************************************************/ | 83 | /********************************************************************************/ |
| 194 | /* common pagetable functions */ | 84 | /* common pagetable functions */ |
| 195 | 85 | ||
| 196 | static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) | 86 | static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) |
| 197 | { | 87 | { |
| 88 | struct saa7146_vv *vv = dev->vv_data; | ||
| 198 | struct pci_dev *pci = dev->pci; | 89 | struct pci_dev *pci = dev->pci; |
| 199 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | 90 | struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); |
| 200 | struct scatterlist *list = dma->sglist; | 91 | struct scatterlist *list = sgt->sgl; |
| 201 | int length = dma->sglen; | 92 | int length = sgt->nents; |
| 202 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); | 93 | struct v4l2_pix_format *pix = &vv->video_fmt; |
| 94 | struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pix->pixelformat); | ||
| 203 | 95 | ||
| 204 | DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); | 96 | DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); |
| 205 | 97 | ||
| @@ -207,31 +99,32 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu · 1 unmodified line | |||
| 207 | struct saa7146_pgtable *pt1 = &buf->pt[0]; | 99 | struct saa7146_pgtable *pt1 = &buf->pt[0]; |
| 208 | struct saa7146_pgtable *pt2 = &buf->pt[1]; | 100 | struct saa7146_pgtable *pt2 = &buf->pt[1]; |
| 209 | struct saa7146_pgtable *pt3 = &buf->pt[2]; | 101 | struct saa7146_pgtable *pt3 = &buf->pt[2]; |
| 102 | struct sg_dma_page_iter dma_iter; | ||
| 210 | __le32 *ptr1, *ptr2, *ptr3; | 103 | __le32 *ptr1, *ptr2, *ptr3; |
| 211 | __le32 fill; | 104 | __le32 fill; |
| 212 | 105 | ||
| 213 | int size = buf->fmt->width*buf->fmt->height; | 106 | int size = pix->width * pix->height; |
| 214 | int i,p,m1,m2,m3,o1,o2; | 107 | int i, m1, m2, m3, o1, o2; |
| 215 | 108 | ||
| 216 | switch( sfmt->depth ) { | 109 | switch( sfmt->depth ) { |
| 217 | case 12: { | 110 | case 12: { |
| 218 | /* create some offsets inside the page table */ | 111 | /* create some offsets inside the page table */ |
| 219 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | 112 | m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 220 | m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; | 113 | m2 = ((size + (size / 4) + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 221 | m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | 114 | m3 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 222 | o1 = size%PAGE_SIZE; | 115 | o1 = size % PAGE_SIZE; |
| 223 | o2 = (size+(size/4))%PAGE_SIZE; | 116 | o2 = (size + (size / 4)) % PAGE_SIZE; |
| 224 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", | 117 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", |
| 225 | size, m1, m2, m3, o1, o2); | 118 | size, m1, m2, m3, o1, o2); |
| 226 | break; | 119 | break; |
| 227 | } | 120 | } |
| 228 | case 16: { | 121 | case 16: { |
| 229 | /* create some offsets inside the page table */ | 122 | /* create some offsets inside the page table */ |
| 230 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | 123 | m1 = ((size + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 231 | m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | 124 | m2 = ((size + (size / 2) + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 232 | m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; | 125 | m3 = ((2 * size + PAGE_SIZE) / PAGE_SIZE) - 1; |
| 233 | o1 = size%PAGE_SIZE; | 126 | o1 = size % PAGE_SIZE; |
| 234 | o2 = (size+(size/2))%PAGE_SIZE; | 127 | o2 = (size + (size / 2)) % PAGE_SIZE; |
| 235 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", | 128 | DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", |
| 236 | size, m1, m2, m3, o1, o2); | 129 | size, m1, m2, m3, o1, o2); |
| 237 | break; | 130 | break; |
| @@ -245,64 +138,37 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu · 7 unmodified lines | |||
| 245 | ptr2 = pt2->cpu; | 138 | ptr2 = pt2->cpu; |
| 246 | ptr3 = pt3->cpu; | 139 | ptr3 = pt3->cpu; |
| 247 | 140 | ||
| 248 | /* walk all pages, copy all page addresses to ptr1 */ | 141 | for_each_sg_dma_page(list, &dma_iter, length, 0) |
| 249 | for (i = 0; i < length; i++, list++) { | 142 | *ptr1++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter) - list->offset); |
| 250 | for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr1++) | ||
| 251 | *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); | ||
| 252 | } | ||
| 253 | /* | ||
| 254 | ptr1 = pt1->cpu; | ||
| 255 | for(j=0;j<40;j++) { | ||
| 256 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | ||
| 257 | } | ||
| 258 | */ | ||
| 259 | 143 | ||
| 260 | /* if we have a user buffer, the first page may not be | 144 | /* if we have a user buffer, the first page may not be |
| 261 | aligned to a page boundary. */ | 145 | aligned to a page boundary. */ |
| 262 | pt1->offset = dma->sglist->offset; | 146 | pt1->offset = sgt->sgl->offset; |
| 263 | pt2->offset = pt1->offset+o1; | 147 | pt2->offset = pt1->offset + o1; |
| 264 | pt3->offset = pt1->offset+o2; | 148 | pt3->offset = pt1->offset + o2; |
| 265 | 149 | ||
| 266 | /* create video-dma2 page table */ | 150 | /* create video-dma2 page table */ |
| 267 | ptr1 = pt1->cpu; | 151 | ptr1 = pt1->cpu; |
| 268 | for(i = m1; i <= m2 ; i++, ptr2++) { | 152 | for (i = m1; i <= m2; i++, ptr2++) |
| 269 | *ptr2 = ptr1[i]; | 153 | *ptr2 = ptr1[i]; |
| 270 | } | 154 | fill = *(ptr2 - 1); |
| 271 | fill = *(ptr2-1); | 155 | for (; i < 1024; i++, ptr2++) |
| 272 | for(;i<1024;i++,ptr2++) { | ||
| 273 | *ptr2 = fill; | 156 | *ptr2 = fill; |
| 274 | } | ||
| 275 | /* create video-dma3 page table */ | 157 | /* create video-dma3 page table */ |
| 276 | ptr1 = pt1->cpu; | 158 | ptr1 = pt1->cpu; |
| 277 | for(i = m2; i <= m3; i++,ptr3++) { | 159 | for (i = m2; i <= m3; i++, ptr3++) |
| 278 | *ptr3 = ptr1[i]; | 160 | *ptr3 = ptr1[i]; |
| 279 | } | 161 | fill = *(ptr3 - 1); |
| 280 | fill = *(ptr3-1); | 162 | for (; i < 1024; i++, ptr3++) |
| 281 | for(;i<1024;i++,ptr3++) { | ||
| 282 | *ptr3 = fill; | 163 | *ptr3 = fill; |
| 283 | } | ||
| 284 | /* finally: finish up video-dma1 page table */ | 164 | /* finally: finish up video-dma1 page table */ |
| 285 | ptr1 = pt1->cpu+m1; | 165 | ptr1 = pt1->cpu + m1; |
| 286 | fill = pt1->cpu[m1]; | 166 | fill = pt1->cpu[m1]; |
| 287 | for(i=m1;i<1024;i++,ptr1++) { | 167 | for (i = m1; i < 1024; i++, ptr1++) |
| 288 | *ptr1 = fill; | 168 | *ptr1 = fill; |
| 289 | } | ||
| 290 | /* | ||
| 291 | ptr1 = pt1->cpu; | ||
| 292 | ptr2 = pt2->cpu; | ||
| 293 | ptr3 = pt3->cpu; | ||
| 294 | for(j=0;j<40;j++) { | ||
| 295 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | ||
| 296 | } | ||
| 297 | for(j=0;j<40;j++) { | ||
| 298 | printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); | ||
| 299 | } | ||
| 300 | for(j=0;j<40;j++) { | ||
| 301 | printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); | ||
| 302 | } | ||
| 303 | */ | ||
| 304 | } else { | 169 | } else { |
| 305 | struct saa7146_pgtable *pt = &buf->pt[0]; | 170 | struct saa7146_pgtable *pt = &buf->pt[0]; |
| 171 | |||
| 306 | return saa7146_pgtable_build_single(pci, pt, list, length); | 172 | return saa7146_pgtable_build_single(pci, pt, list, length); |
| 307 | } | 173 | } |
| 308 | 174 | ||
| @@ -313,34 +179,14 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu · 4 unmodified lines | |||
| 313 | /********************************************************************************/ | 179 | /********************************************************************************/ |
| 314 | /* file operations */ | 180 | /* file operations */ |
| 315 | 181 | ||
| 316 | static int video_begin(struct saa7146_fh *fh) | 182 | static int video_begin(struct saa7146_dev *dev) |
| 317 | { | 183 | { |
| 318 | struct saa7146_dev *dev = fh->dev; | ||
| 319 | struct saa7146_vv *vv = dev->vv_data; | 184 | struct saa7146_vv *vv = dev->vv_data; |
| 320 | struct saa7146_format *fmt = NULL; | 185 | struct saa7146_format *fmt = NULL; |
| 321 | unsigned int resource; | 186 | unsigned int resource; |
| 322 | int ret = 0, err = 0; | 187 | int ret = 0; |
| 323 | |||
| 324 | DEB_EE("dev:%p, fh:%p\n", dev, fh); | ||
| 325 | |||
| 326 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | ||
| 327 | if (vv->video_fh == fh) { | ||
| 328 | DEB_S("already capturing\n"); | ||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | DEB_S("already capturing in another open\n"); | ||
| 332 | return -EBUSY; | ||
| 333 | } | ||
| 334 | 188 | ||
| 335 | if ((vv->video_status & STATUS_OVERLAY) != 0) { | 189 | DEB_EE("dev:%p\n", dev); |
| 336 | DEB_S("warning: suspending overlay video for streaming capture\n"); | ||
| 337 | vv->ov_suspend = vv->video_fh; | ||
| 338 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | ||
| 339 | if (0 != err) { | ||
| 340 | DEB_D("suspending video failed. aborting\n"); | ||
| 341 | return err; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | 190 | ||
| 345 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); | 191 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); |
| 346 | /* we need to have a valid format set here */ | 192 | /* we need to have a valid format set here */ |
| @@ -353,13 +199,9 @@ static int video_begin(struct saa7146_fh *fh) · 6 unmodified lines | |||
| 353 | resource = RESOURCE_DMA1_HPS; | 199 | resource = RESOURCE_DMA1_HPS; |
| 354 | } | 200 | } |
| 355 | 201 | ||
| 356 | ret = saa7146_res_get(fh, resource); | 202 | ret = saa7146_res_get(dev, resource); |
| 357 | if (0 == ret) { | 203 | if (0 == ret) { |
| 358 | DEB_S("cannot get capture resource %d\n", resource); | 204 | DEB_S("cannot get capture resource %d\n", resource); |
| 359 | if (vv->ov_suspend != NULL) { | ||
| 360 | saa7146_start_preview(vv->ov_suspend); | ||
| 361 | vv->ov_suspend = NULL; | ||
| 362 | } | ||
| 363 | return -EBUSY; | 205 | return -EBUSY; |
| 364 | } | 206 | } |
| 365 | 207 | ||
| @@ -369,37 +211,22 @@ static int video_begin(struct saa7146_fh *fh) · 3 unmodified lines | |||
| 369 | /* enable rps0 irqs */ | 211 | /* enable rps0 irqs */ |
| 370 | SAA7146_IER_ENABLE(dev, MASK_27); | 212 | SAA7146_IER_ENABLE(dev, MASK_27); |
| 371 | 213 | ||
| 372 | vv->video_fh = fh; | ||
| 373 | vv->video_status = STATUS_CAPTURE; | ||
| 374 | |||
| 375 | return 0; | 214 | return 0; |
| 376 | } | 215 | } |
| 377 | 216 | ||
| 378 | static int video_end(struct saa7146_fh *fh, struct file *file) | 217 | static void video_end(struct saa7146_dev *dev) |
| 379 | { | 218 | { |
| 380 | struct saa7146_dev *dev = fh->dev; | ||
| 381 | struct saa7146_vv *vv = dev->vv_data; | 219 | struct saa7146_vv *vv = dev->vv_data; |
| 382 | struct saa7146_dmaqueue *q = &vv->video_dmaq; | ||
| 383 | struct saa7146_format *fmt = NULL; | 220 | struct saa7146_format *fmt = NULL; |
| 384 | unsigned long flags; | 221 | unsigned long flags; |
| 385 | unsigned int resource; | 222 | unsigned int resource; |
| 386 | u32 dmas = 0; | 223 | u32 dmas = 0; |
| 387 | DEB_EE("dev:%p, fh:%p\n", dev, fh); | 224 | DEB_EE("dev:%p\n", dev); |
| 388 | |||
| 389 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | ||
| 390 | DEB_S("not capturing\n"); | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | if (vv->video_fh != fh) { | ||
| 395 | DEB_S("capturing, but in another open\n"); | ||
| 396 | return -EBUSY; | ||
| 397 | } | ||
| 398 | 225 | ||
| 399 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); | 226 | fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); |
| 400 | /* we need to have a valid format set here */ | 227 | /* we need to have a valid format set here */ |
| 401 | if (!fmt) | 228 | if (!fmt) |
| 402 | return -EINVAL; | 229 | return; |
| 403 | 230 | ||
| 404 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | 231 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { |
| 405 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | 232 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; |
| @@ -419,88 +246,24 @@ static int video_end(struct saa7146_fh *fh, struct file *file) · 13 unmodified lines | |||
| 419 | /* shut down all used video dma transfers */ | 246 | /* shut down all used video dma transfers */ |
| 420 | saa7146_write(dev, MC1, dmas); | 247 | saa7146_write(dev, MC1, dmas); |
| 421 | 248 | ||
| 422 | if (q->curr) | ||
| 423 | saa7146_buffer_finish(dev, q, VIDEOBUF_DONE); | ||
| 424 | |||
| 425 | spin_unlock_irqrestore(&dev->slock, flags); | 249 | spin_unlock_irqrestore(&dev->slock, flags); |
| 426 | 250 | ||
| 427 | vv->video_fh = NULL; | 251 | saa7146_res_free(dev, resource); |
| 428 | vv->video_status = 0; | ||
| 429 | |||
| 430 | saa7146_res_free(fh, resource); | ||
| 431 | |||
| 432 | if (vv->ov_suspend != NULL) { | ||
| 433 | saa7146_start_preview(vv->ov_suspend); | ||
| 434 | vv->ov_suspend = NULL; | ||
| 435 | } | ||
| 436 | |||
| 437 | return 0; | ||
| 438 | } | 252 | } |
| 439 | 253 | ||
| 440 | static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | 254 | static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) |
| 441 | { | 255 | { |
| 442 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 256 | struct saa7146_dev *dev = video_drvdata(file); |
| 443 | 257 | ||
| 444 | strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver)); | 258 | strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver)); |
| 445 | strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); | 259 | strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); |
| 446 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | | 260 | cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | |
| 447 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | | 261 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | |
| 448 | V4L2_CAP_DEVICE_CAPS; | 262 | V4L2_CAP_DEVICE_CAPS; |
| 449 | cap->capabilities |= dev->ext_vv_data->capabilities; | 263 | cap->capabilities |= dev->ext_vv_data->capabilities; |
| 450 | return 0; | 264 | return 0; |
| 451 | } | 265 | } |
| 452 | 266 | ||
| 453 | static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) | ||
| 454 | { | ||
| 455 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | ||
| 456 | struct saa7146_vv *vv = dev->vv_data; | ||
| 457 | |||
| 458 | *fb = vv->ov_fb; | ||
| 459 | fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; | ||
| 460 | fb->flags = V4L2_FBUF_FLAG_PRIMARY; | ||
| 461 | return 0; | ||
| 462 | } | ||
| 463 | |||
| 464 | static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) | ||
| 465 | { | ||
| 466 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | ||
| 467 | struct saa7146_vv *vv = dev->vv_data; | ||
| 468 | struct saa7146_format *fmt; | ||
| 469 | |||
| 470 | DEB_EE("VIDIOC_S_FBUF\n"); | ||
| 471 | |||
| 472 | if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) | ||
| 473 | return -EPERM; | ||
| 474 | |||
| 475 | /* check args */ | ||
| 476 | fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); | ||
| 477 | if (NULL == fmt) | ||
| 478 | return -EINVAL; | ||
| 479 | |||
| 480 | /* planar formats are not allowed for overlay video, clipping and video dma would clash */ | ||
| 481 | if (fmt->flags & FORMAT_IS_PLANAR) | ||
| 482 | DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", | ||
| 483 | (char *)&fmt->pixelformat); | ||
| 484 | |||
| 485 | /* check if overlay is running */ | ||
| 486 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
| 487 | if (vv->video_fh != fh) { | ||
| 488 | DEB_D("refusing to change framebuffer information while overlay is active in another open\n"); | ||
| 489 | return -EBUSY; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | /* ok, accept it */ | ||
| 494 | vv->ov_fb = *fb; | ||
| 495 | vv->ov_fmt = fmt; | ||
| 496 | |||
| 497 | if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { | ||
| 498 | vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; | ||
| 499 | DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); | ||
| 500 | } | ||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | |||
| 504 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) | 267 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) |
| 505 | { | 268 | { |
| 506 | if (f->index >= ARRAY_SIZE(formats)) | 269 | if (f->index >= ARRAY_SIZE(formats)) |
| @@ -543,13 +306,13 @@ int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) · 36 unmodified lines | |||
| 543 | 306 | ||
| 544 | case V4L2_CID_HFLIP: | 307 | case V4L2_CID_HFLIP: |
| 545 | /* fixme: we can support changing VFLIP and HFLIP here... */ | 308 | /* fixme: we can support changing VFLIP and HFLIP here... */ |
| 546 | if ((vv->video_status & STATUS_CAPTURE)) | 309 | if (vb2_is_busy(&vv->video_dmaq.q)) |
| 547 | return -EBUSY; | 310 | return -EBUSY; |
| 548 | vv->hflip = ctrl->val; | 311 | vv->hflip = ctrl->val; |
| 549 | break; | 312 | break; |
| 550 | 313 | ||
| 551 | case V4L2_CID_VFLIP: | 314 | case V4L2_CID_VFLIP: |
| 552 | if ((vv->video_status & STATUS_CAPTURE)) | 315 | if (vb2_is_busy(&vv->video_dmaq.q)) |
| 553 | return -EBUSY; | 316 | return -EBUSY; |
| 554 | vv->vflip = ctrl->val; | 317 | vv->vflip = ctrl->val; |
| 555 | break; | 318 | break; |
| @@ -557,20 +320,13 @@ int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) · 1 unmodified line | |||
| 557 | default: | 320 | default: |
| 558 | return -EINVAL; | 321 | return -EINVAL; |
| 559 | } | 322 | } |
| 560 | |||
| 561 | if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ | ||
| 562 | struct saa7146_fh *fh = vv->video_fh; | ||
| 563 | |||
| 564 | saa7146_stop_preview(fh); | ||
| 565 | saa7146_start_preview(fh); | ||
| 566 | } | ||
| 567 | return 0; | 323 | return 0; |
| 568 | } | 324 | } |
| 569 | 325 | ||
| 570 | static int vidioc_g_parm(struct file *file, void *fh, | 326 | static int vidioc_g_parm(struct file *file, void *fh, |
| 571 | struct v4l2_streamparm *parm) | 327 | struct v4l2_streamparm *parm) |
| 572 | { | 328 | { |
| 573 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 329 | struct saa7146_dev *dev = video_drvdata(file); |
| 574 | struct saa7146_vv *vv = dev->vv_data; | 330 | struct saa7146_vv *vv = dev->vv_data; |
| 575 | 331 | ||
| 576 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 332 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
| @@ -583,25 +339,16 @@ static int vidioc_g_parm(struct file *file, void *fh, · 6 unmodified lines | |||
| 583 | 339 | ||
| 584 | static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) | 340 | static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
| 585 | { | 341 | { |
| 586 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 342 | struct saa7146_dev *dev = video_drvdata(file); |
| 587 | struct saa7146_vv *vv = dev->vv_data; | 343 | struct saa7146_vv *vv = dev->vv_data; |
| 588 | 344 | ||
| 589 | f->fmt.pix = vv->video_fmt; | 345 | f->fmt.pix = vv->video_fmt; |
| 590 | return 0; | 346 | return 0; |
| 591 | } | 347 | } |
| 592 | 348 | ||
| 593 | static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) | ||
| 594 | { | ||
| 595 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | ||
| 596 | struct saa7146_vv *vv = dev->vv_data; | ||
| 597 | |||
| 598 | f->fmt.win = vv->ov.win; | ||
| 599 | return 0; | ||
| 600 | } | ||
| 601 | |||
| 602 | static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) | 349 | static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) |
| 603 | { | 350 | { |
| 604 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 351 | struct saa7146_dev *dev = video_drvdata(file); |
| 605 | struct saa7146_vv *vv = dev->vv_data; | 352 | struct saa7146_vv *vv = dev->vv_data; |
| 606 | 353 | ||
| 607 | f->fmt.vbi = vv->vbi_fmt; | 354 | f->fmt.vbi = vv->vbi_fmt; |
| @@ -610,7 +357,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format · 2 unmodified lines | |||
| 610 | 357 | ||
| 611 | static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) | 358 | static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) |
| 612 | { | 359 | { |
| 613 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 360 | struct saa7146_dev *dev = video_drvdata(file); |
| 614 | struct saa7146_vv *vv = dev->vv_data; | 361 | struct saa7146_vv *vv = dev->vv_data; |
| 615 | struct saa7146_format *fmt; | 362 | struct saa7146_format *fmt; |
| 616 | enum v4l2_field field; | 363 | enum v4l2_field field; |
| @@ -634,24 +381,21 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma · 17 unmodified lines | |||
| 634 | } | 381 | } |
| 635 | switch (field) { | 382 | switch (field) { |
| 636 | case V4L2_FIELD_ALTERNATE: | 383 | case V4L2_FIELD_ALTERNATE: |
| 637 | vv->last_field = V4L2_FIELD_TOP; | ||
| 638 | maxh = maxh / 2; | ||
| 639 | break; | ||
| 640 | case V4L2_FIELD_TOP: | 384 | case V4L2_FIELD_TOP: |
| 641 | case V4L2_FIELD_BOTTOM: | 385 | case V4L2_FIELD_BOTTOM: |
| 642 | vv->last_field = V4L2_FIELD_INTERLACED; | ||
| 643 | maxh = maxh / 2; | 386 | maxh = maxh / 2; |
| 644 | break; | 387 | break; |
| 645 | case V4L2_FIELD_INTERLACED: | ||
| 646 | vv->last_field = V4L2_FIELD_INTERLACED; | ||
| 647 | break; | ||
| 648 | default: | 388 | default: |
| 649 | DEB_D("no known field mode '%d'\n", field); | 389 | field = V4L2_FIELD_INTERLACED; |
| 650 | return -EINVAL; | 390 | break; |
| 651 | } | 391 | } |
| 652 | 392 | ||
| 653 | f->fmt.pix.field = field; | 393 | f->fmt.pix.field = field; |
| 654 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; | 394 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
| 395 | if (f->fmt.pix.width < 48) | ||
| 396 | f->fmt.pix.width = 48; | ||
| 397 | if (f->fmt.pix.height < 32) | ||
| 398 | f->fmt.pix.height = 32; | ||
| 655 | if (f->fmt.pix.width > maxw) | 399 | if (f->fmt.pix.width > maxw) |
| 656 | f->fmt.pix.width = maxw; | 400 | f->fmt.pix.width = maxw; |
| 657 | if (f->fmt.pix.height > maxh) | 401 | if (f->fmt.pix.height > maxh) |
| @@ -673,169 +417,65 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma · 15 unmodified lines | |||
| 673 | return 0; | 417 | return 0; |
| 674 | } | 418 | } |
| 675 | 419 | ||
| 676 | 420 | static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) | |
| 677 | static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) | ||
| 678 | { | 421 | { |
| 679 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 422 | struct saa7146_dev *dev = video_drvdata(file); |
| 680 | struct saa7146_vv *vv = dev->vv_data; | ||
| 681 | struct v4l2_window *win = &f->fmt.win; | ||
| 682 | enum v4l2_field field; | ||
| 683 | int maxw, maxh; | ||
| 684 | |||
| 685 | DEB_EE("dev:%p\n", dev); | ||
| 686 | |||
| 687 | if (NULL == vv->ov_fb.base) { | ||
| 688 | DEB_D("no fb base set\n"); | ||
| 689 | return -EINVAL; | ||
| 690 | } | ||
| 691 | if (NULL == vv->ov_fmt) { | ||
| 692 | DEB_D("no fb fmt set\n"); | ||
| 693 | return -EINVAL; | ||
| 694 | } | ||
| 695 | if (win->w.width < 48 || win->w.height < 32) { | ||
| 696 | DEB_D("min width/height. (%d,%d)\n", | ||
| 697 | win->w.width, win->w.height); | ||
| 698 | return -EINVAL; | ||
| 699 | } | ||
| 700 | if (win->clipcount > 16) { | ||
| 701 | DEB_D("clipcount too big\n"); | ||
| 702 | return -EINVAL; | ||
| 703 | } | ||
| 704 | |||
| 705 | field = win->field; | ||
| 706 | maxw = vv->standard->h_max_out; | ||
| 707 | maxh = vv->standard->v_max_out; | ||
| 708 | |||
| 709 | if (V4L2_FIELD_ANY == field) { | ||
| 710 | field = (win->w.height > maxh / 2) | ||
| 711 | ? V4L2_FIELD_INTERLACED | ||
| 712 | : V4L2_FIELD_TOP; | ||
| 713 | } | ||
| 714 | switch (field) { | ||
| 715 | case V4L2_FIELD_TOP: | ||
| 716 | case V4L2_FIELD_BOTTOM: | ||
| 717 | case V4L2_FIELD_ALTERNATE: | ||
| 718 | maxh = maxh / 2; | ||
| 719 | break; | ||
| 720 | case V4L2_FIELD_INTERLACED: | ||
| 721 | break; | ||
| 722 | default: | ||
| 723 | DEB_D("no known field mode '%d'\n", field); | ||
| 724 | return -EINVAL; | ||
| 725 | } | ||
| 726 | |||
| 727 | win->field = field; | ||
| 728 | if (win->w.width > maxw) | ||
| 729 | win->w.width = maxw; | ||
| 730 | if (win->w.height > maxh) | ||
| 731 | win->w.height = maxh; | ||
| 732 | |||
| 733 | return 0; | ||
| 734 | } | ||
| 735 | |||
| 736 | static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) | ||
| 737 | { | ||
| 738 | struct saa7146_fh *fh = __fh; | ||
| 739 | struct saa7146_dev *dev = fh->dev; | ||
| 740 | struct saa7146_vv *vv = dev->vv_data; | 423 | struct saa7146_vv *vv = dev->vv_data; |
| 741 | int err; | 424 | int err; |
| 742 | 425 | ||
| 743 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); | 426 | DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p\n", dev); |
| 744 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | 427 | if (vb2_is_busy(&vv->video_dmaq.q)) { |
| 745 | DEB_EE("streaming capture is active\n"); | 428 | DEB_EE("streaming capture is active\n"); |
| 746 | return -EBUSY; | 429 | return -EBUSY; |
| 747 | } | 430 | } |
| 748 | err = vidioc_try_fmt_vid_cap(file, fh, f); | 431 | err = vidioc_try_fmt_vid_cap(file, fh, f); |
| 749 | if (0 != err) | 432 | if (0 != err) |
| 750 | return err; | 433 | return err; |
| 434 | switch (f->fmt.pix.field) { | ||
| 435 | case V4L2_FIELD_ALTERNATE: | ||
| 436 | vv->last_field = V4L2_FIELD_TOP; | ||
| 437 | break; | ||
| 438 | default: | ||
| 439 | vv->last_field = V4L2_FIELD_INTERLACED; | ||
| 440 | break; | ||
| 441 | } | ||
| 751 | vv->video_fmt = f->fmt.pix; | 442 | vv->video_fmt = f->fmt.pix; |
| 752 | DEB_EE("set to pixelformat '%4.4s'\n", | 443 | DEB_EE("set to pixelformat '%4.4s'\n", |
| 753 | (char *)&vv->video_fmt.pixelformat); | 444 | (char *)&vv->video_fmt.pixelformat); |
| 754 | return 0; | 445 | return 0; |
| 755 | } | 446 | } |
| 756 | 447 | ||
| 757 | static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) | ||
| 758 | { | ||
| 759 | struct saa7146_fh *fh = __fh; | ||
| 760 | struct saa7146_dev *dev = fh->dev; | ||
| 761 | struct saa7146_vv *vv = dev->vv_data; | ||
| 762 | int err; | ||
| 763 | |||
| 764 | DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); | ||
| 765 | err = vidioc_try_fmt_vid_overlay(file, fh, f); | ||
| 766 | if (0 != err) | ||
| 767 | return err; | ||
| 768 | vv->ov.win = f->fmt.win; | ||
| 769 | vv->ov.nclips = f->fmt.win.clipcount; | ||
| 770 | if (vv->ov.nclips > 16) | ||
| 771 | vv->ov.nclips = 16; | ||
| 772 | memcpy(vv->ov.clips, f->fmt.win.clips, | ||
| 773 | sizeof(struct v4l2_clip) * vv->ov.nclips); | ||
| 774 | |||
| 775 | /* vv->ov.fh is used to indicate that we have valid overlay information, too */ | ||
| 776 | vv->ov.fh = fh; | ||
| 777 | |||
| 778 | /* check if our current overlay is active */ | ||
| 779 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
| 780 | saa7146_stop_preview(fh); | ||
| 781 | saa7146_start_preview(fh); | ||
| 782 | } | ||
| 783 | return 0; | ||
| 784 | } | ||
| 785 | |||
| 786 | static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) | 448 | static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) |
| 787 | { | 449 | { |
| 788 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 450 | struct saa7146_dev *dev = video_drvdata(file); |
| 789 | struct saa7146_vv *vv = dev->vv_data; | 451 | struct saa7146_vv *vv = dev->vv_data; |
| 790 | 452 | ||
| 791 | *norm = vv->standard->id; | 453 | *norm = vv->standard->id; |
| 792 | return 0; | 454 | return 0; |
| 793 | } | 455 | } |
| 794 | 456 | ||
| 795 | /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) | ||
| 796 | PAL / NTSC / SECAM. if your hardware does not (or does more) | ||
| 797 | -- override this function in your extension */ | ||
| 798 | /* | ||
| 799 | case VIDIOC_ENUMSTD: | ||
| 800 | { | ||
| 801 | struct v4l2_standard *e = arg; | ||
| 802 | if (e->index < 0 ) | ||
| 803 | return -EINVAL; | ||
| 804 | if( e->index < dev->ext_vv_data->num_stds ) { | ||
| 805 | DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); | ||
| 806 | v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); | ||
| 807 | return 0; | ||
| 808 | } | ||
| 809 | return -EINVAL; | ||
| 810 | } | ||
| 811 | */ | ||
| 812 | |||
| 813 | static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) | 457 | static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) |
| 814 | { | 458 | { |
| 815 | struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; | 459 | struct saa7146_dev *dev = video_drvdata(file); |
| 816 | struct saa7146_vv *vv = dev->vv_data; | 460 | struct saa7146_vv *vv = dev->vv_data; |
| 817 | int found = 0; | 461 | int found = 0; |
| 818 | int err, i; | 462 | int i; |
| 819 | 463 | ||
| 820 | DEB_EE("VIDIOC_S_STD\n"); | 464 | DEB_EE("VIDIOC_S_STD\n"); |
| 821 | 465 | ||
| 822 | if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { | 466 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) |
| 467 | if (id & dev->ext_vv_data->stds[i].id) | ||
| 468 | break; | ||
| 469 | |||
| 470 | if (i != dev->ext_vv_data->num_stds && | ||
| 471 | vv->standard == &dev->ext_vv_data->stds[i]) | ||
| 472 | return 0; | ||
| 473 | |||
| 474 | if (vb2_is_busy(&vv->video_dmaq.q) || vb2_is_busy(&vv->vbi_dmaq.q)) { | ||
| 823 | DEB_D("cannot change video standard while streaming capture is active\n"); | 475 | DEB_D("cannot change video standard while streaming capture is active\n"); |
| 824 | return -EBUSY; | 476 | return -EBUSY; |
| 825 | } | 477 | } |
| 826 | 478 | ||
| 827 | if ((vv->video_status & STATUS_OVERLAY) != 0) { | ||
| 828 | vv->ov_suspend = vv->video_fh; | ||
| 829 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | ||
| 830 | if (0 != err) { | ||
| 831 | DEB_D("suspending video failed. aborting\n"); | ||
| 832 | return err; | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | for (i = 0; i < dev->ext_vv_data->num_stds; i++) | ||
| 837 | if (id & dev->ext_vv_data->stds[i].id) | ||
| 838 | break; | ||
| 839 | if (i != dev->ext_vv_data->num_stds) { | 479 | if (i != dev->ext_vv_data->num_stds) { |
| 840 | vv->standard = &dev->ext_vv_data->stds[i]; | 480 | vv->standard = &dev->ext_vv_data->stds[i]; |
| 841 | if (NULL != dev->ext_vv_data->std_callback) | 481 | if (NULL != dev->ext_vv_data->std_callback) |
| @@ -843,11 +483,6 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) · 1 unmodified line | |||
| 843 | found = 1; | 483 | found = 1; |
| 844 | } | 484 | } |
| 845 | 485 | ||
| 846 | if (vv->ov_suspend != NULL) { | ||
| 847 | saa7146_start_preview(vv->ov_suspend); | ||
| 848 | vv->ov_suspend = NULL; | ||
| 849 | } | ||
| 850 | |||
| 851 | if (!found) { | 486 | if (!found) { |
| 852 | DEB_EE("VIDIOC_S_STD: standard not found\n"); | 487 | DEB_EE("VIDIOC_S_STD: standard not found\n"); |
| 853 | return -EINVAL; | 488 | return -EINVAL; |
| @@ -857,138 +492,22 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) · 3 unmodified lines | |||
| 857 | return 0; | 492 | return 0; |
| 858 | } | 493 | } |
| 859 | 494 | ||
| 860 | static int vidioc_overlay(struct file *file, void *fh, unsigned int on) | ||
| 861 | { | ||
| 862 | int err; | ||
| 863 | |||
| 864 | DEB_D("VIDIOC_OVERLAY on:%d\n", on); | ||
| 865 | if (on) | ||
| 866 | err = saa7146_start_preview(fh); | ||
| 867 | else | ||
| 868 | err = saa7146_stop_preview(fh); | ||
| 869 | return err; | ||
| 870 | } | ||
| 871 | |||
| 872 | static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) | ||
| 873 | { | ||
| 874 | struct saa7146_fh *fh = __fh; | ||
| 875 | |||
| 876 | if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 877 | return videobuf_reqbufs(&fh->video_q, b); | ||
| 878 | if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 879 | return videobuf_reqbufs(&fh->vbi_q, b); | ||
| 880 | return -EINVAL; | ||
| 881 | } | ||
| 882 | |||
| 883 | static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) | ||
| 884 | { | ||
| 885 | struct saa7146_fh *fh = __fh; | ||
| 886 | |||
| 887 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 888 | return videobuf_querybuf(&fh->video_q, buf); | ||
| 889 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 890 | return videobuf_querybuf(&fh->vbi_q, buf); | ||
| 891 | return -EINVAL; | ||
| 892 | } | ||
| 893 | |||
| 894 | static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) | ||
| 895 | { | ||
| 896 | struct saa7146_fh *fh = __fh; | ||
| 897 | |||
| 898 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 899 | return videobuf_qbuf(&fh->video_q, buf); | ||
| 900 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 901 | return videobuf_qbuf(&fh->vbi_q, buf); | ||
| 902 | return -EINVAL; | ||
| 903 | } | ||
| 904 | |||
| 905 | static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) | ||
| 906 | { | ||
| 907 | struct saa7146_fh *fh = __fh; | ||
| 908 | |||
| 909 | if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 910 | return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); | ||
| 911 | if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 912 | return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); | ||
| 913 | return -EINVAL; | ||
| 914 | } | ||
| 915 | |||
| 916 | static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) | ||
| 917 | { | ||
| 918 | struct saa7146_fh *fh = __fh; | ||
| 919 | int err; | ||
| 920 | |||
| 921 | DEB_D("VIDIOC_STREAMON, type:%d\n", type); | ||
| 922 | |||
| 923 | err = video_begin(fh); | ||
| 924 | if (err) | ||
| 925 | return err; | ||
| 926 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 927 | return videobuf_streamon(&fh->video_q); | ||
| 928 | if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 929 | return videobuf_streamon(&fh->vbi_q); | ||
| 930 | return -EINVAL; | ||
| 931 | } | ||
| 932 | |||
| 933 | static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) | ||
| 934 | { | ||
| 935 | struct saa7146_fh *fh = __fh; | ||
| 936 | struct saa7146_dev *dev = fh->dev; | ||
| 937 | struct saa7146_vv *vv = dev->vv_data; | ||
| 938 | int err; | ||
| 939 | |||
| 940 | DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); | ||
| 941 | |||
| 942 | /* ugly: we need to copy some checks from video_end(), | ||
| 943 | because videobuf_streamoff() relies on the capture running. | ||
| 944 | check and fix this */ | ||
| 945 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | ||
| 946 | DEB_S("not capturing\n"); | ||
| 947 | return 0; | ||
| 948 | } | ||
| 949 | |||
| 950 | if (vv->video_fh != fh) { | ||
| 951 | DEB_S("capturing, but in another open\n"); | ||
| 952 | return -EBUSY; | ||
| 953 | } | ||
| 954 | |||
| 955 | err = -EINVAL; | ||
| 956 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
| 957 | err = videobuf_streamoff(&fh->video_q); | ||
| 958 | else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) | ||
| 959 | err = videobuf_streamoff(&fh->vbi_q); | ||
| 960 | if (0 != err) { | ||
| 961 | DEB_D("warning: videobuf_streamoff() failed\n"); | ||
| 962 | video_end(fh, file); | ||
| 963 | } else { | ||
| 964 | err = video_end(fh, file); | ||
| 965 | } | ||
| 966 | return err; | ||
| 967 | } | ||
| 968 | |||
| 969 | const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { | 495 | const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { |
| 970 | .vidioc_querycap = vidioc_querycap, | 496 | .vidioc_querycap = vidioc_querycap, |
| 971 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | 497 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, |
| 972 | .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap, | ||
| 973 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, | 498 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
| 974 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | 499 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, |
| 975 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | 500 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, |
| 976 | .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, | ||
| 977 | .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, | ||
| 978 | .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, | ||
| 979 | |||
| 980 | .vidioc_overlay = vidioc_overlay, | ||
| 981 | .vidioc_g_fbuf = vidioc_g_fbuf, | ||
| 982 | .vidioc_s_fbuf = vidioc_s_fbuf, | ||
| 983 | .vidioc_reqbufs = vidioc_reqbufs, | ||
| 984 | .vidioc_querybuf = vidioc_querybuf, | ||
| 985 | .vidioc_qbuf = vidioc_qbuf, | ||
| 986 | .vidioc_dqbuf = vidioc_dqbuf, | ||
| 987 | .vidioc_g_std = vidioc_g_std, | 501 | .vidioc_g_std = vidioc_g_std, |
| 988 | .vidioc_s_std = vidioc_s_std, | 502 | .vidioc_s_std = vidioc_s_std, |
| 989 | .vidioc_streamon = vidioc_streamon, | ||
| 990 | .vidioc_streamoff = vidioc_streamoff, | ||
| 991 | .vidioc_g_parm = vidioc_g_parm, | 503 | .vidioc_g_parm = vidioc_g_parm, |
| 504 | .vidioc_reqbufs = vb2_ioctl_reqbufs, | ||
| 505 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
| 506 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
| 507 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
| 508 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
| 509 | .vidioc_streamon = vb2_ioctl_streamon, | ||
| 510 | .vidioc_streamoff = vb2_ioctl_streamoff, | ||
| 992 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | 511 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
| 993 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | 512 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
| 994 | }; | 513 | }; |
| @@ -996,16 +515,18 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { · 1 unmodified line | |||
| 996 | const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { | 515 | const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { |
| 997 | .vidioc_querycap = vidioc_querycap, | 516 | .vidioc_querycap = vidioc_querycap, |
| 998 | .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, | 517 | .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, |
| 999 | 518 | .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, | |
| 1000 | .vidioc_reqbufs = vidioc_reqbufs, | 519 | .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, |
| 1001 | .vidioc_querybuf = vidioc_querybuf, | ||
| 1002 | .vidioc_qbuf = vidioc_qbuf, | ||
| 1003 | .vidioc_dqbuf = vidioc_dqbuf, | ||
| 1004 | .vidioc_g_std = vidioc_g_std, | 520 | .vidioc_g_std = vidioc_g_std, |
| 1005 | .vidioc_s_std = vidioc_s_std, | 521 | .vidioc_s_std = vidioc_s_std, |
| 1006 | .vidioc_streamon = vidioc_streamon, | ||
| 1007 | .vidioc_streamoff = vidioc_streamoff, | ||
| 1008 | .vidioc_g_parm = vidioc_g_parm, | 522 | .vidioc_g_parm = vidioc_g_parm, |
| 523 | .vidioc_reqbufs = vb2_ioctl_reqbufs, | ||
| 524 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
| 525 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
| 526 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
| 527 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
| 528 | .vidioc_streamon = vb2_ioctl_streamon, | ||
| 529 | .vidioc_streamoff = vb2_ioctl_streamoff, | ||
| 1009 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, | 530 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
| 1010 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | 531 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
| 1011 | }; | 532 | }; |
| @@ -1019,7 +540,6 @@ static int buffer_activate (struct saa7146_dev *dev, · 7 unmodified lines | |||
| 1019 | { | 540 | { |
| 1020 | struct saa7146_vv *vv = dev->vv_data; | 541 | struct saa7146_vv *vv = dev->vv_data; |
| 1021 | 542 | ||
| 1022 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
| 1023 | saa7146_set_capture(dev,buf,next); | 543 | saa7146_set_capture(dev,buf,next); |
| 1024 | 544 | ||
| 1025 | mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); | 545 | mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); |
| @@ -1033,140 +553,136 @@ static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf * · 7 unmodified lines | |||
| 1033 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | 553 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); |
| 1034 | } | 554 | } |
| 1035 | 555 | ||
| 1036 | static int buffer_prepare(struct videobuf_queue *q, | 556 | static int queue_setup(struct vb2_queue *q, |
| 1037 | struct videobuf_buffer *vb, enum v4l2_field field) | 557 | unsigned int *num_buffers, unsigned int *num_planes, |
| 558 | unsigned int sizes[], struct device *alloc_devs[]) | ||
| 1038 | { | 559 | { |
| 1039 | struct file *file = q->priv_data; | 560 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 1040 | struct saa7146_fh *fh = file->private_data; | 561 | unsigned int size = dev->vv_data->video_fmt.sizeimage; |
| 1041 | struct saa7146_dev *dev = fh->dev; | ||
| 1042 | struct saa7146_vv *vv = dev->vv_data; | ||
| 1043 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
| 1044 | int size,err = 0; | ||
| 1045 | |||
| 1046 | DEB_CAP("vbuf:%p\n", vb); | ||
| 1047 | |||
| 1048 | /* sanity checks */ | ||
| 1049 | if (vv->video_fmt.width < 48 || | ||
| 1050 | vv->video_fmt.height < 32 || | ||
| 1051 | vv->video_fmt.width > vv->standard->h_max_out || | ||
| 1052 | vv->video_fmt.height > vv->standard->v_max_out) { | ||
| 1053 | DEB_D("w (%d) / h (%d) out of bounds\n", | ||
| 1054 | vv->video_fmt.width, vv->video_fmt.height); | ||
| 1055 | return -EINVAL; | ||
| 1056 | } | ||
| 1057 | 562 | ||
| 1058 | size = vv->video_fmt.sizeimage; | 563 | if (*num_planes) |
| 1059 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { | 564 | return sizes[0] < size ? -EINVAL : 0; |
| 1060 | DEB_D("size mismatch\n"); | 565 | *num_planes = 1; |
| 1061 | return -EINVAL; | 566 | sizes[0] = size; |
| 1062 | } | ||
| 1063 | 567 | ||
| 1064 | DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", | 568 | return 0; |
| 1065 | vv->video_fmt.width, vv->video_fmt.height, | 569 | } |
| 1066 | size, v4l2_field_names[vv->video_fmt.field]); | ||
| 1067 | if (buf->vb.width != vv->video_fmt.width || | ||
| 1068 | buf->vb.bytesperline != vv->video_fmt.bytesperline || | ||
| 1069 | buf->vb.height != vv->video_fmt.height || | ||
| 1070 | buf->vb.size != size || | ||
| 1071 | buf->vb.field != field || | ||
| 1072 | buf->vb.field != vv->video_fmt.field || | ||
| 1073 | buf->fmt != &vv->video_fmt) { | ||
| 1074 | saa7146_dma_free(dev,q,buf); | ||
| 1075 | } | ||
| 1076 | 570 | ||
| 1077 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | 571 | static void buf_queue(struct vb2_buffer *vb) |
| 1078 | struct saa7146_format *sfmt; | 572 | { |
| 573 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | ||
| 574 | struct vb2_queue *vq = vb->vb2_queue; | ||
| 575 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); | ||
| 576 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); | ||
| 577 | unsigned long flags; | ||
| 1079 | 578 | ||
| 1080 | buf->vb.bytesperline = vv->video_fmt.bytesperline; | 579 | spin_lock_irqsave(&dev->slock, flags); |
| 1081 | buf->vb.width = vv->video_fmt.width; | ||
| 1082 | buf->vb.height = vv->video_fmt.height; | ||
| 1083 | buf->vb.size = size; | ||
| 1084 | buf->vb.field = field; | ||
| 1085 | buf->fmt = &vv->video_fmt; | ||
| 1086 | buf->vb.field = vv->video_fmt.field; | ||
| 1087 | 580 | ||
| 1088 | sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); | 581 | saa7146_buffer_queue(dev, &dev->vv_data->video_dmaq, buf); |
| 582 | spin_unlock_irqrestore(&dev->slock, flags); | ||
| 583 | } | ||
| 1089 | 584 | ||
| 1090 | release_all_pagetables(dev, buf); | 585 | static int buf_init(struct vb2_buffer *vb) |
| 1091 | if( 0 != IS_PLANAR(sfmt->trans)) { | 586 | { |
| 1092 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | 587 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
| 1093 | saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); | 588 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); |
| 1094 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | 589 | struct vb2_queue *vq = vb->vb2_queue; |
| 1095 | } else { | 590 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); |
| 1096 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | 591 | struct saa7146_vv *vv = dev->vv_data; |
| 1097 | } | 592 | struct saa7146_format *sfmt; |
| 593 | int ret; | ||
| 1098 | 594 | ||
| 1099 | err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); | ||
| 1100 | if (err) | ||
| 1101 | goto oops; | ||
| 1102 | err = saa7146_pgtable_build(dev,buf); | ||
| 1103 | if (err) | ||
| 1104 | goto oops; | ||
| 1105 | } | ||
| 1106 | buf->vb.state = VIDEOBUF_PREPARED; | ||
| 1107 | buf->activate = buffer_activate; | 595 | buf->activate = buffer_activate; |
| 596 | sfmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); | ||
| 1108 | 597 | ||
| 1109 | return 0; | 598 | if (IS_PLANAR(sfmt->trans)) { |
| 1110 | 599 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | |
| 1111 | oops: | 600 | saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); |
| 1112 | DEB_D("error out\n"); | 601 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); |
| 1113 | saa7146_dma_free(dev,q,buf); | 602 | } else { |
| 603 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | ||
| 604 | } | ||
| 1114 | 605 | ||
| 1115 | return err; | 606 | ret = saa7146_pgtable_build(dev, buf); |
| 607 | if (ret) | ||
| 608 | release_all_pagetables(dev, buf); | ||
| 609 | return ret; | ||
| 1116 | } | 610 | } |
| 1117 | 611 | ||
| 1118 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | 612 | static int buf_prepare(struct vb2_buffer *vb) |
| 1119 | { | 613 | { |
| 1120 | struct file *file = q->priv_data; | 614 | struct vb2_queue *vq = vb->vb2_queue; |
| 1121 | struct saa7146_fh *fh = file->private_data; | 615 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); |
| 1122 | struct saa7146_vv *vv = fh->dev->vv_data; | 616 | struct saa7146_vv *vv = dev->vv_data; |
| 1123 | 617 | unsigned int size = vv->video_fmt.sizeimage; | |
| 1124 | if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) | ||
| 1125 | *count = MAX_SAA7146_CAPTURE_BUFFERS; | ||
| 1126 | |||
| 1127 | *size = vv->video_fmt.sizeimage; | ||
| 1128 | 618 | ||
| 1129 | /* check if we exceed the "max_memory" parameter */ | 619 | if (vb2_plane_size(vb, 0) < size) |
| 1130 | if( (*count * *size) > (max_memory*1048576) ) { | 620 | return -EINVAL; |
| 1131 | *count = (max_memory*1048576) / *size; | 621 | vb2_set_plane_payload(vb, 0, size); |
| 1132 | } | 622 | return 0; |
| 623 | } | ||
| 1133 | 624 | ||
| 1134 | DEB_CAP("%d buffers, %d bytes each\n", *count, *size); | 625 | static void buf_cleanup(struct vb2_buffer *vb) |
| 626 | { | ||
| 627 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | ||
| 628 | struct saa7146_buf *buf = container_of(vbuf, struct saa7146_buf, vb); | ||
| 629 | struct vb2_queue *vq = vb->vb2_queue; | ||
| 630 | struct saa7146_dev *dev = vb2_get_drv_priv(vq); | ||
| 1135 | 631 | ||
| 1136 | return 0; | 632 | release_all_pagetables(dev, buf); |
| 1137 | } | 633 | } |
| 1138 | 634 | ||
| 1139 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | 635 | static void return_buffers(struct vb2_queue *q, int state) |
| 1140 | { | 636 | { |
| 1141 | struct file *file = q->priv_data; | 637 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 1142 | struct saa7146_fh *fh = file->private_data; | 638 | struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq; |
| 1143 | struct saa7146_dev *dev = fh->dev; | 639 | struct saa7146_buf *buf; |
| 1144 | struct saa7146_vv *vv = dev->vv_data; | ||
| 1145 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
| 1146 | 640 | ||
| 1147 | DEB_CAP("vbuf:%p\n", vb); | 641 | if (dq->curr) { |
| 1148 | saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); | 642 | buf = dq->curr; |
| 643 | dq->curr = NULL; | ||
| 644 | vb2_buffer_done(&buf->vb.vb2_buf, state); | ||
| 645 | } | ||
| 646 | while (!list_empty(&dq->queue)) { | ||
| 647 | buf = list_entry(dq->queue.next, struct saa7146_buf, list); | ||
| 648 | list_del(&buf->list); | ||
| 649 | vb2_buffer_done(&buf->vb.vb2_buf, state); | ||
| 650 | } | ||
| 1149 | } | 651 | } |
| 1150 | 652 | ||
| 1151 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | 653 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
| 1152 | { | 654 | { |
| 1153 | struct file *file = q->priv_data; | 655 | struct saa7146_dev *dev = vb2_get_drv_priv(q); |
| 1154 | struct saa7146_fh *fh = file->private_data; | 656 | int ret; |
| 1155 | struct saa7146_dev *dev = fh->dev; | ||
| 1156 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
| 1157 | 657 | ||
| 1158 | DEB_CAP("vbuf:%p\n", vb); | 658 | if (!vb2_is_streaming(&dev->vv_data->video_dmaq.q)) |
| 659 | dev->vv_data->seqnr = 0; | ||
| 660 | ret = video_begin(dev); | ||
| 661 | if (ret) | ||
| 662 | return_buffers(q, VB2_BUF_STATE_QUEUED); | ||
| 663 | return ret; | ||
| 664 | } | ||
| 1159 | 665 | ||
| 1160 | saa7146_dma_free(dev,q,buf); | 666 | static void stop_streaming(struct vb2_queue *q) |
| 667 | { | ||
| 668 | struct saa7146_dev *dev = vb2_get_drv_priv(q); | ||
| 669 | struct saa7146_dmaqueue *dq = &dev->vv_data->video_dmaq; | ||
| 1161 | 670 | ||
| 1162 | release_all_pagetables(dev, buf); | 671 | del_timer(&dq->timeout); |
| 672 | video_end(dev); | ||
| 673 | return_buffers(q, VB2_BUF_STATE_ERROR); | ||
| 1163 | } | 674 | } |
| 1164 | 675 | ||
| 1165 | static const struct videobuf_queue_ops video_qops = { | 676 | const struct vb2_ops video_qops = { |
| 1166 | .buf_setup = buffer_setup, | 677 | .queue_setup = queue_setup, |
| 1167 | .buf_prepare = buffer_prepare, | 678 | .buf_queue = buf_queue, |
| 1168 | .buf_queue = buffer_queue, | 679 | .buf_init = buf_init, |
| 1169 | .buf_release = buffer_release, | 680 | .buf_prepare = buf_prepare, |
| 681 | .buf_cleanup = buf_cleanup, | ||
| 682 | .start_streaming = start_streaming, | ||
| 683 | .stop_streaming = stop_streaming, | ||
| 684 | .wait_prepare = vb2_ops_wait_prepare, | ||
| 685 | .wait_finish = vb2_ops_wait_finish, | ||
| 1170 | }; | 686 | }; |
| 1171 | 687 | ||
| 1172 | /********************************************************************************/ | 688 | /********************************************************************************/ |
| @@ -1187,38 +703,6 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) · 14 unmodified lines | |||
| 1187 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; | 703 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; |
| 1188 | } | 704 | } |
| 1189 | 705 | ||
| 1190 | |||
| 1191 | static int video_open(struct saa7146_dev *dev, struct file *file) | ||
| 1192 | { | ||
| 1193 | struct saa7146_fh *fh = file->private_data; | ||
| 1194 | |||
| 1195 | videobuf_queue_sg_init(&fh->video_q, &video_qops, | ||
| 1196 | &dev->pci->dev, &dev->slock, | ||
| 1197 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
| 1198 | V4L2_FIELD_INTERLACED, | ||
| 1199 | sizeof(struct saa7146_buf), | ||
| 1200 | file, &dev->v4l2_lock); | ||
| 1201 | |||
| 1202 | return 0; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | |||
| 1206 | static void video_close(struct saa7146_dev *dev, struct file *file) | ||
| 1207 | { | ||
| 1208 | struct saa7146_fh *fh = file->private_data; | ||
| 1209 | struct saa7146_vv *vv = dev->vv_data; | ||
| 1210 | struct videobuf_queue *q = &fh->video_q; | ||
| 1211 | |||
| 1212 | if (IS_CAPTURE_ACTIVE(fh) != 0) | ||
| 1213 | video_end(fh, file); | ||
| 1214 | else if (IS_OVERLAY_ACTIVE(fh) != 0) | ||
| 1215 | saa7146_stop_preview(fh); | ||
| 1216 | |||
| 1217 | videobuf_stop(q); | ||
| 1218 | /* hmm, why is this function declared void? */ | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | |||
| 1222 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) | 706 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) |
| 1223 | { | 707 | { |
| 1224 | struct saa7146_vv *vv = dev->vv_data; | 708 | struct saa7146_vv *vv = dev->vv_data; |
| @@ -1228,59 +712,14 @@ static void video_irq_done(struct saa7146_dev *dev, unsigned long st) · 3 unmodified lines | |||
| 1228 | DEB_CAP("called\n"); | 712 | DEB_CAP("called\n"); |
| 1229 | 713 | ||
| 1230 | /* only finish the buffer if we have one... */ | 714 | /* only finish the buffer if we have one... */ |
| 1231 | if( NULL != q->curr ) { | 715 | if (q->curr) |
| 1232 | saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); | 716 | saa7146_buffer_finish(dev, q, VB2_BUF_STATE_DONE); |
| 1233 | } | ||
| 1234 | saa7146_buffer_next(dev,q,0); | 717 | saa7146_buffer_next(dev,q,0); |
| 1235 | 718 | ||
| 1236 | spin_unlock(&dev->slock); | 719 | spin_unlock(&dev->slock); |
| 1237 | } | 720 | } |
| 1238 | 721 | ||
| 1239 | static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
| 1240 | { | ||
| 1241 | struct saa7146_fh *fh = file->private_data; | ||
| 1242 | struct saa7146_dev *dev = fh->dev; | ||
| 1243 | struct saa7146_vv *vv = dev->vv_data; | ||
| 1244 | ssize_t ret = 0; | ||
| 1245 | |||
| 1246 | DEB_EE("called\n"); | ||
| 1247 | |||
| 1248 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | ||
| 1249 | /* fixme: should we allow read() captures while streaming capture? */ | ||
| 1250 | if (vv->video_fh == fh) { | ||
| 1251 | DEB_S("already capturing\n"); | ||
| 1252 | return -EBUSY; | ||
| 1253 | } | ||
| 1254 | DEB_S("already capturing in another open\n"); | ||
| 1255 | return -EBUSY; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | ret = video_begin(fh); | ||
| 1259 | if( 0 != ret) { | ||
| 1260 | goto out; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | ret = videobuf_read_one(&fh->video_q , data, count, ppos, | ||
| 1264 | file->f_flags & O_NONBLOCK); | ||
| 1265 | if (ret != 0) { | ||
| 1266 | video_end(fh, file); | ||
| 1267 | } else { | ||
| 1268 | ret = video_end(fh, file); | ||
| 1269 | } | ||
| 1270 | out: | ||
| 1271 | /* restart overlay if it was active before */ | ||
| 1272 | if (vv->ov_suspend != NULL) { | ||
| 1273 | saa7146_start_preview(vv->ov_suspend); | ||
| 1274 | vv->ov_suspend = NULL; | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | return ret; | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | const struct saa7146_use_ops saa7146_video_uops = { | 722 | const struct saa7146_use_ops saa7146_video_uops = { |
| 1281 | .init = video_init, | 723 | .init = video_init, |
| 1282 | .open = video_open, | ||
| 1283 | .release = video_close, | ||
| 1284 | .irq_done = video_irq_done, | 724 | .irq_done = video_irq_done, |
| 1285 | .read = video_read, | ||
| 1286 | }; | 725 | }; |
| @@ -1171,10 +1171,11 @@ ssize_t vb2_fop_read(struct file *file, char __user *buf, · 1170 unmodified lines | |||
| 1171 | return -ERESTARTSYS; | 1171 | return -ERESTARTSYS; |
| 1172 | if (vb2_queue_is_busy(vdev->queue, file)) | 1172 | if (vb2_queue_is_busy(vdev->queue, file)) |
| 1173 | goto exit; | 1173 | goto exit; |
| 1174 | vdev->queue->owner = file->private_data; | ||
| 1174 | err = vb2_read(vdev->queue, buf, count, ppos, | 1175 | err = vb2_read(vdev->queue, buf, count, ppos, |
| 1175 | file->f_flags & O_NONBLOCK); | 1176 | file->f_flags & O_NONBLOCK); |
| 1176 | if (vdev->queue->fileio) | 1177 | if (!vdev->queue->fileio) |
| 1177 | vdev->queue->owner = file->private_data; | 1178 | vdev->queue->owner = NULL; |
| 1178 | exit: | 1179 | exit: |
| 1179 | if (lock) | 1180 | if (lock) |
| 1180 | mutex_unlock(lock); | 1181 | mutex_unlock(lock); |
| @@ -11070,7 +11070,7 @@ ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode) · 11069 unmodified lines | |||
| 11070 | sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; | 11070 | sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; |
| 11071 | break; | 11071 | break; |
| 11072 | default: | 11072 | default: |
| 11073 | /* Unknow sleep mode */ | 11073 | /* Unknown sleep mode */ |
| 11074 | return -EINVAL; | 11074 | return -EINVAL; |
| 11075 | } | 11075 | } |
| 11076 | 11076 | ||
| @@ -1585,7 +1585,7 @@ static int ctrl_power_mode(struct drxk_state *state, enum drx_power_mode *mode) · 1584 unmodified lines | |||
| 1585 | sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; | 1585 | sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC; |
| 1586 | break; | 1586 | break; |
| 1587 | default: | 1587 | default: |
| 1588 | /* Unknow sleep mode */ | 1588 | /* Unknown sleep mode */ |
| 1589 | return -EINVAL; | 1589 | return -EINVAL; |
| 1590 | } | 1590 | } |
| 1591 | 1591 | ||
| @@ -1463,7 +1463,7 @@ err: · 1462 unmodified lines | |||
| 1463 | return ret; | 1463 | return ret; |
| 1464 | } | 1464 | } |
| 1465 | 1465 | ||
| 1466 | static int rtl2832_sdr_remove(struct platform_device *pdev) | 1466 | static void rtl2832_sdr_remove(struct platform_device *pdev) |
| 1467 | { | 1467 | { |
| 1468 | struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev); | 1468 | struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev); |
| 1469 | 1469 | ||
| @@ -1479,8 +1479,6 @@ static int rtl2832_sdr_remove(struct platform_device *pdev) · 9 unmodified lines | |||
| 1479 | mutex_unlock(&dev->vb_queue_lock); | 1479 | mutex_unlock(&dev->vb_queue_lock); |
| 1480 | v4l2_device_put(&dev->v4l2_dev); | 1480 | v4l2_device_put(&dev->v4l2_dev); |
| 1481 | module_put(pdev->dev.parent->driver->owner); | 1481 | module_put(pdev->dev.parent->driver->owner); |
| 1482 | |||
| 1483 | return 0; | ||
| 1484 | } | 1482 | } |
| 1485 | 1483 | ||
| 1486 | static struct platform_driver rtl2832_sdr_driver = { | 1484 | static struct platform_driver rtl2832_sdr_driver = { |
| @@ -1488,7 +1486,7 @@ static struct platform_driver rtl2832_sdr_driver = { · 1 unmodified line | |||
| 1488 | .name = "rtl2832_sdr", | 1486 | .name = "rtl2832_sdr", |
| 1489 | }, | 1487 | }, |
| 1490 | .probe = rtl2832_sdr_probe, | 1488 | .probe = rtl2832_sdr_probe, |
| 1491 | .remove = rtl2832_sdr_remove, | 1489 | .remove_new = rtl2832_sdr_remove, |
| 1492 | }; | 1490 | }; |
| 1493 | module_platform_driver(rtl2832_sdr_driver); | 1491 | module_platform_driver(rtl2832_sdr_driver); |
| 1494 | 1492 | ||
| @@ -515,7 +515,7 @@ err: · 514 unmodified lines | |||
| 515 | return ret; | 515 | return ret; |
| 516 | } | 516 | } |
| 517 | 517 | ||
| 518 | static int zd1301_demod_remove(struct platform_device *pdev) | 518 | static void zd1301_demod_remove(struct platform_device *pdev) |
| 519 | { | 519 | { |
| 520 | struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); | 520 | struct zd1301_demod_dev *dev = platform_get_drvdata(pdev); |
| 521 | 521 | ||
| @@ -523,8 +523,6 @@ static int zd1301_demod_remove(struct platform_device *pdev) · 1 unmodified line | |||
| 523 | 523 | ||
| 524 | i2c_del_adapter(&dev->adapter); | 524 | i2c_del_adapter(&dev->adapter); |
| 525 | kfree(dev); | 525 | kfree(dev); |
| 526 | |||
| 527 | return 0; | ||
| 528 | } | 526 | } |
| 529 | 527 | ||
| 530 | static struct platform_driver zd1301_demod_driver = { | 528 | static struct platform_driver zd1301_demod_driver = { |
| @@ -533,7 +531,7 @@ static struct platform_driver zd1301_demod_driver = { · 2 unmodified lines | |||
| 533 | .suppress_bind_attrs = true, | 531 | .suppress_bind_attrs = true, |
| 534 | }, | 532 | }, |
| 535 | .probe = zd1301_demod_probe, | 533 | .probe = zd1301_demod_probe, |
| 536 | .remove = zd1301_demod_remove, | 534 | .remove_new = zd1301_demod_remove, |
| 537 | }; | 535 | }; |
| 538 | module_platform_driver(zd1301_demod_driver); | 536 | module_platform_driver(zd1301_demod_driver); |
| 539 | 537 | ||
| @@ -267,16 +267,6 @@ config VIDEO_MT9M001 · 266 unmodified lines | |||
| 267 | This driver supports MT9M001 cameras from Micron, monochrome | 267 | This driver supports MT9M001 cameras from Micron, monochrome |
| 268 | and colour models. | 268 | and colour models. |
| 269 | 269 | ||
| 270 | config VIDEO_MT9M032 | ||
| 271 | tristate "MT9M032 camera sensor support" | ||
| 272 | depends on I2C && VIDEO_DEV | ||
| 273 | select MEDIA_CONTROLLER | ||
| 274 | select VIDEO_V4L2_SUBDEV_API | ||
| 275 | select VIDEO_APTINA_PLL | ||
| 276 | help | ||
| 277 | This driver supports MT9M032 camera sensors from Aptina, monochrome | ||
| 278 | models only. | ||
| 279 | |||
| 280 | config VIDEO_MT9M111 | 270 | config VIDEO_MT9M111 |
| 281 | tristate "mt9m111, mt9m112 and mt9m131 support" | 271 | tristate "mt9m111, mt9m112 and mt9m131 support" |
| 282 | depends on I2C && VIDEO_DEV | 272 | depends on I2C && VIDEO_DEV |
| @@ -296,15 +286,6 @@ config VIDEO_MT9P031 · 13 unmodified lines | |||
| 296 | This is a Video4Linux2 sensor driver for the Aptina | 286 | This is a Video4Linux2 sensor driver for the Aptina |
| 297 | (Micron) mt9p031 5 Mpixel camera. | 287 | (Micron) mt9p031 5 Mpixel camera. |
| 298 | 288 | ||
| 299 | config VIDEO_MT9T001 | ||
| 300 | tristate "Aptina MT9T001 support" | ||
| 301 | depends on I2C && VIDEO_DEV | ||
| 302 | select MEDIA_CONTROLLER | ||
| 303 | select VIDEO_V4L2_SUBDEV_API | ||
| 304 | help | ||
| 305 | This is a Video4Linux2 sensor driver for the Aptina | ||
| 306 | (Micron) mt0t001 3 Mpixel camera. | ||
| 307 | |||
| 308 | config VIDEO_MT9T112 | 289 | config VIDEO_MT9T112 |
| 309 | tristate "Aptina MT9T111/MT9T112 support" | 290 | tristate "Aptina MT9T111/MT9T112 support" |
| 310 | depends on I2C && VIDEO_DEV | 291 | depends on I2C && VIDEO_DEV |
| @@ -344,14 +325,6 @@ config VIDEO_MT9V111 · 33 unmodified lines | |||
| 344 | To compile this driver as a module, choose M here: the | 325 | To compile this driver as a module, choose M here: the |
| 345 | module will be called mt9v111. | 326 | module will be called mt9v111. |
| 346 | 327 | ||
| 347 | config VIDEO_NOON010PC30 | ||
| 348 | tristate "Siliconfile NOON010PC30 sensor support" | ||
| 349 | depends on I2C && VIDEO_DEV | ||
| 350 | select MEDIA_CONTROLLER | ||
| 351 | select VIDEO_V4L2_SUBDEV_API | ||
| 352 | help | ||
| 353 | This driver supports NOON010PC30 CIF camera from Siliconfile | ||
| 354 | |||
| 355 | config VIDEO_OG01A1B | 328 | config VIDEO_OG01A1B |
| 356 | tristate "OmniVision OG01A1B sensor support" | 329 | tristate "OmniVision OG01A1B sensor support" |
| 357 | depends on I2C && VIDEO_DEV | 330 | depends on I2C && VIDEO_DEV |
| @@ -462,6 +435,7 @@ config VIDEO_OV2685 · 104 unmodified lines | |||
| 462 | tristate "OmniVision OV2685 sensor support" | 435 | tristate "OmniVision OV2685 sensor support" |
| 463 | depends on VIDEO_DEV && I2C | 436 | depends on VIDEO_DEV && I2C |
| 464 | select MEDIA_CONTROLLER | 437 | select MEDIA_CONTROLLER |
| 438 | select VIDEO_V4L2_SUBDEV_API | ||
| 465 | select V4L2_FWNODE | 439 | select V4L2_FWNODE |
| 466 | help | 440 | help |
| 467 | This is a Video4Linux2 sensor driver for the OmniVision | 441 | This is a Video4Linux2 sensor driver for the OmniVision |
| @@ -810,21 +784,6 @@ config VIDEO_S5K6A3 · 342 unmodified lines | |||
| 810 | This is a V4L2 sensor driver for Samsung S5K6A3 raw | 784 | This is a V4L2 sensor driver for Samsung S5K6A3 raw |
| 811 | camera sensor. | 785 | camera sensor. |
| 812 | 786 | ||
| 813 | config VIDEO_S5K6AA | ||
| 814 | tristate "Samsung S5K6AAFX sensor support" | ||
| 815 | depends on I2C && VIDEO_DEV | ||
| 816 | select MEDIA_CONTROLLER | ||
| 817 | select VIDEO_V4L2_SUBDEV_API | ||
| 818 | help | ||
| 819 | This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M | ||
| 820 | camera sensor with an embedded SoC image signal processor. | ||
| 821 | |||
| 822 | config VIDEO_SR030PC30 | ||
| 823 | tristate "Siliconfile SR030PC30 sensor support" | ||
| 824 | depends on I2C && VIDEO_DEV | ||
| 825 | help | ||
| 826 | This driver supports SR030PC30 VGA camera from Siliconfile | ||
| 827 | |||
| 828 | config VIDEO_ST_VGXY61 | 787 | config VIDEO_ST_VGXY61 |
| 829 | tristate "ST VGXY61 sensor support" | 788 | tristate "ST VGXY61 sensor support" |
| 830 | depends on OF && GPIOLIB && VIDEO_DEV && I2C | 789 | depends on OF && GPIOLIB && VIDEO_DEV && I2C |
| @@ -835,19 +794,8 @@ config VIDEO_ST_VGXY61 · 4 unmodified lines | |||
| 835 | This is a Video4Linux2 sensor driver for the ST VGXY61 | 794 | This is a Video4Linux2 sensor driver for the ST VGXY61 |
| 836 | camera sensor. | 795 | camera sensor. |
| 837 | 796 | ||
| 838 | config VIDEO_VS6624 | ||
| 839 | tristate "ST VS6624 sensor support" | ||
| 840 | depends on VIDEO_DEV && I2C | ||
| 841 | help | ||
| 842 | This is a Video4Linux2 sensor driver for the ST VS6624 | ||
| 843 | camera. | ||
| 844 | |||
| 845 | To compile this driver as a module, choose M here: the | ||
| 846 | module will be called vs6624. | ||
| 847 | |||
| 848 | source "drivers/media/i2c/ccs/Kconfig" | 797 | source "drivers/media/i2c/ccs/Kconfig" |
| 849 | source "drivers/media/i2c/et8ek8/Kconfig" | 798 | source "drivers/media/i2c/et8ek8/Kconfig" |
| 850 | source "drivers/media/i2c/m5mols/Kconfig" | ||
| 851 | 799 | ||
| 852 | endmenu | 800 | endmenu |
| 853 | 801 | ||
| @@ -1453,20 +1401,6 @@ endmenu · 599 unmodified lines | |||
| 1453 | menu "Video encoders" | 1401 | menu "Video encoders" |
| 1454 | visible if !MEDIA_HIDE_ANCILLARY_SUBDRV | 1402 | visible if !MEDIA_HIDE_ANCILLARY_SUBDRV |
| 1455 | 1403 | ||
| 1456 | config VIDEO_AD9389B | ||
| 1457 | tristate "Analog Devices AD9389B encoder" | ||
| 1458 | depends on VIDEO_DEV && I2C | ||
| 1459 | select MEDIA_CONTROLLER | ||
| 1460 | select VIDEO_V4L2_SUBDEV_API | ||
| 1461 | |||
| 1462 | help | ||
| 1463 | Support for the Analog Devices AD9389B video encoder. | ||
| 1464 | |||
| 1465 | This is a Analog Devices HDMI transmitter. | ||
| 1466 | |||
| 1467 | To compile this driver as a module, choose M here: the | ||
| 1468 | module will be called ad9389b. | ||
| 1469 | |||
| 1470 | config VIDEO_ADV7170 | 1404 | config VIDEO_ADV7170 |
| 1471 | tristate "Analog Devices ADV7170 video encoder" | 1405 | tristate "Analog Devices ADV7170 video encoder" |
| 1472 | depends on VIDEO_DEV && I2C | 1406 | depends on VIDEO_DEV && I2C |
| @@ -4,7 +4,6 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o · 3 unmodified lines | |||
| 4 | 4 | ||
| 5 | obj-$(CONFIG_SDR_MAX2175) += max2175.o | 5 | obj-$(CONFIG_SDR_MAX2175) += max2175.o |
| 6 | obj-$(CONFIG_VIDEO_AD5820) += ad5820.o | 6 | obj-$(CONFIG_VIDEO_AD5820) += ad5820.o |
| 7 | obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o | ||
| 8 | obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o | 7 | obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o |
| 9 | obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o | 8 | obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o |
| 10 | obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o | 9 | obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o |
| @@ -56,21 +55,17 @@ obj-$(CONFIG_VIDEO_KS0127) += ks0127.o · 45 unmodified lines | |||
| 56 | obj-$(CONFIG_VIDEO_LM3560) += lm3560.o | 55 | obj-$(CONFIG_VIDEO_LM3560) += lm3560.o |
| 57 | obj-$(CONFIG_VIDEO_LM3646) += lm3646.o | 56 | obj-$(CONFIG_VIDEO_LM3646) += lm3646.o |
| 58 | obj-$(CONFIG_VIDEO_M52790) += m52790.o | 57 | obj-$(CONFIG_VIDEO_M52790) += m52790.o |
| 59 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ | ||
| 60 | obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o | 58 | obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o |
| 61 | obj-$(CONFIG_VIDEO_MAX9286) += max9286.o | 59 | obj-$(CONFIG_VIDEO_MAX9286) += max9286.o |
| 62 | obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o | 60 | obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o |
| 63 | obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o | 61 | obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o |
| 64 | obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o | 62 | obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o |
| 65 | obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o | ||
| 66 | obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o | 63 | obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o |
| 67 | obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o | 64 | obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o |
| 68 | obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o | ||
| 69 | obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o | 65 | obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o |
| 70 | obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o | 66 | obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o |
| 71 | obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o | 67 | obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o |
| 72 | obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o | 68 | obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o |
| 73 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o | ||
| 74 | obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o | 69 | obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o |
| 75 | obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o | 70 | obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o |
| 76 | obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o | 71 | obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o |
| @@ -110,7 +105,6 @@ obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o · 33 unmodified lines | |||
| 110 | obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ | 105 | obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ |
| 111 | obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o | 106 | obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o |
| 112 | obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o | 107 | obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o |
| 113 | obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o | ||
| 114 | obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o | 108 | obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o |
| 115 | obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o | 109 | obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o |
| 116 | obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o | 110 | obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o |
| @@ -119,7 +113,6 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o · 2 unmodified lines | |||
| 119 | obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o | 113 | obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o |
| 120 | obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o | 114 | obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o |
| 121 | obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o | 115 | obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o |
| 122 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o | ||
| 123 | obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o | 116 | obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o |
| 124 | obj-$(CONFIG_VIDEO_ST_VGXY61) += st-vgxy61.o | 117 | obj-$(CONFIG_VIDEO_ST_VGXY61) += st-vgxy61.o |
| 125 | obj-$(CONFIG_VIDEO_TC358743) += tc358743.o | 118 | obj-$(CONFIG_VIDEO_TC358743) += tc358743.o |
| @@ -145,6 +138,5 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o · 19 unmodified lines | |||
| 145 | obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o | 138 | obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o |
| 146 | obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o | 139 | obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o |
| 147 | obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o | 140 | obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o |
| 148 | obj-$(CONFIG_VIDEO_VS6624) += vs6624.o | ||
| 149 | obj-$(CONFIG_VIDEO_WM8739) += wm8739.o | 141 | obj-$(CONFIG_VIDEO_WM8739) += wm8739.o |
| 150 | obj-$(CONFIG_VIDEO_WM8775) += wm8775.o | 142 | obj-$(CONFIG_VIDEO_WM8775) += wm8775.o |
| @@ -1,1215 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* | ||
| 3 | * Analog Devices AD9389B/AD9889B video encoder driver | ||
| 4 | * | ||
| 5 | * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | ||
| 6 | */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * References (c = chapter, p = page): | ||
| 10 | * REF_01 - Analog Devices, Programming Guide, AD9889B/AD9389B, | ||
| 11 | * HDMI Transitter, Rev. A, October 2010 | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/i2c.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/videodev2.h> | ||
| 20 | #include <linux/workqueue.h> | ||
| 21 | #include <linux/v4l2-dv-timings.h> | ||
| 22 | #include <media/v4l2-device.h> | ||
| 23 | #include <media/v4l2-common.h> | ||
| 24 | #include <media/v4l2-dv-timings.h> | ||
| 25 | #include <media/v4l2-ctrls.h> | ||
| 26 | #include <media/i2c/ad9389b.h> | ||
| 27 | |||
| 28 | static int debug; | ||
| 29 | module_param(debug, int, 0644); | ||
| 30 | MODULE_PARM_DESC(debug, "debug level (0-2)"); | ||
| 31 | |||
| 32 | MODULE_DESCRIPTION("Analog Devices AD9389B/AD9889B video encoder driver"); | ||
| 33 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); | ||
| 34 | MODULE_AUTHOR("Martin Bugge <marbugge@cisco.com>"); | ||
| 35 | MODULE_LICENSE("GPL"); | ||
| 36 | |||
| 37 | #define MASK_AD9389B_EDID_RDY_INT 0x04 | ||
| 38 | #define MASK_AD9389B_MSEN_INT 0x40 | ||
| 39 | #define MASK_AD9389B_HPD_INT 0x80 | ||
| 40 | |||
| 41 | #define MASK_AD9389B_HPD_DETECT 0x40 | ||
| 42 | #define MASK_AD9389B_MSEN_DETECT 0x20 | ||
| 43 | #define MASK_AD9389B_EDID_RDY 0x10 | ||
| 44 | |||
| 45 | #define EDID_MAX_RETRIES (8) | ||
| 46 | #define EDID_DELAY 250 | ||
| 47 | #define EDID_MAX_SEGM 8 | ||
| 48 | |||
| 49 | /* | ||
| 50 | ********************************************************************** | ||
| 51 | * | ||
| 52 | * Arrays with configuration parameters for the AD9389B | ||
| 53 | * | ||
| 54 | ********************************************************************** | ||
| 55 | */ | ||
| 56 | |||
| 57 | struct ad9389b_state_edid { | ||
| 58 | /* total number of blocks */ | ||
| 59 | u32 blocks; | ||
| 60 | /* Number of segments read */ | ||
| 61 | u32 segments; | ||
| 62 | u8 data[EDID_MAX_SEGM * 256]; | ||
| 63 | /* Number of EDID read retries left */ | ||
| 64 | unsigned read_retries; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct ad9389b_state { | ||
| 68 | struct ad9389b_platform_data pdata; | ||
| 69 | struct v4l2_subdev sd; | ||
| 70 | struct media_pad pad; | ||
| 71 | struct v4l2_ctrl_handler hdl; | ||
| 72 | int chip_revision; | ||
| 73 | /* Is the ad9389b powered on? */ | ||
| 74 | bool power_on; | ||
| 75 | /* Did we receive hotplug and rx-sense signals? */ | ||
| 76 | bool have_monitor; | ||
| 77 | /* timings from s_dv_timings */ | ||
| 78 | struct v4l2_dv_timings dv_timings; | ||
| 79 | /* controls */ | ||
| 80 | struct v4l2_ctrl *hdmi_mode_ctrl; | ||
| 81 | struct v4l2_ctrl *hotplug_ctrl; | ||
| 82 | struct v4l2_ctrl *rx_sense_ctrl; | ||
| 83 | struct v4l2_ctrl *have_edid0_ctrl; | ||
| 84 | struct v4l2_ctrl *rgb_quantization_range_ctrl; | ||
| 85 | struct i2c_client *edid_i2c_client; | ||
| 86 | struct ad9389b_state_edid edid; | ||
| 87 | /* Running counter of the number of detected EDIDs (for debugging) */ | ||
| 88 | unsigned edid_detect_counter; | ||
| 89 | struct delayed_work edid_handler; /* work entry */ | ||
| 90 | }; | ||
| 91 | |||
| 92 | static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd); | ||
| 93 | static bool ad9389b_check_edid_status(struct v4l2_subdev *sd); | ||
| 94 | static void ad9389b_setup(struct v4l2_subdev *sd); | ||
| 95 | static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); | ||
| 96 | static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq); | ||
| 97 | |||
| 98 | static inline struct ad9389b_state *get_ad9389b_state(struct v4l2_subdev *sd) | ||
| 99 | { | ||
| 100 | return container_of(sd, struct ad9389b_state, sd); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | ||
| 104 | { | ||
| 105 | return &container_of(ctrl->handler, struct ad9389b_state, hdl)->sd; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* ------------------------ I2C ----------------------------------------------- */ | ||
| 109 | |||
| 110 | static int ad9389b_rd(struct v4l2_subdev *sd, u8 reg) | ||
| 111 | { | ||
| 112 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 113 | |||
| 114 | return i2c_smbus_read_byte_data(client, reg); | ||
| 115 | } | ||
| 116 | |||
| 117 | static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val) | ||
| 118 | { | ||
| 119 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 120 | int ret; | ||
| 121 | int i; | ||
| 122 | |||
| 123 | for (i = 0; i < 3; i++) { | ||
| 124 | ret = i2c_smbus_write_byte_data(client, reg, val); | ||
| 125 | if (ret == 0) | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | v4l2_err(sd, "%s: failed reg 0x%x, val 0x%x\n", __func__, reg, val); | ||
| 129 | return ret; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* To set specific bits in the register, a clear-mask is given (to be AND-ed), | ||
| 133 | and then the value-mask (to be OR-ed). */ | ||
| 134 | static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg, | ||
| 135 | u8 clr_mask, u8 val_mask) | ||
| 136 | { | ||
| 137 | ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void ad9389b_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) | ||
| 141 | { | ||
| 142 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 143 | int i; | ||
| 144 | |||
| 145 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | ||
| 146 | |||
| 147 | for (i = 0; i < len; i++) | ||
| 148 | buf[i] = i2c_smbus_read_byte_data(state->edid_i2c_client, i); | ||
| 149 | } | ||
| 150 | |||
| 151 | static inline bool ad9389b_have_hotplug(struct v4l2_subdev *sd) | ||
| 152 | { | ||
| 153 | return ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT; | ||
| 154 | } | ||
| 155 | |||
| 156 | static inline bool ad9389b_have_rx_sense(struct v4l2_subdev *sd) | ||
| 157 | { | ||
| 158 | return ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void ad9389b_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode) | ||
| 162 | { | ||
| 163 | ad9389b_wr_and_or(sd, 0x17, 0xe7, (mode & 0x3)<<3); | ||
| 164 | ad9389b_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void ad9389b_csc_coeff(struct v4l2_subdev *sd, | ||
| 168 | u16 A1, u16 A2, u16 A3, u16 A4, | ||
| 169 | u16 B1, u16 B2, u16 B3, u16 B4, | ||
| 170 | u16 C1, u16 C2, u16 C3, u16 C4) | ||
| 171 | { | ||
| 172 | /* A */ | ||
| 173 | ad9389b_wr_and_or(sd, 0x18, 0xe0, A1>>8); | ||
| 174 | ad9389b_wr(sd, 0x19, A1); | ||
| 175 | ad9389b_wr_and_or(sd, 0x1A, 0xe0, A2>>8); | ||
| 176 | ad9389b_wr(sd, 0x1B, A2); | ||
| 177 | ad9389b_wr_and_or(sd, 0x1c, 0xe0, A3>>8); | ||
| 178 | ad9389b_wr(sd, 0x1d, A3); | ||
| 179 | ad9389b_wr_and_or(sd, 0x1e, 0xe0, A4>>8); | ||
| 180 | ad9389b_wr(sd, 0x1f, A4); | ||
| 181 | |||
| 182 | /* B */ | ||
| 183 | ad9389b_wr_and_or(sd, 0x20, 0xe0, B1>>8); | ||
| 184 | ad9389b_wr(sd, 0x21, B1); | ||
| 185 | ad9389b_wr_and_or(sd, 0x22, 0xe0, B2>>8); | ||
| 186 | ad9389b_wr(sd, 0x23, B2); | ||
| 187 | ad9389b_wr_and_or(sd, 0x24, 0xe0, B3>>8); | ||
| 188 | ad9389b_wr(sd, 0x25, B3); | ||
| 189 | ad9389b_wr_and_or(sd, 0x26, 0xe0, B4>>8); | ||
| 190 | ad9389b_wr(sd, 0x27, B4); | ||
| 191 | |||
| 192 | /* C */ | ||
| 193 | ad9389b_wr_and_or(sd, 0x28, 0xe0, C1>>8); | ||
| 194 | ad9389b_wr(sd, 0x29, C1); | ||
| 195 | ad9389b_wr_and_or(sd, 0x2A, 0xe0, C2>>8); | ||
| 196 | ad9389b_wr(sd, 0x2B, C2); | ||
| 197 | ad9389b_wr_and_or(sd, 0x2C, 0xe0, C3>>8); | ||
| 198 | ad9389b_wr(sd, 0x2D, C3); | ||
| 199 | ad9389b_wr_and_or(sd, 0x2E, 0xe0, C4>>8); | ||
| 200 | ad9389b_wr(sd, 0x2F, C4); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void ad9389b_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) | ||
| 204 | { | ||
| 205 | if (enable) { | ||
| 206 | u8 csc_mode = 0; | ||
| 207 | |||
| 208 | ad9389b_csc_conversion_mode(sd, csc_mode); | ||
| 209 | ad9389b_csc_coeff(sd, | ||
| 210 | 4096-564, 0, 0, 256, | ||
| 211 | 0, 4096-564, 0, 256, | ||
| 212 | 0, 0, 4096-564, 256); | ||
| 213 | /* enable CSC */ | ||
| 214 | ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x1); | ||
| 215 | /* AVI infoframe: Limited range RGB (16-235) */ | ||
| 216 | ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x02); | ||
| 217 | } else { | ||
| 218 | /* disable CSC */ | ||
| 219 | ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x0); | ||
| 220 | /* AVI infoframe: Full range RGB (0-255) */ | ||
| 221 | ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x04); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | static void ad9389b_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) | ||
| 226 | { | ||
| 227 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 228 | |||
| 229 | if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { | ||
| 230 | /* CE format, not IT */ | ||
| 231 | ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x00); | ||
| 232 | } else { | ||
| 233 | /* IT format */ | ||
| 234 | ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x40); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | static int ad9389b_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) | ||
| 239 | { | ||
| 240 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 241 | |||
| 242 | switch (ctrl->val) { | ||
| 243 | case V4L2_DV_RGB_RANGE_AUTO: | ||
| 244 | /* automatic */ | ||
| 245 | if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { | ||
| 246 | /* CE format, RGB limited range (16-235) */ | ||
| 247 | ad9389b_csc_rgb_full2limit(sd, true); | ||
| 248 | } else { | ||
| 249 | /* not CE format, RGB full range (0-255) */ | ||
| 250 | ad9389b_csc_rgb_full2limit(sd, false); | ||
| 251 | } | ||
| 252 | break; | ||
| 253 | case V4L2_DV_RGB_RANGE_LIMITED: | ||
| 254 | /* RGB limited range (16-235) */ | ||
| 255 | ad9389b_csc_rgb_full2limit(sd, true); | ||
| 256 | break; | ||
| 257 | case V4L2_DV_RGB_RANGE_FULL: | ||
| 258 | /* RGB full range (0-255) */ | ||
| 259 | ad9389b_csc_rgb_full2limit(sd, false); | ||
| 260 | break; | ||
| 261 | default: | ||
| 262 | return -EINVAL; | ||
| 263 | } | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static void ad9389b_set_manual_pll_gear(struct v4l2_subdev *sd, u32 pixelclock) | ||
| 268 | { | ||
| 269 | u8 gear; | ||
| 270 | |||
| 271 | /* Workaround for TMDS PLL problem | ||
| 272 | * The TMDS PLL in AD9389b change gear when the chip is heated above a | ||
| 273 | * certain temperature. The output is disabled when the PLL change gear | ||
| 274 | * so the monitor has to lock on the signal again. A workaround for | ||
| 275 | * this is to use the manual PLL gears. This is a solution from Analog | ||
| 276 | * Devices that is not documented in the datasheets. | ||
| 277 | * 0x98 [7] = enable manual gearing. 0x98 [6:4] = gear | ||
| 278 | * | ||
| 279 | * The pixel frequency ranges are based on readout of the gear the | ||
| 280 | * automatic gearing selects for different pixel clocks | ||
| 281 | * (read from 0x9e [3:1]). | ||
| 282 | */ | ||
| 283 | |||
| 284 | if (pixelclock > 140000000) | ||
| 285 | gear = 0xc0; /* 4th gear */ | ||
| 286 | else if (pixelclock > 117000000) | ||
| 287 | gear = 0xb0; /* 3rd gear */ | ||
| 288 | else if (pixelclock > 87000000) | ||
| 289 | gear = 0xa0; /* 2nd gear */ | ||
| 290 | else if (pixelclock > 60000000) | ||
| 291 | gear = 0x90; /* 1st gear */ | ||
| 292 | else | ||
| 293 | gear = 0x80; /* 0th gear */ | ||
| 294 | |||
| 295 | ad9389b_wr_and_or(sd, 0x98, 0x0f, gear); | ||
| 296 | } | ||
| 297 | |||
| 298 | /* ------------------------------ CTRL OPS ------------------------------ */ | ||
| 299 | |||
| 300 | static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 301 | { | ||
| 302 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
| 303 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 304 | |||
| 305 | v4l2_dbg(1, debug, sd, | ||
| 306 | "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); | ||
| 307 | |||
| 308 | if (state->hdmi_mode_ctrl == ctrl) { | ||
| 309 | /* Set HDMI or DVI-D */ | ||
| 310 | ad9389b_wr_and_or(sd, 0xaf, 0xfd, | ||
| 311 | ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); | ||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | if (state->rgb_quantization_range_ctrl == ctrl) | ||
| 315 | return ad9389b_set_rgb_quantization_mode(sd, ctrl); | ||
| 316 | return -EINVAL; | ||
| 317 | } | ||
| 318 | |||
| 319 | static const struct v4l2_ctrl_ops ad9389b_ctrl_ops = { | ||
| 320 | .s_ctrl = ad9389b_s_ctrl, | ||
| 321 | }; | ||
| 322 | |||
| 323 | /* ---------------------------- CORE OPS ------------------------------------------- */ | ||
| 324 | |||
| 325 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
| 326 | static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) | ||
| 327 | { | ||
| 328 | reg->val = ad9389b_rd(sd, reg->reg & 0xff); | ||
| 329 | reg->size = 1; | ||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | static int ad9389b_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) | ||
| 334 | { | ||
| 335 | ad9389b_wr(sd, reg->reg & 0xff, reg->val & 0xff); | ||
| 336 | return 0; | ||
| 337 | } | ||
| 338 | #endif | ||
| 339 | |||
| 340 | static int ad9389b_log_status(struct v4l2_subdev *sd) | ||
| 341 | { | ||
| 342 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 343 | struct ad9389b_state_edid *edid = &state->edid; | ||
| 344 | |||
| 345 | static const char * const states[] = { | ||
| 346 | "in reset", | ||
| 347 | "reading EDID", | ||
| 348 | "idle", | ||
| 349 | "initializing HDCP", | ||
| 350 | "HDCP enabled", | ||
| 351 | "initializing HDCP repeater", | ||
| 352 | "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" | ||
| 353 | }; | ||
| 354 | static const char * const errors[] = { | ||
| 355 | "no error", | ||
| 356 | "bad receiver BKSV", | ||
| 357 | "Ri mismatch", | ||
| 358 | "Pj mismatch", | ||
| 359 | "i2c error", | ||
| 360 | "timed out", | ||
| 361 | "max repeater cascade exceeded", | ||
| 362 | "hash check failed", | ||
| 363 | "too many devices", | ||
| 364 | "9", "A", "B", "C", "D", "E", "F" | ||
| 365 | }; | ||
| 366 | |||
| 367 | u8 manual_gear; | ||
| 368 | |||
| 369 | v4l2_info(sd, "chip revision %d\n", state->chip_revision); | ||
| 370 | v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); | ||
| 371 | v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", | ||
| 372 | (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? | ||
| 373 | "detected" : "no", | ||
| 374 | (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? | ||
| 375 | "detected" : "no", | ||
| 376 | edid->segments ? "found" : "no", edid->blocks); | ||
| 377 | v4l2_info(sd, "%s output %s\n", | ||
| 378 | (ad9389b_rd(sd, 0xaf) & 0x02) ? | ||
| 379 | "HDMI" : "DVI-D", | ||
| 380 | (ad9389b_rd(sd, 0xa1) & 0x3c) ? | ||
| 381 | "disabled" : "enabled"); | ||
| 382 | v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ? | ||
| 383 | "encrypted" : "no encryption"); | ||
| 384 | v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", | ||
| 385 | states[ad9389b_rd(sd, 0xc8) & 0xf], | ||
| 386 | errors[ad9389b_rd(sd, 0xc8) >> 4], | ||
| 387 | state->edid_detect_counter, | ||
| 388 | ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); | ||
| 389 | manual_gear = ad9389b_rd(sd, 0x98) & 0x80; | ||
| 390 | v4l2_info(sd, "ad9389b: RGB quantization: %s range\n", | ||
| 391 | ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); | ||
| 392 | v4l2_info(sd, "ad9389b: %s gear %d\n", | ||
| 393 | manual_gear ? "manual" : "automatic", | ||
| 394 | manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) : | ||
| 395 | ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); | ||
| 396 | if (ad9389b_rd(sd, 0xaf) & 0x02) { | ||
| 397 | /* HDMI only */ | ||
| 398 | u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; | ||
| 399 | u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | | ||
| 400 | ad9389b_rd(sd, 0x02) << 8 | | ||
| 401 | ad9389b_rd(sd, 0x03); | ||
| 402 | u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; | ||
| 403 | u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; | ||
| 404 | u32 CTS; | ||
| 405 | |||
| 406 | if (manual_cts) | ||
| 407 | CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | | ||
| 408 | ad9389b_rd(sd, 0x08) << 8 | | ||
| 409 | ad9389b_rd(sd, 0x09); | ||
| 410 | else | ||
| 411 | CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | | ||
| 412 | ad9389b_rd(sd, 0x05) << 8 | | ||
| 413 | ad9389b_rd(sd, 0x06); | ||
| 414 | N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | | ||
| 415 | ad9389b_rd(sd, 0x02) << 8 | | ||
| 416 | ad9389b_rd(sd, 0x03); | ||
| 417 | |||
| 418 | v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", | ||
| 419 | manual_cts ? "manual" : "automatic", N, CTS); | ||
| 420 | |||
| 421 | v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", | ||
| 422 | vic_detect, vic_sent); | ||
| 423 | } | ||
| 424 | if (state->dv_timings.type == V4L2_DV_BT_656_1120) | ||
| 425 | v4l2_print_dv_timings(sd->name, "timings: ", | ||
| 426 | &state->dv_timings, false); | ||
| 427 | else | ||
| 428 | v4l2_info(sd, "no timings set\n"); | ||
| 429 | return 0; | ||
| 430 | } | ||
| 431 | |||
| 432 | /* Power up/down ad9389b */ | ||
| 433 | static int ad9389b_s_power(struct v4l2_subdev *sd, int on) | ||
| 434 | { | ||
| 435 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 436 | struct ad9389b_platform_data *pdata = &state->pdata; | ||
| 437 | const int retries = 20; | ||
| 438 | int i; | ||
| 439 | |||
| 440 | v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); | ||
| 441 | |||
| 442 | state->power_on = on; | ||
| 443 | |||
| 444 | if (!on) { | ||
| 445 | /* Power down */ | ||
| 446 | ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); | ||
| 447 | return true; | ||
| 448 | } | ||
| 449 | |||
| 450 | /* Power up */ | ||
| 451 | /* The ad9389b does not always come up immediately. | ||
| 452 | Retry multiple times. */ | ||
| 453 | for (i = 0; i < retries; i++) { | ||
| 454 | ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x0); | ||
| 455 | if ((ad9389b_rd(sd, 0x41) & 0x40) == 0) | ||
| 456 | break; | ||
| 457 | ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); | ||
| 458 | msleep(10); | ||
| 459 | } | ||
| 460 | if (i == retries) { | ||
| 461 | v4l2_dbg(1, debug, sd, "failed to powerup the ad9389b\n"); | ||
| 462 | ad9389b_s_power(sd, 0); | ||
| 463 | return false; | ||
| 464 | } | ||
| 465 | if (i > 1) | ||
| 466 | v4l2_dbg(1, debug, sd, | ||
| 467 | "needed %d retries to powerup the ad9389b\n", i); | ||
| 468 | |||
| 469 | /* Select chip: AD9389B */ | ||
| 470 | ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10); | ||
| 471 | |||
| 472 | /* Reserved registers that must be set according to REF_01 p. 11*/ | ||
| 473 | ad9389b_wr_and_or(sd, 0x98, 0xf0, 0x07); | ||
| 474 | ad9389b_wr(sd, 0x9c, 0x38); | ||
| 475 | ad9389b_wr_and_or(sd, 0x9d, 0xfc, 0x01); | ||
| 476 | |||
| 477 | /* Differential output drive strength */ | ||
| 478 | if (pdata->diff_data_drive_strength > 0) | ||
| 479 | ad9389b_wr(sd, 0xa2, pdata->diff_data_drive_strength); | ||
| 480 | else | ||
| 481 | ad9389b_wr(sd, 0xa2, 0x87); | ||
| 482 | |||
| 483 | if (pdata->diff_clk_drive_strength > 0) | ||
| 484 | ad9389b_wr(sd, 0xa3, pdata->diff_clk_drive_strength); | ||
| 485 | else | ||
| 486 | ad9389b_wr(sd, 0xa3, 0x87); | ||
| 487 | |||
| 488 | ad9389b_wr(sd, 0x0a, 0x01); | ||
| 489 | ad9389b_wr(sd, 0xbb, 0xff); | ||
| 490 | |||
| 491 | /* Set number of attempts to read the EDID */ | ||
| 492 | ad9389b_wr(sd, 0xc9, 0xf); | ||
| 493 | return true; | ||
| 494 | } | ||
| 495 | |||
| 496 | /* Enable interrupts */ | ||
| 497 | static void ad9389b_set_isr(struct v4l2_subdev *sd, bool enable) | ||
| 498 | { | ||
| 499 | u8 irqs = MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT; | ||
| 500 | u8 irqs_rd; | ||
| 501 | int retries = 100; | ||
| 502 | |||
| 503 | /* The datasheet says that the EDID ready interrupt should be | ||
| 504 | disabled if there is no hotplug. */ | ||
| 505 | if (!enable) | ||
| 506 | irqs = 0; | ||
| 507 | else if (ad9389b_have_hotplug(sd)) | ||
| 508 | irqs |= MASK_AD9389B_EDID_RDY_INT; | ||
| 509 | |||
| 510 | /* | ||
| 511 | * This i2c write can fail (approx. 1 in 1000 writes). But it | ||
| 512 | * is essential that this register is correct, so retry it | ||
| 513 | * multiple times. | ||
| 514 | * | ||
| 515 | * Note that the i2c write does not report an error, but the readback | ||
| 516 | * clearly shows the wrong value. | ||
| 517 | */ | ||
| 518 | do { | ||
| 519 | ad9389b_wr(sd, 0x94, irqs); | ||
| 520 | irqs_rd = ad9389b_rd(sd, 0x94); | ||
| 521 | } while (retries-- && irqs_rd != irqs); | ||
| 522 | |||
| 523 | if (irqs_rd != irqs) | ||
| 524 | v4l2_err(sd, "Could not set interrupts: hw failure?\n"); | ||
| 525 | } | ||
| 526 | |||
| 527 | /* Interrupt handler */ | ||
| 528 | static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled) | ||
| 529 | { | ||
| 530 | u8 irq_status; | ||
| 531 | |||
| 532 | /* disable interrupts to prevent a race condition */ | ||
| 533 | ad9389b_set_isr(sd, false); | ||
| 534 | irq_status = ad9389b_rd(sd, 0x96); | ||
| 535 | /* clear detected interrupts */ | ||
| 536 | ad9389b_wr(sd, 0x96, irq_status); | ||
| 537 | /* enable interrupts */ | ||
| 538 | ad9389b_set_isr(sd, true); | ||
| 539 | |||
| 540 | v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status); | ||
| 541 | |||
| 542 | if (irq_status & (MASK_AD9389B_HPD_INT)) | ||
| 543 | ad9389b_check_monitor_present_status(sd); | ||
| 544 | if (irq_status & MASK_AD9389B_EDID_RDY_INT) | ||
| 545 | ad9389b_check_edid_status(sd); | ||
| 546 | |||
| 547 | *handled = true; | ||
| 548 | return 0; | ||
| 549 | } | ||
| 550 | |||
| 551 | static const struct v4l2_subdev_core_ops ad9389b_core_ops = { | ||
| 552 | .log_status = ad9389b_log_status, | ||
| 553 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
| 554 | .g_register = ad9389b_g_register, | ||
| 555 | .s_register = ad9389b_s_register, | ||
| 556 | #endif | ||
| 557 | .s_power = ad9389b_s_power, | ||
| 558 | .interrupt_service_routine = ad9389b_isr, | ||
| 559 | }; | ||
| 560 | |||
| 561 | /* ------------------------------ VIDEO OPS ------------------------------ */ | ||
| 562 | |||
| 563 | /* Enable/disable ad9389b output */ | ||
| 564 | static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) | ||
| 565 | { | ||
| 566 | v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); | ||
| 567 | |||
| 568 | ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); | ||
| 569 | if (enable) { | ||
| 570 | ad9389b_check_monitor_present_status(sd); | ||
| 571 | } else { | ||
| 572 | ad9389b_s_power(sd, 0); | ||
| 573 | } | ||
| 574 | return 0; | ||
| 575 | } | ||
| 576 | |||
| 577 | static const struct v4l2_dv_timings_cap ad9389b_timings_cap = { | ||
| 578 | .type = V4L2_DV_BT_656_1120, | ||
| 579 | /* keep this initialization for compatibility with GCC < 4.4.6 */ | ||
| 580 | .reserved = { 0 }, | ||
| 581 | V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, | ||
| 582 | V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | | ||
| 583 | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, | ||
| 584 | V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | | ||
| 585 | V4L2_DV_BT_CAP_CUSTOM) | ||
| 586 | }; | ||
| 587 | |||
| 588 | static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, | ||
| 589 | struct v4l2_dv_timings *timings) | ||
| 590 | { | ||
| 591 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 592 | |||
| 593 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | ||
| 594 | |||
| 595 | /* quick sanity check */ | ||
| 596 | if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL)) | ||
| 597 | return -EINVAL; | ||
| 598 | |||
| 599 | /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings | ||
| 600 | if the format is one of the CEA or DMT timings. */ | ||
| 601 | v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL); | ||
| 602 | |||
| 603 | timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; | ||
| 604 | |||
| 605 | /* save timings */ | ||
| 606 | state->dv_timings = *timings; | ||
| 607 | |||
| 608 | /* update quantization range based on new dv_timings */ | ||
| 609 | ad9389b_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); | ||
| 610 | |||
| 611 | /* update PLL gear based on new dv_timings */ | ||
| 612 | if (state->pdata.tmds_pll_gear == AD9389B_TMDS_PLL_GEAR_SEMI_AUTOMATIC) | ||
| 613 | ad9389b_set_manual_pll_gear(sd, (u32)timings->bt.pixelclock); | ||
| 614 | |||
| 615 | /* update AVI infoframe */ | ||
| 616 | ad9389b_set_IT_content_AVI_InfoFrame(sd); | ||
| 617 | |||
| 618 | return 0; | ||
| 619 | } | ||
| 620 | |||
| 621 | static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, | ||
| 622 | struct v4l2_dv_timings *timings) | ||
| 623 | { | ||
| 624 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 625 | |||
| 626 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | ||
| 627 | |||
| 628 | if (!timings) | ||
| 629 | return -EINVAL; | ||
| 630 | |||
| 631 | *timings = state->dv_timings; | ||
| 632 | |||
| 633 | return 0; | ||
| 634 | } | ||
| 635 | |||
| 636 | static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, | ||
| 637 | struct v4l2_enum_dv_timings *timings) | ||
| 638 | { | ||
| 639 | if (timings->pad != 0) | ||
| 640 | return -EINVAL; | ||
| 641 | |||
| 642 | return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, | ||
| 643 | NULL, NULL); | ||
| 644 | } | ||
| 645 | |||
| 646 | static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, | ||
| 647 | struct v4l2_dv_timings_cap *cap) | ||
| 648 | { | ||
| 649 | if (cap->pad != 0) | ||
| 650 | return -EINVAL; | ||
| 651 | |||
| 652 | *cap = ad9389b_timings_cap; | ||
| 653 | return 0; | ||
| 654 | } | ||
| 655 | |||
| 656 | static const struct v4l2_subdev_video_ops ad9389b_video_ops = { | ||
| 657 | .s_stream = ad9389b_s_stream, | ||
| 658 | .s_dv_timings = ad9389b_s_dv_timings, | ||
| 659 | .g_dv_timings = ad9389b_g_dv_timings, | ||
| 660 | }; | ||
| 661 | |||
| 662 | /* ------------------------------ PAD OPS ------------------------------ */ | ||
| 663 | |||
| 664 | static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) | ||
| 665 | { | ||
| 666 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 667 | |||
| 668 | if (edid->pad != 0) | ||
| 669 | return -EINVAL; | ||
| 670 | if (edid->blocks == 0 || edid->blocks > 256) | ||
| 671 | return -EINVAL; | ||
| 672 | if (!state->edid.segments) { | ||
| 673 | v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); | ||
| 674 | return -ENODATA; | ||
| 675 | } | ||
| 676 | if (edid->start_block >= state->edid.segments * 2) | ||
| 677 | return -E2BIG; | ||
| 678 | if (edid->blocks + edid->start_block >= state->edid.segments * 2) | ||
| 679 | edid->blocks = state->edid.segments * 2 - edid->start_block; | ||
| 680 | memcpy(edid->edid, &state->edid.data[edid->start_block * 128], | ||
| 681 | 128 * edid->blocks); | ||
| 682 | return 0; | ||
| 683 | } | ||
| 684 | |||
| 685 | static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { | ||
| 686 | .get_edid = ad9389b_get_edid, | ||
| 687 | .enum_dv_timings = ad9389b_enum_dv_timings, | ||
| 688 | .dv_timings_cap = ad9389b_dv_timings_cap, | ||
| 689 | }; | ||
| 690 | |||
| 691 | /* ------------------------------ AUDIO OPS ------------------------------ */ | ||
| 692 | |||
| 693 | static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable) | ||
| 694 | { | ||
| 695 | v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); | ||
| 696 | |||
| 697 | if (enable) | ||
| 698 | ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x80); | ||
| 699 | else | ||
| 700 | ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x40); | ||
| 701 | |||
| 702 | return 0; | ||
| 703 | } | ||
| 704 | |||
| 705 | static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) | ||
| 706 | { | ||
| 707 | u32 N; | ||
| 708 | |||
| 709 | switch (freq) { | ||
| 710 | case 32000: N = 4096; break; | ||
| 711 | case 44100: N = 6272; break; | ||
| 712 | case 48000: N = 6144; break; | ||
| 713 | case 88200: N = 12544; break; | ||
| 714 | case 96000: N = 12288; break; | ||
| 715 | case 176400: N = 25088; break; | ||
| 716 | case 192000: N = 24576; break; | ||
| 717 | default: | ||
| 718 | return -EINVAL; | ||
| 719 | } | ||
| 720 | |||
| 721 | /* Set N (used with CTS to regenerate the audio clock) */ | ||
| 722 | ad9389b_wr(sd, 0x01, (N >> 16) & 0xf); | ||
| 723 | ad9389b_wr(sd, 0x02, (N >> 8) & 0xff); | ||
| 724 | ad9389b_wr(sd, 0x03, N & 0xff); | ||
| 725 | |||
| 726 | return 0; | ||
| 727 | } | ||
| 728 | |||
| 729 | static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) | ||
| 730 | { | ||
| 731 | u32 i2s_sf; | ||
| 732 | |||
| 733 | switch (freq) { | ||
| 734 | case 32000: i2s_sf = 0x30; break; | ||
| 735 | case 44100: i2s_sf = 0x00; break; | ||
| 736 | case 48000: i2s_sf = 0x20; break; | ||
| 737 | case 88200: i2s_sf = 0x80; break; | ||
| 738 | case 96000: i2s_sf = 0xa0; break; | ||
| 739 | case 176400: i2s_sf = 0xc0; break; | ||
| 740 | case 192000: i2s_sf = 0xe0; break; | ||
| 741 | default: | ||
| 742 | return -EINVAL; | ||
| 743 | } | ||
| 744 | |||
| 745 | /* Set sampling frequency for I2S audio to 48 kHz */ | ||
| 746 | ad9389b_wr_and_or(sd, 0x15, 0xf, i2s_sf); | ||
| 747 | |||
| 748 | return 0; | ||
| 749 | } | ||
| 750 | |||
| 751 | static int ad9389b_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) | ||
| 752 | { | ||
| 753 | /* TODO based on input/output/config */ | ||
| 754 | /* TODO See datasheet "Programmers guide" p. 39-40 */ | ||
| 755 | |||
| 756 | /* Only 2 channels in use for application */ | ||
| 757 | ad9389b_wr_and_or(sd, 0x50, 0x1f, 0x20); | ||
| 758 | /* Speaker mapping */ | ||
| 759 | ad9389b_wr(sd, 0x51, 0x00); | ||
| 760 | |||
| 761 | /* TODO Where should this be placed? */ | ||
| 762 | /* 16 bit audio word length */ | ||
| 763 | ad9389b_wr_and_or(sd, 0x14, 0xf0, 0x02); | ||
| 764 | |||
| 765 | return 0; | ||
| 766 | } | ||
| 767 | |||
| 768 | static const struct v4l2_subdev_audio_ops ad9389b_audio_ops = { | ||
| 769 | .s_stream = ad9389b_s_audio_stream, | ||
| 770 | .s_clock_freq = ad9389b_s_clock_freq, | ||
| 771 | .s_i2s_clock_freq = ad9389b_s_i2s_clock_freq, | ||
| 772 | .s_routing = ad9389b_s_routing, | ||
| 773 | }; | ||
| 774 | |||
| 775 | /* --------------------- SUBDEV OPS --------------------------------------- */ | ||
| 776 | |||
| 777 | static const struct v4l2_subdev_ops ad9389b_ops = { | ||
| 778 | .core = &ad9389b_core_ops, | ||
| 779 | .video = &ad9389b_video_ops, | ||
| 780 | .audio = &ad9389b_audio_ops, | ||
| 781 | .pad = &ad9389b_pad_ops, | ||
| 782 | }; | ||
| 783 | |||
| 784 | /* ----------------------------------------------------------------------- */ | ||
| 785 | static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, | ||
| 786 | int segment, u8 *buf) | ||
| 787 | { | ||
| 788 | int i, j; | ||
| 789 | |||
| 790 | if (debug < lvl) | ||
| 791 | return; | ||
| 792 | |||
| 793 | v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); | ||
| 794 | for (i = 0; i < 256; i += 16) { | ||
| 795 | u8 b[128]; | ||
| 796 | u8 *bp = b; | ||
| 797 | |||
| 798 | if (i == 128) | ||
| 799 | v4l2_dbg(lvl, debug, sd, "\n"); | ||
| 800 | for (j = i; j < i + 16; j++) { | ||
| 801 | sprintf(bp, "0x%02x, ", buf[j]); | ||
| 802 | bp += 6; | ||
| 803 | } | ||
| 804 | bp[0] = '\0'; | ||
| 805 | v4l2_dbg(lvl, debug, sd, "%s\n", b); | ||
| 806 | } | ||
| 807 | } | ||
| 808 | |||
| 809 | static void ad9389b_edid_handler(struct work_struct *work) | ||
| 810 | { | ||
| 811 | struct delayed_work *dwork = to_delayed_work(work); | ||
| 812 | struct ad9389b_state *state = | ||
| 813 | container_of(dwork, struct ad9389b_state, edid_handler); | ||
| 814 | struct v4l2_subdev *sd = &state->sd; | ||
| 815 | struct ad9389b_edid_detect ed; | ||
| 816 | |||
| 817 | v4l2_dbg(1, debug, sd, "%s:\n", __func__); | ||
| 818 | |||
| 819 | if (ad9389b_check_edid_status(sd)) { | ||
| 820 | /* Return if we received the EDID. */ | ||
| 821 | return; | ||
| 822 | } | ||
| 823 | |||
| 824 | if (ad9389b_have_hotplug(sd)) { | ||
| 825 | /* We must retry reading the EDID several times, it is possible | ||
| 826 | * that initially the EDID couldn't be read due to i2c errors | ||
| 827 | * (DVI connectors are particularly prone to this problem). */ | ||
| 828 | if (state->edid.read_retries) { | ||
| 829 | state->edid.read_retries--; | ||
| 830 | v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); | ||
| 831 | ad9389b_s_power(sd, false); | ||
| 832 | ad9389b_s_power(sd, true); | ||
| 833 | schedule_delayed_work(&state->edid_handler, EDID_DELAY); | ||
| 834 | return; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | |||
| 838 | /* We failed to read the EDID, so send an event for this. */ | ||
| 839 | ed.present = false; | ||
| 840 | ed.segment = ad9389b_rd(sd, 0xc4); | ||
| 841 | v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); | ||
| 842 | v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); | ||
| 843 | } | ||
| 844 | |||
| 845 | static void ad9389b_audio_setup(struct v4l2_subdev *sd) | ||
| 846 | { | ||
| 847 | v4l2_dbg(1, debug, sd, "%s\n", __func__); | ||
| 848 | |||
| 849 | ad9389b_s_i2s_clock_freq(sd, 48000); | ||
| 850 | ad9389b_s_clock_freq(sd, 48000); | ||
| 851 | ad9389b_s_routing(sd, 0, 0, 0); | ||
| 852 | } | ||
| 853 | |||
| 854 | /* Initial setup of AD9389b */ | ||
| 855 | |||
| 856 | /* Configure hdmi transmitter. */ | ||
| 857 | static void ad9389b_setup(struct v4l2_subdev *sd) | ||
| 858 | { | ||
| 859 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 860 | |||
| 861 | v4l2_dbg(1, debug, sd, "%s\n", __func__); | ||
| 862 | |||
| 863 | /* Input format: RGB 4:4:4 */ | ||
| 864 | ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0); | ||
| 865 | /* Output format: RGB 4:4:4 */ | ||
| 866 | ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0); | ||
| 867 | /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, | ||
| 868 | Aspect ratio: 16:9 */ | ||
| 869 | ad9389b_wr_and_or(sd, 0x17, 0xf9, 0x06); | ||
| 870 | /* Output format: RGB 4:4:4, Active Format Information is valid. */ | ||
| 871 | ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08); | ||
| 872 | /* Underscanned */ | ||
| 873 | ad9389b_wr_and_or(sd, 0x46, 0x3f, 0x80); | ||
| 874 | /* Setup video format */ | ||
| 875 | ad9389b_wr(sd, 0x3c, 0x0); | ||
| 876 | /* Active format aspect ratio: same as picure. */ | ||
| 877 | ad9389b_wr(sd, 0x47, 0x80); | ||
| 878 | /* No encryption */ | ||
| 879 | ad9389b_wr_and_or(sd, 0xaf, 0xef, 0x0); | ||
| 880 | /* Positive clk edge capture for input video clock */ | ||
| 881 | ad9389b_wr_and_or(sd, 0xba, 0x1f, 0x60); | ||
| 882 | |||
| 883 | ad9389b_audio_setup(sd); | ||
| 884 | |||
| 885 | v4l2_ctrl_handler_setup(&state->hdl); | ||
| 886 | |||
| 887 | ad9389b_set_IT_content_AVI_InfoFrame(sd); | ||
| 888 | } | ||
| 889 | |||
| 890 | static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd) | ||
| 891 | { | ||
| 892 | struct ad9389b_monitor_detect mdt; | ||
| 893 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 894 | |||
| 895 | mdt.present = state->have_monitor; | ||
| 896 | v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt); | ||
| 897 | } | ||
| 898 | |||
| 899 | static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd) | ||
| 900 | { | ||
| 901 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 902 | /* read hotplug and rx-sense state */ | ||
| 903 | u8 status = ad9389b_rd(sd, 0x42); | ||
| 904 | |||
| 905 | v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", | ||
| 906 | __func__, | ||
| 907 | status, | ||
| 908 | status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", | ||
| 909 | status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); | ||
| 910 | |||
| 911 | if (status & MASK_AD9389B_HPD_DETECT) { | ||
| 912 | v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); | ||
| 913 | state->have_monitor = true; | ||
| 914 | if (!ad9389b_s_power(sd, true)) { | ||
| 915 | v4l2_dbg(1, debug, sd, | ||
| 916 | "%s: monitor detected, powerup failed\n", __func__); | ||
| 917 | return; | ||
| 918 | } | ||
| 919 | ad9389b_setup(sd); | ||
| 920 | ad9389b_notify_monitor_detect(sd); | ||
| 921 | state->edid.read_retries = EDID_MAX_RETRIES; | ||
| 922 | schedule_delayed_work(&state->edid_handler, EDID_DELAY); | ||
| 923 | } else if (!(status & MASK_AD9389B_HPD_DETECT)) { | ||
| 924 | v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); | ||
| 925 | state->have_monitor = false; | ||
| 926 | ad9389b_notify_monitor_detect(sd); | ||
| 927 | ad9389b_s_power(sd, false); | ||
| 928 | memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); | ||
| 929 | } | ||
| 930 | |||
| 931 | /* update read only ctrls */ | ||
| 932 | v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0); | ||
| 933 | v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0); | ||
| 934 | v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); | ||
| 935 | |||
| 936 | /* update with setting from ctrls */ | ||
| 937 | ad9389b_s_ctrl(state->rgb_quantization_range_ctrl); | ||
| 938 | ad9389b_s_ctrl(state->hdmi_mode_ctrl); | ||
| 939 | } | ||
| 940 | |||
| 941 | static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) | ||
| 942 | { | ||
| 943 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 944 | int retry = 0; | ||
| 945 | |||
| 946 | ad9389b_update_monitor_present_status(sd); | ||
| 947 | |||
| 948 | /* | ||
| 949 | * Rapid toggling of the hotplug may leave the chip powered off, | ||
| 950 | * even if we think it is on. In that case reset and power up again. | ||
| 951 | */ | ||
| 952 | while (state->power_on && (ad9389b_rd(sd, 0x41) & 0x40)) { | ||
| 953 | if (++retry > 5) { | ||
| 954 | v4l2_err(sd, "retried %d times, give up\n", retry); | ||
| 955 | return; | ||
| 956 | } | ||
| 957 | v4l2_dbg(1, debug, sd, "%s: reset and re-check status (%d)\n", __func__, retry); | ||
| 958 | ad9389b_notify_monitor_detect(sd); | ||
| 959 | cancel_delayed_work_sync(&state->edid_handler); | ||
| 960 | memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); | ||
| 961 | ad9389b_s_power(sd, false); | ||
| 962 | ad9389b_update_monitor_present_status(sd); | ||
| 963 | } | ||
| 964 | } | ||
| 965 | |||
| 966 | static bool edid_block_verify_crc(u8 *edid_block) | ||
| 967 | { | ||
| 968 | u8 sum = 0; | ||
| 969 | int i; | ||
| 970 | |||
| 971 | for (i = 0; i < 128; i++) | ||
| 972 | sum += edid_block[i]; | ||
| 973 | return sum == 0; | ||
| 974 | } | ||
| 975 | |||
| 976 | static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) | ||
| 977 | { | ||
| 978 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 979 | u32 blocks = state->edid.blocks; | ||
| 980 | u8 *data = state->edid.data; | ||
| 981 | |||
| 982 | if (edid_block_verify_crc(&data[segment * 256])) { | ||
| 983 | if ((segment + 1) * 2 <= blocks) | ||
| 984 | return edid_block_verify_crc(&data[segment * 256 + 128]); | ||
| 985 | return true; | ||
| 986 | } | ||
| 987 | return false; | ||
| 988 | } | ||
| 989 | |||
| 990 | static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) | ||
| 991 | { | ||
| 992 | static const u8 hdmi_header[] = { | ||
| 993 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 | ||
| 994 | }; | ||
| 995 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 996 | u8 *data = state->edid.data; | ||
| 997 | int i; | ||
| 998 | |||
| 999 | if (segment) | ||
| 1000 | return true; | ||
| 1001 | |||
| 1002 | for (i = 0; i < ARRAY_SIZE(hdmi_header); i++) | ||
| 1003 | if (data[i] != hdmi_header[i]) | ||
| 1004 | return false; | ||
| 1005 | |||
| 1006 | return true; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) | ||
| 1010 | { | ||
| 1011 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 1012 | struct ad9389b_edid_detect ed; | ||
| 1013 | int segment; | ||
| 1014 | u8 edidRdy = ad9389b_rd(sd, 0xc5); | ||
| 1015 | |||
| 1016 | v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", | ||
| 1017 | __func__, EDID_MAX_RETRIES - state->edid.read_retries); | ||
| 1018 | |||
| 1019 | if (!(edidRdy & MASK_AD9389B_EDID_RDY)) | ||
| 1020 | return false; | ||
| 1021 | |||
| 1022 | segment = ad9389b_rd(sd, 0xc4); | ||
| 1023 | if (segment >= EDID_MAX_SEGM) { | ||
| 1024 | v4l2_err(sd, "edid segment number too big\n"); | ||
| 1025 | return false; | ||
| 1026 | } | ||
| 1027 | v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); | ||
| 1028 | ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]); | ||
| 1029 | ad9389b_dbg_dump_edid(2, debug, sd, segment, | ||
| 1030 | &state->edid.data[segment * 256]); | ||
| 1031 | if (segment == 0) { | ||
| 1032 | state->edid.blocks = state->edid.data[0x7e] + 1; | ||
| 1033 | v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", | ||
| 1034 | __func__, state->edid.blocks); | ||
| 1035 | } | ||
| 1036 | if (!edid_verify_crc(sd, segment) || | ||
| 1037 | !edid_verify_header(sd, segment)) { | ||
| 1038 | /* edid crc error, force reread of edid segment */ | ||
| 1039 | v4l2_err(sd, "%s: edid crc or header error\n", __func__); | ||
| 1040 | ad9389b_s_power(sd, false); | ||
| 1041 | ad9389b_s_power(sd, true); | ||
| 1042 | return false; | ||
| 1043 | } | ||
| 1044 | /* one more segment read ok */ | ||
| 1045 | state->edid.segments = segment + 1; | ||
| 1046 | if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { | ||
| 1047 | /* Request next EDID segment */ | ||
| 1048 | v4l2_dbg(1, debug, sd, "%s: request segment %d\n", | ||
| 1049 | __func__, state->edid.segments); | ||
| 1050 | ad9389b_wr(sd, 0xc9, 0xf); | ||
| 1051 | ad9389b_wr(sd, 0xc4, state->edid.segments); | ||
| 1052 | state->edid.read_retries = EDID_MAX_RETRIES; | ||
| 1053 | schedule_delayed_work(&state->edid_handler, EDID_DELAY); | ||
| 1054 | return false; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | /* report when we have all segments but report only for segment 0 */ | ||
| 1058 | ed.present = true; | ||
| 1059 | ed.segment = 0; | ||
| 1060 | v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); | ||
| 1061 | state->edid_detect_counter++; | ||
| 1062 | v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); | ||
| 1063 | return ed.present; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | /* ----------------------------------------------------------------------- */ | ||
| 1067 | |||
| 1068 | static void ad9389b_init_setup(struct v4l2_subdev *sd) | ||
| 1069 | { | ||
| 1070 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 1071 | struct ad9389b_state_edid *edid = &state->edid; | ||
| 1072 | |||
| 1073 | v4l2_dbg(1, debug, sd, "%s\n", __func__); | ||
| 1074 | |||
| 1075 | /* clear all interrupts */ | ||
| 1076 | ad9389b_wr(sd, 0x96, 0xff); | ||
| 1077 | |||
| 1078 | memset(edid, 0, sizeof(struct ad9389b_state_edid)); | ||
| 1079 | state->have_monitor = false; | ||
| 1080 | ad9389b_set_isr(sd, false); | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | static int ad9389b_probe(struct i2c_client *client) | ||
| 1084 | { | ||
| 1085 | const struct v4l2_dv_timings dv1080p60 = V4L2_DV_BT_CEA_1920X1080P60; | ||
| 1086 | struct ad9389b_state *state; | ||
| 1087 | struct ad9389b_platform_data *pdata = client->dev.platform_data; | ||
| 1088 | struct v4l2_ctrl_handler *hdl; | ||
| 1089 | struct v4l2_subdev *sd; | ||
| 1090 | int err = -EIO; | ||
| 1091 | |||
| 1092 | /* Check if the adapter supports the needed features */ | ||
| 1093 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
| 1094 | return -EIO; | ||
| 1095 | |||
| 1096 | v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n", | ||
| 1097 | client->addr << 1); | ||
| 1098 | |||
| 1099 | state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); | ||
| 1100 | if (!state) | ||
| 1101 | return -ENOMEM; | ||
| 1102 | |||
| 1103 | /* Platform data */ | ||
| 1104 | if (pdata == NULL) { | ||
| 1105 | v4l_err(client, "No platform data!\n"); | ||
| 1106 | return -ENODEV; | ||
| 1107 | } | ||
| 1108 | memcpy(&state->pdata, pdata, sizeof(state->pdata)); | ||
| 1109 | |||
| 1110 | sd = &state->sd; | ||
| 1111 | v4l2_i2c_subdev_init(sd, client, &ad9389b_ops); | ||
| 1112 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 1113 | |||
| 1114 | hdl = &state->hdl; | ||
| 1115 | v4l2_ctrl_handler_init(hdl, 5); | ||
| 1116 | |||
| 1117 | state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, | ||
| 1118 | V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, | ||
| 1119 | 0, V4L2_DV_TX_MODE_DVI_D); | ||
| 1120 | state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, | ||
| 1121 | V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); | ||
| 1122 | state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, | ||
| 1123 | V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); | ||
| 1124 | state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, | ||
| 1125 | V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); | ||
| 1126 | state->rgb_quantization_range_ctrl = | ||
| 1127 | v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, | ||
| 1128 | V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, | ||
| 1129 | 0, V4L2_DV_RGB_RANGE_AUTO); | ||
| 1130 | sd->ctrl_handler = hdl; | ||
| 1131 | if (hdl->error) { | ||
| 1132 | err = hdl->error; | ||
| 1133 | |||
| 1134 | goto err_hdl; | ||
| 1135 | } | ||
| 1136 | state->pad.flags = MEDIA_PAD_FL_SINK; | ||
| 1137 | sd->entity.function = MEDIA_ENT_F_DV_ENCODER; | ||
| 1138 | err = media_entity_pads_init(&sd->entity, 1, &state->pad); | ||
| 1139 | if (err) | ||
| 1140 | goto err_hdl; | ||
| 1141 | |||
| 1142 | state->chip_revision = ad9389b_rd(sd, 0x0); | ||
| 1143 | if (state->chip_revision != 2) { | ||
| 1144 | v4l2_err(sd, "chip_revision %d != 2\n", state->chip_revision); | ||
| 1145 | err = -EIO; | ||
| 1146 | goto err_entity; | ||
| 1147 | } | ||
| 1148 | v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", | ||
| 1149 | ad9389b_rd(sd, 0x41), state->chip_revision); | ||
| 1150 | |||
| 1151 | state->edid_i2c_client = i2c_new_dummy_device(client->adapter, (0x7e >> 1)); | ||
| 1152 | if (IS_ERR(state->edid_i2c_client)) { | ||
| 1153 | v4l2_err(sd, "failed to register edid i2c client\n"); | ||
| 1154 | err = PTR_ERR(state->edid_i2c_client); | ||
| 1155 | goto err_entity; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | INIT_DELAYED_WORK(&state->edid_handler, ad9389b_edid_handler); | ||
| 1159 | state->dv_timings = dv1080p60; | ||
| 1160 | |||
| 1161 | ad9389b_init_setup(sd); | ||
| 1162 | ad9389b_set_isr(sd, true); | ||
| 1163 | |||
| 1164 | v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, | ||
| 1165 | client->addr << 1, client->adapter->name); | ||
| 1166 | return 0; | ||
| 1167 | |||
| 1168 | err_entity: | ||
| 1169 | media_entity_cleanup(&sd->entity); | ||
| 1170 | err_hdl: | ||
| 1171 | v4l2_ctrl_handler_free(&state->hdl); | ||
| 1172 | return err; | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | /* ----------------------------------------------------------------------- */ | ||
| 1176 | |||
| 1177 | static void ad9389b_remove(struct i2c_client *client) | ||
| 1178 | { | ||
| 1179 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
| 1180 | struct ad9389b_state *state = get_ad9389b_state(sd); | ||
| 1181 | |||
| 1182 | state->chip_revision = -1; | ||
| 1183 | |||
| 1184 | v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, | ||
| 1185 | client->addr << 1, client->adapter->name); | ||
| 1186 | |||
| 1187 | ad9389b_s_stream(sd, false); | ||
| 1188 | ad9389b_s_audio_stream(sd, false); | ||
| 1189 | ad9389b_init_setup(sd); | ||
| 1190 | cancel_delayed_work_sync(&state->edid_handler); | ||
| 1191 | i2c_unregister_device(state->edid_i2c_client); | ||
| 1192 | v4l2_device_unregister_subdev(sd); | ||
| 1193 | media_entity_cleanup(&sd->entity); | ||
| 1194 | v4l2_ctrl_handler_free(sd->ctrl_handler); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /* ----------------------------------------------------------------------- */ | ||
| 1198 | |||
| 1199 | static const struct i2c_device_id ad9389b_id[] = { | ||
| 1200 | { "ad9389b", 0 }, | ||
| 1201 | { "ad9889b", 0 }, | ||
| 1202 | { } | ||
| 1203 | }; | ||
| 1204 | MODULE_DEVICE_TABLE(i2c, ad9389b_id); | ||
| 1205 | |||
| 1206 | static struct i2c_driver ad9389b_driver = { | ||
| 1207 | .driver = { | ||
| 1208 | .name = "ad9389b", | ||
| 1209 | }, | ||
| 1210 | .probe_new = ad9389b_probe, | ||
| 1211 | .remove = ad9389b_remove, | ||
| 1212 | .id_table = ad9389b_id, | ||
| 1213 | }; | ||
| 1214 | |||
| 1215 | module_i2c_driver(ad9389b_driver); | ||
| @@ -176,9 +176,9 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, · 175 unmodified lines | |||
| 176 | unsigned int i; | 176 | unsigned int i; |
| 177 | 177 | ||
| 178 | for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) { | 178 | for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) { |
| 179 | if (!v4l2_match_dv_timings(timings, &stds[i].timings, 250000, | 179 | if (v4l2_match_dv_timings(timings, &stds[i].timings, 250000, |
| 180 | false)) | 180 | false)) |
| 181 | continue; | 181 | break; |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards)) | 184 | if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards)) |
| @@ -283,6 +283,16 @@ static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, · 98 unmodified lines | |||
| 283 | 283 | ||
| 284 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); | 284 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); |
| 285 | 285 | ||
| 286 | /* | ||
| 287 | * If the pattern generator is enabled the device shall not be queried | ||
| 288 | * for timings. Instead the timings programmed shall be reported as they | ||
| 289 | * are the ones being used to generate the pattern. | ||
| 290 | */ | ||
| 291 | if (cp_read(state, ADV748X_CP_PAT_GEN) & ADV748X_CP_PAT_GEN_EN) { | ||
| 292 | *timings = hdmi->timings; | ||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 286 | if (!adv748x_hdmi_has_signal(state)) | 296 | if (!adv748x_hdmi_has_signal(state)) |
| 287 | return -ENOLINK; | 297 | return -ENOLINK; |
| 288 | 298 | ||
| @@ -721,11 +731,10 @@ static int adv748x_hdmi_init_controls(struct adv748x_hdmi *hdmi) · 432 unmodified lines | |||
| 721 | int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) | 731 | int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) |
| 722 | { | 732 | { |
| 723 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); | 733 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
| 724 | static const struct v4l2_dv_timings cea1280x720 = | 734 | struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; |
| 725 | V4L2_DV_BT_CEA_1280X720P30; | ||
| 726 | int ret; | 735 | int ret; |
| 727 | 736 | ||
| 728 | hdmi->timings = cea1280x720; | 737 | adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720); |
| 729 | 738 | ||
| 730 | /* Initialise a default 16:9 aspect ratio */ | 739 | /* Initialise a default 16:9 aspect ratio */ |
| 731 | hdmi->aspect_ratio.numerator = 16; | 740 | hdmi->aspect_ratio.numerator = 16; |
| @@ -1805,6 +1805,9 @@ static void select_input(struct v4l2_subdev *sd) · 1804 unmodified lines | |||
| 1805 | v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", | 1805 | v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", |
| 1806 | __func__, state->selected_input); | 1806 | __func__, state->selected_input); |
| 1807 | } | 1807 | } |
| 1808 | |||
| 1809 | /* Enable video adjustment (contrast, saturation, brightness and hue) */ | ||
| 1810 | cp_write_clr_set(sd, 0x3e, 0x80, 0x80); | ||
| 1808 | } | 1811 | } |
| 1809 | 1812 | ||
| 1810 | static int adv76xx_s_routing(struct v4l2_subdev *sd, | 1813 | static int adv76xx_s_routing(struct v4l2_subdev *sd, |
| @@ -3545,7 +3548,7 @@ static int adv76xx_probe(struct i2c_client *client) · 1734 unmodified lines | |||
| 3545 | v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, | 3548 | v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, |
| 3546 | V4L2_CID_SATURATION, 0, 255, 1, 128); | 3549 | V4L2_CID_SATURATION, 0, 255, 1, 128); |
| 3547 | v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, | 3550 | v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, |
| 3548 | V4L2_CID_HUE, 0, 128, 1, 0); | 3551 | V4L2_CID_HUE, 0, 255, 1, 0); |
| 3549 | ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, | 3552 | ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, |
| 3550 | V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, | 3553 | V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, |
| 3551 | 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); | 3554 | 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); |
| @@ -569,8 +569,6 @@ static u32 ccs_pixel_order(struct ccs_sensor *sensor) · 568 unmodified lines | |||
| 569 | flip |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; | 569 | flip |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; |
| 570 | } | 570 | } |
| 571 | 571 | ||
| 572 | flip ^= sensor->hvflip_inv_mask; | ||
| 573 | |||
| 574 | dev_dbg(&client->dev, "flip %u\n", flip); | 572 | dev_dbg(&client->dev, "flip %u\n", flip); |
| 575 | return sensor->default_pixel_order ^ flip; | 573 | return sensor->default_pixel_order ^ flip; |
| 576 | } | 574 | } |
| @@ -632,8 +630,6 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) · 55 unmodified lines | |||
| 632 | if (sensor->vflip->val) | 630 | if (sensor->vflip->val) |
| 633 | orient |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; | 631 | orient |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; |
| 634 | 632 | ||
| 635 | orient ^= sensor->hvflip_inv_mask; | ||
| 636 | |||
| 637 | ccs_update_mbus_formats(sensor); | 633 | ccs_update_mbus_formats(sensor); |
| 638 | 634 | ||
| 639 | break; | 635 | break; |
| @@ -800,14 +796,24 @@ static const struct v4l2_ctrl_ops ccs_ctrl_ops = { · 160 unmodified lines | |||
| 800 | static int ccs_init_controls(struct ccs_sensor *sensor) | 796 | static int ccs_init_controls(struct ccs_sensor *sensor) |
| 801 | { | 797 | { |
| 802 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); | 798 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); |
| 799 | struct v4l2_fwnode_device_properties props; | ||
| 803 | int rval; | 800 | int rval; |
| 804 | 801 | ||
| 805 | rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 17); | 802 | rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 19); |
| 806 | if (rval) | 803 | if (rval) |
| 807 | return rval; | 804 | return rval; |
| 808 | 805 | ||
| 809 | sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; | 806 | sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; |
| 810 | 807 | ||
| 808 | rval = v4l2_fwnode_device_parse(&client->dev, &props); | ||
| 809 | if (rval) | ||
| 810 | return rval; | ||
| 811 | |||
| 812 | rval = v4l2_ctrl_new_fwnode_properties(&sensor->pixel_array->ctrl_handler, | ||
| 813 | &ccs_ctrl_ops, &props); | ||
| 814 | if (rval) | ||
| 815 | return rval; | ||
| 816 | |||
| 811 | switch (CCS_LIM(sensor, ANALOG_GAIN_CAPABILITY)) { | 817 | switch (CCS_LIM(sensor, ANALOG_GAIN_CAPABILITY)) { |
| 812 | case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: { | 818 | case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: { |
| 813 | struct { | 819 | struct { |
| @@ -2828,6 +2834,10 @@ static int ccs_identify_module(struct ccs_sensor *sensor) · 2014 unmodified lines | |||
| 2828 | rval = ccs_read_addr_8only(sensor, | 2834 | rval = ccs_read_addr_8only(sensor, |
| 2829 | CCS_R_SENSOR_REVISION_NUMBER, | 2835 | CCS_R_SENSOR_REVISION_NUMBER, |
| 2830 | &minfo->sensor_revision_number); | 2836 | &minfo->sensor_revision_number); |
| 2837 | if (!rval && !minfo->sensor_revision_number) | ||
| 2838 | rval = ccs_read_addr_8only(sensor, | ||
| 2839 | CCS_R_SENSOR_REVISION_NUMBER_16, | ||
| 2840 | &minfo->sensor_revision_number); | ||
| 2831 | if (!rval) | 2841 | if (!rval) |
| 2832 | rval = ccs_read_addr_8only(sensor, | 2842 | rval = ccs_read_addr_8only(sensor, |
| 2833 | CCS_R_SENSOR_FIRMWARE_VERSION, | 2843 | CCS_R_SENSOR_FIRMWARE_VERSION, |
| @@ -2870,7 +2880,7 @@ static int ccs_identify_module(struct ccs_sensor *sensor) · 36 unmodified lines | |||
| 2870 | minfo->sensor_model_id); | 2880 | minfo->sensor_model_id); |
| 2871 | 2881 | ||
| 2872 | dev_dbg(&client->dev, | 2882 | dev_dbg(&client->dev, |
| 2873 | "sensor revision 0x%2.2x firmware version 0x%2.2x\n", | 2883 | "sensor revision 0x%4.4x firmware version 0x%2.2x\n", |
| 2874 | minfo->sensor_revision_number, minfo->sensor_firmware_version); | 2884 | minfo->sensor_revision_number, minfo->sensor_firmware_version); |
| 2875 | 2885 | ||
| 2876 | if (minfo->ccs_version) { | 2886 | if (minfo->ccs_version) { |
| @@ -2884,19 +2894,18 @@ static int ccs_identify_module(struct ccs_sensor *sensor) · 7 unmodified lines | |||
| 2884 | "smia version %2.2d smiapp version %2.2d\n", | 2894 | "smia version %2.2d smiapp version %2.2d\n", |
| 2885 | minfo->smia_version, minfo->smiapp_version); | 2895 | minfo->smia_version, minfo->smiapp_version); |
| 2886 | minfo->name = SMIAPP_NAME; | 2896 | minfo->name = SMIAPP_NAME; |
| 2887 | } | 2897 | /* |
| 2888 | 2898 | * Some modules have bad data in the lvalues below. Hope the | |
| 2889 | /* | 2899 | * rvalues have better stuff. The lvalues are module |
| 2890 | * Some modules have bad data in the lvalues below. Hope the | 2900 | * parameters whereas the rvalues are sensor parameters. |
| 2891 | * rvalues have better stuff. The lvalues are module | 2901 | */ |
| 2892 | * parameters whereas the rvalues are sensor parameters. | 2902 | if (minfo->sensor_smia_manufacturer_id && |
| 2893 | */ | 2903 | !minfo->smia_manufacturer_id && !minfo->model_id) { |
| 2894 | if (minfo->sensor_smia_manufacturer_id && | 2904 | minfo->smia_manufacturer_id = |
| 2895 | !minfo->smia_manufacturer_id && !minfo->model_id) { | 2905 | minfo->sensor_smia_manufacturer_id; |
| 2896 | minfo->smia_manufacturer_id = | 2906 | minfo->model_id = minfo->sensor_model_id; |
| 2897 | minfo->sensor_smia_manufacturer_id; | 2907 | minfo->revision_number = minfo->sensor_revision_number; |
| 2898 | minfo->model_id = minfo->sensor_model_id; | 2908 | } |
| 2899 | minfo->revision_number = minfo->sensor_revision_number; | ||
| 2900 | } | 2909 | } |
| 2901 | 2910 | ||
| 2902 | for (i = 0; i < ARRAY_SIZE(ccs_module_idents); i++) { | 2911 | for (i = 0; i < ARRAY_SIZE(ccs_module_idents); i++) { |
| @@ -3185,7 +3194,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) · 282 unmodified lines | |||
| 3185 | struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN }; | 3194 | struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN }; |
| 3186 | struct fwnode_handle *ep; | 3195 | struct fwnode_handle *ep; |
| 3187 | struct fwnode_handle *fwnode = dev_fwnode(dev); | 3196 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
| 3188 | u32 rotation; | ||
| 3189 | unsigned int i; | 3197 | unsigned int i; |
| 3190 | int rval; | 3198 | int rval; |
| 3191 | 3199 | ||
| @@ -3224,22 +3232,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) · 32 unmodified lines | |||
| 3224 | goto out_err; | 3232 | goto out_err; |
| 3225 | } | 3233 | } |
| 3226 | 3234 | ||
| 3227 | rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); | ||
| 3228 | if (!rval) { | ||
| 3229 | switch (rotation) { | ||
| 3230 | case 180: | ||
| 3231 | hwcfg->module_board_orient = | ||
| 3232 | CCS_MODULE_BOARD_ORIENT_180; | ||
| 3233 | fallthrough; | ||
| 3234 | case 0: | ||
| 3235 | break; | ||
| 3236 | default: | ||
| 3237 | dev_err(dev, "invalid rotation %u\n", rotation); | ||
| 3238 | rval = -EINVAL; | ||
| 3239 | goto out_err; | ||
| 3240 | } | ||
| 3241 | } | ||
| 3242 | |||
| 3243 | rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", | 3235 | rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", |
| 3244 | &hwcfg->ext_clk); | 3236 | &hwcfg->ext_clk); |
| 3245 | if (rval) | 3237 | if (rval) |
| @@ -3279,8 +3271,47 @@ out_err: · 33 unmodified lines | |||
| 3279 | return rval; | 3271 | return rval; |
| 3280 | } | 3272 | } |
| 3281 | 3273 | ||
| 3274 | static int ccs_firmware_name(struct i2c_client *client, | ||
| 3275 | struct ccs_sensor *sensor, char *filename, | ||
| 3276 | size_t filename_size, bool is_module) | ||
| 3277 | { | ||
| 3278 | const struct ccs_device *ccsdev = device_get_match_data(&client->dev); | ||
| 3279 | bool is_ccs = !(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA); | ||
| 3280 | bool is_smiapp = sensor->minfo.smiapp_version; | ||
| 3281 | u16 manufacturer_id; | ||
| 3282 | u16 model_id; | ||
| 3283 | u16 revision_number; | ||
| 3284 | |||
| 3285 | /* | ||
| 3286 | * Old SMIA is module-agnostic. Its sensor identification is based on | ||
| 3287 | * what now are those of the module. | ||
| 3288 | */ | ||
| 3289 | if (is_module || (!is_ccs && !is_smiapp)) { | ||
| 3290 | manufacturer_id = is_ccs ? | ||
| 3291 | sensor->minfo.mipi_manufacturer_id : | ||
| 3292 | sensor->minfo.smia_manufacturer_id; | ||
| 3293 | model_id = sensor->minfo.model_id; | ||
| 3294 | revision_number = sensor->minfo.revision_number; | ||
| 3295 | } else { | ||
| 3296 | manufacturer_id = is_ccs ? | ||
| 3297 | sensor->minfo.sensor_mipi_manufacturer_id : | ||
| 3298 | sensor->minfo.sensor_smia_manufacturer_id; | ||
| 3299 | model_id = sensor->minfo.sensor_model_id; | ||
| 3300 | revision_number = sensor->minfo.sensor_revision_number; | ||
| 3301 | } | ||
| 3302 | |||
| 3303 | return snprintf(filename, filename_size, | ||
| 3304 | "ccs/%s-%s-%0*x-%4.4x-%0*x.fw", | ||
| 3305 | is_ccs ? "ccs" : is_smiapp ? "smiapp" : "smia", | ||
| 3306 | is_module || (!is_ccs && !is_smiapp) ? | ||
| 3307 | "module" : "sensor", | ||
| 3308 | is_ccs ? 4 : 2, manufacturer_id, model_id, | ||
| 3309 | !is_ccs && !is_module ? 2 : 4, revision_number); | ||
| 3310 | } | ||
| 3311 | |||
| 3282 | static int ccs_probe(struct i2c_client *client) | 3312 | static int ccs_probe(struct i2c_client *client) |
| 3283 | { | 3313 | { |
| 3314 | const struct ccs_device *ccsdev = device_get_match_data(&client->dev); | ||
| 3284 | struct ccs_sensor *sensor; | 3315 | struct ccs_sensor *sensor; |
| 3285 | const struct firmware *fw; | 3316 | const struct firmware *fw; |
| 3286 | char filename[40]; | 3317 | char filename[40]; |
| @@ -3389,11 +3420,8 @@ static int ccs_probe(struct i2c_client *client) · 102 unmodified lines | |||
| 3389 | goto out_power_off; | 3420 | goto out_power_off; |
| 3390 | } | 3421 | } |
| 3391 | 3422 | ||
| 3392 | rval = snprintf(filename, sizeof(filename), | 3423 | rval = ccs_firmware_name(client, sensor, filename, sizeof(filename), |
| 3393 | "ccs/ccs-sensor-%4.4x-%4.4x-%4.4x.fw", | 3424 | false); |
| 3394 | sensor->minfo.sensor_mipi_manufacturer_id, | ||
| 3395 | sensor->minfo.sensor_model_id, | ||
| 3396 | sensor->minfo.sensor_revision_number); | ||
| 3397 | if (rval >= sizeof(filename)) { | 3425 | if (rval >= sizeof(filename)) { |
| 3398 | rval = -ENOMEM; | 3426 | rval = -ENOMEM; |
| 3399 | goto out_power_off; | 3427 | goto out_power_off; |
| @@ -3406,21 +3434,21 @@ static int ccs_probe(struct i2c_client *client) · 6 unmodified lines | |||
| 3406 | release_firmware(fw); | 3434 | release_firmware(fw); |
| 3407 | } | 3435 | } |
| 3408 | 3436 | ||
| 3409 | rval = snprintf(filename, sizeof(filename), | 3437 | if (!(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) || |
| 3410 | "ccs/ccs-module-%4.4x-%4.4x-%4.4x.fw", | 3438 | sensor->minfo.smiapp_version) { |
| 3411 | sensor->minfo.mipi_manufacturer_id, | 3439 | rval = ccs_firmware_name(client, sensor, filename, |
| 3412 | sensor->minfo.model_id, | 3440 | sizeof(filename), true); |
| 3413 | sensor->minfo.revision_number); | 3441 | if (rval >= sizeof(filename)) { |
| 3414 | if (rval >= sizeof(filename)) { | 3442 | rval = -ENOMEM; |
| 3415 | rval = -ENOMEM; | 3443 | goto out_release_sdata; |
| 3416 | goto out_release_sdata; | 3444 | } |
| 3417 | } | ||
| 3418 | 3445 | ||
| 3419 | rval = request_firmware(&fw, filename, &client->dev); | 3446 | rval = request_firmware(&fw, filename, &client->dev); |
| 3420 | if (!rval) { | 3447 | if (!rval) { |
| 3421 | ccs_data_parse(&sensor->mdata, fw->data, fw->size, &client->dev, | 3448 | ccs_data_parse(&sensor->mdata, fw->data, fw->size, |
| 3422 | true); | 3449 | &client->dev, true); |
| 3423 | release_firmware(fw); | 3450 | release_firmware(fw); |
| 3451 | } | ||
| 3424 | } | 3452 | } |
| 3425 | 3453 | ||
| 3426 | rval = ccs_read_all_limits(sensor); | 3454 | rval = ccs_read_all_limits(sensor); |
| @@ -3437,25 +3465,6 @@ static int ccs_probe(struct i2c_client *client) · 10 unmodified lines | |||
| 3437 | if (rval < 0) | 3465 | if (rval < 0) |
| 3438 | goto out_free_ccs_limits; | 3466 | goto out_free_ccs_limits; |
| 3439 | 3467 | ||
| 3440 | /* | ||
| 3441 | * Handle Sensor Module orientation on the board. | ||
| 3442 | * | ||
| 3443 | * The application of H-FLIP and V-FLIP on the sensor is modified by | ||
| 3444 | * the sensor orientation on the board. | ||
| 3445 | * | ||
| 3446 | * For CCS_BOARD_SENSOR_ORIENT_180 the default behaviour is to set | ||
| 3447 | * both H-FLIP and V-FLIP for normal operation which also implies | ||
| 3448 | * that a set/unset operation for user space HFLIP and VFLIP v4l2 | ||
| 3449 | * controls will need to be internally inverted. | ||
| 3450 | * | ||
| 3451 | * Rotation also changes the bayer pattern. | ||
| 3452 | */ | ||
| 3453 | if (sensor->hwcfg.module_board_orient == | ||
| 3454 | CCS_MODULE_BOARD_ORIENT_180) | ||
| 3455 | sensor->hvflip_inv_mask = | ||
| 3456 | CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR | | ||
| 3457 | CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; | ||
| 3458 | |||
| 3459 | rval = ccs_call_quirk(sensor, limits); | 3468 | rval = ccs_call_quirk(sensor, limits); |
| 3460 | if (rval) { | 3469 | if (rval) { |
| 3461 | dev_err(&client->dev, "limits quirks failed\n"); | 3470 | dev_err(&client->dev, "limits quirks failed\n"); |
| @@ -57,17 +57,6 @@ · 56 unmodified lines | |||
| 57 | #define CCS_LIM_AT(sensor, limit, offset) \ | 57 | #define CCS_LIM_AT(sensor, limit, offset) \ |
| 58 | ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset)) | 58 | ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset)) |
| 59 | 59 | ||
| 60 | /* | ||
| 61 | * Sometimes due to board layout considerations the camera module can be | ||
| 62 | * mounted rotated. The typical rotation used is 180 degrees which can be | ||
| 63 | * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. | ||
| 64 | * FIXME: rotation also changes the bayer pattern. | ||
| 65 | */ | ||
| 66 | enum ccs_module_board_orient { | ||
| 67 | CCS_MODULE_BOARD_ORIENT_0 = 0, | ||
| 68 | CCS_MODULE_BOARD_ORIENT_180, | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct ccs_flash_strobe_parms { | 60 | struct ccs_flash_strobe_parms { |
| 72 | u8 mode; | 61 | u8 mode; |
| 73 | u32 strobe_width_high_us; | 62 | u32 strobe_width_high_us; |
| @@ -90,8 +79,6 @@ struct ccs_hwconfig { · 16 unmodified lines | |||
| 90 | u32 csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ | 79 | u32 csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ |
| 91 | u64 *op_sys_clock; | 80 | u64 *op_sys_clock; |
| 92 | 81 | ||
| 93 | enum ccs_module_board_orient module_board_orient; | ||
| 94 | |||
| 95 | struct ccs_flash_strobe_parms *strobe_setup; | 82 | struct ccs_flash_strobe_parms *strobe_setup; |
| 96 | }; | 83 | }; |
| 97 | 84 | ||
| @@ -243,7 +230,6 @@ struct ccs_sensor { · 145 unmodified lines | |||
| 243 | u8 scale_m; | 230 | u8 scale_m; |
| 244 | u8 scaling_mode; | 231 | u8 scaling_mode; |
| 245 | 232 | ||
| 246 | u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ | ||
| 247 | u8 frame_skip; | 233 | u8 frame_skip; |
| 248 | u16 embedded_start; /* embedded data start line */ | 234 | u16 embedded_start; /* embedded data start line */ |
| 249 | u16 embedded_end; | 235 | u16 embedded_end; |
| @@ -63,6 +63,14 @@ · 62 unmodified lines | |||
| 63 | #define HI556_REG_ISP_TPG_EN 0x01 | 63 | #define HI556_REG_ISP_TPG_EN 0x01 |
| 64 | #define HI556_REG_TEST_PATTERN 0x0201 | 64 | #define HI556_REG_TEST_PATTERN 0x0201 |
| 65 | 65 | ||
| 66 | /* HI556 native and active pixel array size. */ | ||
| 67 | #define HI556_NATIVE_WIDTH 2592U | ||
| 68 | #define HI556_NATIVE_HEIGHT 1944U | ||
| 69 | #define HI556_PIXEL_ARRAY_LEFT 0U | ||
| 70 | #define HI556_PIXEL_ARRAY_TOP 0U | ||
| 71 | #define HI556_PIXEL_ARRAY_WIDTH 2592U | ||
| 72 | #define HI556_PIXEL_ARRAY_HEIGHT 1944U | ||
| 73 | |||
| 66 | enum { | 74 | enum { |
| 67 | HI556_LINK_FREQ_437MHZ_INDEX, | 75 | HI556_LINK_FREQ_437MHZ_INDEX, |
| 68 | }; | 76 | }; |
| @@ -88,6 +96,9 @@ struct hi556_mode { · 19 unmodified lines | |||
| 88 | /* Frame height in pixels */ | 96 | /* Frame height in pixels */ |
| 89 | u32 height; | 97 | u32 height; |
| 90 | 98 | ||
| 99 | /* Analog crop rectangle. */ | ||
| 100 | struct v4l2_rect crop; | ||
| 101 | |||
| 91 | /* Horizontal timining size */ | 102 | /* Horizontal timining size */ |
| 92 | u32 llp; | 103 | u32 llp; |
| 93 | 104 | ||
| @@ -378,6 +389,49 @@ static const struct hi556_reg mode_2592x1944_regs[] = { · 284 unmodified lines | |||
| 378 | {0x0958, 0xbb80}, | 389 | {0x0958, 0xbb80}, |
| 379 | }; | 390 | }; |
| 380 | 391 | ||
| 392 | static const struct hi556_reg mode_2592x1444_regs[] = { | ||
| 393 | {0x0a00, 0x0000}, | ||
| 394 | {0x0b0a, 0x8252}, | ||
| 395 | {0x0f30, 0xe545}, | ||
| 396 | {0x0f32, 0x7067}, | ||
| 397 | {0x004a, 0x0100}, | ||
| 398 | {0x004c, 0x0000}, | ||
| 399 | {0x000c, 0x0022}, | ||
| 400 | {0x0008, 0x0b00}, | ||
| 401 | {0x005a, 0x0202}, | ||
| 402 | {0x0012, 0x000e}, | ||
| 403 | {0x0018, 0x0a33}, | ||
| 404 | {0x0022, 0x0008}, | ||
| 405 | {0x0028, 0x0017}, | ||
| 406 | {0x0024, 0x0122}, | ||
| 407 | {0x002a, 0x0127}, | ||
| 408 | {0x0026, 0x012a}, | ||
| 409 | {0x002c, 0x06cf}, | ||
| 410 | {0x002e, 0x1111}, | ||
| 411 | {0x0030, 0x1111}, | ||
| 412 | {0x0032, 0x1111}, | ||
| 413 | {0x0006, 0x0821}, | ||
| 414 | {0x0a22, 0x0000}, | ||
| 415 | {0x0a12, 0x0a20}, | ||
| 416 | {0x0a14, 0x05a4}, | ||
| 417 | {0x003e, 0x0000}, | ||
| 418 | {0x0074, 0x081f}, | ||
| 419 | {0x0070, 0x040f}, | ||
| 420 | {0x0804, 0x0300}, | ||
| 421 | {0x0806, 0x0100}, | ||
| 422 | {0x0a04, 0x014a}, | ||
| 423 | {0x090c, 0x0fdc}, | ||
| 424 | {0x090e, 0x002d}, | ||
| 425 | {0x0902, 0x4319}, | ||
| 426 | {0x0914, 0xc10a}, | ||
| 427 | {0x0916, 0x071f}, | ||
| 428 | {0x0918, 0x0408}, | ||
| 429 | {0x091a, 0x0c0d}, | ||
| 430 | {0x091c, 0x0f09}, | ||
| 431 | {0x091e, 0x0a00}, | ||
| 432 | {0x0958, 0xbb80}, | ||
| 433 | }; | ||
| 434 | |||
| 381 | static const struct hi556_reg mode_1296x972_regs[] = { | 435 | static const struct hi556_reg mode_1296x972_regs[] = { |
| 382 | {0x0a00, 0x0000}, | 436 | {0x0a00, 0x0000}, |
| 383 | {0x0b0a, 0x8259}, | 437 | {0x0b0a, 0x8259}, |
| @@ -450,8 +504,14 @@ static const struct hi556_link_freq_config link_freq_configs[] = { · 66 unmodified lines | |||
| 450 | 504 | ||
| 451 | static const struct hi556_mode supported_modes[] = { | 505 | static const struct hi556_mode supported_modes[] = { |
| 452 | { | 506 | { |
| 453 | .width = 2592, | 507 | .width = HI556_PIXEL_ARRAY_WIDTH, |
| 454 | .height = 1944, | 508 | .height = HI556_PIXEL_ARRAY_HEIGHT, |
| 509 | .crop = { | ||
| 510 | .left = HI556_PIXEL_ARRAY_LEFT, | ||
| 511 | .top = HI556_PIXEL_ARRAY_TOP, | ||
| 512 | .width = HI556_PIXEL_ARRAY_WIDTH, | ||
| 513 | .height = HI556_PIXEL_ARRAY_HEIGHT | ||
| 514 | }, | ||
| 455 | .fll_def = HI556_FLL_30FPS, | 515 | .fll_def = HI556_FLL_30FPS, |
| 456 | .fll_min = HI556_FLL_30FPS_MIN, | 516 | .fll_min = HI556_FLL_30FPS_MIN, |
| 457 | .llp = 0x0b00, | 517 | .llp = 0x0b00, |
| @@ -461,9 +521,33 @@ static const struct hi556_mode supported_modes[] = { · 3 unmodified lines | |||
| 461 | }, | 521 | }, |
| 462 | .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, | 522 | .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, |
| 463 | }, | 523 | }, |
| 524 | { | ||
| 525 | .width = HI556_PIXEL_ARRAY_WIDTH, | ||
| 526 | .height = 1444, | ||
| 527 | .crop = { | ||
| 528 | .left = HI556_PIXEL_ARRAY_LEFT, | ||
| 529 | .top = 250, | ||
| 530 | .width = HI556_PIXEL_ARRAY_WIDTH, | ||
| 531 | .height = 1444 | ||
| 532 | }, | ||
| 533 | .fll_def = 0x821, | ||
| 534 | .fll_min = 0x821, | ||
| 535 | .llp = 0x0b00, | ||
| 536 | .reg_list = { | ||
| 537 | .num_of_regs = ARRAY_SIZE(mode_2592x1444_regs), | ||
| 538 | .regs = mode_2592x1444_regs, | ||
| 539 | }, | ||
| 540 | .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, | ||
| 541 | }, | ||
| 464 | { | 542 | { |
| 465 | .width = 1296, | 543 | .width = 1296, |
| 466 | .height = 972, | 544 | .height = 972, |
| 545 | .crop = { | ||
| 546 | .left = HI556_PIXEL_ARRAY_LEFT, | ||
| 547 | .top = HI556_PIXEL_ARRAY_TOP, | ||
| 548 | .width = HI556_PIXEL_ARRAY_WIDTH, | ||
| 549 | .height = HI556_PIXEL_ARRAY_HEIGHT | ||
| 550 | }, | ||
| 467 | .fll_def = HI556_FLL_30FPS, | 551 | .fll_def = HI556_FLL_30FPS, |
| 468 | .fll_min = HI556_FLL_30FPS_MIN, | 552 | .fll_min = HI556_FLL_30FPS_MIN, |
| 469 | .llp = 0x0b00, | 553 | .llp = 0x0b00, |
| @@ -785,6 +869,58 @@ static int hi556_identify_module(struct hi556 *hi556) · 315 unmodified lines | |||
| 785 | return 0; | 869 | return 0; |
| 786 | } | 870 | } |
| 787 | 871 | ||
| 872 | static const struct v4l2_rect * | ||
| 873 | __hi556_get_pad_crop(struct hi556 *hi556, | ||
| 874 | struct v4l2_subdev_state *sd_state, | ||
| 875 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
| 876 | { | ||
| 877 | switch (which) { | ||
| 878 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 879 | return v4l2_subdev_get_try_crop(&hi556->sd, sd_state, pad); | ||
| 880 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 881 | return &hi556->cur_mode->crop; | ||
| 882 | } | ||
| 883 | |||
| 884 | return NULL; | ||
| 885 | } | ||
| 886 | |||
| 887 | static int hi556_get_selection(struct v4l2_subdev *sd, | ||
| 888 | struct v4l2_subdev_state *sd_state, | ||
| 889 | struct v4l2_subdev_selection *sel) | ||
| 890 | { | ||
| 891 | switch (sel->target) { | ||
| 892 | case V4L2_SEL_TGT_CROP: { | ||
| 893 | struct hi556 *hi556 = to_hi556(sd); | ||
| 894 | |||
| 895 | mutex_lock(&hi556->mutex); | ||
| 896 | sel->r = *__hi556_get_pad_crop(hi556, sd_state, sel->pad, | ||
| 897 | sel->which); | ||
| 898 | mutex_unlock(&hi556->mutex); | ||
| 899 | |||
| 900 | return 0; | ||
| 901 | } | ||
| 902 | |||
| 903 | case V4L2_SEL_TGT_NATIVE_SIZE: | ||
| 904 | sel->r.top = 0; | ||
| 905 | sel->r.left = 0; | ||
| 906 | sel->r.width = HI556_NATIVE_WIDTH; | ||
| 907 | sel->r.height = HI556_NATIVE_HEIGHT; | ||
| 908 | |||
| 909 | return 0; | ||
| 910 | |||
| 911 | case V4L2_SEL_TGT_CROP_DEFAULT: | ||
| 912 | case V4L2_SEL_TGT_CROP_BOUNDS: | ||
| 913 | sel->r.top = HI556_PIXEL_ARRAY_TOP; | ||
| 914 | sel->r.left = HI556_PIXEL_ARRAY_LEFT; | ||
| 915 | sel->r.width = HI556_PIXEL_ARRAY_WIDTH; | ||
| 916 | sel->r.height = HI556_PIXEL_ARRAY_HEIGHT; | ||
| 917 | |||
| 918 | return 0; | ||
| 919 | } | ||
| 920 | |||
| 921 | return -EINVAL; | ||
| 922 | } | ||
| 923 | |||
| 788 | static int hi556_start_streaming(struct hi556 *hi556) | 924 | static int hi556_start_streaming(struct hi556 *hi556) |
| 789 | { | 925 | { |
| 790 | struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); | 926 | struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); |
| @@ -1000,10 +1136,19 @@ static int hi556_enum_frame_size(struct v4l2_subdev *sd, · 209 unmodified lines | |||
| 1000 | static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | 1136 | static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
| 1001 | { | 1137 | { |
| 1002 | struct hi556 *hi556 = to_hi556(sd); | 1138 | struct hi556 *hi556 = to_hi556(sd); |
| 1139 | struct v4l2_rect *try_crop; | ||
| 1003 | 1140 | ||
| 1004 | mutex_lock(&hi556->mutex); | 1141 | mutex_lock(&hi556->mutex); |
| 1005 | hi556_assign_pad_format(&supported_modes[0], | 1142 | hi556_assign_pad_format(&supported_modes[0], |
| 1006 | v4l2_subdev_get_try_format(sd, fh->state, 0)); | 1143 | v4l2_subdev_get_try_format(sd, fh->state, 0)); |
| 1144 | |||
| 1145 | /* Initialize try_crop rectangle. */ | ||
| 1146 | try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); | ||
| 1147 | try_crop->top = HI556_PIXEL_ARRAY_TOP; | ||
| 1148 | try_crop->left = HI556_PIXEL_ARRAY_LEFT; | ||
| 1149 | try_crop->width = HI556_PIXEL_ARRAY_WIDTH; | ||
| 1150 | try_crop->height = HI556_PIXEL_ARRAY_HEIGHT; | ||
| 1151 | |||
| 1007 | mutex_unlock(&hi556->mutex); | 1152 | mutex_unlock(&hi556->mutex); |
| 1008 | 1153 | ||
| 1009 | return 0; | 1154 | return 0; |
| @@ -1016,6 +1161,7 @@ static const struct v4l2_subdev_video_ops hi556_video_ops = { · 6 unmodified lines | |||
| 1016 | static const struct v4l2_subdev_pad_ops hi556_pad_ops = { | 1161 | static const struct v4l2_subdev_pad_ops hi556_pad_ops = { |
| 1017 | .set_fmt = hi556_set_format, | 1162 | .set_fmt = hi556_set_format, |
| 1018 | .get_fmt = hi556_get_format, | 1163 | .get_fmt = hi556_get_format, |
| 1164 | .get_selection = hi556_get_selection, | ||
| 1019 | .enum_mbus_code = hi556_enum_mbus_code, | 1165 | .enum_mbus_code = hi556_enum_mbus_code, |
| 1020 | .enum_frame_size = hi556_enum_frame_size, | 1166 | .enum_frame_size = hi556_enum_frame_size, |
| 1021 | }; | 1167 | }; |
| @@ -1472,21 +1472,26 @@ static int hi846_init_controls(struct hi846 *hi846) · 1471 unmodified lines | |||
| 1472 | if (ctrl_hdlr->error) { | 1472 | if (ctrl_hdlr->error) { |
| 1473 | dev_err(&client->dev, "v4l ctrl handler error: %d\n", | 1473 | dev_err(&client->dev, "v4l ctrl handler error: %d\n", |
| 1474 | ctrl_hdlr->error); | 1474 | ctrl_hdlr->error); |
| 1475 | return ctrl_hdlr->error; | 1475 | ret = ctrl_hdlr->error; |
| 1476 | goto error; | ||
| 1476 | } | 1477 | } |
| 1477 | 1478 | ||
| 1478 | ret = v4l2_fwnode_device_parse(&client->dev, &props); | 1479 | ret = v4l2_fwnode_device_parse(&client->dev, &props); |
| 1479 | if (ret) | 1480 | if (ret) |
| 1480 | return ret; | 1481 | goto error; |
| 1481 | 1482 | ||
| 1482 | ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hi846_ctrl_ops, | 1483 | ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hi846_ctrl_ops, |
| 1483 | &props); | 1484 | &props); |
| 1484 | if (ret) | 1485 | if (ret) |
| 1485 | return ret; | 1486 | goto error; |
| 1486 | 1487 | ||
| 1487 | hi846->sd.ctrl_handler = ctrl_hdlr; | 1488 | hi846->sd.ctrl_handler = ctrl_hdlr; |
| 1488 | 1489 | ||
| 1489 | return 0; | 1490 | return 0; |
| 1491 | |||
| 1492 | error: | ||
| 1493 | v4l2_ctrl_handler_free(ctrl_hdlr); | ||
| 1494 | return ret; | ||
| 1490 | } | 1495 | } |
| 1491 | 1496 | ||
| 1492 | static int hi846_set_video_mode(struct hi846 *hi846, int fps) | 1497 | static int hi846_set_video_mode(struct hi846 *hi846, int fps) |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <linux/pm_runtime.h> | 9 | #include <linux/pm_runtime.h> |
| 10 | #include <media/v4l2-ctrls.h> | 10 | #include <media/v4l2-ctrls.h> |
| 11 | #include <media/v4l2-device.h> | 11 | #include <media/v4l2-device.h> |
| 12 | #include <media/v4l2-fwnode.h> | ||
| 12 | #include <asm/unaligned.h> | 13 | #include <asm/unaligned.h> |
| 13 | 14 | ||
| 14 | #define IMX258_REG_VALUE_08BIT 1 | 15 | #define IMX258_REG_VALUE_08BIT 1 |
| @@ -1148,7 +1149,9 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = { · 1133 unmodified lines | |||
| 1148 | static int imx258_init_controls(struct imx258 *imx258) | 1149 | static int imx258_init_controls(struct imx258 *imx258) |
| 1149 | { | 1150 | { |
| 1150 | struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); | 1151 | struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); |
| 1152 | struct v4l2_fwnode_device_properties props; | ||
| 1151 | struct v4l2_ctrl_handler *ctrl_hdlr; | 1153 | struct v4l2_ctrl_handler *ctrl_hdlr; |
| 1154 | struct v4l2_ctrl *vflip, *hflip; | ||
| 1152 | s64 vblank_def; | 1155 | s64 vblank_def; |
| 1153 | s64 vblank_min; | 1156 | s64 vblank_min; |
| 1154 | s64 pixel_rate_min; | 1157 | s64 pixel_rate_min; |
| @@ -1156,7 +1159,7 @@ static int imx258_init_controls(struct imx258 *imx258) · 1 unmodified line | |||
| 1156 | int ret; | 1159 | int ret; |
| 1157 | 1160 | ||
| 1158 | ctrl_hdlr = &imx258->ctrl_handler; | 1161 | ctrl_hdlr = &imx258->ctrl_handler; |
| 1159 | ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); | 1162 | ret = v4l2_ctrl_handler_init(ctrl_hdlr, 13); |
| 1160 | if (ret) | 1163 | if (ret) |
| 1161 | return ret; | 1164 | return ret; |
| 1162 | 1165 | ||
| @@ -1172,6 +1175,17 @@ static int imx258_init_controls(struct imx258 *imx258) · 9 unmodified lines | |||
| 1172 | if (imx258->link_freq) | 1175 | if (imx258->link_freq) |
| 1173 | imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 1176 | imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 1174 | 1177 | ||
| 1178 | /* The driver only supports one bayer order and flips by default. */ | ||
| 1179 | hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, | ||
| 1180 | V4L2_CID_HFLIP, 1, 1, 1, 1); | ||
| 1181 | if (hflip) | ||
| 1182 | hflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
| 1183 | |||
| 1184 | vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, | ||
| 1185 | V4L2_CID_VFLIP, 1, 1, 1, 1); | ||
| 1186 | if (vflip) | ||
| 1187 | vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
| 1188 | |||
| 1175 | pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); | 1189 | pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); |
| 1176 | pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); | 1190 | pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); |
| 1177 | /* By default, PIXEL_RATE is read only */ | 1191 | /* By default, PIXEL_RATE is read only */ |
| @@ -1232,6 +1246,15 @@ static int imx258_init_controls(struct imx258 *imx258) · 54 unmodified lines | |||
| 1232 | goto error; | 1246 | goto error; |
| 1233 | } | 1247 | } |
| 1234 | 1248 | ||
| 1249 | ret = v4l2_fwnode_device_parse(&client->dev, &props); | ||
| 1250 | if (ret) | ||
| 1251 | goto error; | ||
| 1252 | |||
| 1253 | ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx258_ctrl_ops, | ||
| 1254 | &props); | ||
| 1255 | if (ret) | ||
| 1256 | goto error; | ||
| 1257 | |||
| 1235 | imx258->sd.ctrl_handler = ctrl_hdlr; | 1258 | imx258->sd.ctrl_handler = ctrl_hdlr; |
| 1236 | 1259 | ||
| 1237 | return 0; | 1260 | return 0; |
| @@ -1276,14 +1299,6 @@ static int imx258_probe(struct i2c_client *client) · 38 unmodified lines | |||
| 1276 | return -EINVAL; | 1299 | return -EINVAL; |
| 1277 | } | 1300 | } |
| 1278 | 1301 | ||
| 1279 | /* | ||
| 1280 | * Check that the device is mounted upside down. The driver only | ||
| 1281 | * supports a single pixel order right now. | ||
| 1282 | */ | ||
| 1283 | ret = device_property_read_u32(&client->dev, "rotation", &val); | ||
| 1284 | if (ret || val != 180) | ||
| 1285 | return -EINVAL; | ||
| 1286 | |||
| 1287 | /* Initialize subdev */ | 1302 | /* Initialize subdev */ |
| 1288 | v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); | 1303 | v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); |
| 1289 | 1304 | ||
| @@ -13,12 +13,17 @@ · 12 unmodified lines | |||
| 13 | #include <linux/gpio/consumer.h> | 13 | #include <linux/gpio/consumer.h> |
| 14 | #include <linux/i2c.h> | 14 | #include <linux/i2c.h> |
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/of_device.h> | ||
| 16 | #include <linux/pm_runtime.h> | 17 | #include <linux/pm_runtime.h> |
| 17 | #include <linux/regmap.h> | 18 | #include <linux/regmap.h> |
| 18 | #include <linux/regulator/consumer.h> | 19 | #include <linux/regulator/consumer.h> |
| 20 | |||
| 21 | #include <asm/unaligned.h> | ||
| 22 | |||
| 19 | #include <media/media-entity.h> | 23 | #include <media/media-entity.h> |
| 20 | #include <media/v4l2-ctrls.h> | 24 | #include <media/v4l2-ctrls.h> |
| 21 | #include <media/v4l2-device.h> | 25 | #include <media/v4l2-device.h> |
| 26 | #include <media/v4l2-event.h> | ||
| 22 | #include <media/v4l2-fwnode.h> | 27 | #include <media/v4l2-fwnode.h> |
| 23 | #include <media/v4l2-subdev.h> | 28 | #include <media/v4l2-subdev.h> |
| 24 | 29 | ||
| @@ -44,7 +49,9 @@ · 19 unmodified lines | |||
| 44 | #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) | 49 | #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) |
| 45 | #define IMX290_GAIN IMX290_REG_8BIT(0x3014) | 50 | #define IMX290_GAIN IMX290_REG_8BIT(0x3014) |
| 46 | #define IMX290_VMAX IMX290_REG_24BIT(0x3018) | 51 | #define IMX290_VMAX IMX290_REG_24BIT(0x3018) |
| 52 | #define IMX290_VMAX_MAX 0x3ffff | ||
| 47 | #define IMX290_HMAX IMX290_REG_16BIT(0x301c) | 53 | #define IMX290_HMAX IMX290_REG_16BIT(0x301c) |
| 54 | #define IMX290_HMAX_MAX 0xffff | ||
| 48 | #define IMX290_SHS1 IMX290_REG_24BIT(0x3020) | 55 | #define IMX290_SHS1 IMX290_REG_24BIT(0x3020) |
| 49 | #define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) | 56 | #define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) |
| 50 | #define IMX290_WINPV IMX290_REG_16BIT(0x303c) | 57 | #define IMX290_WINPV IMX290_REG_16BIT(0x303c) |
| @@ -98,13 +105,16 @@ · 47 unmodified lines | |||
| 98 | #define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) | 105 | #define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) |
| 99 | #define IMX290_TLPX IMX290_REG_16BIT(0x3454) | 106 | #define IMX290_TLPX IMX290_REG_16BIT(0x3454) |
| 100 | #define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) | 107 | #define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) |
| 108 | #define IMX290_INCKSEL7 IMX290_REG_8BIT(0x3480) | ||
| 101 | 109 | ||
| 102 | #define IMX290_PGCTRL_REGEN BIT(0) | 110 | #define IMX290_PGCTRL_REGEN BIT(0) |
| 103 | #define IMX290_PGCTRL_THRU BIT(1) | 111 | #define IMX290_PGCTRL_THRU BIT(1) |
| 104 | #define IMX290_PGCTRL_MODE(n) ((n) << 4) | 112 | #define IMX290_PGCTRL_MODE(n) ((n) << 4) |
| 105 | 113 | ||
| 106 | #define IMX290_VMAX_DEFAULT 1125 | 114 | /* Number of lines by which exposure must be less than VMAX */ |
| 115 | #define IMX290_EXPOSURE_OFFSET 2 | ||
| 107 | 116 | ||
| 117 | #define IMX290_PIXEL_RATE 148500000 | ||
| 108 | 118 | ||
| 109 | /* | 119 | /* |
| 110 | * The IMX290 pixel array is organized as follows: | 120 | * The IMX290 pixel array is organized as follows: |
| @@ -157,26 +167,81 @@ · 46 unmodified lines | |||
| 157 | 167 | ||
| 158 | #define IMX290_NUM_SUPPLIES 3 | 168 | #define IMX290_NUM_SUPPLIES 3 |
| 159 | 169 | ||
| 170 | enum imx290_colour_variant { | ||
| 171 | IMX290_VARIANT_COLOUR, | ||
| 172 | IMX290_VARIANT_MONO, | ||
| 173 | IMX290_VARIANT_MAX | ||
| 174 | }; | ||
| 175 | |||
| 176 | enum imx290_model { | ||
| 177 | IMX290_MODEL_IMX290LQR, | ||
| 178 | IMX290_MODEL_IMX290LLR, | ||
| 179 | IMX290_MODEL_IMX327LQR, | ||
| 180 | }; | ||
| 181 | |||
| 182 | struct imx290_model_info { | ||
| 183 | enum imx290_colour_variant colour_variant; | ||
| 184 | const struct imx290_regval *init_regs; | ||
| 185 | size_t init_regs_num; | ||
| 186 | const char *name; | ||
| 187 | }; | ||
| 188 | |||
| 189 | enum imx290_clk_freq { | ||
| 190 | IMX290_CLK_37_125, | ||
| 191 | IMX290_CLK_74_25, | ||
| 192 | IMX290_NUM_CLK | ||
| 193 | }; | ||
| 194 | |||
| 160 | struct imx290_regval { | 195 | struct imx290_regval { |
| 161 | u32 reg; | 196 | u32 reg; |
| 162 | u32 val; | 197 | u32 val; |
| 163 | }; | 198 | }; |
| 164 | 199 | ||
| 200 | /* | ||
| 201 | * Clock configuration for registers INCKSEL1 to INCKSEL6. | ||
| 202 | */ | ||
| 203 | struct imx290_clk_cfg { | ||
| 204 | u8 incksel1; | ||
| 205 | u8 incksel2; | ||
| 206 | u8 incksel3; | ||
| 207 | u8 incksel4; | ||
| 208 | u8 incksel5; | ||
| 209 | u8 incksel6; | ||
| 210 | }; | ||
| 211 | |||
| 165 | struct imx290_mode { | 212 | struct imx290_mode { |
| 166 | u32 width; | 213 | u32 width; |
| 167 | u32 height; | 214 | u32 height; |
| 168 | u32 hmax; | 215 | u32 hmax_min; |
| 216 | u32 vmax_min; | ||
| 169 | u8 link_freq_index; | 217 | u8 link_freq_index; |
| 218 | u8 ctrl_07; | ||
| 170 | 219 | ||
| 171 | const struct imx290_regval *data; | 220 | const struct imx290_regval *data; |
| 172 | u32 data_size; | 221 | u32 data_size; |
| 222 | |||
| 223 | const struct imx290_clk_cfg *clk_cfg; | ||
| 224 | }; | ||
| 225 | |||
| 226 | struct imx290_csi_cfg { | ||
| 227 | u16 repetition; | ||
| 228 | u16 tclkpost; | ||
| 229 | u16 thszero; | ||
| 230 | u16 thsprepare; | ||
| 231 | u16 tclktrail; | ||
| 232 | u16 thstrail; | ||
| 233 | u16 tclkzero; | ||
| 234 | u16 tclkprepare; | ||
| 235 | u16 tlpx; | ||
| 173 | }; | 236 | }; |
| 174 | 237 | ||
| 175 | struct imx290 { | 238 | struct imx290 { |
| 176 | struct device *dev; | 239 | struct device *dev; |
| 177 | struct clk *xclk; | 240 | struct clk *xclk; |
| 178 | struct regmap *regmap; | 241 | struct regmap *regmap; |
| 242 | enum imx290_clk_freq xclk_idx; | ||
| 179 | u8 nlanes; | 243 | u8 nlanes; |
| 244 | const struct imx290_model_info *model; | ||
| 180 | 245 | ||
| 181 | struct v4l2_subdev sd; | 246 | struct v4l2_subdev sd; |
| 182 | struct media_pad pad; | 247 | struct media_pad pad; |
| @@ -188,9 +253,13 @@ struct imx290 { · 5 unmodified lines | |||
| 188 | 253 | ||
| 189 | struct v4l2_ctrl_handler ctrls; | 254 | struct v4l2_ctrl_handler ctrls; |
| 190 | struct v4l2_ctrl *link_freq; | 255 | struct v4l2_ctrl *link_freq; |
| 191 | struct v4l2_ctrl *pixel_rate; | ||
| 192 | struct v4l2_ctrl *hblank; | 256 | struct v4l2_ctrl *hblank; |
| 193 | struct v4l2_ctrl *vblank; | 257 | struct v4l2_ctrl *vblank; |
| 258 | struct v4l2_ctrl *exposure; | ||
| 259 | struct { | ||
| 260 | struct v4l2_ctrl *hflip; | ||
| 261 | struct v4l2_ctrl *vflip; | ||
| 262 | }; | ||
| 194 | }; | 263 | }; |
| 195 | 264 | ||
| 196 | static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) | 265 | static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) |
| @@ -203,9 +272,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) · 6 unmodified lines | |||
| 203 | */ | 272 | */ |
| 204 | 273 | ||
| 205 | static const struct imx290_regval imx290_global_init_settings[] = { | 274 | static const struct imx290_regval imx290_global_init_settings[] = { |
| 206 | { IMX290_CTRL_07, IMX290_WINMODE_1080P }, | ||
| 207 | { IMX290_VMAX, IMX290_VMAX_DEFAULT }, | ||
| 208 | { IMX290_EXTCK_FREQ, 0x2520 }, | ||
| 209 | { IMX290_WINWV_OB, 12 }, | 275 | { IMX290_WINWV_OB, 12 }, |
| 210 | { IMX290_WINPH, 0 }, | 276 | { IMX290_WINPH, 0 }, |
| 211 | { IMX290_WINPV, 0 }, | 277 | { IMX290_WINPV, 0 }, |
| @@ -213,10 +279,14 @@ static const struct imx290_regval imx290_global_init_settings[] = { · 1 unmodified line | |||
| 213 | { IMX290_WINWV, 1097 }, | 279 | { IMX290_WINWV, 1097 }, |
| 214 | { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | | 280 | { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | |
| 215 | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, | 281 | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, |
| 216 | { IMX290_REG_8BIT(0x300f), 0x00 }, | 282 | { IMX290_REG_8BIT(0x3011), 0x02 }, |
| 217 | { IMX290_REG_8BIT(0x3010), 0x21 }, | ||
| 218 | { IMX290_REG_8BIT(0x3012), 0x64 }, | 283 | { IMX290_REG_8BIT(0x3012), 0x64 }, |
| 219 | { IMX290_REG_8BIT(0x3013), 0x00 }, | 284 | { IMX290_REG_8BIT(0x3013), 0x00 }, |
| 285 | }; | ||
| 286 | |||
| 287 | static const struct imx290_regval imx290_global_init_settings_290[] = { | ||
| 288 | { IMX290_REG_8BIT(0x300f), 0x00 }, | ||
| 289 | { IMX290_REG_8BIT(0x3010), 0x21 }, | ||
| 220 | { IMX290_REG_8BIT(0x3016), 0x09 }, | 290 | { IMX290_REG_8BIT(0x3016), 0x09 }, |
| 221 | { IMX290_REG_8BIT(0x3070), 0x02 }, | 291 | { IMX290_REG_8BIT(0x3070), 0x02 }, |
| 222 | { IMX290_REG_8BIT(0x3071), 0x11 }, | 292 | { IMX290_REG_8BIT(0x3071), 0x11 }, |
| @@ -255,57 +325,40 @@ static const struct imx290_regval imx290_global_init_settings[] = { · 32 unmodified lines | |||
| 255 | { IMX290_REG_8BIT(0x33b0), 0x50 }, | 325 | { IMX290_REG_8BIT(0x33b0), 0x50 }, |
| 256 | { IMX290_REG_8BIT(0x33b2), 0x1a }, | 326 | { IMX290_REG_8BIT(0x33b2), 0x1a }, |
| 257 | { IMX290_REG_8BIT(0x33b3), 0x04 }, | 327 | { IMX290_REG_8BIT(0x33b3), 0x04 }, |
| 258 | { IMX290_REG_8BIT(0x3480), 0x49 }, | 328 | }; |
| 329 | |||
| 330 | #define IMX290_NUM_CLK_REGS 2 | ||
| 331 | static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { | ||
| 332 | [IMX290_CLK_37_125] = { | ||
| 333 | { IMX290_EXTCK_FREQ, (37125 * 256) / 1000 }, | ||
| 334 | { IMX290_INCKSEL7, 0x49 }, | ||
| 335 | }, | ||
| 336 | [IMX290_CLK_74_25] = { | ||
| 337 | { IMX290_EXTCK_FREQ, (74250 * 256) / 1000 }, | ||
| 338 | { IMX290_INCKSEL7, 0x92 }, | ||
| 339 | }, | ||
| 340 | }; | ||
| 341 | |||
| 342 | static const struct imx290_regval imx290_global_init_settings_327[] = { | ||
| 343 | { IMX290_REG_8BIT(0x309e), 0x4A }, | ||
| 344 | { IMX290_REG_8BIT(0x309f), 0x4A }, | ||
| 345 | { IMX290_REG_8BIT(0x313b), 0x61 }, | ||
| 259 | }; | 346 | }; |
| 260 | 347 | ||
| 261 | static const struct imx290_regval imx290_1080p_settings[] = { | 348 | static const struct imx290_regval imx290_1080p_settings[] = { |
| 262 | /* mode settings */ | 349 | /* mode settings */ |
| 263 | { IMX290_CTRL_07, IMX290_WINMODE_1080P }, | ||
| 264 | { IMX290_WINWV_OB, 12 }, | 350 | { IMX290_WINWV_OB, 12 }, |
| 265 | { IMX290_OPB_SIZE_V, 10 }, | 351 | { IMX290_OPB_SIZE_V, 10 }, |
| 266 | { IMX290_X_OUT_SIZE, 1920 }, | 352 | { IMX290_X_OUT_SIZE, 1920 }, |
| 267 | { IMX290_Y_OUT_SIZE, 1080 }, | 353 | { IMX290_Y_OUT_SIZE, 1080 }, |
| 268 | { IMX290_INCKSEL1, 0x18 }, | ||
| 269 | { IMX290_INCKSEL2, 0x03 }, | ||
| 270 | { IMX290_INCKSEL3, 0x20 }, | ||
| 271 | { IMX290_INCKSEL4, 0x01 }, | ||
| 272 | { IMX290_INCKSEL5, 0x1a }, | ||
| 273 | { IMX290_INCKSEL6, 0x1a }, | ||
| 274 | /* data rate settings */ | ||
| 275 | { IMX290_REPETITION, 0x10 }, | ||
| 276 | { IMX290_TCLKPOST, 87 }, | ||
| 277 | { IMX290_THSZERO, 55 }, | ||
| 278 | { IMX290_THSPREPARE, 31 }, | ||
| 279 | { IMX290_TCLKTRAIL, 31 }, | ||
| 280 | { IMX290_THSTRAIL, 31 }, | ||
| 281 | { IMX290_TCLKZERO, 119 }, | ||
| 282 | { IMX290_TCLKPREPARE, 31 }, | ||
| 283 | { IMX290_TLPX, 23 }, | ||
| 284 | }; | 354 | }; |
| 285 | 355 | ||
| 286 | static const struct imx290_regval imx290_720p_settings[] = { | 356 | static const struct imx290_regval imx290_720p_settings[] = { |
| 287 | /* mode settings */ | 357 | /* mode settings */ |
| 288 | { IMX290_CTRL_07, IMX290_WINMODE_720P }, | ||
| 289 | { IMX290_WINWV_OB, 6 }, | 358 | { IMX290_WINWV_OB, 6 }, |
| 290 | { IMX290_OPB_SIZE_V, 4 }, | 359 | { IMX290_OPB_SIZE_V, 4 }, |
| 291 | { IMX290_X_OUT_SIZE, 1280 }, | 360 | { IMX290_X_OUT_SIZE, 1280 }, |
| 292 | { IMX290_Y_OUT_SIZE, 720 }, | 361 | { IMX290_Y_OUT_SIZE, 720 }, |
| 293 | { IMX290_INCKSEL1, 0x20 }, | ||
| 294 | { IMX290_INCKSEL2, 0x00 }, | ||
| 295 | { IMX290_INCKSEL3, 0x20 }, | ||
| 296 | { IMX290_INCKSEL4, 0x01 }, | ||
| 297 | { IMX290_INCKSEL5, 0x1a }, | ||
| 298 | { IMX290_INCKSEL6, 0x1a }, | ||
| 299 | /* data rate settings */ | ||
| 300 | { IMX290_REPETITION, 0x10 }, | ||
| 301 | { IMX290_TCLKPOST, 79 }, | ||
| 302 | { IMX290_THSZERO, 47 }, | ||
| 303 | { IMX290_THSPREPARE, 23 }, | ||
| 304 | { IMX290_TCLKTRAIL, 23 }, | ||
| 305 | { IMX290_THSTRAIL, 23 }, | ||
| 306 | { IMX290_TCLKZERO, 87 }, | ||
| 307 | { IMX290_TCLKPREPARE, 23 }, | ||
| 308 | { IMX290_TLPX, 23 }, | ||
| 309 | }; | 362 | }; |
| 310 | 363 | ||
| 311 | static const struct imx290_regval imx290_10bit_settings[] = { | 364 | static const struct imx290_regval imx290_10bit_settings[] = { |
| @@ -326,6 +379,58 @@ static const struct imx290_regval imx290_12bit_settings[] = { · 14 unmodified lines | |||
| 326 | { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, | 379 | { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, |
| 327 | }; | 380 | }; |
| 328 | 381 | ||
| 382 | static const struct imx290_csi_cfg imx290_csi_222_75mhz = { | ||
| 383 | /* 222.75MHz or 445.5Mbit/s per lane */ | ||
| 384 | .repetition = 0x10, | ||
| 385 | .tclkpost = 87, | ||
| 386 | .thszero = 55, | ||
| 387 | .thsprepare = 31, | ||
| 388 | .tclktrail = 31, | ||
| 389 | .thstrail = 31, | ||
| 390 | .tclkzero = 119, | ||
| 391 | .tclkprepare = 31, | ||
| 392 | .tlpx = 23, | ||
| 393 | }; | ||
| 394 | |||
| 395 | static const struct imx290_csi_cfg imx290_csi_445_5mhz = { | ||
| 396 | /* 445.5MHz or 891Mbit/s per lane */ | ||
| 397 | .repetition = 0x00, | ||
| 398 | .tclkpost = 119, | ||
| 399 | .thszero = 103, | ||
| 400 | .thsprepare = 71, | ||
| 401 | .tclktrail = 55, | ||
| 402 | .thstrail = 63, | ||
| 403 | .tclkzero = 255, | ||
| 404 | .tclkprepare = 63, | ||
| 405 | .tlpx = 55, | ||
| 406 | }; | ||
| 407 | |||
| 408 | static const struct imx290_csi_cfg imx290_csi_148_5mhz = { | ||
| 409 | /* 148.5MHz or 297Mbit/s per lane */ | ||
| 410 | .repetition = 0x10, | ||
| 411 | .tclkpost = 79, | ||
| 412 | .thszero = 47, | ||
| 413 | .thsprepare = 23, | ||
| 414 | .tclktrail = 23, | ||
| 415 | .thstrail = 23, | ||
| 416 | .tclkzero = 87, | ||
| 417 | .tclkprepare = 23, | ||
| 418 | .tlpx = 23, | ||
| 419 | }; | ||
| 420 | |||
| 421 | static const struct imx290_csi_cfg imx290_csi_297mhz = { | ||
| 422 | /* 297MHz or 594Mbit/s per lane */ | ||
| 423 | .repetition = 0x00, | ||
| 424 | .tclkpost = 103, | ||
| 425 | .thszero = 87, | ||
| 426 | .thsprepare = 47, | ||
| 427 | .tclktrail = 39, | ||
| 428 | .thstrail = 47, | ||
| 429 | .tclkzero = 191, | ||
| 430 | .tclkprepare = 47, | ||
| 431 | .tlpx = 39, | ||
| 432 | }; | ||
| 433 | |||
| 329 | /* supported link frequencies */ | 434 | /* supported link frequencies */ |
| 330 | #define FREQ_INDEX_1080P 0 | 435 | #define FREQ_INDEX_1080P 0 |
| 331 | #define FREQ_INDEX_720P 1 | 436 | #define FREQ_INDEX_720P 1 |
| @@ -333,6 +438,7 @@ static const s64 imx290_link_freq_2lanes[] = { · 1 unmodified line | |||
| 333 | [FREQ_INDEX_1080P] = 445500000, | 438 | [FREQ_INDEX_1080P] = 445500000, |
| 334 | [FREQ_INDEX_720P] = 297000000, | 439 | [FREQ_INDEX_720P] = 297000000, |
| 335 | }; | 440 | }; |
| 441 | |||
| 336 | static const s64 imx290_link_freq_4lanes[] = { | 442 | static const s64 imx290_link_freq_4lanes[] = { |
| 337 | [FREQ_INDEX_1080P] = 222750000, | 443 | [FREQ_INDEX_1080P] = 222750000, |
| 338 | [FREQ_INDEX_720P] = 148500000, | 444 | [FREQ_INDEX_720P] = 148500000, |
| @@ -358,23 +464,71 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290) · 19 unmodified lines | |||
| 358 | return ARRAY_SIZE(imx290_link_freq_4lanes); | 464 | return ARRAY_SIZE(imx290_link_freq_4lanes); |
| 359 | } | 465 | } |
| 360 | 466 | ||
| 467 | static const struct imx290_clk_cfg imx290_1080p_clock_config[] = { | ||
| 468 | [IMX290_CLK_37_125] = { | ||
| 469 | /* 37.125MHz clock config */ | ||
| 470 | .incksel1 = 0x18, | ||
| 471 | .incksel2 = 0x03, | ||
| 472 | .incksel3 = 0x20, | ||
| 473 | .incksel4 = 0x01, | ||
| 474 | .incksel5 = 0x1a, | ||
| 475 | .incksel6 = 0x1a, | ||
| 476 | }, | ||
| 477 | [IMX290_CLK_74_25] = { | ||
| 478 | /* 74.25MHz clock config */ | ||
| 479 | .incksel1 = 0x0c, | ||
| 480 | .incksel2 = 0x03, | ||
| 481 | .incksel3 = 0x10, | ||
| 482 | .incksel4 = 0x01, | ||
| 483 | .incksel5 = 0x1b, | ||
| 484 | .incksel6 = 0x1b, | ||
| 485 | }, | ||
| 486 | }; | ||
| 487 | |||
| 488 | static const struct imx290_clk_cfg imx290_720p_clock_config[] = { | ||
| 489 | [IMX290_CLK_37_125] = { | ||
| 490 | /* 37.125MHz clock config */ | ||
| 491 | .incksel1 = 0x20, | ||
| 492 | .incksel2 = 0x00, | ||
| 493 | .incksel3 = 0x20, | ||
| 494 | .incksel4 = 0x01, | ||
| 495 | .incksel5 = 0x1a, | ||
| 496 | .incksel6 = 0x1a, | ||
| 497 | }, | ||
| 498 | [IMX290_CLK_74_25] = { | ||
| 499 | /* 74.25MHz clock config */ | ||
| 500 | .incksel1 = 0x10, | ||
| 501 | .incksel2 = 0x00, | ||
| 502 | .incksel3 = 0x10, | ||
| 503 | .incksel4 = 0x01, | ||
| 504 | .incksel5 = 0x1b, | ||
| 505 | .incksel6 = 0x1b, | ||
| 506 | }, | ||
| 507 | }; | ||
| 508 | |||
| 361 | /* Mode configs */ | 509 | /* Mode configs */ |
| 362 | static const struct imx290_mode imx290_modes_2lanes[] = { | 510 | static const struct imx290_mode imx290_modes_2lanes[] = { |
| 363 | { | 511 | { |
| 364 | .width = 1920, | 512 | .width = 1920, |
| 365 | .height = 1080, | 513 | .height = 1080, |
| 366 | .hmax = 4400, | 514 | .hmax_min = 2200, |
| 515 | .vmax_min = 1125, | ||
| 367 | .link_freq_index = FREQ_INDEX_1080P, | 516 | .link_freq_index = FREQ_INDEX_1080P, |
| 517 | .ctrl_07 = IMX290_WINMODE_1080P, | ||
| 368 | .data = imx290_1080p_settings, | 518 | .data = imx290_1080p_settings, |
| 369 | .data_size = ARRAY_SIZE(imx290_1080p_settings), | 519 | .data_size = ARRAY_SIZE(imx290_1080p_settings), |
| 520 | .clk_cfg = imx290_1080p_clock_config, | ||
| 370 | }, | 521 | }, |
| 371 | { | 522 | { |
| 372 | .width = 1280, | 523 | .width = 1280, |
| 373 | .height = 720, | 524 | .height = 720, |
| 374 | .hmax = 6600, | 525 | .hmax_min = 3300, |
| 526 | .vmax_min = 750, | ||
| 375 | .link_freq_index = FREQ_INDEX_720P, | 527 | .link_freq_index = FREQ_INDEX_720P, |
| 528 | .ctrl_07 = IMX290_WINMODE_720P, | ||
| 376 | .data = imx290_720p_settings, | 529 | .data = imx290_720p_settings, |
| 377 | .data_size = ARRAY_SIZE(imx290_720p_settings), | 530 | .data_size = ARRAY_SIZE(imx290_720p_settings), |
| 531 | .clk_cfg = imx290_720p_clock_config, | ||
| 378 | }, | 532 | }, |
| 379 | }; | 533 | }; |
| 380 | 534 | ||
| @@ -382,18 +536,24 @@ static const struct imx290_mode imx290_modes_4lanes[] = { · 1 unmodified line | |||
| 382 | { | 536 | { |
| 383 | .width = 1920, | 537 | .width = 1920, |
| 384 | .height = 1080, | 538 | .height = 1080, |
| 385 | .hmax = 2200, | 539 | .hmax_min = 2200, |
| 540 | .vmax_min = 1125, | ||
| 386 | .link_freq_index = FREQ_INDEX_1080P, | 541 | .link_freq_index = FREQ_INDEX_1080P, |
| 542 | .ctrl_07 = IMX290_WINMODE_1080P, | ||
| 387 | .data = imx290_1080p_settings, | 543 | .data = imx290_1080p_settings, |
| 388 | .data_size = ARRAY_SIZE(imx290_1080p_settings), | 544 | .data_size = ARRAY_SIZE(imx290_1080p_settings), |
| 545 | .clk_cfg = imx290_1080p_clock_config, | ||
| 389 | }, | 546 | }, |
| 390 | { | 547 | { |
| 391 | .width = 1280, | 548 | .width = 1280, |
| 392 | .height = 720, | 549 | .height = 720, |
| 393 | .hmax = 3300, | 550 | .hmax_min = 3300, |
| 551 | .vmax_min = 750, | ||
| 394 | .link_freq_index = FREQ_INDEX_720P, | 552 | .link_freq_index = FREQ_INDEX_720P, |
| 553 | .ctrl_07 = IMX290_WINMODE_720P, | ||
| 395 | .data = imx290_720p_settings, | 554 | .data = imx290_720p_settings, |
| 396 | .data_size = ARRAY_SIZE(imx290_720p_settings), | 555 | .data_size = ARRAY_SIZE(imx290_720p_settings), |
| 556 | .clk_cfg = imx290_720p_clock_config, | ||
| 397 | }, | 557 | }, |
| 398 | }; | 558 | }; |
| 399 | 559 | ||
| @@ -414,7 +574,7 @@ static inline int imx290_modes_num(const struct imx290 *imx290) · 14 unmodified lines | |||
| 414 | } | 574 | } |
| 415 | 575 | ||
| 416 | struct imx290_format_info { | 576 | struct imx290_format_info { |
| 417 | u32 code; | 577 | u32 code[IMX290_VARIANT_MAX]; |
| 418 | u8 bpp; | 578 | u8 bpp; |
| 419 | const struct imx290_regval *regs; | 579 | const struct imx290_regval *regs; |
| 420 | unsigned int num_regs; | 580 | unsigned int num_regs; |
| @@ -422,26 +582,33 @@ struct imx290_format_info { · 1 unmodified line | |||
| 422 | 582 | ||
| 423 | static const struct imx290_format_info imx290_formats[] = { | 583 | static const struct imx290_format_info imx290_formats[] = { |
| 424 | { | 584 | { |
| 425 | .code = MEDIA_BUS_FMT_SRGGB10_1X10, | 585 | .code = { |
| 586 | [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB10_1X10, | ||
| 587 | [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y10_1X10 | ||
| 588 | }, | ||
| 426 | .bpp = 10, | 589 | .bpp = 10, |
| 427 | .regs = imx290_10bit_settings, | 590 | .regs = imx290_10bit_settings, |
| 428 | .num_regs = ARRAY_SIZE(imx290_10bit_settings), | 591 | .num_regs = ARRAY_SIZE(imx290_10bit_settings), |
| 429 | }, { | 592 | }, { |
| 430 | .code = MEDIA_BUS_FMT_SRGGB12_1X12, | 593 | .code = { |
| 594 | [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB12_1X12, | ||
| 595 | [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y12_1X12 | ||
| 596 | }, | ||
| 431 | .bpp = 12, | 597 | .bpp = 12, |
| 432 | .regs = imx290_12bit_settings, | 598 | .regs = imx290_12bit_settings, |
| 433 | .num_regs = ARRAY_SIZE(imx290_12bit_settings), | 599 | .num_regs = ARRAY_SIZE(imx290_12bit_settings), |
| 434 | } | 600 | } |
| 435 | }; | 601 | }; |
| 436 | 602 | ||
| 437 | static const struct imx290_format_info *imx290_format_info(u32 code) | 603 | static const struct imx290_format_info * |
| 604 | imx290_format_info(const struct imx290 *imx290, u32 code) | ||
| 438 | { | 605 | { |
| 439 | unsigned int i; | 606 | unsigned int i; |
| 440 | 607 | ||
| 441 | for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) { | 608 | for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) { |
| 442 | const struct imx290_format_info *info = &imx290_formats[i]; | 609 | const struct imx290_format_info *info = &imx290_formats[i]; |
| 443 | 610 | ||
| 444 | if (info->code == code) | 611 | if (info->code[imx290->model->colour_variant] == code) |
| 445 | return info; | 612 | return info; |
| 446 | } | 613 | } |
| 447 | 614 | ||
| @@ -461,28 +628,30 @@ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *val · 13 unmodified lines | |||
| 461 | data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); | 628 | data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); |
| 462 | if (ret < 0) { | 629 | if (ret < 0) { |
| 463 | dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", | 630 | dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", |
| 464 | ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, | 631 | ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, |
| 465 | addr & IMX290_REG_ADDR_MASK, ret); | 632 | addr & IMX290_REG_ADDR_MASK, ret); |
| 466 | return ret; | 633 | return ret; |
| 467 | } | 634 | } |
| 468 | 635 | ||
| 469 | *value = (data[2] << 16) | (data[1] << 8) | data[0]; | 636 | *value = get_unaligned_le24(data); |
| 470 | return 0; | 637 | return 0; |
| 471 | } | 638 | } |
| 472 | 639 | ||
| 473 | static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) | 640 | static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) |
| 474 | { | 641 | { |
| 475 | u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 }; | 642 | u8 data[3]; |
| 476 | int ret; | 643 | int ret; |
| 477 | 644 | ||
| 478 | if (err && *err) | 645 | if (err && *err) |
| 479 | return *err; | 646 | return *err; |
| 480 | 647 | ||
| 648 | put_unaligned_le24(value, data); | ||
| 649 | |||
| 481 | ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, | 650 | ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, |
| 482 | data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); | 651 | data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); |
| 483 | if (ret < 0) { | 652 | if (ret < 0) { |
| 484 | dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", | 653 | dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", |
| 485 | ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, | 654 | ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, |
| 486 | addr & IMX290_REG_ADDR_MASK, ret); | 655 | addr & IMX290_REG_ADDR_MASK, ret); |
| 487 | if (err) | 656 | if (err) |
| 488 | *err = ret; | 657 | *err = ret; |
| @@ -510,24 +679,33 @@ static int imx290_set_register_array(struct imx290 *imx290, · 21 unmodified lines | |||
| 510 | return 0; | 679 | return 0; |
| 511 | } | 680 | } |
| 512 | 681 | ||
| 682 | static int imx290_set_clock(struct imx290 *imx290) | ||
| 683 | { | ||
| 684 | const struct imx290_mode *mode = imx290->current_mode; | ||
| 685 | enum imx290_clk_freq clk_idx = imx290->xclk_idx; | ||
| 686 | const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx]; | ||
| 687 | int ret; | ||
| 688 | |||
| 689 | ret = imx290_set_register_array(imx290, xclk_regs[clk_idx], | ||
| 690 | IMX290_NUM_CLK_REGS); | ||
| 691 | |||
| 692 | imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); | ||
| 693 | imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); | ||
| 694 | imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); | ||
| 695 | imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); | ||
| 696 | imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); | ||
| 697 | imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); | ||
| 698 | |||
| 699 | return ret; | ||
| 700 | } | ||
| 701 | |||
| 513 | static int imx290_set_data_lanes(struct imx290 *imx290) | 702 | static int imx290_set_data_lanes(struct imx290 *imx290) |
| 514 | { | 703 | { |
| 515 | int ret = 0; | 704 | int ret = 0; |
| 516 | u32 frsel; | ||
| 517 | |||
| 518 | switch (imx290->nlanes) { | ||
| 519 | case 2: | ||
| 520 | default: | ||
| 521 | frsel = 0x02; | ||
| 522 | break; | ||
| 523 | case 4: | ||
| 524 | frsel = 0x01; | ||
| 525 | break; | ||
| 526 | } | ||
| 527 | 705 | ||
| 528 | imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); | 706 | imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); |
| 529 | imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); | 707 | imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); |
| 530 | imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); | 708 | imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret); |
| 531 | 709 | ||
| 532 | return ret; | 710 | return ret; |
| 533 | } | 711 | } |
| @@ -536,19 +714,55 @@ static int imx290_set_black_level(struct imx290 *imx290, · 2 unmodified lines | |||
| 536 | const struct v4l2_mbus_framefmt *format, | 714 | const struct v4l2_mbus_framefmt *format, |
| 537 | unsigned int black_level, int *err) | 715 | unsigned int black_level, int *err) |
| 538 | { | 716 | { |
| 539 | unsigned int bpp = imx290_format_info(format->code)->bpp; | 717 | unsigned int bpp = imx290_format_info(imx290, format->code)->bpp; |
| 540 | 718 | ||
| 541 | return imx290_write(imx290, IMX290_BLKLEVEL, | 719 | return imx290_write(imx290, IMX290_BLKLEVEL, |
| 542 | black_level >> (16 - bpp), err); | 720 | black_level >> (16 - bpp), err); |
| 543 | } | 721 | } |
| 544 | 722 | ||
| 723 | static int imx290_set_csi_config(struct imx290 *imx290) | ||
| 724 | { | ||
| 725 | const s64 *link_freqs = imx290_link_freqs_ptr(imx290); | ||
| 726 | const struct imx290_csi_cfg *csi_cfg; | ||
| 727 | int ret = 0; | ||
| 728 | |||
| 729 | switch (link_freqs[imx290->current_mode->link_freq_index]) { | ||
| 730 | case 445500000: | ||
| 731 | csi_cfg = &imx290_csi_445_5mhz; | ||
| 732 | break; | ||
| 733 | case 297000000: | ||
| 734 | csi_cfg = &imx290_csi_297mhz; | ||
| 735 | break; | ||
| 736 | case 222750000: | ||
| 737 | csi_cfg = &imx290_csi_222_75mhz; | ||
| 738 | break; | ||
| 739 | case 148500000: | ||
| 740 | csi_cfg = &imx290_csi_148_5mhz; | ||
| 741 | break; | ||
| 742 | default: | ||
| 743 | return -EINVAL; | ||
| 744 | } | ||
| 745 | |||
| 746 | imx290_write(imx290, IMX290_REPETITION, csi_cfg->repetition, &ret); | ||
| 747 | imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); | ||
| 748 | imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret); | ||
| 749 | imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); | ||
| 750 | imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); | ||
| 751 | imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); | ||
| 752 | imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); | ||
| 753 | imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret); | ||
| 754 | imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret); | ||
| 755 | |||
| 756 | return ret; | ||
| 757 | } | ||
| 758 | |||
| 545 | static int imx290_setup_format(struct imx290 *imx290, | 759 | static int imx290_setup_format(struct imx290 *imx290, |
| 546 | const struct v4l2_mbus_framefmt *format) | 760 | const struct v4l2_mbus_framefmt *format) |
| 547 | { | 761 | { |
| 548 | const struct imx290_format_info *info; | 762 | const struct imx290_format_info *info; |
| 549 | int ret; | 763 | int ret; |
| 550 | 764 | ||
| 551 | info = imx290_format_info(format->code); | 765 | info = imx290_format_info(imx290, format->code); |
| 552 | 766 | ||
| 553 | ret = imx290_set_register_array(imx290, info->regs, info->num_regs); | 767 | ret = imx290_set_register_array(imx290, info->regs, info->num_regs); |
| 554 | if (ret < 0) { | 768 | if (ret < 0) { |
| @@ -563,6 +777,16 @@ static int imx290_setup_format(struct imx290 *imx290, · 8 unmodified lines | |||
| 563 | /* ---------------------------------------------------------------------------- | 777 | /* ---------------------------------------------------------------------------- |
| 564 | * Controls | 778 | * Controls |
| 565 | */ | 779 | */ |
| 780 | static void imx290_exposure_update(struct imx290 *imx290, | ||
| 781 | const struct imx290_mode *mode) | ||
| 782 | { | ||
| 783 | unsigned int exposure_max; | ||
| 784 | |||
| 785 | exposure_max = imx290->vblank->val + mode->height - | ||
| 786 | IMX290_EXPOSURE_OFFSET; | ||
| 787 | __v4l2_ctrl_modify_range(imx290->exposure, 1, exposure_max, 1, | ||
| 788 | exposure_max); | ||
| 789 | } | ||
| 566 | 790 | ||
| 567 | static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) | 791 | static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) |
| 568 | { | 792 | { |
| @@ -570,7 +794,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) · 1 unmodified line | |||
| 570 | struct imx290, ctrls); | 794 | struct imx290, ctrls); |
| 571 | const struct v4l2_mbus_framefmt *format; | 795 | const struct v4l2_mbus_framefmt *format; |
| 572 | struct v4l2_subdev_state *state; | 796 | struct v4l2_subdev_state *state; |
| 573 | int ret = 0; | 797 | int ret = 0, vmax; |
| 574 | 798 | ||
| 575 | /* | 799 | /* |
| 576 | * Return immediately for controls that don't need to be applied to the | 800 | * Return immediately for controls that don't need to be applied to the |
| @@ -579,6 +803,11 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) · 2 unmodified lines | |||
| 579 | if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) | 803 | if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) |
| 580 | return 0; | 804 | return 0; |
| 581 | 805 | ||
| 806 | if (ctrl->id == V4L2_CID_VBLANK) { | ||
| 807 | /* Changing vblank changes the allowed range for exposure. */ | ||
| 808 | imx290_exposure_update(imx290, imx290->current_mode); | ||
| 809 | } | ||
| 810 | |||
| 582 | /* V4L2 controls values will be applied only when power is already up */ | 811 | /* V4L2 controls values will be applied only when power is already up */ |
| 583 | if (!pm_runtime_get_if_in_use(imx290->dev)) | 812 | if (!pm_runtime_get_if_in_use(imx290->dev)) |
| 584 | return 0; | 813 | return 0; |
| @@ -591,9 +820,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) · 6 unmodified lines | |||
| 591 | ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); | 820 | ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); |
| 592 | break; | 821 | break; |
| 593 | 822 | ||
| 823 | case V4L2_CID_VBLANK: | ||
| 824 | ret = imx290_write(imx290, IMX290_VMAX, | ||
| 825 | ctrl->val + imx290->current_mode->height, | ||
| 826 | NULL); | ||
| 827 | /* | ||
| 828 | * Due to the way that exposure is programmed in this sensor in | ||
| 829 | * relation to VMAX, we have to reprogramme it whenever VMAX is | ||
| 830 | * changed. | ||
| 831 | * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to | ||
| 832 | * it. | ||
| 833 | */ | ||
| 834 | ctrl = imx290->exposure; | ||
| 835 | fallthrough; | ||
| 594 | case V4L2_CID_EXPOSURE: | 836 | case V4L2_CID_EXPOSURE: |
| 837 | vmax = imx290->vblank->val + imx290->current_mode->height; | ||
| 595 | ret = imx290_write(imx290, IMX290_SHS1, | 838 | ret = imx290_write(imx290, IMX290_SHS1, |
| 596 | IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL); | 839 | vmax - ctrl->val - 1, NULL); |
| 597 | break; | 840 | break; |
| 598 | 841 | ||
| 599 | case V4L2_CID_TEST_PATTERN: | 842 | case V4L2_CID_TEST_PATTERN: |
| @@ -612,6 +855,26 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) · 12 unmodified lines | |||
| 612 | } | 855 | } |
| 613 | break; | 856 | break; |
| 614 | 857 | ||
| 858 | case V4L2_CID_HBLANK: | ||
| 859 | ret = imx290_write(imx290, IMX290_HMAX, | ||
| 860 | ctrl->val + imx290->current_mode->width, | ||
| 861 | NULL); | ||
| 862 | break; | ||
| 863 | |||
| 864 | case V4L2_CID_HFLIP: | ||
| 865 | case V4L2_CID_VFLIP: | ||
| 866 | { | ||
| 867 | u32 reg; | ||
| 868 | |||
| 869 | reg = imx290->current_mode->ctrl_07; | ||
| 870 | if (imx290->hflip->val) | ||
| 871 | reg |= IMX290_HREVERSE; | ||
| 872 | if (imx290->vflip->val) | ||
| 873 | reg |= IMX290_VREVERSE; | ||
| 874 | ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL); | ||
| 875 | break; | ||
| 876 | } | ||
| 877 | |||
| 615 | default: | 878 | default: |
| 616 | ret = -EINVAL; | 879 | ret = -EINVAL; |
| 617 | break; | 880 | break; |
| @@ -642,20 +905,17 @@ static void imx290_ctrl_update(struct imx290 *imx290, · 24 unmodified lines | |||
| 642 | const struct v4l2_mbus_framefmt *format, | 905 | const struct v4l2_mbus_framefmt *format, |
| 643 | const struct imx290_mode *mode) | 906 | const struct imx290_mode *mode) |
| 644 | { | 907 | { |
| 645 | unsigned int hblank = mode->hmax - mode->width; | 908 | unsigned int hblank_min = mode->hmax_min - mode->width; |
| 646 | unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; | 909 | unsigned int hblank_max = IMX290_HMAX_MAX - mode->width; |
| 647 | s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; | 910 | unsigned int vblank_min = mode->vmax_min - mode->height; |
| 648 | u64 pixel_rate; | 911 | unsigned int vblank_max = IMX290_VMAX_MAX - mode->height; |
| 649 | |||
| 650 | /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ | ||
| 651 | pixel_rate = link_freq * 2 * imx290->nlanes; | ||
| 652 | do_div(pixel_rate, imx290_format_info(format->code)->bpp); | ||
| 653 | 912 | ||
| 654 | __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); | 913 | __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); |
| 655 | __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate); | ||
| 656 | 914 | ||
| 657 | __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); | 915 | __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1, |
| 658 | __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); | 916 | hblank_min); |
| 917 | __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1, | ||
| 918 | vblank_min); | ||
| 659 | } | 919 | } |
| 660 | 920 | ||
| 661 | static int imx290_ctrl_init(struct imx290 *imx290) | 921 | static int imx290_ctrl_init(struct imx290 *imx290) |
| @@ -667,7 +927,7 @@ static int imx290_ctrl_init(struct imx290 *imx290) · 5 unmodified lines | |||
| 667 | if (ret < 0) | 927 | if (ret < 0) |
| 668 | return ret; | 928 | return ret; |
| 669 | 929 | ||
| 670 | v4l2_ctrl_handler_init(&imx290->ctrls, 9); | 930 | v4l2_ctrl_handler_init(&imx290->ctrls, 11); |
| 671 | 931 | ||
| 672 | /* | 932 | /* |
| 673 | * The sensor has an analog gain and a digital gain, both controlled | 933 | * The sensor has an analog gain and a digital gain, both controlled |
| @@ -685,9 +945,13 @@ static int imx290_ctrl_init(struct imx290 *imx290) · 11 unmodified lines | |||
| 685 | v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | 945 | v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 686 | V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); | 946 | V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); |
| 687 | 947 | ||
| 688 | v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | 948 | /* |
| 689 | V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, | 949 | * Correct range will be determined through imx290_ctrl_update setting |
| 690 | IMX290_VMAX_DEFAULT - 2); | 950 | * V4L2_CID_VBLANK. |
| 951 | */ | ||
| 952 | imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | ||
| 953 | V4L2_CID_EXPOSURE, 1, 65535, 1, | ||
| 954 | 65535); | ||
| 691 | 955 | ||
| 692 | /* | 956 | /* |
| 693 | * Set the link frequency, pixel rate, horizontal blanking and vertical | 957 | * Set the link frequency, pixel rate, horizontal blanking and vertical |
| @@ -702,24 +966,29 @@ static int imx290_ctrl_init(struct imx290 *imx290) · 8 unmodified lines | |||
| 702 | if (imx290->link_freq) | 966 | if (imx290->link_freq) |
| 703 | imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 967 | imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 704 | 968 | ||
| 705 | imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | 969 | v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, |
| 706 | V4L2_CID_PIXEL_RATE, | 970 | IMX290_PIXEL_RATE, IMX290_PIXEL_RATE, 1, |
| 707 | 1, INT_MAX, 1, 1); | 971 | IMX290_PIXEL_RATE); |
| 708 | 972 | ||
| 709 | v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, | 973 | v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, |
| 710 | V4L2_CID_TEST_PATTERN, | 974 | V4L2_CID_TEST_PATTERN, |
| 711 | ARRAY_SIZE(imx290_test_pattern_menu) - 1, | 975 | ARRAY_SIZE(imx290_test_pattern_menu) - 1, |
| 712 | 0, 0, imx290_test_pattern_menu); | 976 | 0, 0, imx290_test_pattern_menu); |
| 713 | 977 | ||
| 978 | /* | ||
| 979 | * Actual range will be set from imx290_ctrl_update later in the probe. | ||
| 980 | */ | ||
| 714 | imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | 981 | imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 715 | V4L2_CID_HBLANK, 1, 1, 1, 1); | 982 | V4L2_CID_HBLANK, 1, 1, 1, 1); |
| 716 | if (imx290->hblank) | ||
| 717 | imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
| 718 | 983 | ||
| 719 | imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | 984 | imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 720 | V4L2_CID_VBLANK, 1, 1, 1, 1); | 985 | V4L2_CID_VBLANK, 1, 1, 1, 1); |
| 721 | if (imx290->vblank) | 986 | |
| 722 | imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 987 | imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, |
| 988 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
| 989 | imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, | ||
| 990 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
| 991 | v4l2_ctrl_cluster(2, &imx290->hflip); | ||
| 723 | 992 | ||
| 724 | v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, | 993 | v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, |
| 725 | &props); | 994 | &props); |
| @@ -748,17 +1017,37 @@ static int imx290_start_streaming(struct imx290 *imx290, · 22 unmodified lines | |||
| 748 | 1017 | ||
| 749 | /* Set init register settings */ | 1018 | /* Set init register settings */ |
| 750 | ret = imx290_set_register_array(imx290, imx290_global_init_settings, | 1019 | ret = imx290_set_register_array(imx290, imx290_global_init_settings, |
| 751 | ARRAY_SIZE( | 1020 | ARRAY_SIZE(imx290_global_init_settings)); |
| 752 | imx290_global_init_settings)); | ||
| 753 | if (ret < 0) { | 1021 | if (ret < 0) { |
| 754 | dev_err(imx290->dev, "Could not set init registers\n"); | 1022 | dev_err(imx290->dev, "Could not set init registers\n"); |
| 755 | return ret; | 1023 | return ret; |
| 756 | } | 1024 | } |
| 757 | 1025 | ||
| 1026 | /* Set mdel specific init register settings */ | ||
| 1027 | ret = imx290_set_register_array(imx290, imx290->model->init_regs, | ||
| 1028 | imx290->model->init_regs_num); | ||
| 1029 | if (ret < 0) { | ||
| 1030 | dev_err(imx290->dev, "Could not set model specific init registers\n"); | ||
| 1031 | return ret; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /* Set clock parameters based on mode and xclk */ | ||
| 1035 | ret = imx290_set_clock(imx290); | ||
| 1036 | if (ret < 0) { | ||
| 1037 | dev_err(imx290->dev, "Could not set clocks - %d\n", ret); | ||
| 1038 | return ret; | ||
| 1039 | } | ||
| 1040 | |||
| 758 | /* Set data lane count */ | 1041 | /* Set data lane count */ |
| 759 | ret = imx290_set_data_lanes(imx290); | 1042 | ret = imx290_set_data_lanes(imx290); |
| 760 | if (ret < 0) { | 1043 | if (ret < 0) { |
| 761 | dev_err(imx290->dev, "Could not set data lanes\n"); | 1044 | dev_err(imx290->dev, "Could not set data lanes - %d\n", ret); |
| 1045 | return ret; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | ret = imx290_set_csi_config(imx290); | ||
| 1049 | if (ret < 0) { | ||
| 1050 | dev_err(imx290->dev, "Could not set csi cfg - %d\n", ret); | ||
| 762 | return ret; | 1051 | return ret; |
| 763 | } | 1052 | } |
| 764 | 1053 | ||
| @@ -766,7 +1055,7 @@ static int imx290_start_streaming(struct imx290 *imx290, · 1 unmodified line | |||
| 766 | format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); | 1055 | format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); |
| 767 | ret = imx290_setup_format(imx290, format); | 1056 | ret = imx290_setup_format(imx290, format); |
| 768 | if (ret < 0) { | 1057 | if (ret < 0) { |
| 769 | dev_err(imx290->dev, "Could not set frame format\n"); | 1058 | dev_err(imx290->dev, "Could not set frame format - %d\n", ret); |
| 770 | return ret; | 1059 | return ret; |
| 771 | } | 1060 | } |
| 772 | 1061 | ||
| @@ -774,19 +1063,14 @@ static int imx290_start_streaming(struct imx290 *imx290, · 1 unmodified line | |||
| 774 | ret = imx290_set_register_array(imx290, imx290->current_mode->data, | 1063 | ret = imx290_set_register_array(imx290, imx290->current_mode->data, |
| 775 | imx290->current_mode->data_size); | 1064 | imx290->current_mode->data_size); |
| 776 | if (ret < 0) { | 1065 | if (ret < 0) { |
| 777 | dev_err(imx290->dev, "Could not set current mode\n"); | 1066 | dev_err(imx290->dev, "Could not set current mode - %d\n", ret); |
| 778 | return ret; | 1067 | return ret; |
| 779 | } | 1068 | } |
| 780 | 1069 | ||
| 781 | ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, | ||
| 782 | NULL); | ||
| 783 | if (ret) | ||
| 784 | return ret; | ||
| 785 | |||
| 786 | /* Apply customized values from user */ | 1070 | /* Apply customized values from user */ |
| 787 | ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); | 1071 | ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); |
| 788 | if (ret) { | 1072 | if (ret) { |
| 789 | dev_err(imx290->dev, "Could not sync v4l2 controls\n"); | 1073 | dev_err(imx290->dev, "Could not sync v4l2 controls - %d\n", ret); |
| 790 | return ret; | 1074 | return ret; |
| 791 | } | 1075 | } |
| 792 | 1076 | ||
| @@ -835,6 +1119,13 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable) · 42 unmodified lines | |||
| 835 | pm_runtime_put_autosuspend(imx290->dev); | 1119 | pm_runtime_put_autosuspend(imx290->dev); |
| 836 | } | 1120 | } |
| 837 | 1121 | ||
| 1122 | /* | ||
| 1123 | * vflip and hflip should not be changed during streaming as the sensor | ||
| 1124 | * will produce an invalid frame. | ||
| 1125 | */ | ||
| 1126 | __v4l2_ctrl_grab(imx290->vflip, enable); | ||
| 1127 | __v4l2_ctrl_grab(imx290->hflip, enable); | ||
| 1128 | |||
| 838 | unlock: | 1129 | unlock: |
| 839 | v4l2_subdev_unlock_state(state); | 1130 | v4l2_subdev_unlock_state(state); |
| 840 | return ret; | 1131 | return ret; |
| @@ -844,10 +1135,12 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, · 3 unmodified lines | |||
| 844 | struct v4l2_subdev_state *sd_state, | 1135 | struct v4l2_subdev_state *sd_state, |
| 845 | struct v4l2_subdev_mbus_code_enum *code) | 1136 | struct v4l2_subdev_mbus_code_enum *code) |
| 846 | { | 1137 | { |
| 1138 | const struct imx290 *imx290 = to_imx290(sd); | ||
| 1139 | |||
| 847 | if (code->index >= ARRAY_SIZE(imx290_formats)) | 1140 | if (code->index >= ARRAY_SIZE(imx290_formats)) |
| 848 | return -EINVAL; | 1141 | return -EINVAL; |
| 849 | 1142 | ||
| 850 | code->code = imx290_formats[code->index].code; | 1143 | code->code = imx290_formats[code->index].code[imx290->model->colour_variant]; |
| 851 | 1144 | ||
| 852 | return 0; | 1145 | return 0; |
| 853 | } | 1146 | } |
| @@ -859,7 +1152,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd, · 5 unmodified lines | |||
| 859 | const struct imx290 *imx290 = to_imx290(sd); | 1152 | const struct imx290 *imx290 = to_imx290(sd); |
| 860 | const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); | 1153 | const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); |
| 861 | 1154 | ||
| 862 | if (!imx290_format_info(fse->code)) | 1155 | if (!imx290_format_info(imx290, fse->code)) |
| 863 | return -EINVAL; | 1156 | return -EINVAL; |
| 864 | 1157 | ||
| 865 | if (fse->index >= imx290_modes_num(imx290)) | 1158 | if (fse->index >= imx290_modes_num(imx290)) |
| @@ -888,10 +1181,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, · 22 unmodified lines | |||
| 888 | fmt->format.width = mode->width; | 1181 | fmt->format.width = mode->width; |
| 889 | fmt->format.height = mode->height; | 1182 | fmt->format.height = mode->height; |
| 890 | 1183 | ||
| 891 | if (!imx290_format_info(fmt->format.code)) | 1184 | if (!imx290_format_info(imx290, fmt->format.code)) |
| 892 | fmt->format.code = imx290_formats[0].code; | 1185 | fmt->format.code = imx290_formats[0].code[imx290->model->colour_variant]; |
| 893 | 1186 | ||
| 894 | fmt->format.field = V4L2_FIELD_NONE; | 1187 | fmt->format.field = V4L2_FIELD_NONE; |
| 1188 | fmt->format.colorspace = V4L2_COLORSPACE_RAW; | ||
| 1189 | fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; | ||
| 1190 | fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; | ||
| 1191 | fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; | ||
| 895 | 1192 | ||
| 896 | format = v4l2_subdev_get_pad_format(sd, sd_state, 0); | 1193 | format = v4l2_subdev_get_pad_format(sd, sd_state, 0); |
| 897 | 1194 | ||
| @@ -899,6 +1196,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, · 1 unmodified line | |||
| 899 | imx290->current_mode = mode; | 1196 | imx290->current_mode = mode; |
| 900 | 1197 | ||
| 901 | imx290_ctrl_update(imx290, &fmt->format, mode); | 1198 | imx290_ctrl_update(imx290, &fmt->format, mode); |
| 1199 | imx290_exposure_update(imx290, mode); | ||
| 902 | } | 1200 | } |
| 903 | 1201 | ||
| 904 | *format = fmt->format; | 1202 | *format = fmt->format; |
| @@ -910,16 +1208,23 @@ static int imx290_get_selection(struct v4l2_subdev *sd, · 5 unmodified lines | |||
| 910 | struct v4l2_subdev_state *sd_state, | 1208 | struct v4l2_subdev_state *sd_state, |
| 911 | struct v4l2_subdev_selection *sel) | 1209 | struct v4l2_subdev_selection *sel) |
| 912 | { | 1210 | { |
| 1211 | struct imx290 *imx290 = to_imx290(sd); | ||
| 913 | struct v4l2_mbus_framefmt *format; | 1212 | struct v4l2_mbus_framefmt *format; |
| 914 | 1213 | ||
| 915 | switch (sel->target) { | 1214 | switch (sel->target) { |
| 916 | case V4L2_SEL_TGT_CROP: { | 1215 | case V4L2_SEL_TGT_CROP: { |
| 917 | format = v4l2_subdev_get_pad_format(sd, sd_state, 0); | 1216 | format = v4l2_subdev_get_pad_format(sd, sd_state, 0); |
| 918 | 1217 | ||
| 1218 | /* | ||
| 1219 | * The sensor moves the readout by 1 pixel based on flips to | ||
| 1220 | * keep the Bayer order the same. | ||
| 1221 | */ | ||
| 919 | sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP | 1222 | sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP |
| 920 | + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2; | 1223 | + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2 |
| 1224 | + imx290->vflip->val; | ||
| 921 | sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT | 1225 | sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT |
| 922 | + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2; | 1226 | + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2 |
| 1227 | + imx290->hflip->val; | ||
| 923 | sel->r.width = format->width; | 1228 | sel->r.width = format->width; |
| 924 | sel->r.height = format->height; | 1229 | sel->r.height = format->height; |
| 925 | 1230 | ||
| @@ -964,6 +1269,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, · 38 unmodified lines | |||
| 964 | return 0; | 1269 | return 0; |
| 965 | } | 1270 | } |
| 966 | 1271 | ||
| 1272 | static const struct v4l2_subdev_core_ops imx290_core_ops = { | ||
| 1273 | .subscribe_event = v4l2_ctrl_subdev_subscribe_event, | ||
| 1274 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, | ||
| 1275 | }; | ||
| 1276 | |||
| 967 | static const struct v4l2_subdev_video_ops imx290_video_ops = { | 1277 | static const struct v4l2_subdev_video_ops imx290_video_ops = { |
| 968 | .s_stream = imx290_set_stream, | 1278 | .s_stream = imx290_set_stream, |
| 969 | }; | 1279 | }; |
| @@ -978,6 +1288,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = { · 8 unmodified lines | |||
| 978 | }; | 1288 | }; |
| 979 | 1289 | ||
| 980 | static const struct v4l2_subdev_ops imx290_subdev_ops = { | 1290 | static const struct v4l2_subdev_ops imx290_subdev_ops = { |
| 1291 | .core = &imx290_core_ops, | ||
| 981 | .video = &imx290_video_ops, | 1292 | .video = &imx290_video_ops, |
| 982 | .pad = &imx290_pad_ops, | 1293 | .pad = &imx290_pad_ops, |
| 983 | }; | 1294 | }; |
| @@ -996,7 +1307,8 @@ static int imx290_subdev_init(struct imx290 *imx290) · 12 unmodified lines | |||
| 996 | imx290->current_mode = &imx290_modes_ptr(imx290)[0]; | 1307 | imx290->current_mode = &imx290_modes_ptr(imx290)[0]; |
| 997 | 1308 | ||
| 998 | v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); | 1309 | v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); |
| 999 | imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | 1310 | imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | |
| 1311 | V4L2_SUBDEV_FL_HAS_EVENTS; | ||
| 1000 | imx290->sd.dev = imx290->dev; | 1312 | imx290->sd.dev = imx290->dev; |
| 1001 | imx290->sd.entity.ops = &imx290_subdev_entity_ops; | 1313 | imx290->sd.entity.ops = &imx290_subdev_entity_ops; |
| 1002 | imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; | 1314 | imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; |
| @@ -1132,15 +1444,22 @@ static int imx290_init_clk(struct imx290 *imx290) · 129 unmodified lines | |||
| 1132 | u32 xclk_freq; | 1444 | u32 xclk_freq; |
| 1133 | int ret; | 1445 | int ret; |
| 1134 | 1446 | ||
| 1135 | ret = fwnode_property_read_u32(dev_fwnode(imx290->dev), | 1447 | ret = device_property_read_u32(imx290->dev, "clock-frequency", |
| 1136 | "clock-frequency", &xclk_freq); | 1448 | &xclk_freq); |
| 1137 | if (ret) { | 1449 | if (ret) { |
| 1138 | dev_err(imx290->dev, "Could not get xclk frequency\n"); | 1450 | dev_err(imx290->dev, "Could not get xclk frequency\n"); |
| 1139 | return ret; | 1451 | return ret; |
| 1140 | } | 1452 | } |
| 1141 | 1453 | ||
| 1142 | /* external clock must be 37.125 MHz */ | 1454 | /* external clock must be 37.125 MHz or 74.25MHz */ |
| 1143 | if (xclk_freq != 37125000) { | 1455 | switch (xclk_freq) { |
| 1456 | case 37125000: | ||
| 1457 | imx290->xclk_idx = IMX290_CLK_37_125; | ||
| 1458 | break; | ||
| 1459 | case 74250000: | ||
| 1460 | imx290->xclk_idx = IMX290_CLK_74_25; | ||
| 1461 | break; | ||
| 1462 | default: | ||
| 1144 | dev_err(imx290->dev, "External clock frequency %u is not supported\n", | 1463 | dev_err(imx290->dev, "External clock frequency %u is not supported\n", |
| 1145 | xclk_freq); | 1464 | xclk_freq); |
| 1146 | return -EINVAL; | 1465 | return -EINVAL; |
| @@ -1177,6 +1496,27 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290, · 30 unmodified lines | |||
| 1177 | return 0; | 1496 | return 0; |
| 1178 | } | 1497 | } |
| 1179 | 1498 | ||
| 1499 | static const struct imx290_model_info imx290_models[] = { | ||
| 1500 | [IMX290_MODEL_IMX290LQR] = { | ||
| 1501 | .colour_variant = IMX290_VARIANT_COLOUR, | ||
| 1502 | .init_regs = imx290_global_init_settings_290, | ||
| 1503 | .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), | ||
| 1504 | .name = "imx290", | ||
| 1505 | }, | ||
| 1506 | [IMX290_MODEL_IMX290LLR] = { | ||
| 1507 | .colour_variant = IMX290_VARIANT_MONO, | ||
| 1508 | .init_regs = imx290_global_init_settings_290, | ||
| 1509 | .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), | ||
| 1510 | .name = "imx290", | ||
| 1511 | }, | ||
| 1512 | [IMX290_MODEL_IMX327LQR] = { | ||
| 1513 | .colour_variant = IMX290_VARIANT_COLOUR, | ||
| 1514 | .init_regs = imx290_global_init_settings_327, | ||
| 1515 | .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), | ||
| 1516 | .name = "imx327", | ||
| 1517 | }, | ||
| 1518 | }; | ||
| 1519 | |||
| 1180 | static int imx290_parse_dt(struct imx290 *imx290) | 1520 | static int imx290_parse_dt(struct imx290 *imx290) |
| 1181 | { | 1521 | { |
| 1182 | /* Only CSI2 is supported for now: */ | 1522 | /* Only CSI2 is supported for now: */ |
| @@ -1187,6 +1527,8 @@ static int imx290_parse_dt(struct imx290 *imx290) · 4 unmodified lines | |||
| 1187 | int ret; | 1527 | int ret; |
| 1188 | s64 fq; | 1528 | s64 fq; |
| 1189 | 1529 | ||
| 1530 | imx290->model = of_device_get_match_data(imx290->dev); | ||
| 1531 | |||
| 1190 | endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL); | 1532 | endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL); |
| 1191 | if (!endpoint) { | 1533 | if (!endpoint) { |
| 1192 | dev_err(imx290->dev, "Endpoint node not found\n"); | 1534 | dev_err(imx290->dev, "Endpoint node not found\n"); |
| @@ -1260,7 +1602,7 @@ static int imx290_probe(struct i2c_client *client) · 67 unmodified lines | |||
| 1260 | imx290->xclk = devm_clk_get(dev, "xclk"); | 1602 | imx290->xclk = devm_clk_get(dev, "xclk"); |
| 1261 | if (IS_ERR(imx290->xclk)) | 1603 | if (IS_ERR(imx290->xclk)) |
| 1262 | return dev_err_probe(dev, PTR_ERR(imx290->xclk), | 1604 | return dev_err_probe(dev, PTR_ERR(imx290->xclk), |
| 1263 | "Could not get xclk"); | 1605 | "Could not get xclk\n"); |
| 1264 | 1606 | ||
| 1265 | ret = imx290_get_regulators(dev, imx290); | 1607 | ret = imx290_get_regulators(dev, imx290); |
| 1266 | if (ret < 0) | 1608 | if (ret < 0) |
| @@ -1304,6 +1646,9 @@ static int imx290_probe(struct i2c_client *client) · 37 unmodified lines | |||
| 1304 | if (ret) | 1646 | if (ret) |
| 1305 | goto err_pm; | 1647 | goto err_pm; |
| 1306 | 1648 | ||
| 1649 | v4l2_i2c_subdev_set_name(&imx290->sd, client, | ||
| 1650 | imx290->model->name, NULL); | ||
| 1651 | |||
| 1307 | /* | 1652 | /* |
| 1308 | * Finally, register the V4L2 subdev. This must be done after | 1653 | * Finally, register the V4L2 subdev. This must be done after |
| 1309 | * initializing everything as the subdev can be used immediately after | 1654 | * initializing everything as the subdev can be used immediately after |
| @@ -1352,8 +1697,21 @@ static void imx290_remove(struct i2c_client *client) · 42 unmodified lines | |||
| 1352 | } | 1697 | } |
| 1353 | 1698 | ||
| 1354 | static const struct of_device_id imx290_of_match[] = { | 1699 | static const struct of_device_id imx290_of_match[] = { |
| 1355 | { .compatible = "sony,imx290" }, | 1700 | { |
| 1356 | { /* sentinel */ } | 1701 | /* Deprecated - synonym for "sony,imx290lqr" */ |
| 1702 | .compatible = "sony,imx290", | ||
| 1703 | .data = &imx290_models[IMX290_MODEL_IMX290LQR], | ||
| 1704 | }, { | ||
| 1705 | .compatible = "sony,imx290lqr", | ||
| 1706 | .data = &imx290_models[IMX290_MODEL_IMX290LQR], | ||
| 1707 | }, { | ||
| 1708 | .compatible = "sony,imx290llr", | ||
| 1709 | .data = &imx290_models[IMX290_MODEL_IMX290LLR], | ||
| 1710 | }, { | ||
| 1711 | .compatible = "sony,imx327lqr", | ||
| 1712 | .data = &imx290_models[IMX290_MODEL_IMX327LQR], | ||
| 1713 | }, | ||
| 1714 | { /* sentinel */ }, | ||
| 1357 | }; | 1715 | }; |
| 1358 | MODULE_DEVICE_TABLE(of, imx290_of_match); | 1716 | MODULE_DEVICE_TABLE(of, imx290_of_match); |
| 1359 | 1717 | ||
| @@ -685,15 +685,6 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd, · 684 unmodified lines | |||
| 685 | return 0; | 685 | return 0; |
| 686 | } | 686 | } |
| 687 | 687 | ||
| 688 | static int imx296_get_format(struct v4l2_subdev *sd, | ||
| 689 | struct v4l2_subdev_state *state, | ||
| 690 | struct v4l2_subdev_format *fmt) | ||
| 691 | { | ||
| 692 | fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad); | ||
| 693 | |||
| 694 | return 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | static int imx296_set_format(struct v4l2_subdev *sd, | 688 | static int imx296_set_format(struct v4l2_subdev *sd, |
| 698 | struct v4l2_subdev_state *state, | 689 | struct v4l2_subdev_state *state, |
| 699 | struct v4l2_subdev_format *fmt) | 690 | struct v4l2_subdev_format *fmt) |
| @@ -845,7 +836,7 @@ static const struct v4l2_subdev_video_ops imx296_subdev_video_ops = { · 145 unmodified lines | |||
| 845 | static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { | 836 | static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { |
| 846 | .enum_mbus_code = imx296_enum_mbus_code, | 837 | .enum_mbus_code = imx296_enum_mbus_code, |
| 847 | .enum_frame_size = imx296_enum_frame_size, | 838 | .enum_frame_size = imx296_enum_frame_size, |
| 848 | .get_fmt = imx296_get_format, | 839 | .get_fmt = v4l2_subdev_get_fmt, |
| 849 | .set_fmt = imx296_set_format, | 840 | .set_fmt = imx296_set_format, |
| 850 | .get_selection = imx296_get_selection, | 841 | .get_selection = imx296_get_selection, |
| 851 | .set_selection = imx296_set_selection, | 842 | .set_selection = imx296_set_selection, |
| @@ -79,7 +79,6 @@ struct imx334_reg_list { · 78 unmodified lines | |||
| 79 | * struct imx334_mode - imx334 sensor mode structure | 79 | * struct imx334_mode - imx334 sensor mode structure |
| 80 | * @width: Frame width | 80 | * @width: Frame width |
| 81 | * @height: Frame height | 81 | * @height: Frame height |
| 82 | * @code: Format code | ||
| 83 | * @hblank: Horizontal blanking in lines | 82 | * @hblank: Horizontal blanking in lines |
| 84 | * @vblank: Vertical blanking in lines | 83 | * @vblank: Vertical blanking in lines |
| 85 | * @vblank_min: Minimal vertical blanking in lines | 84 | * @vblank_min: Minimal vertical blanking in lines |
| @@ -91,7 +90,6 @@ struct imx334_reg_list { · 5 unmodified lines | |||
| 91 | struct imx334_mode { | 90 | struct imx334_mode { |
| 92 | u32 width; | 91 | u32 width; |
| 93 | u32 height; | 92 | u32 height; |
| 94 | u32 code; | ||
| 95 | u32 hblank; | 93 | u32 hblank; |
| 96 | u32 vblank; | 94 | u32 vblank; |
| 97 | u32 vblank_min; | 95 | u32 vblank_min; |
| @@ -119,6 +117,7 @@ struct imx334_mode { · 21 unmodified lines | |||
| 119 | * @vblank: Vertical blanking in lines | 117 | * @vblank: Vertical blanking in lines |
| 120 | * @cur_mode: Pointer to current selected sensor mode | 118 | * @cur_mode: Pointer to current selected sensor mode |
| 121 | * @mutex: Mutex for serializing sensor controls | 119 | * @mutex: Mutex for serializing sensor controls |
| 120 | * @cur_code: current selected format code | ||
| 122 | * @streaming: Flag indicating streaming state | 121 | * @streaming: Flag indicating streaming state |
| 123 | */ | 122 | */ |
| 124 | struct imx334 { | 123 | struct imx334 { |
| @@ -140,6 +139,7 @@ struct imx334 { · 15 unmodified lines | |||
| 140 | u32 vblank; | 139 | u32 vblank; |
| 141 | const struct imx334_mode *cur_mode; | 140 | const struct imx334_mode *cur_mode; |
| 142 | struct mutex mutex; | 141 | struct mutex mutex; |
| 142 | u32 cur_code; | ||
| 143 | bool streaming; | 143 | bool streaming; |
| 144 | }; | 144 | }; |
| 145 | 145 | ||
| @@ -147,7 +147,170 @@ static const s64 link_freq[] = { · 1 unmodified line | |||
| 147 | IMX334_LINK_FREQ, | 147 | IMX334_LINK_FREQ, |
| 148 | }; | 148 | }; |
| 149 | 149 | ||
| 150 | /* Sensor mode registers */ | 150 | /* Sensor mode registers for 1920x1080@30fps */ |
| 151 | static const struct imx334_reg mode_1920x1080_regs[] = { | ||
| 152 | {0x3000, 0x01}, | ||
| 153 | {0x3018, 0x04}, | ||
| 154 | {0x3030, 0xca}, | ||
| 155 | {0x3031, 0x08}, | ||
| 156 | {0x3032, 0x00}, | ||
| 157 | {0x3034, 0x4c}, | ||
| 158 | {0x3035, 0x04}, | ||
| 159 | {0x302c, 0xf0}, | ||
| 160 | {0x302d, 0x03}, | ||
| 161 | {0x302e, 0x80}, | ||
| 162 | {0x302f, 0x07}, | ||
| 163 | {0x3074, 0xcc}, | ||
| 164 | {0x3075, 0x02}, | ||
| 165 | {0x308e, 0xcd}, | ||
| 166 | {0x308f, 0x02}, | ||
| 167 | {0x3076, 0x38}, | ||
| 168 | {0x3077, 0x04}, | ||
| 169 | {0x3090, 0x38}, | ||
| 170 | {0x3091, 0x04}, | ||
| 171 | {0x3308, 0x38}, | ||
| 172 | {0x3309, 0x04}, | ||
| 173 | {0x30C6, 0x00}, | ||
| 174 | {0x30c7, 0x00}, | ||
| 175 | {0x30ce, 0x00}, | ||
| 176 | {0x30cf, 0x00}, | ||
| 177 | {0x30d8, 0x18}, | ||
| 178 | {0x30d9, 0x0a}, | ||
| 179 | {0x304c, 0x00}, | ||
| 180 | {0x304e, 0x00}, | ||
| 181 | {0x304f, 0x00}, | ||
| 182 | {0x3050, 0x00}, | ||
| 183 | {0x30b6, 0x00}, | ||
| 184 | {0x30b7, 0x00}, | ||
| 185 | {0x3116, 0x08}, | ||
| 186 | {0x3117, 0x00}, | ||
| 187 | {0x31a0, 0x20}, | ||
| 188 | {0x31a1, 0x0f}, | ||
| 189 | {0x300c, 0x3b}, | ||
| 190 | {0x300d, 0x29}, | ||
| 191 | {0x314c, 0x29}, | ||
| 192 | {0x314d, 0x01}, | ||
| 193 | {0x315a, 0x06}, | ||
| 194 | {0x3168, 0xa0}, | ||
| 195 | {0x316a, 0x7e}, | ||
| 196 | {0x319e, 0x02}, | ||
| 197 | {0x3199, 0x00}, | ||
| 198 | {0x319d, 0x00}, | ||
| 199 | {0x31dd, 0x03}, | ||
| 200 | {0x3300, 0x00}, | ||
| 201 | {0x341c, 0xff}, | ||
| 202 | {0x341d, 0x01}, | ||
| 203 | {0x3a01, 0x03}, | ||
| 204 | {0x3a18, 0x7f}, | ||
| 205 | {0x3a19, 0x00}, | ||
| 206 | {0x3a1a, 0x37}, | ||
| 207 | {0x3a1b, 0x00}, | ||
| 208 | {0x3a1c, 0x37}, | ||
| 209 | {0x3a1d, 0x00}, | ||
| 210 | {0x3a1e, 0xf7}, | ||
| 211 | {0x3a1f, 0x00}, | ||
| 212 | {0x3a20, 0x3f}, | ||
| 213 | {0x3a21, 0x00}, | ||
| 214 | {0x3a20, 0x6f}, | ||
| 215 | {0x3a21, 0x00}, | ||
| 216 | {0x3a20, 0x3f}, | ||
| 217 | {0x3a21, 0x00}, | ||
| 218 | {0x3a20, 0x5f}, | ||
| 219 | {0x3a21, 0x00}, | ||
| 220 | {0x3a20, 0x2f}, | ||
| 221 | {0x3a21, 0x00}, | ||
| 222 | {0x3078, 0x02}, | ||
| 223 | {0x3079, 0x00}, | ||
| 224 | {0x307a, 0x00}, | ||
| 225 | {0x307b, 0x00}, | ||
| 226 | {0x3080, 0x02}, | ||
| 227 | {0x3081, 0x00}, | ||
| 228 | {0x3082, 0x00}, | ||
| 229 | {0x3083, 0x00}, | ||
| 230 | {0x3088, 0x02}, | ||
| 231 | {0x3094, 0x00}, | ||
| 232 | {0x3095, 0x00}, | ||
| 233 | {0x3096, 0x00}, | ||
| 234 | {0x309b, 0x02}, | ||
| 235 | {0x309c, 0x00}, | ||
| 236 | {0x309d, 0x00}, | ||
| 237 | {0x309e, 0x00}, | ||
| 238 | {0x30a4, 0x00}, | ||
| 239 | {0x30a5, 0x00}, | ||
| 240 | {0x3288, 0x21}, | ||
| 241 | {0x328a, 0x02}, | ||
| 242 | {0x3414, 0x05}, | ||
| 243 | {0x3416, 0x18}, | ||
| 244 | {0x35Ac, 0x0e}, | ||
| 245 | {0x3648, 0x01}, | ||
| 246 | {0x364a, 0x04}, | ||
| 247 | {0x364c, 0x04}, | ||
| 248 | {0x3678, 0x01}, | ||
| 249 | {0x367c, 0x31}, | ||
| 250 | {0x367e, 0x31}, | ||
| 251 | {0x3708, 0x02}, | ||
| 252 | {0x3714, 0x01}, | ||
| 253 | {0x3715, 0x02}, | ||
| 254 | {0x3716, 0x02}, | ||
| 255 | {0x3717, 0x02}, | ||
| 256 | {0x371c, 0x3d}, | ||
| 257 | {0x371d, 0x3f}, | ||
| 258 | {0x372c, 0x00}, | ||
| 259 | {0x372d, 0x00}, | ||
| 260 | {0x372e, 0x46}, | ||
| 261 | {0x372f, 0x00}, | ||
| 262 | {0x3730, 0x89}, | ||
| 263 | {0x3731, 0x00}, | ||
| 264 | {0x3732, 0x08}, | ||
| 265 | {0x3733, 0x01}, | ||
| 266 | {0x3734, 0xfe}, | ||
| 267 | {0x3735, 0x05}, | ||
| 268 | {0x375d, 0x00}, | ||
| 269 | {0x375e, 0x00}, | ||
| 270 | {0x375f, 0x61}, | ||
| 271 | {0x3760, 0x06}, | ||
| 272 | {0x3768, 0x1b}, | ||
| 273 | {0x3769, 0x1b}, | ||
| 274 | {0x376a, 0x1a}, | ||
| 275 | {0x376b, 0x19}, | ||
| 276 | {0x376c, 0x18}, | ||
| 277 | {0x376d, 0x14}, | ||
| 278 | {0x376e, 0x0f}, | ||
| 279 | {0x3776, 0x00}, | ||
| 280 | {0x3777, 0x00}, | ||
| 281 | {0x3778, 0x46}, | ||
| 282 | {0x3779, 0x00}, | ||
| 283 | {0x377a, 0x08}, | ||
| 284 | {0x377b, 0x01}, | ||
| 285 | {0x377c, 0x45}, | ||
| 286 | {0x377d, 0x01}, | ||
| 287 | {0x377e, 0x23}, | ||
| 288 | {0x377f, 0x02}, | ||
| 289 | {0x3780, 0xd9}, | ||
| 290 | {0x3781, 0x03}, | ||
| 291 | {0x3782, 0xf5}, | ||
| 292 | {0x3783, 0x06}, | ||
| 293 | {0x3784, 0xa5}, | ||
| 294 | {0x3788, 0x0f}, | ||
| 295 | {0x378a, 0xd9}, | ||
| 296 | {0x378b, 0x03}, | ||
| 297 | {0x378c, 0xeb}, | ||
| 298 | {0x378d, 0x05}, | ||
| 299 | {0x378e, 0x87}, | ||
| 300 | {0x378f, 0x06}, | ||
| 301 | {0x3790, 0xf5}, | ||
| 302 | {0x3792, 0x43}, | ||
| 303 | {0x3794, 0x7a}, | ||
| 304 | {0x3796, 0xa1}, | ||
| 305 | {0x37b0, 0x37}, | ||
| 306 | {0x3e04, 0x0e}, | ||
| 307 | {0x30e8, 0x50}, | ||
| 308 | {0x30e9, 0x00}, | ||
| 309 | {0x3e04, 0x0e}, | ||
| 310 | {0x3002, 0x00}, | ||
| 311 | }; | ||
| 312 | |||
| 313 | /* Sensor mode registers for 3840x2160@30fps */ | ||
| 151 | static const struct imx334_reg mode_3840x2160_regs[] = { | 314 | static const struct imx334_reg mode_3840x2160_regs[] = { |
| 152 | {0x3000, 0x01}, | 315 | {0x3000, 0x01}, |
| 153 | {0x3002, 0x00}, | 316 | {0x3002, 0x00}, |
| @@ -166,6 +329,7 @@ static const struct imx334_reg mode_3840x2160_regs[] = { · 12 unmodified lines | |||
| 166 | {0x3288, 0x21}, | 329 | {0x3288, 0x21}, |
| 167 | {0x328a, 0x02}, | 330 | {0x328a, 0x02}, |
| 168 | {0x302c, 0x3c}, | 331 | {0x302c, 0x3c}, |
| 332 | {0x302d, 0x00}, | ||
| 169 | {0x302e, 0x00}, | 333 | {0x302e, 0x00}, |
| 170 | {0x302f, 0x0f}, | 334 | {0x302f, 0x0f}, |
| 171 | {0x3076, 0x70}, | 335 | {0x3076, 0x70}, |
| @@ -240,23 +404,75 @@ static const struct imx334_reg mode_3840x2160_regs[] = { · 68 unmodified lines | |||
| 240 | {0x3794, 0x7a}, | 404 | {0x3794, 0x7a}, |
| 241 | {0x3796, 0xa1}, | 405 | {0x3796, 0xa1}, |
| 242 | {0x3e04, 0x0e}, | 406 | {0x3e04, 0x0e}, |
| 407 | {0x319e, 0x00}, | ||
| 243 | {0x3a00, 0x01}, | 408 | {0x3a00, 0x01}, |
| 409 | {0x3a18, 0xbf}, | ||
| 410 | {0x3a19, 0x00}, | ||
| 411 | {0x3a1a, 0x67}, | ||
| 412 | {0x3a1b, 0x00}, | ||
| 413 | {0x3a1c, 0x6f}, | ||
| 414 | {0x3a1d, 0x00}, | ||
| 415 | {0x3a1e, 0xd7}, | ||
| 416 | {0x3a1f, 0x01}, | ||
| 417 | {0x3a20, 0x6f}, | ||
| 418 | {0x3a21, 0x00}, | ||
| 419 | {0x3a22, 0xcf}, | ||
| 420 | {0x3a23, 0x00}, | ||
| 421 | {0x3a24, 0x6f}, | ||
| 422 | {0x3a25, 0x00}, | ||
| 423 | {0x3a26, 0xb7}, | ||
| 424 | {0x3a27, 0x00}, | ||
| 425 | {0x3a28, 0x5f}, | ||
| 426 | {0x3a29, 0x00}, | ||
| 427 | }; | ||
| 428 | |||
| 429 | static const struct imx334_reg raw10_framefmt_regs[] = { | ||
| 430 | {0x3050, 0x00}, | ||
| 431 | {0x319d, 0x00}, | ||
| 432 | {0x341c, 0xff}, | ||
| 433 | {0x341d, 0x01}, | ||
| 434 | }; | ||
| 435 | |||
| 436 | static const struct imx334_reg raw12_framefmt_regs[] = { | ||
| 437 | {0x3050, 0x01}, | ||
| 438 | {0x319d, 0x01}, | ||
| 439 | {0x341c, 0x47}, | ||
| 440 | {0x341d, 0x00}, | ||
| 441 | }; | ||
| 442 | |||
| 443 | static const u32 imx334_mbus_codes[] = { | ||
| 444 | MEDIA_BUS_FMT_SRGGB12_1X12, | ||
| 445 | MEDIA_BUS_FMT_SRGGB10_1X10, | ||
| 244 | }; | 446 | }; |
| 245 | 447 | ||
| 246 | /* Supported sensor mode configurations */ | 448 | /* Supported sensor mode configurations */ |
| 247 | static const struct imx334_mode supported_mode = { | 449 | static const struct imx334_mode supported_modes[] = { |
| 248 | .width = 3840, | 450 | { |
| 249 | .height = 2160, | 451 | .width = 3840, |
| 250 | .hblank = 560, | 452 | .height = 2160, |
| 251 | .vblank = 2340, | 453 | .hblank = 560, |
| 252 | .vblank_min = 90, | 454 | .vblank = 2340, |
| 253 | .vblank_max = 132840, | 455 | .vblank_min = 90, |
| 254 | .pclk = 594000000, | 456 | .vblank_max = 132840, |
| 255 | .link_freq_idx = 0, | 457 | .pclk = 594000000, |
| 256 | .code = MEDIA_BUS_FMT_SRGGB12_1X12, | 458 | .link_freq_idx = 0, |
| 257 | .reg_list = { | 459 | .reg_list = { |
| 258 | .num_of_regs = ARRAY_SIZE(mode_3840x2160_regs), | 460 | .num_of_regs = ARRAY_SIZE(mode_3840x2160_regs), |
| 259 | .regs = mode_3840x2160_regs, | 461 | .regs = mode_3840x2160_regs, |
| 462 | }, | ||
| 463 | }, { | ||
| 464 | .width = 1920, | ||
| 465 | .height = 1080, | ||
| 466 | .hblank = 2480, | ||
| 467 | .vblank = 1170, | ||
| 468 | .vblank_min = 45, | ||
| 469 | .vblank_max = 132840, | ||
| 470 | .pclk = 297000000, | ||
| 471 | .link_freq_idx = 0, | ||
| 472 | .reg_list = { | ||
| 473 | .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), | ||
| 474 | .regs = mode_1920x1080_regs, | ||
| 475 | }, | ||
| 260 | }, | 476 | }, |
| 261 | }; | 477 | }; |
| 262 | 478 | ||
| @@ -382,7 +598,8 @@ static int imx334_update_controls(struct imx334 *imx334, · 119 unmodified lines | |||
| 382 | if (ret) | 598 | if (ret) |
| 383 | return ret; | 599 | return ret; |
| 384 | 600 | ||
| 385 | ret = __v4l2_ctrl_s_ctrl(imx334->hblank_ctrl, mode->hblank); | 601 | ret = __v4l2_ctrl_modify_range(imx334->hblank_ctrl, mode->hblank, |
| 602 | mode->hblank, 1, mode->hblank); | ||
| 386 | if (ret) | 603 | if (ret) |
| 387 | return ret; | 604 | return ret; |
| 388 | 605 | ||
| @@ -480,6 +697,9 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) · 91 unmodified lines | |||
| 480 | 697 | ||
| 481 | pm_runtime_put(imx334->dev); | 698 | pm_runtime_put(imx334->dev); |
| 482 | 699 | ||
| 700 | break; | ||
| 701 | case V4L2_CID_HBLANK: | ||
| 702 | ret = 0; | ||
| 483 | break; | 703 | break; |
| 484 | default: | 704 | default: |
| 485 | dev_err(imx334->dev, "Invalid control %d", ctrl->id); | 705 | dev_err(imx334->dev, "Invalid control %d", ctrl->id); |
| @@ -494,6 +714,18 @@ static const struct v4l2_ctrl_ops imx334_ctrl_ops = { · 8 unmodified lines | |||
| 494 | .s_ctrl = imx334_set_ctrl, | 714 | .s_ctrl = imx334_set_ctrl, |
| 495 | }; | 715 | }; |
| 496 | 716 | ||
| 717 | static int imx334_get_format_code(struct imx334 *imx334, u32 code) | ||
| 718 | { | ||
| 719 | unsigned int i; | ||
| 720 | |||
| 721 | for (i = 0; i < ARRAY_SIZE(imx334_mbus_codes); i++) { | ||
| 722 | if (imx334_mbus_codes[i] == code) | ||
| 723 | return imx334_mbus_codes[i]; | ||
| 724 | } | ||
| 725 | |||
| 726 | return imx334_mbus_codes[0]; | ||
| 727 | } | ||
| 728 | |||
| 497 | /** | 729 | /** |
| 498 | * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes | 730 | * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes |
| 499 | * @sd: pointer to imx334 V4L2 sub-device structure | 731 | * @sd: pointer to imx334 V4L2 sub-device structure |
| @@ -506,10 +738,10 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, · 6 unmodified lines | |||
| 506 | struct v4l2_subdev_state *sd_state, | 738 | struct v4l2_subdev_state *sd_state, |
| 507 | struct v4l2_subdev_mbus_code_enum *code) | 739 | struct v4l2_subdev_mbus_code_enum *code) |
| 508 | { | 740 | { |
| 509 | if (code->index > 0) | 741 | if (code->index >= ARRAY_SIZE(imx334_mbus_codes)) |
| 510 | return -EINVAL; | 742 | return -EINVAL; |
| 511 | 743 | ||
| 512 | code->code = supported_mode.code; | 744 | code->code = imx334_mbus_codes[code->index]; |
| 513 | 745 | ||
| 514 | return 0; | 746 | return 0; |
| 515 | } | 747 | } |
| @@ -526,15 +758,20 @@ static int imx334_enum_frame_size(struct v4l2_subdev *sd, · 10 unmodified lines | |||
| 526 | struct v4l2_subdev_state *sd_state, | 758 | struct v4l2_subdev_state *sd_state, |
| 527 | struct v4l2_subdev_frame_size_enum *fsize) | 759 | struct v4l2_subdev_frame_size_enum *fsize) |
| 528 | { | 760 | { |
| 529 | if (fsize->index > 0) | 761 | struct imx334 *imx334 = to_imx334(sd); |
| 762 | u32 code; | ||
| 763 | |||
| 764 | if (fsize->index >= ARRAY_SIZE(supported_modes)) | ||
| 530 | return -EINVAL; | 765 | return -EINVAL; |
| 531 | 766 | ||
| 532 | if (fsize->code != supported_mode.code) | 767 | code = imx334_get_format_code(imx334, fsize->code); |
| 768 | |||
| 769 | if (fsize->code != code) | ||
| 533 | return -EINVAL; | 770 | return -EINVAL; |
| 534 | 771 | ||
| 535 | fsize->min_width = supported_mode.width; | 772 | fsize->min_width = supported_modes[fsize->index].width; |
| 536 | fsize->max_width = fsize->min_width; | 773 | fsize->max_width = fsize->min_width; |
| 537 | fsize->min_height = supported_mode.height; | 774 | fsize->min_height = supported_modes[fsize->index].height; |
| 538 | fsize->max_height = fsize->min_height; | 775 | fsize->max_height = fsize->min_height; |
| 539 | 776 | ||
| 540 | return 0; | 777 | return 0; |
| @@ -553,7 +790,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334, · 12 unmodified lines | |||
| 553 | { | 790 | { |
| 554 | fmt->format.width = mode->width; | 791 | fmt->format.width = mode->width; |
| 555 | fmt->format.height = mode->height; | 792 | fmt->format.height = mode->height; |
| 556 | fmt->format.code = mode->code; | ||
| 557 | fmt->format.field = V4L2_FIELD_NONE; | 793 | fmt->format.field = V4L2_FIELD_NONE; |
| 558 | fmt->format.colorspace = V4L2_COLORSPACE_RAW; | 794 | fmt->format.colorspace = V4L2_COLORSPACE_RAW; |
| 559 | fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; | 795 | fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; |
| @@ -583,6 +819,7 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, · 23 unmodified lines | |||
| 583 | framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); | 819 | framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); |
| 584 | fmt->format = *framefmt; | 820 | fmt->format = *framefmt; |
| 585 | } else { | 821 | } else { |
| 822 | fmt->format.code = imx334->cur_code; | ||
| 586 | imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); | 823 | imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); |
| 587 | } | 824 | } |
| 588 | 825 | ||
| @@ -609,15 +846,21 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, · 20 unmodified lines | |||
| 609 | 846 | ||
| 610 | mutex_lock(&imx334->mutex); | 847 | mutex_lock(&imx334->mutex); |
| 611 | 848 | ||
| 612 | mode = &supported_mode; | 849 | mode = v4l2_find_nearest_size(supported_modes, |
| 850 | ARRAY_SIZE(supported_modes), | ||
| 851 | width, height, | ||
| 852 | fmt->format.width, fmt->format.height); | ||
| 853 | |||
| 613 | imx334_fill_pad_format(imx334, mode, fmt); | 854 | imx334_fill_pad_format(imx334, mode, fmt); |
| 855 | fmt->format.code = imx334_get_format_code(imx334, fmt->format.code); | ||
| 614 | 856 | ||
| 615 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | 857 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { |
| 616 | struct v4l2_mbus_framefmt *framefmt; | 858 | struct v4l2_mbus_framefmt *framefmt; |
| 617 | 859 | ||
| 618 | framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); | 860 | framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); |
| 619 | *framefmt = fmt->format; | 861 | *framefmt = fmt->format; |
| 620 | } else { | 862 | } else if (imx334->cur_mode != mode || imx334->cur_code != fmt->format.code) { |
| 863 | imx334->cur_code = fmt->format.code; | ||
| 621 | ret = imx334_update_controls(imx334, mode); | 864 | ret = imx334_update_controls(imx334, mode); |
| 622 | if (!ret) | 865 | if (!ret) |
| 623 | imx334->cur_mode = mode; | 866 | imx334->cur_mode = mode; |
| @@ -642,11 +885,26 @@ static int imx334_init_pad_cfg(struct v4l2_subdev *sd, · 18 unmodified lines | |||
| 642 | struct v4l2_subdev_format fmt = { 0 }; | 885 | struct v4l2_subdev_format fmt = { 0 }; |
| 643 | 886 | ||
| 644 | fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | 887 | fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; |
| 645 | imx334_fill_pad_format(imx334, &supported_mode, &fmt); | 888 | imx334_fill_pad_format(imx334, &supported_modes[0], &fmt); |
| 646 | 889 | ||
| 647 | return imx334_set_pad_format(sd, sd_state, &fmt); | 890 | return imx334_set_pad_format(sd, sd_state, &fmt); |
| 648 | } | 891 | } |
| 649 | 892 | ||
| 893 | static int imx334_set_framefmt(struct imx334 *imx334) | ||
| 894 | { | ||
| 895 | switch (imx334->cur_code) { | ||
| 896 | case MEDIA_BUS_FMT_SRGGB10_1X10: | ||
| 897 | return imx334_write_regs(imx334, raw10_framefmt_regs, | ||
| 898 | ARRAY_SIZE(raw10_framefmt_regs)); | ||
| 899 | |||
| 900 | case MEDIA_BUS_FMT_SRGGB12_1X12: | ||
| 901 | return imx334_write_regs(imx334, raw12_framefmt_regs, | ||
| 902 | ARRAY_SIZE(raw12_framefmt_regs)); | ||
| 903 | } | ||
| 904 | |||
| 905 | return -EINVAL; | ||
| 906 | } | ||
| 907 | |||
| 650 | /** | 908 | /** |
| 651 | * imx334_start_streaming() - Start sensor stream | 909 | * imx334_start_streaming() - Start sensor stream |
| 652 | * @imx334: pointer to imx334 device | 910 | * @imx334: pointer to imx334 device |
| @@ -667,6 +925,13 @@ static int imx334_start_streaming(struct imx334 *imx334) · 14 unmodified lines | |||
| 667 | return ret; | 925 | return ret; |
| 668 | } | 926 | } |
| 669 | 927 | ||
| 928 | ret = imx334_set_framefmt(imx334); | ||
| 929 | if (ret) { | ||
| 930 | dev_err(imx334->dev, "%s failed to set frame format: %d\n", | ||
| 931 | __func__, ret); | ||
| 932 | return ret; | ||
| 933 | } | ||
| 934 | |||
| 670 | /* Setup handler will write actual exposure and gain */ | 935 | /* Setup handler will write actual exposure and gain */ |
| 671 | ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); | 936 | ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); |
| 672 | if (ret) { | 937 | if (ret) { |
| @@ -1037,7 +1302,8 @@ static int imx334_probe(struct i2c_client *client) · 364 unmodified lines | |||
| 1037 | } | 1302 | } |
| 1038 | 1303 | ||
| 1039 | /* Set default mode to max resolution */ | 1304 | /* Set default mode to max resolution */ |
| 1040 | imx334->cur_mode = &supported_mode; | 1305 | imx334->cur_mode = &supported_modes[0]; |
| 1306 | imx334->cur_code = imx334_mbus_codes[0]; | ||
| 1041 | imx334->vblank = imx334->cur_mode->vblank; | 1307 | imx334->vblank = imx334->cur_mode->vblank; |
| 1042 | 1308 | ||
| 1043 | ret = imx334_init_controls(imx334); | 1309 | ret = imx334_init_controls(imx334); |
| @@ -1,8 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | config VIDEO_M5MOLS | ||
| 3 | tristate "Fujitsu M-5MOLS 8MP sensor support" | ||
| 4 | depends on I2C && VIDEO_DEV | ||
| 5 | select MEDIA_CONTROLLER | ||
| 6 | select VIDEO_V4L2_SUBDEV_API | ||
| 7 | help | ||
| 8 | This driver supports Fujitsu M-5MOLS camera sensor with ISP | ||
| @@ -1,4 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o | ||
| 3 | |||
| 4 | obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o | ||
| @@ -1,349 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
| 2 | /* | ||
| 3 | * Header for M-5MOLS 8M Pixel camera sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
| 6 | * Author: HeungJun Kim <riverful.kim@samsung.com> | ||
| 7 | * | ||
| 8 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
| 9 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef M5MOLS_H | ||
| 13 | #define M5MOLS_H | ||
| 14 | |||
| 15 | #include <linux/sizes.h> | ||
| 16 | #include <linux/gpio/consumer.h> | ||
| 17 | #include <media/v4l2-subdev.h> | ||
| 18 | #include "m5mols_reg.h" | ||
| 19 | |||
| 20 | |||
| 21 | /* An amount of data transmitted in addition to the value | ||
| 22 | * determined by CAPP_JPEG_SIZE_MAX register. | ||
| 23 | */ | ||
| 24 | #define M5MOLS_JPEG_TAGS_SIZE 0x20000 | ||
| 25 | #define M5MOLS_MAIN_JPEG_SIZE_MAX (5 * SZ_1M) | ||
| 26 | |||
| 27 | extern int m5mols_debug; | ||
| 28 | |||
| 29 | enum m5mols_restype { | ||
| 30 | M5MOLS_RESTYPE_MONITOR, | ||
| 31 | M5MOLS_RESTYPE_CAPTURE, | ||
| 32 | M5MOLS_RESTYPE_MAX, | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * struct m5mols_resolution - structure for the resolution | ||
| 37 | * @type: resolution type according to the pixel code | ||
| 38 | * @width: width of the resolution | ||
| 39 | * @height: height of the resolution | ||
| 40 | * @reg: resolution preset register value | ||
| 41 | */ | ||
| 42 | struct m5mols_resolution { | ||
| 43 | u8 reg; | ||
| 44 | enum m5mols_restype type; | ||
| 45 | u16 width; | ||
| 46 | u16 height; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * struct m5mols_exif - structure for the EXIF information of M-5MOLS | ||
| 51 | * @exposure_time: exposure time register value | ||
| 52 | * @shutter_speed: speed of the shutter register value | ||
| 53 | * @aperture: aperture register value | ||
| 54 | * @brightness: brightness register value | ||
| 55 | * @exposure_bias: it calls also EV bias | ||
| 56 | * @iso_speed: ISO register value | ||
| 57 | * @flash: status register value of the flash | ||
| 58 | * @sdr: status register value of the Subject Distance Range | ||
| 59 | * @qval: not written exact meaning in document | ||
| 60 | */ | ||
| 61 | struct m5mols_exif { | ||
| 62 | u32 exposure_time; | ||
| 63 | u32 shutter_speed; | ||
| 64 | u32 aperture; | ||
| 65 | u32 brightness; | ||
| 66 | u32 exposure_bias; | ||
| 67 | u16 iso_speed; | ||
| 68 | u16 flash; | ||
| 69 | u16 sdr; | ||
| 70 | u16 qval; | ||
| 71 | }; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * struct m5mols_capture - Structure for the capture capability | ||
| 75 | * @exif: EXIF information | ||
| 76 | * @buf_size: internal JPEG frame buffer size, in bytes | ||
| 77 | * @main: size in bytes of the main image | ||
| 78 | * @thumb: size in bytes of the thumb image, if it was accompanied | ||
| 79 | * @total: total size in bytes of the produced image | ||
| 80 | */ | ||
| 81 | struct m5mols_capture { | ||
| 82 | struct m5mols_exif exif; | ||
| 83 | unsigned int buf_size; | ||
| 84 | u32 main; | ||
| 85 | u32 thumb; | ||
| 86 | u32 total; | ||
| 87 | }; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * struct m5mols_scenemode - structure for the scenemode capability | ||
| 91 | * @metering: metering light register value | ||
| 92 | * @ev_bias: EV bias register value | ||
| 93 | * @wb_mode: mode which means the WhiteBalance is Auto or Manual | ||
| 94 | * @wb_preset: whitebalance preset register value in the Manual mode | ||
| 95 | * @chroma_en: register value whether the Chroma capability is enabled or not | ||
| 96 | * @chroma_lvl: chroma's level register value | ||
| 97 | * @edge_en: register value Whether the Edge capability is enabled or not | ||
| 98 | * @edge_lvl: edge's level register value | ||
| 99 | * @af_range: Auto Focus's range | ||
| 100 | * @fd_mode: Face Detection mode | ||
| 101 | * @mcc: Multi-axis Color Conversion which means emotion color | ||
| 102 | * @light: status of the Light | ||
| 103 | * @flash: status of the Flash | ||
| 104 | * @tone: Tone color which means Contrast | ||
| 105 | * @iso: ISO register value | ||
| 106 | * @capt_mode: Mode of the Image Stabilization while the camera capturing | ||
| 107 | * @wdr: Wide Dynamic Range register value | ||
| 108 | * | ||
| 109 | * The each value according to each scenemode is recommended in the documents. | ||
| 110 | */ | ||
| 111 | struct m5mols_scenemode { | ||
| 112 | u8 metering; | ||
| 113 | u8 ev_bias; | ||
| 114 | u8 wb_mode; | ||
| 115 | u8 wb_preset; | ||
| 116 | u8 chroma_en; | ||
| 117 | u8 chroma_lvl; | ||
| 118 | u8 edge_en; | ||
| 119 | u8 edge_lvl; | ||
| 120 | u8 af_range; | ||
| 121 | u8 fd_mode; | ||
| 122 | u8 mcc; | ||
| 123 | u8 light; | ||
| 124 | u8 flash; | ||
| 125 | u8 tone; | ||
| 126 | u8 iso; | ||
| 127 | u8 capt_mode; | ||
| 128 | u8 wdr; | ||
| 129 | }; | ||
| 130 | |||
| 131 | #define VERSION_STRING_SIZE 22 | ||
| 132 | |||
| 133 | /** | ||
| 134 | * struct m5mols_version - firmware version information | ||
| 135 | * @customer: customer information | ||
| 136 | * @project: version of project information according to customer | ||
| 137 | * @fw: firmware revision | ||
| 138 | * @hw: hardware revision | ||
| 139 | * @param: version of the parameter | ||
| 140 | * @awb: Auto WhiteBalance algorithm version | ||
| 141 | * @str: information about manufacturer and packaging vendor | ||
| 142 | * @af: Auto Focus version | ||
| 143 | * | ||
| 144 | * The register offset starts the customer version at 0x0, and it ends | ||
| 145 | * the awb version at 0x09. The customer, project information occupies 1 bytes | ||
| 146 | * each. And also the fw, hw, param, awb each requires 2 bytes. The str is | ||
| 147 | * unique string associated with firmware's version. It includes information | ||
| 148 | * about manufacturer and the vendor of the sensor's packaging. The least | ||
| 149 | * significant 2 bytes of the string indicate packaging manufacturer. | ||
| 150 | */ | ||
| 151 | struct m5mols_version { | ||
| 152 | u8 customer; | ||
| 153 | u8 project; | ||
| 154 | u16 fw; | ||
| 155 | u16 hw; | ||
| 156 | u16 param; | ||
| 157 | u16 awb; | ||
| 158 | u8 str[VERSION_STRING_SIZE]; | ||
| 159 | u8 af; | ||
| 160 | }; | ||
| 161 | |||
| 162 | /** | ||
| 163 | * struct m5mols_info - M-5MOLS driver data structure | ||
| 164 | * @pdata: platform data | ||
| 165 | * @sd: v4l-subdev instance | ||
| 166 | * @pad: media pad | ||
| 167 | * @irq_waitq: waitqueue for the capture | ||
| 168 | * @irq_done: set to 1 in the interrupt handler | ||
| 169 | * @handle: control handler | ||
| 170 | * @auto_exposure: auto/manual exposure control | ||
| 171 | * @exposure_bias: exposure compensation control | ||
| 172 | * @exposure: manual exposure control | ||
| 173 | * @metering: exposure metering control | ||
| 174 | * @auto_iso: auto/manual ISO sensitivity control | ||
| 175 | * @iso: manual ISO sensitivity control | ||
| 176 | * @auto_wb: auto white balance control | ||
| 177 | * @lock_3a: 3A lock control | ||
| 178 | * @colorfx: color effect control | ||
| 179 | * @saturation: saturation control | ||
| 180 | * @zoom: zoom control | ||
| 181 | * @wdr: wide dynamic range control | ||
| 182 | * @stabilization: image stabilization control | ||
| 183 | * @jpeg_quality: JPEG compression quality control | ||
| 184 | * @set_power: optional power callback to the board code | ||
| 185 | * @reset: GPIO driving the reset pin of M-5MOLS | ||
| 186 | * @lock: mutex protecting the structure fields below | ||
| 187 | * @ffmt: current fmt according to resolution type | ||
| 188 | * @res_type: current resolution type | ||
| 189 | * @ver: information of the version | ||
| 190 | * @cap: the capture mode attributes | ||
| 191 | * @isp_ready: 1 when the ISP controller has completed booting | ||
| 192 | * @power: current sensor's power status | ||
| 193 | * @ctrl_sync: 1 when the control handler state is restored in H/W | ||
| 194 | * @resolution: register value for current resolution | ||
| 195 | * @mode: register value for current operation mode | ||
| 196 | */ | ||
| 197 | struct m5mols_info { | ||
| 198 | const struct m5mols_platform_data *pdata; | ||
| 199 | struct v4l2_subdev sd; | ||
| 200 | struct media_pad pad; | ||
| 201 | |||
| 202 | wait_queue_head_t irq_waitq; | ||
| 203 | atomic_t irq_done; | ||
| 204 | |||
| 205 | struct v4l2_ctrl_handler handle; | ||
| 206 | struct { | ||
| 207 | /* exposure/exposure bias/auto exposure cluster */ | ||
| 208 | struct v4l2_ctrl *auto_exposure; | ||
| 209 | struct v4l2_ctrl *exposure_bias; | ||
| 210 | struct v4l2_ctrl *exposure; | ||
| 211 | struct v4l2_ctrl *metering; | ||
| 212 | }; | ||
| 213 | struct { | ||
| 214 | /* iso/auto iso cluster */ | ||
| 215 | struct v4l2_ctrl *auto_iso; | ||
| 216 | struct v4l2_ctrl *iso; | ||
| 217 | }; | ||
| 218 | struct v4l2_ctrl *auto_wb; | ||
| 219 | |||
| 220 | struct v4l2_ctrl *lock_3a; | ||
| 221 | struct v4l2_ctrl *colorfx; | ||
| 222 | struct v4l2_ctrl *saturation; | ||
| 223 | struct v4l2_ctrl *zoom; | ||
| 224 | struct v4l2_ctrl *wdr; | ||
| 225 | struct v4l2_ctrl *stabilization; | ||
| 226 | struct v4l2_ctrl *jpeg_quality; | ||
| 227 | |||
| 228 | int (*set_power)(struct device *dev, int on); | ||
| 229 | struct gpio_desc *reset; | ||
| 230 | |||
| 231 | struct mutex lock; | ||
| 232 | |||
| 233 | struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; | ||
| 234 | int res_type; | ||
| 235 | |||
| 236 | struct m5mols_version ver; | ||
| 237 | struct m5mols_capture cap; | ||
| 238 | |||
| 239 | unsigned int isp_ready:1; | ||
| 240 | unsigned int power:1; | ||
| 241 | unsigned int ctrl_sync:1; | ||
| 242 | |||
| 243 | u8 resolution; | ||
| 244 | u8 mode; | ||
| 245 | }; | ||
| 246 | |||
| 247 | #define is_available_af(__info) (__info->ver.af) | ||
| 248 | #define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) | ||
| 249 | #define is_manufacturer(__info, __manufacturer) \ | ||
| 250 | (__info->ver.str[0] == __manufacturer[0] && \ | ||
| 251 | __info->ver.str[1] == __manufacturer[1]) | ||
| 252 | /* | ||
| 253 | * I2C operation of the M-5MOLS | ||
| 254 | * | ||
| 255 | * The I2C read operation of the M-5MOLS requires 2 messages. The first | ||
| 256 | * message sends the information about the command, command category, and total | ||
| 257 | * message size. The second message is used to retrieve the data specified in | ||
| 258 | * the first message | ||
| 259 | * | ||
| 260 | * 1st message 2nd message | ||
| 261 | * +-------+---+----------+-----+-------+ +------+------+------+------+ | ||
| 262 | * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | | ||
| 263 | * +-------+---+----------+-----+-------+ +------+------+------+------+ | ||
| 264 | * - size1: message data size(5 in this case) | ||
| 265 | * - size2: desired buffer size of the 2nd message | ||
| 266 | * - d[0..3]: according to size2 | ||
| 267 | * | ||
| 268 | * The I2C write operation needs just one message. The message includes | ||
| 269 | * category, command, total size, and desired data. | ||
| 270 | * | ||
| 271 | * 1st message | ||
| 272 | * +-------+---+----------+-----+------+------+------+------+ | ||
| 273 | * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | | ||
| 274 | * +-------+---+----------+-----+------+------+------+------+ | ||
| 275 | * - d[0..3]: according to size1 | ||
| 276 | */ | ||
| 277 | int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); | ||
| 278 | int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); | ||
| 279 | int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); | ||
| 280 | int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); | ||
| 281 | |||
| 282 | int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, | ||
| 283 | int timeout); | ||
| 284 | |||
| 285 | /* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ | ||
| 286 | #define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) | ||
| 287 | /* ISP state transition timeout, in ms */ | ||
| 288 | #define M5MOLS_MODE_CHANGE_TIMEOUT 200 | ||
| 289 | #define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 | ||
| 290 | |||
| 291 | /* | ||
| 292 | * Mode operation of the M-5MOLS | ||
| 293 | * | ||
| 294 | * Changing the mode of the M-5MOLS is needed right executing order. | ||
| 295 | * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed | ||
| 296 | * by user. There are various categories associated with each mode. | ||
| 297 | * | ||
| 298 | * +============================================================+ | ||
| 299 | * | mode | category | | ||
| 300 | * +============================================================+ | ||
| 301 | * | FLASH | FLASH(only after Stand-by or Power-on) | | ||
| 302 | * | SYSTEM | SYSTEM(only after sensor arm-booting) | | ||
| 303 | * | PARAMETER | PARAMETER | | ||
| 304 | * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | | ||
| 305 | * | CAPTURE | Single CAPTURE, Preview(recording) | | ||
| 306 | * +============================================================+ | ||
| 307 | * | ||
| 308 | * The available executing order between each modes are as follows: | ||
| 309 | * PARAMETER <---> MONITOR <---> CAPTURE | ||
| 310 | */ | ||
| 311 | int m5mols_set_mode(struct m5mols_info *info, u8 mode); | ||
| 312 | |||
| 313 | int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); | ||
| 314 | int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); | ||
| 315 | int m5mols_restore_controls(struct m5mols_info *info); | ||
| 316 | int m5mols_start_capture(struct m5mols_info *info); | ||
| 317 | int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); | ||
| 318 | int m5mols_lock_3a(struct m5mols_info *info, bool lock); | ||
| 319 | int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); | ||
| 320 | int m5mols_init_controls(struct v4l2_subdev *sd); | ||
| 321 | |||
| 322 | /* The firmware function */ | ||
| 323 | int m5mols_update_fw(struct v4l2_subdev *sd, | ||
| 324 | int (*set_power)(struct m5mols_info *, bool)); | ||
| 325 | |||
| 326 | static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) | ||
| 327 | { | ||
| 328 | return container_of(subdev, struct m5mols_info, sd); | ||
| 329 | } | ||
| 330 | |||
| 331 | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | ||
| 332 | { | ||
| 333 | struct m5mols_info *info = container_of(ctrl->handler, | ||
| 334 | struct m5mols_info, handle); | ||
| 335 | return &info->sd; | ||
| 336 | } | ||
| 337 | |||
| 338 | static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, | ||
| 339 | unsigned int mode) | ||
| 340 | { | ||
| 341 | ctrl->priv = (void *)(uintptr_t)mode; | ||
| 342 | } | ||
| 343 | |||
| 344 | static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) | ||
| 345 | { | ||
| 346 | return (unsigned int)(uintptr_t)ctrl->priv; | ||
| 347 | } | ||
| 348 | |||
| 349 | #endif /* M5MOLS_H */ | ||
| @@ -1,158 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | |||
| 3 | /* | ||
| 4 | * The Capture code for Fujitsu M-5MOLS ISP | ||
| 5 | * | ||
| 6 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
| 7 | * Author: HeungJun Kim <riverful.kim@samsung.com> | ||
| 8 | * | ||
| 9 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
| 10 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/i2c.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/irq.h> | ||
| 16 | #include <linux/interrupt.h> | ||
| 17 | #include <linux/delay.h> | ||
| 18 | #include <linux/regulator/consumer.h> | ||
| 19 | #include <linux/videodev2.h> | ||
| 20 | #include <media/v4l2-ctrls.h> | ||
| 21 | #include <media/v4l2-device.h> | ||
| 22 | #include <media/v4l2-subdev.h> | ||
| 23 | #include <media/i2c/m5mols.h> | ||
| 24 | #include <media/drv-intf/exynos-fimc.h> | ||
| 25 | |||
| 26 | #include "m5mols.h" | ||
| 27 | #include "m5mols_reg.h" | ||
| 28 | |||
| 29 | /** | ||
| 30 | * m5mols_read_rational - I2C read of a rational number | ||
| 31 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 32 | * @addr_num: numerator register | ||
| 33 | * @addr_den: denominator register | ||
| 34 | * @val: place to store the division result | ||
| 35 | * | ||
| 36 | * Read numerator and denominator from registers @addr_num and @addr_den | ||
| 37 | * respectively and return the division result in @val. | ||
| 38 | */ | ||
| 39 | static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, | ||
| 40 | u32 addr_den, u32 *val) | ||
| 41 | { | ||
| 42 | u32 num, den; | ||
| 43 | |||
| 44 | int ret = m5mols_read_u32(sd, addr_num, &num); | ||
| 45 | if (!ret) | ||
| 46 | ret = m5mols_read_u32(sd, addr_den, &den); | ||
| 47 | if (ret) | ||
| 48 | return ret; | ||
| 49 | *val = den == 0 ? 0 : num / den; | ||
| 50 | return ret; | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * m5mols_capture_info - Gather captured image information | ||
| 55 | * @info: M-5MOLS driver data structure | ||
| 56 | * | ||
| 57 | * For now it gathers only EXIF information and file size. | ||
| 58 | */ | ||
| 59 | static int m5mols_capture_info(struct m5mols_info *info) | ||
| 60 | { | ||
| 61 | struct m5mols_exif *exif = &info->cap.exif; | ||
| 62 | struct v4l2_subdev *sd = &info->sd; | ||
| 63 | int ret; | ||
| 64 | |||
| 65 | ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, | ||
| 66 | EXIF_INFO_EXPTIME_DE, &exif->exposure_time); | ||
| 67 | if (ret) | ||
| 68 | return ret; | ||
| 69 | ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, | ||
| 70 | &exif->shutter_speed); | ||
| 71 | if (ret) | ||
| 72 | return ret; | ||
| 73 | ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, | ||
| 74 | &exif->aperture); | ||
| 75 | if (ret) | ||
| 76 | return ret; | ||
| 77 | ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, | ||
| 78 | &exif->brightness); | ||
| 79 | if (ret) | ||
| 80 | return ret; | ||
| 81 | ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, | ||
| 82 | &exif->exposure_bias); | ||
| 83 | if (ret) | ||
| 84 | return ret; | ||
| 85 | |||
| 86 | ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); | ||
| 87 | if (!ret) | ||
| 88 | ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); | ||
| 89 | if (!ret) | ||
| 90 | ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); | ||
| 91 | if (!ret) | ||
| 92 | ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); | ||
| 93 | if (ret) | ||
| 94 | return ret; | ||
| 95 | |||
| 96 | if (!ret) | ||
| 97 | ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); | ||
| 98 | if (!ret) | ||
| 99 | ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); | ||
| 100 | if (!ret) | ||
| 101 | info->cap.total = info->cap.main + info->cap.thumb; | ||
| 102 | |||
| 103 | return ret; | ||
| 104 | } | ||
| 105 | |||
| 106 | int m5mols_start_capture(struct m5mols_info *info) | ||
| 107 | { | ||
| 108 | unsigned int framesize = info->cap.buf_size - M5MOLS_JPEG_TAGS_SIZE; | ||
| 109 | struct v4l2_subdev *sd = &info->sd; | ||
| 110 | int ret; | ||
| 111 | |||
| 112 | /* | ||
| 113 | * Synchronize the controls, set the capture frame resolution and color | ||
| 114 | * format. The frame capture is initiated during switching from Monitor | ||
| 115 | * to Capture mode. | ||
| 116 | */ | ||
| 117 | ret = m5mols_set_mode(info, REG_MONITOR); | ||
| 118 | if (!ret) | ||
| 119 | ret = m5mols_restore_controls(info); | ||
| 120 | if (!ret) | ||
| 121 | ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); | ||
| 122 | if (!ret) | ||
| 123 | ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); | ||
| 124 | if (!ret) | ||
| 125 | ret = m5mols_write(sd, CAPP_JPEG_SIZE_MAX, framesize); | ||
| 126 | if (!ret) | ||
| 127 | ret = m5mols_set_mode(info, REG_CAPTURE); | ||
| 128 | if (!ret) | ||
| 129 | /* Wait until a frame is captured to ISP internal memory */ | ||
| 130 | ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); | ||
| 131 | if (ret) | ||
| 132 | return ret; | ||
| 133 | |||
| 134 | /* | ||
| 135 | * Initiate the captured data transfer to a MIPI-CSI receiver. | ||
| 136 | */ | ||
| 137 | ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); | ||
| 138 | if (!ret) | ||
| 139 | ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); | ||
| 140 | if (!ret) { | ||
| 141 | bool captured = false; | ||
| 142 | unsigned int size; | ||
| 143 | |||
| 144 | /* Wait for the capture completion interrupt */ | ||
| 145 | ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); | ||
| 146 | if (!ret) { | ||
| 147 | captured = true; | ||
| 148 | ret = m5mols_capture_info(info); | ||
| 149 | } | ||
| 150 | size = captured ? info->cap.main : 0; | ||
| 151 | v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", | ||
| 152 | __func__, size, info->cap.thumb); | ||
| 153 | |||
| 154 | v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); | ||
| 155 | } | ||
| 156 | |||
| 157 | return ret; | ||
| 158 | } | ||
| @@ -1,625 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /* | ||
| 3 | * Controls for M-5MOLS 8M Pixel camera sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
| 6 | * Author: HeungJun Kim <riverful.kim@samsung.com> | ||
| 7 | * | ||
| 8 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
| 9 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/i2c.h> | ||
| 13 | #include <linux/delay.h> | ||
| 14 | #include <linux/videodev2.h> | ||
| 15 | #include <media/v4l2-ctrls.h> | ||
| 16 | |||
| 17 | #include "m5mols.h" | ||
| 18 | #include "m5mols_reg.h" | ||
| 19 | |||
| 20 | static struct m5mols_scenemode m5mols_default_scenemode[] = { | ||
| 21 | [REG_SCENE_NORMAL] = { | ||
| 22 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 23 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 24 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 25 | REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 26 | 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 27 | }, | ||
| 28 | [REG_SCENE_PORTRAIT] = { | ||
| 29 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 30 | REG_CHROMA_ON, 3, REG_EDGE_ON, 4, | ||
| 31 | REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, | ||
| 32 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 33 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 34 | }, | ||
| 35 | [REG_SCENE_LANDSCAPE] = { | ||
| 36 | REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 37 | REG_CHROMA_ON, 4, REG_EDGE_ON, 6, | ||
| 38 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 39 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 40 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 41 | }, | ||
| 42 | [REG_SCENE_SPORTS] = { | ||
| 43 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 44 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 45 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 46 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 47 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 48 | }, | ||
| 49 | [REG_SCENE_PARTY_INDOOR] = { | ||
| 50 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 51 | REG_CHROMA_ON, 4, REG_EDGE_ON, 5, | ||
| 52 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 53 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 54 | 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, | ||
| 55 | }, | ||
| 56 | [REG_SCENE_BEACH_SNOW] = { | ||
| 57 | REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, | ||
| 58 | REG_CHROMA_ON, 4, REG_EDGE_ON, 5, | ||
| 59 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 60 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 61 | 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, | ||
| 62 | }, | ||
| 63 | [REG_SCENE_SUNSET] = { | ||
| 64 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, | ||
| 65 | REG_AWB_DAYLIGHT, | ||
| 66 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 67 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 68 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 69 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 70 | }, | ||
| 71 | [REG_SCENE_DAWN_DUSK] = { | ||
| 72 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, | ||
| 73 | REG_AWB_FLUORESCENT_1, | ||
| 74 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 75 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 76 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 77 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 78 | }, | ||
| 79 | [REG_SCENE_FALL] = { | ||
| 80 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 81 | REG_CHROMA_ON, 5, REG_EDGE_ON, 5, | ||
| 82 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 83 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 84 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 85 | }, | ||
| 86 | [REG_SCENE_NIGHT] = { | ||
| 87 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 88 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 89 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 90 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 91 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 92 | }, | ||
| 93 | [REG_SCENE_AGAINST_LIGHT] = { | ||
| 94 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 95 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 96 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 97 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 98 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 99 | }, | ||
| 100 | [REG_SCENE_FIRE] = { | ||
| 101 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 102 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 103 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 104 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 105 | 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, | ||
| 106 | }, | ||
| 107 | [REG_SCENE_TEXT] = { | ||
| 108 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 109 | REG_CHROMA_ON, 3, REG_EDGE_ON, 7, | ||
| 110 | REG_AF_MACRO, REG_FD_OFF, | ||
| 111 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 112 | 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, | ||
| 113 | }, | ||
| 114 | [REG_SCENE_CANDLE] = { | ||
| 115 | REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, | ||
| 116 | REG_CHROMA_ON, 3, REG_EDGE_ON, 5, | ||
| 117 | REG_AF_NORMAL, REG_FD_OFF, | ||
| 118 | REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, | ||
| 119 | 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, | ||
| 120 | }, | ||
| 121 | }; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * m5mols_do_scenemode() - Change current scenemode | ||
| 125 | * @info: M-5MOLS driver data structure | ||
| 126 | * @mode: Desired mode of the scenemode | ||
| 127 | * | ||
| 128 | * WARNING: The execution order is important. Do not change the order. | ||
| 129 | */ | ||
| 130 | int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) | ||
| 131 | { | ||
| 132 | struct v4l2_subdev *sd = &info->sd; | ||
| 133 | struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; | ||
| 134 | int ret; | ||
| 135 | |||
| 136 | if (mode > REG_SCENE_CANDLE) | ||
| 137 | return -EINVAL; | ||
| 138 | |||
| 139 | ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); | ||
| 140 | if (!ret) | ||
| 141 | ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); | ||
| 142 | if (!ret) | ||
| 143 | ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); | ||
| 144 | if (!ret) | ||
| 145 | ret = m5mols_write(sd, AE_MODE, scenemode.metering); | ||
| 146 | if (!ret) | ||
| 147 | ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); | ||
| 148 | if (!ret) | ||
| 149 | ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); | ||
| 150 | if (!ret) | ||
| 151 | ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); | ||
| 152 | if (!ret) | ||
| 153 | ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); | ||
| 154 | if (!ret) | ||
| 155 | ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); | ||
| 156 | if (!ret) | ||
| 157 | ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); | ||
| 158 | if (!ret) | ||
| 159 | ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); | ||
| 160 | if (!ret && is_available_af(info)) | ||
| 161 | ret = m5mols_write(sd, AF_MODE, scenemode.af_range); | ||
| 162 | if (!ret && is_available_af(info)) | ||
| 163 | ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); | ||
| 164 | if (!ret) | ||
| 165 | ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); | ||
| 166 | if (!ret) | ||
| 167 | ret = m5mols_write(sd, AE_ISO, scenemode.iso); | ||
| 168 | if (!ret) | ||
| 169 | ret = m5mols_set_mode(info, REG_CAPTURE); | ||
| 170 | if (!ret) | ||
| 171 | ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); | ||
| 172 | if (!ret) | ||
| 173 | ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); | ||
| 174 | if (!ret) | ||
| 175 | ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); | ||
| 176 | if (!ret) | ||
| 177 | ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); | ||
| 178 | if (!ret) | ||
| 179 | ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); | ||
| 180 | if (!ret) | ||
| 181 | ret = m5mols_set_mode(info, REG_MONITOR); | ||
| 182 | |||
| 183 | return ret; | ||
| 184 | } | ||
| 185 | |||
| 186 | static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) | ||
| 187 | { | ||
| 188 | bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; | ||
| 189 | int ret = 0; | ||
| 190 | |||
| 191 | if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { | ||
| 192 | bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; | ||
| 193 | |||
| 194 | ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? | ||
| 195 | REG_AE_LOCK : REG_AE_UNLOCK); | ||
| 196 | if (ret) | ||
| 197 | return ret; | ||
| 198 | } | ||
| 199 | |||
| 200 | if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) | ||
| 201 | && info->auto_wb->val) { | ||
| 202 | bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; | ||
| 203 | |||
| 204 | ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? | ||
| 205 | REG_AWB_LOCK : REG_AWB_UNLOCK); | ||
| 206 | if (ret) | ||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (!info->ver.af || !af_lock) | ||
| 211 | return ret; | ||
| 212 | |||
| 213 | if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) | ||
| 214 | ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); | ||
| 215 | |||
| 216 | return ret; | ||
| 217 | } | ||
| 218 | |||
| 219 | static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) | ||
| 220 | { | ||
| 221 | unsigned int metering; | ||
| 222 | |||
| 223 | switch (mode) { | ||
| 224 | case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: | ||
| 225 | metering = REG_AE_CENTER; | ||
| 226 | break; | ||
| 227 | case V4L2_EXPOSURE_METERING_SPOT: | ||
| 228 | metering = REG_AE_SPOT; | ||
| 229 | break; | ||
| 230 | default: | ||
| 231 | metering = REG_AE_ALL; | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | |||
| 235 | return m5mols_write(&info->sd, AE_MODE, metering); | ||
| 236 | } | ||
| 237 | |||
| 238 | static int m5mols_set_exposure(struct m5mols_info *info, int exposure) | ||
| 239 | { | ||
| 240 | struct v4l2_subdev *sd = &info->sd; | ||
| 241 | int ret = 0; | ||
| 242 | |||
| 243 | if (exposure == V4L2_EXPOSURE_AUTO) { | ||
| 244 | /* Unlock auto exposure */ | ||
| 245 | info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; | ||
| 246 | m5mols_3a_lock(info, info->lock_3a); | ||
| 247 | |||
| 248 | ret = m5mols_set_metering_mode(info, info->metering->val); | ||
| 249 | if (ret < 0) | ||
| 250 | return ret; | ||
| 251 | |||
| 252 | v4l2_dbg(1, m5mols_debug, sd, | ||
| 253 | "%s: exposure bias: %#x, metering: %#x\n", | ||
| 254 | __func__, info->exposure_bias->val, | ||
| 255 | info->metering->val); | ||
| 256 | |||
| 257 | return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); | ||
| 258 | } | ||
| 259 | |||
| 260 | if (exposure == V4L2_EXPOSURE_MANUAL) { | ||
| 261 | ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); | ||
| 262 | if (ret == 0) | ||
| 263 | ret = m5mols_write(sd, AE_MAN_GAIN_MON, | ||
| 264 | info->exposure->val); | ||
| 265 | if (ret == 0) | ||
| 266 | ret = m5mols_write(sd, AE_MAN_GAIN_CAP, | ||
| 267 | info->exposure->val); | ||
| 268 | |||
| 269 | v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", | ||
| 270 | __func__, info->exposure->val); | ||
| 271 | } | ||
| 272 | |||
| 273 | return ret; | ||
| 274 | } | ||
| 275 | |||
| 276 | static int m5mols_set_white_balance(struct m5mols_info *info, int val) | ||
| 277 | { | ||
| 278 | static const unsigned short wb[][2] = { | ||
| 279 | { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, | ||
| 280 | { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, | ||
| 281 | { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, | ||
| 282 | { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, | ||
| 283 | { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, | ||
| 284 | { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, | ||
| 285 | { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, | ||
| 286 | { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, | ||
| 287 | { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, | ||
| 288 | }; | ||
| 289 | int i; | ||
| 290 | struct v4l2_subdev *sd = &info->sd; | ||
| 291 | int ret = -EINVAL; | ||
| 292 | |||
| 293 | for (i = 0; i < ARRAY_SIZE(wb); i++) { | ||
| 294 | int awb; | ||
| 295 | if (wb[i][0] != val) | ||
| 296 | continue; | ||
| 297 | |||
| 298 | v4l2_dbg(1, m5mols_debug, sd, | ||
| 299 | "Setting white balance to: %#x\n", wb[i][0]); | ||
| 300 | |||
| 301 | awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; | ||
| 302 | ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : | ||
| 303 | REG_AWB_PRESET); | ||
| 304 | if (ret < 0) | ||
| 305 | return ret; | ||
| 306 | |||
| 307 | if (!awb) | ||
| 308 | ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); | ||
| 309 | } | ||
| 310 | |||
| 311 | return ret; | ||
| 312 | } | ||
| 313 | |||
| 314 | static int m5mols_set_saturation(struct m5mols_info *info, int val) | ||
| 315 | { | ||
| 316 | int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); | ||
| 317 | if (ret < 0) | ||
| 318 | return ret; | ||
| 319 | |||
| 320 | return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); | ||
| 321 | } | ||
| 322 | |||
| 323 | static int m5mols_set_color_effect(struct m5mols_info *info, int val) | ||
| 324 | { | ||
| 325 | unsigned int m_effect = REG_COLOR_EFFECT_OFF; | ||
| 326 | unsigned int p_effect = REG_EFFECT_OFF; | ||
| 327 | unsigned int cfix_r = 0, cfix_b = 0; | ||
| 328 | struct v4l2_subdev *sd = &info->sd; | ||
| 329 | int ret = 0; | ||
| 330 | |||
| 331 | switch (val) { | ||
| 332 | case V4L2_COLORFX_BW: | ||
| 333 | m_effect = REG_COLOR_EFFECT_ON; | ||
| 334 | break; | ||
| 335 | case V4L2_COLORFX_NEGATIVE: | ||
| 336 | p_effect = REG_EFFECT_NEGA; | ||
| 337 | break; | ||
| 338 | case V4L2_COLORFX_EMBOSS: | ||
| 339 | p_effect = REG_EFFECT_EMBOSS; | ||
| 340 | break; | ||
| 341 | case V4L2_COLORFX_SEPIA: | ||
| 342 | m_effect = REG_COLOR_EFFECT_ON; | ||
| 343 | cfix_r = REG_CFIXR_SEPIA; | ||
| 344 | cfix_b = REG_CFIXB_SEPIA; | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | |||
| 348 | ret = m5mols_write(sd, PARM_EFFECT, p_effect); | ||
| 349 | if (!ret) | ||
| 350 | ret = m5mols_write(sd, MON_EFFECT, m_effect); | ||
| 351 | |||
| 352 | if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { | ||
| 353 | ret = m5mols_write(sd, MON_CFIXR, cfix_r); | ||
| 354 | if (!ret) | ||
| 355 | ret = m5mols_write(sd, MON_CFIXB, cfix_b); | ||
| 356 | } | ||
| 357 | |||
| 358 | v4l2_dbg(1, m5mols_debug, sd, | ||
| 359 | "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", | ||
| 360 | p_effect, m_effect, cfix_r, cfix_b, ret); | ||
| 361 | |||
| 362 | return ret; | ||
| 363 | } | ||
| 364 | |||
| 365 | static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) | ||
| 366 | { | ||
| 367 | u32 iso = auto_iso ? 0 : info->iso->val + 1; | ||
| 368 | |||
| 369 | return m5mols_write(&info->sd, AE_ISO, iso); | ||
| 370 | } | ||
| 371 | |||
| 372 | static int m5mols_set_wdr(struct m5mols_info *info, int wdr) | ||
| 373 | { | ||
| 374 | int ret; | ||
| 375 | |||
| 376 | ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); | ||
| 377 | if (ret < 0) | ||
| 378 | return ret; | ||
| 379 | |||
| 380 | ret = m5mols_set_mode(info, REG_CAPTURE); | ||
| 381 | if (ret < 0) | ||
| 382 | return ret; | ||
| 383 | |||
| 384 | return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); | ||
| 385 | } | ||
| 386 | |||
| 387 | static int m5mols_set_stabilization(struct m5mols_info *info, int val) | ||
| 388 | { | ||
| 389 | struct v4l2_subdev *sd = &info->sd; | ||
| 390 | unsigned int evp = val ? 0xe : 0x0; | ||
| 391 | int ret; | ||
| 392 | |||
| 393 | ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); | ||
| 394 | if (ret < 0) | ||
| 395 | return ret; | ||
| 396 | |||
| 397 | return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); | ||
| 398 | } | ||
| 399 | |||
| 400 | static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||
| 401 | { | ||
| 402 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
| 403 | struct m5mols_info *info = to_m5mols(sd); | ||
| 404 | int ret = 0; | ||
| 405 | u8 status = REG_ISO_AUTO; | ||
| 406 | |||
| 407 | v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", | ||
| 408 | __func__, ctrl->name, info->isp_ready); | ||
| 409 | |||
| 410 | if (!info->isp_ready) | ||
| 411 | return -EBUSY; | ||
| 412 | |||
| 413 | switch (ctrl->id) { | ||
| 414 | case V4L2_CID_ISO_SENSITIVITY_AUTO: | ||
| 415 | ret = m5mols_read_u8(sd, AE_ISO, &status); | ||
| 416 | if (ret == 0) | ||
| 417 | ctrl->val = !status; | ||
| 418 | if (status != REG_ISO_AUTO) | ||
| 419 | info->iso->val = status - 1; | ||
| 420 | break; | ||
| 421 | |||
| 422 | case V4L2_CID_3A_LOCK: | ||
| 423 | ctrl->val &= ~0x7; | ||
| 424 | |||
| 425 | ret = m5mols_read_u8(sd, AE_LOCK, &status); | ||
| 426 | if (ret) | ||
| 427 | return ret; | ||
| 428 | if (status) | ||
| 429 | info->lock_3a->val |= V4L2_LOCK_EXPOSURE; | ||
| 430 | |||
| 431 | ret = m5mols_read_u8(sd, AWB_LOCK, &status); | ||
| 432 | if (ret) | ||
| 433 | return ret; | ||
| 434 | if (status) | ||
| 435 | info->lock_3a->val |= V4L2_LOCK_EXPOSURE; | ||
| 436 | |||
| 437 | ret = m5mols_read_u8(sd, AF_EXECUTE, &status); | ||
| 438 | if (!status) | ||
| 439 | info->lock_3a->val |= V4L2_LOCK_EXPOSURE; | ||
| 440 | break; | ||
| 441 | } | ||
| 442 | |||
| 443 | return ret; | ||
| 444 | } | ||
| 445 | |||
| 446 | static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 447 | { | ||
| 448 | unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); | ||
| 449 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
| 450 | struct m5mols_info *info = to_m5mols(sd); | ||
| 451 | int last_mode = info->mode; | ||
| 452 | int ret = 0; | ||
| 453 | |||
| 454 | /* | ||
| 455 | * If needed, defer restoring the controls until | ||
| 456 | * the device is fully initialized. | ||
| 457 | */ | ||
| 458 | if (!info->isp_ready) { | ||
| 459 | info->ctrl_sync = 0; | ||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %p\n", | ||
| 464 | __func__, ctrl->name, ctrl->val, ctrl->priv); | ||
| 465 | |||
| 466 | if (ctrl_mode && ctrl_mode != info->mode) { | ||
| 467 | ret = m5mols_set_mode(info, ctrl_mode); | ||
| 468 | if (ret < 0) | ||
| 469 | return ret; | ||
| 470 | } | ||
| 471 | |||
| 472 | switch (ctrl->id) { | ||
| 473 | case V4L2_CID_3A_LOCK: | ||
| 474 | ret = m5mols_3a_lock(info, ctrl); | ||
| 475 | break; | ||
| 476 | |||
| 477 | case V4L2_CID_ZOOM_ABSOLUTE: | ||
| 478 | ret = m5mols_write(sd, MON_ZOOM, ctrl->val); | ||
| 479 | break; | ||
| 480 | |||
| 481 | case V4L2_CID_EXPOSURE_AUTO: | ||
| 482 | ret = m5mols_set_exposure(info, ctrl->val); | ||
| 483 | break; | ||
| 484 | |||
| 485 | case V4L2_CID_ISO_SENSITIVITY: | ||
| 486 | ret = m5mols_set_iso(info, ctrl->val); | ||
| 487 | break; | ||
| 488 | |||
| 489 | case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: | ||
| 490 | ret = m5mols_set_white_balance(info, ctrl->val); | ||
| 491 | break; | ||
| 492 | |||
| 493 | case V4L2_CID_SATURATION: | ||
| 494 | ret = m5mols_set_saturation(info, ctrl->val); | ||
| 495 | break; | ||
| 496 | |||
| 497 | case V4L2_CID_COLORFX: | ||
| 498 | ret = m5mols_set_color_effect(info, ctrl->val); | ||
| 499 | break; | ||
| 500 | |||
| 501 | case V4L2_CID_WIDE_DYNAMIC_RANGE: | ||
| 502 | ret = m5mols_set_wdr(info, ctrl->val); | ||
| 503 | break; | ||
| 504 | |||
| 505 | case V4L2_CID_IMAGE_STABILIZATION: | ||
| 506 | ret = m5mols_set_stabilization(info, ctrl->val); | ||
| 507 | break; | ||
| 508 | |||
| 509 | case V4L2_CID_JPEG_COMPRESSION_QUALITY: | ||
| 510 | ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | |||
| 514 | if (ret == 0 && info->mode != last_mode) | ||
| 515 | ret = m5mols_set_mode(info, last_mode); | ||
| 516 | |||
| 517 | return ret; | ||
| 518 | } | ||
| 519 | |||
| 520 | static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { | ||
| 521 | .g_volatile_ctrl = m5mols_g_volatile_ctrl, | ||
| 522 | .s_ctrl = m5mols_s_ctrl, | ||
| 523 | }; | ||
| 524 | |||
| 525 | /* Supported manual ISO values */ | ||
| 526 | static const s64 iso_qmenu[] = { | ||
| 527 | /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ | ||
| 528 | 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 | ||
| 529 | }; | ||
| 530 | |||
| 531 | /* Supported Exposure Bias values, -2.0EV...+2.0EV */ | ||
| 532 | static const s64 ev_bias_qmenu[] = { | ||
| 533 | /* AE_INDEX: 0x00...0x08 */ | ||
| 534 | -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 | ||
| 535 | }; | ||
| 536 | |||
| 537 | int m5mols_init_controls(struct v4l2_subdev *sd) | ||
| 538 | { | ||
| 539 | struct m5mols_info *info = to_m5mols(sd); | ||
| 540 | u16 exposure_max; | ||
| 541 | u16 zoom_step; | ||
| 542 | int ret; | ||
| 543 | |||
| 544 | /* Determine the firmware dependent control range and step values */ | ||
| 545 | ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); | ||
| 546 | if (ret < 0) | ||
| 547 | return ret; | ||
| 548 | |||
| 549 | zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; | ||
| 550 | v4l2_ctrl_handler_init(&info->handle, 20); | ||
| 551 | |||
| 552 | info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, | ||
| 553 | &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, | ||
| 554 | 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); | ||
| 555 | |||
| 556 | /* Exposure control cluster */ | ||
| 557 | info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, | ||
| 558 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, | ||
| 559 | 1, ~0x03, V4L2_EXPOSURE_AUTO); | ||
| 560 | |||
| 561 | info->exposure = v4l2_ctrl_new_std(&info->handle, | ||
| 562 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, | ||
| 563 | 0, exposure_max, 1, exposure_max / 2); | ||
| 564 | |||
| 565 | info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, | ||
| 566 | &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, | ||
| 567 | ARRAY_SIZE(ev_bias_qmenu) - 1, | ||
| 568 | ARRAY_SIZE(ev_bias_qmenu)/2 - 1, | ||
| 569 | ev_bias_qmenu); | ||
| 570 | |||
| 571 | info->metering = v4l2_ctrl_new_std_menu(&info->handle, | ||
| 572 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, | ||
| 573 | 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); | ||
| 574 | |||
| 575 | /* ISO control cluster */ | ||
| 576 | info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, | ||
| 577 | V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); | ||
| 578 | |||
| 579 | info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, | ||
| 580 | V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, | ||
| 581 | ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); | ||
| 582 | |||
| 583 | info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 584 | V4L2_CID_SATURATION, 1, 5, 1, 3); | ||
| 585 | |||
| 586 | info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 587 | V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); | ||
| 588 | |||
| 589 | info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, | ||
| 590 | V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); | ||
| 591 | |||
| 592 | info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 593 | V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); | ||
| 594 | |||
| 595 | info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 596 | V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); | ||
| 597 | |||
| 598 | info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 599 | V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); | ||
| 600 | |||
| 601 | info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, | ||
| 602 | V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); | ||
| 603 | |||
| 604 | if (info->handle.error) { | ||
| 605 | int ret = info->handle.error; | ||
| 606 | v4l2_err(sd, "Failed to initialize controls: %d\n", ret); | ||
| 607 | v4l2_ctrl_handler_free(&info->handle); | ||
| 608 | return ret; | ||
| 609 | } | ||
| 610 | |||
| 611 | v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); | ||
| 612 | info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | | ||
| 613 | V4L2_CTRL_FLAG_UPDATE; | ||
| 614 | v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); | ||
| 615 | |||
| 616 | info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; | ||
| 617 | |||
| 618 | m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); | ||
| 619 | m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); | ||
| 620 | m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); | ||
| 621 | |||
| 622 | sd->ctrl_handler = &info->handle; | ||
| 623 | |||
| 624 | return 0; | ||
| 625 | } | ||
| @@ -1,1051 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /* | ||
| 3 | * Driver for M-5MOLS 8M Pixel camera sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
| 6 | * Author: HeungJun Kim <riverful.kim@samsung.com> | ||
| 7 | * | ||
| 8 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
| 9 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/i2c.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/irq.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <linux/gpio/consumer.h> | ||
| 18 | #include <linux/regulator/consumer.h> | ||
| 19 | #include <linux/videodev2.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <media/v4l2-ctrls.h> | ||
| 22 | #include <media/v4l2-device.h> | ||
| 23 | #include <media/v4l2-subdev.h> | ||
| 24 | #include <media/i2c/m5mols.h> | ||
| 25 | |||
| 26 | #include "m5mols.h" | ||
| 27 | #include "m5mols_reg.h" | ||
| 28 | |||
| 29 | int m5mols_debug; | ||
| 30 | module_param(m5mols_debug, int, 0644); | ||
| 31 | |||
| 32 | #define MODULE_NAME "M5MOLS" | ||
| 33 | #define M5MOLS_I2C_CHECK_RETRY 500 | ||
| 34 | |||
| 35 | /* The regulator consumer names for external voltage regulators */ | ||
| 36 | static struct regulator_bulk_data supplies[] = { | ||
| 37 | { | ||
| 38 | .supply = "core", /* ARM core power, 1.2V */ | ||
| 39 | }, { | ||
| 40 | .supply = "dig_18", /* digital power 1, 1.8V */ | ||
| 41 | }, { | ||
| 42 | .supply = "d_sensor", /* sensor power 1, 1.8V */ | ||
| 43 | }, { | ||
| 44 | .supply = "dig_28", /* digital power 2, 2.8V */ | ||
| 45 | }, { | ||
| 46 | .supply = "a_sensor", /* analog power */ | ||
| 47 | }, { | ||
| 48 | .supply = "dig_12", /* digital power 3, 1.2V */ | ||
| 49 | }, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { | ||
| 53 | [M5MOLS_RESTYPE_MONITOR] = { | ||
| 54 | .width = 1920, | ||
| 55 | .height = 1080, | ||
| 56 | .code = MEDIA_BUS_FMT_VYUY8_2X8, | ||
| 57 | .field = V4L2_FIELD_NONE, | ||
| 58 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 59 | }, | ||
| 60 | [M5MOLS_RESTYPE_CAPTURE] = { | ||
| 61 | .width = 1920, | ||
| 62 | .height = 1080, | ||
| 63 | .code = MEDIA_BUS_FMT_JPEG_1X8, | ||
| 64 | .field = V4L2_FIELD_NONE, | ||
| 65 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 66 | }, | ||
| 67 | }; | ||
| 68 | #define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) | ||
| 69 | |||
| 70 | static const struct m5mols_resolution m5mols_reg_res[] = { | ||
| 71 | { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ | ||
| 72 | { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ | ||
| 73 | { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ | ||
| 74 | { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, | ||
| 75 | { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ | ||
| 76 | { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ | ||
| 77 | { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ | ||
| 78 | { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ | ||
| 79 | { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ | ||
| 80 | { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, | ||
| 81 | { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ | ||
| 82 | { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ | ||
| 83 | { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, | ||
| 84 | { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ | ||
| 85 | { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ | ||
| 86 | { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ | ||
| 87 | { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ | ||
| 88 | { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ | ||
| 89 | { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ | ||
| 90 | |||
| 91 | { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ | ||
| 92 | { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ | ||
| 93 | { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, | ||
| 94 | { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ | ||
| 95 | { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ | ||
| 96 | { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ | ||
| 97 | { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ | ||
| 98 | { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ | ||
| 99 | { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ | ||
| 100 | { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ | ||
| 101 | { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ | ||
| 102 | { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, | ||
| 103 | { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ | ||
| 104 | { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, | ||
| 105 | { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ | ||
| 106 | { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ | ||
| 107 | { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, | ||
| 108 | { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ | ||
| 109 | }; | ||
| 110 | |||
| 111 | /** | ||
| 112 | * m5mols_swap_byte - an byte array to integer conversion function | ||
| 113 | * @data: byte array | ||
| 114 | * @length: size in bytes of I2C packet defined in the M-5MOLS datasheet | ||
| 115 | * | ||
| 116 | * Convert I2C data byte array with performing any required byte | ||
| 117 | * reordering to assure proper values for each data type, regardless | ||
| 118 | * of the architecture endianness. | ||
| 119 | */ | ||
| 120 | static u32 m5mols_swap_byte(u8 *data, u8 length) | ||
| 121 | { | ||
| 122 | if (length == 1) | ||
| 123 | return *data; | ||
| 124 | else if (length == 2) | ||
| 125 | return be16_to_cpu(*((__be16 *)data)); | ||
| 126 | else | ||
| 127 | return be32_to_cpu(*((__be32 *)data)); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * m5mols_read - I2C read function | ||
| 132 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 133 | * @size: desired size of I2C packet | ||
| 134 | * @reg: combination of size, category and command for the I2C packet | ||
| 135 | * @val: read value | ||
| 136 | * | ||
| 137 | * Returns 0 on success, or else negative errno. | ||
| 138 | */ | ||
| 139 | static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) | ||
| 140 | { | ||
| 141 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 142 | struct m5mols_info *info = to_m5mols(sd); | ||
| 143 | u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; | ||
| 144 | u8 category = I2C_CATEGORY(reg); | ||
| 145 | u8 cmd = I2C_COMMAND(reg); | ||
| 146 | struct i2c_msg msg[2]; | ||
| 147 | u8 wbuf[5]; | ||
| 148 | int ret; | ||
| 149 | |||
| 150 | if (!client->adapter) | ||
| 151 | return -ENODEV; | ||
| 152 | |||
| 153 | msg[0].addr = client->addr; | ||
| 154 | msg[0].flags = 0; | ||
| 155 | msg[0].len = 5; | ||
| 156 | msg[0].buf = wbuf; | ||
| 157 | wbuf[0] = 5; | ||
| 158 | wbuf[1] = M5MOLS_BYTE_READ; | ||
| 159 | wbuf[2] = category; | ||
| 160 | wbuf[3] = cmd; | ||
| 161 | wbuf[4] = size; | ||
| 162 | |||
| 163 | msg[1].addr = client->addr; | ||
| 164 | msg[1].flags = I2C_M_RD; | ||
| 165 | msg[1].len = size + 1; | ||
| 166 | msg[1].buf = rbuf; | ||
| 167 | |||
| 168 | /* minimum stabilization time */ | ||
| 169 | usleep_range(200, 300); | ||
| 170 | |||
| 171 | ret = i2c_transfer(client->adapter, msg, 2); | ||
| 172 | |||
| 173 | if (ret == 2) { | ||
| 174 | *val = m5mols_swap_byte(&rbuf[1], size); | ||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (info->isp_ready) | ||
| 179 | v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", | ||
| 180 | size, category, cmd, ret); | ||
| 181 | |||
| 182 | return ret < 0 ? ret : -EIO; | ||
| 183 | } | ||
| 184 | |||
| 185 | int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) | ||
| 186 | { | ||
| 187 | u32 val_32; | ||
| 188 | int ret; | ||
| 189 | |||
| 190 | if (I2C_SIZE(reg) != 1) { | ||
| 191 | v4l2_err(sd, "Wrong data size\n"); | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 194 | |||
| 195 | ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); | ||
| 196 | if (ret) | ||
| 197 | return ret; | ||
| 198 | |||
| 199 | *val = (u8)val_32; | ||
| 200 | return ret; | ||
| 201 | } | ||
| 202 | |||
| 203 | int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) | ||
| 204 | { | ||
| 205 | u32 val_32; | ||
| 206 | int ret; | ||
| 207 | |||
| 208 | if (I2C_SIZE(reg) != 2) { | ||
| 209 | v4l2_err(sd, "Wrong data size\n"); | ||
| 210 | return -EINVAL; | ||
| 211 | } | ||
| 212 | |||
| 213 | ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); | ||
| 214 | if (ret) | ||
| 215 | return ret; | ||
| 216 | |||
| 217 | *val = (u16)val_32; | ||
| 218 | return ret; | ||
| 219 | } | ||
| 220 | |||
| 221 | int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) | ||
| 222 | { | ||
| 223 | if (I2C_SIZE(reg) != 4) { | ||
| 224 | v4l2_err(sd, "Wrong data size\n"); | ||
| 225 | return -EINVAL; | ||
| 226 | } | ||
| 227 | |||
| 228 | return m5mols_read(sd, I2C_SIZE(reg), reg, val); | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * m5mols_write - I2C command write function | ||
| 233 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 234 | * @reg: combination of size, category and command for the I2C packet | ||
| 235 | * @val: value to write | ||
| 236 | * | ||
| 237 | * Returns 0 on success, or else negative errno. | ||
| 238 | */ | ||
| 239 | int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) | ||
| 240 | { | ||
| 241 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 242 | struct m5mols_info *info = to_m5mols(sd); | ||
| 243 | u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; | ||
| 244 | u8 category = I2C_CATEGORY(reg); | ||
| 245 | u8 cmd = I2C_COMMAND(reg); | ||
| 246 | u8 size = I2C_SIZE(reg); | ||
| 247 | u32 *buf = (u32 *)&wbuf[4]; | ||
| 248 | struct i2c_msg msg[1]; | ||
| 249 | int ret; | ||
| 250 | |||
| 251 | if (!client->adapter) | ||
| 252 | return -ENODEV; | ||
| 253 | |||
| 254 | if (size != 1 && size != 2 && size != 4) { | ||
| 255 | v4l2_err(sd, "Wrong data size\n"); | ||
| 256 | return -EINVAL; | ||
| 257 | } | ||
| 258 | |||
| 259 | msg->addr = client->addr; | ||
| 260 | msg->flags = 0; | ||
| 261 | msg->len = (u16)size + 4; | ||
| 262 | msg->buf = wbuf; | ||
| 263 | wbuf[0] = size + 4; | ||
| 264 | wbuf[1] = M5MOLS_BYTE_WRITE; | ||
| 265 | wbuf[2] = category; | ||
| 266 | wbuf[3] = cmd; | ||
| 267 | |||
| 268 | *buf = m5mols_swap_byte((u8 *)&val, size); | ||
| 269 | |||
| 270 | /* minimum stabilization time */ | ||
| 271 | usleep_range(200, 300); | ||
| 272 | |||
| 273 | ret = i2c_transfer(client->adapter, msg, 1); | ||
| 274 | if (ret == 1) | ||
| 275 | return 0; | ||
| 276 | |||
| 277 | if (info->isp_ready) | ||
| 278 | v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", | ||
| 279 | category, cmd, ret); | ||
| 280 | |||
| 281 | return ret < 0 ? ret : -EIO; | ||
| 282 | } | ||
| 283 | |||
| 284 | /** | ||
| 285 | * m5mols_busy_wait - Busy waiting with I2C register polling | ||
| 286 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 287 | * @reg: the I2C_REG() address of an 8-bit status register to check | ||
| 288 | * @value: expected status register value | ||
| 289 | * @mask: bit mask for the read status register value | ||
| 290 | * @timeout: timeout in milliseconds, or -1 for default timeout | ||
| 291 | * | ||
| 292 | * The @reg register value is ORed with @mask before comparing with @value. | ||
| 293 | * | ||
| 294 | * Return: 0 if the requested condition became true within less than | ||
| 295 | * @timeout ms, or else negative errno. | ||
| 296 | */ | ||
| 297 | int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, | ||
| 298 | int timeout) | ||
| 299 | { | ||
| 300 | int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; | ||
| 301 | unsigned long end = jiffies + msecs_to_jiffies(ms); | ||
| 302 | u8 status; | ||
| 303 | |||
| 304 | do { | ||
| 305 | int ret = m5mols_read_u8(sd, reg, &status); | ||
| 306 | |||
| 307 | if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) | ||
| 308 | return ret; | ||
| 309 | if (!ret && (status & mask & 0xff) == (value & 0xff)) | ||
| 310 | return 0; | ||
| 311 | usleep_range(100, 250); | ||
| 312 | } while (ms > 0 && time_is_after_jiffies(end)); | ||
| 313 | |||
| 314 | return -EBUSY; | ||
| 315 | } | ||
| 316 | |||
| 317 | /** | ||
| 318 | * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts | ||
| 319 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 320 | * @reg: combination of size, category and command for the I2C packet | ||
| 321 | * | ||
| 322 | * Before writing desired interrupt value the INT_FACTOR register should | ||
| 323 | * be read to clear pending interrupts. | ||
| 324 | */ | ||
| 325 | int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) | ||
| 326 | { | ||
| 327 | struct m5mols_info *info = to_m5mols(sd); | ||
| 328 | u8 mask = is_available_af(info) ? REG_INT_AF : 0; | ||
| 329 | u8 dummy; | ||
| 330 | int ret; | ||
| 331 | |||
| 332 | ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); | ||
| 333 | if (!ret) | ||
| 334 | ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); | ||
| 335 | return ret; | ||
| 336 | } | ||
| 337 | |||
| 338 | int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) | ||
| 339 | { | ||
| 340 | struct m5mols_info *info = to_m5mols(sd); | ||
| 341 | |||
| 342 | int ret = wait_event_interruptible_timeout(info->irq_waitq, | ||
| 343 | atomic_add_unless(&info->irq_done, -1, 0), | ||
| 344 | msecs_to_jiffies(timeout)); | ||
| 345 | if (ret <= 0) | ||
| 346 | return ret ? ret : -ETIMEDOUT; | ||
| 347 | |||
| 348 | return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, | ||
| 349 | M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); | ||
| 350 | } | ||
| 351 | |||
| 352 | /** | ||
| 353 | * m5mols_reg_mode - Write the mode and check busy status | ||
| 354 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 355 | * @mode: the required operation mode | ||
| 356 | * | ||
| 357 | * It always accompanies a little delay changing the M-5MOLS mode, so it is | ||
| 358 | * needed checking current busy status to guarantee right mode. | ||
| 359 | */ | ||
| 360 | static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) | ||
| 361 | { | ||
| 362 | int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); | ||
| 363 | if (ret < 0) | ||
| 364 | return ret; | ||
| 365 | return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, | ||
| 366 | M5MOLS_MODE_CHANGE_TIMEOUT); | ||
| 367 | } | ||
| 368 | |||
| 369 | /** | ||
| 370 | * m5mols_set_mode - set the M-5MOLS controller mode | ||
| 371 | * @info: M-5MOLS driver data structure | ||
| 372 | * @mode: the required operation mode | ||
| 373 | * | ||
| 374 | * The commands of M-5MOLS are grouped into specific modes. Each functionality | ||
| 375 | * can be guaranteed only when the sensor is operating in mode which a command | ||
| 376 | * belongs to. | ||
| 377 | */ | ||
| 378 | int m5mols_set_mode(struct m5mols_info *info, u8 mode) | ||
| 379 | { | ||
| 380 | struct v4l2_subdev *sd = &info->sd; | ||
| 381 | int ret = -EINVAL; | ||
| 382 | u8 reg; | ||
| 383 | |||
| 384 | if (mode < REG_PARAMETER || mode > REG_CAPTURE) | ||
| 385 | return ret; | ||
| 386 | |||
| 387 | ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); | ||
| 388 | if (ret || reg == mode) | ||
| 389 | return ret; | ||
| 390 | |||
| 391 | switch (reg) { | ||
| 392 | case REG_PARAMETER: | ||
| 393 | ret = m5mols_reg_mode(sd, REG_MONITOR); | ||
| 394 | if (mode == REG_MONITOR) | ||
| 395 | break; | ||
| 396 | if (!ret) | ||
| 397 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | ||
| 398 | break; | ||
| 399 | |||
| 400 | case REG_MONITOR: | ||
| 401 | if (mode == REG_PARAMETER) { | ||
| 402 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | ||
| 403 | break; | ||
| 404 | } | ||
| 405 | |||
| 406 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | ||
| 407 | break; | ||
| 408 | |||
| 409 | case REG_CAPTURE: | ||
| 410 | ret = m5mols_reg_mode(sd, REG_MONITOR); | ||
| 411 | if (mode == REG_MONITOR) | ||
| 412 | break; | ||
| 413 | if (!ret) | ||
| 414 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | ||
| 415 | break; | ||
| 416 | |||
| 417 | default: | ||
| 418 | v4l2_warn(sd, "Wrong mode: %d\n", mode); | ||
| 419 | } | ||
| 420 | |||
| 421 | if (!ret) | ||
| 422 | info->mode = mode; | ||
| 423 | |||
| 424 | return ret; | ||
| 425 | } | ||
| 426 | |||
| 427 | /** | ||
| 428 | * m5mols_get_version - retrieve full revisions information of M-5MOLS | ||
| 429 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 430 | * | ||
| 431 | * The version information includes revisions of hardware and firmware, | ||
| 432 | * AutoFocus alghorithm version and the version string. | ||
| 433 | */ | ||
| 434 | static int m5mols_get_version(struct v4l2_subdev *sd) | ||
| 435 | { | ||
| 436 | struct m5mols_info *info = to_m5mols(sd); | ||
| 437 | struct m5mols_version *ver = &info->ver; | ||
| 438 | u8 *str = ver->str; | ||
| 439 | int i; | ||
| 440 | int ret; | ||
| 441 | |||
| 442 | ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); | ||
| 443 | if (!ret) | ||
| 444 | ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); | ||
| 445 | if (!ret) | ||
| 446 | ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); | ||
| 447 | if (!ret) | ||
| 448 | ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); | ||
| 449 | if (!ret) | ||
| 450 | ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); | ||
| 451 | if (!ret) | ||
| 452 | ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); | ||
| 453 | if (!ret) | ||
| 454 | ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); | ||
| 455 | if (ret) | ||
| 456 | return ret; | ||
| 457 | |||
| 458 | for (i = 0; i < VERSION_STRING_SIZE; i++) { | ||
| 459 | ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); | ||
| 460 | if (ret) | ||
| 461 | return ret; | ||
| 462 | } | ||
| 463 | |||
| 464 | v4l2_info(sd, "Manufacturer\t[%s]\n", | ||
| 465 | is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? | ||
| 466 | "Samsung Electro-Mechanics" : | ||
| 467 | is_manufacturer(info, REG_SAMSUNG_OPTICS) ? | ||
| 468 | "Samsung Fiber-Optics" : | ||
| 469 | is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? | ||
| 470 | "Samsung Techwin" : "None"); | ||
| 471 | v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", | ||
| 472 | info->ver.customer, info->ver.project); | ||
| 473 | |||
| 474 | if (!is_available_af(info)) | ||
| 475 | v4l2_info(sd, "No support Auto Focus on this firmware\n"); | ||
| 476 | |||
| 477 | return ret; | ||
| 478 | } | ||
| 479 | |||
| 480 | /** | ||
| 481 | * __find_restype - Lookup M-5MOLS resolution type according to pixel code | ||
| 482 | * @code: pixel code | ||
| 483 | */ | ||
| 484 | static enum m5mols_restype __find_restype(u32 code) | ||
| 485 | { | ||
| 486 | enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; | ||
| 487 | |||
| 488 | do { | ||
| 489 | if (code == m5mols_default_ffmt[type].code) | ||
| 490 | return type; | ||
| 491 | } while (++type != SIZE_DEFAULT_FFMT); | ||
| 492 | |||
| 493 | return 0; | ||
| 494 | } | ||
| 495 | |||
| 496 | /** | ||
| 497 | * __find_resolution - Lookup preset and type of M-5MOLS's resolution | ||
| 498 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 499 | * @mf: pixel format to find/negotiate the resolution preset for | ||
| 500 | * @type: M-5MOLS resolution type | ||
| 501 | * @resolution: M-5MOLS resolution preset register value | ||
| 502 | * | ||
| 503 | * Find nearest resolution matching resolution preset and adjust mf | ||
| 504 | * to supported values. | ||
| 505 | */ | ||
| 506 | static int __find_resolution(struct v4l2_subdev *sd, | ||
| 507 | struct v4l2_mbus_framefmt *mf, | ||
| 508 | enum m5mols_restype *type, | ||
| 509 | u32 *resolution) | ||
| 510 | { | ||
| 511 | const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; | ||
| 512 | const struct m5mols_resolution *match = NULL; | ||
| 513 | enum m5mols_restype stype = __find_restype(mf->code); | ||
| 514 | int i = ARRAY_SIZE(m5mols_reg_res); | ||
| 515 | unsigned int min_err = ~0; | ||
| 516 | |||
| 517 | while (i--) { | ||
| 518 | int err; | ||
| 519 | if (stype == fsize->type) { | ||
| 520 | err = abs(fsize->width - mf->width) | ||
| 521 | + abs(fsize->height - mf->height); | ||
| 522 | |||
| 523 | if (err < min_err) { | ||
| 524 | min_err = err; | ||
| 525 | match = fsize; | ||
| 526 | } | ||
| 527 | } | ||
| 528 | fsize++; | ||
| 529 | } | ||
| 530 | if (match) { | ||
| 531 | mf->width = match->width; | ||
| 532 | mf->height = match->height; | ||
| 533 | *resolution = match->reg; | ||
| 534 | *type = stype; | ||
| 535 | return 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | return -EINVAL; | ||
| 539 | } | ||
| 540 | |||
| 541 | static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, | ||
| 542 | struct v4l2_subdev_state *sd_state, | ||
| 543 | enum v4l2_subdev_format_whence which, | ||
| 544 | enum m5mols_restype type) | ||
| 545 | { | ||
| 546 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
| 547 | return sd_state ? v4l2_subdev_get_try_format(&info->sd, | ||
| 548 | sd_state, 0) : NULL; | ||
| 549 | |||
| 550 | return &info->ffmt[type]; | ||
| 551 | } | ||
| 552 | |||
| 553 | static int m5mols_get_fmt(struct v4l2_subdev *sd, | ||
| 554 | struct v4l2_subdev_state *sd_state, | ||
| 555 | struct v4l2_subdev_format *fmt) | ||
| 556 | { | ||
| 557 | struct m5mols_info *info = to_m5mols(sd); | ||
| 558 | struct v4l2_mbus_framefmt *format; | ||
| 559 | int ret = 0; | ||
| 560 | |||
| 561 | mutex_lock(&info->lock); | ||
| 562 | |||
| 563 | format = __find_format(info, sd_state, fmt->which, info->res_type); | ||
| 564 | if (format) | ||
| 565 | fmt->format = *format; | ||
| 566 | else | ||
| 567 | ret = -EINVAL; | ||
| 568 | |||
| 569 | mutex_unlock(&info->lock); | ||
| 570 | return ret; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int m5mols_set_fmt(struct v4l2_subdev *sd, | ||
| 574 | struct v4l2_subdev_state *sd_state, | ||
| 575 | struct v4l2_subdev_format *fmt) | ||
| 576 | { | ||
| 577 | struct m5mols_info *info = to_m5mols(sd); | ||
| 578 | struct v4l2_mbus_framefmt *format = &fmt->format; | ||
| 579 | struct v4l2_mbus_framefmt *sfmt; | ||
| 580 | enum m5mols_restype type; | ||
| 581 | u32 resolution = 0; | ||
| 582 | int ret; | ||
| 583 | |||
| 584 | ret = __find_resolution(sd, format, &type, &resolution); | ||
| 585 | if (ret < 0) | ||
| 586 | return ret; | ||
| 587 | |||
| 588 | sfmt = __find_format(info, sd_state, fmt->which, type); | ||
| 589 | if (!sfmt) | ||
| 590 | return 0; | ||
| 591 | |||
| 592 | mutex_lock(&info->lock); | ||
| 593 | |||
| 594 | format->code = m5mols_default_ffmt[type].code; | ||
| 595 | format->colorspace = V4L2_COLORSPACE_JPEG; | ||
| 596 | format->field = V4L2_FIELD_NONE; | ||
| 597 | |||
| 598 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
| 599 | *sfmt = *format; | ||
| 600 | info->resolution = resolution; | ||
| 601 | info->res_type = type; | ||
| 602 | } | ||
| 603 | |||
| 604 | mutex_unlock(&info->lock); | ||
| 605 | return ret; | ||
| 606 | } | ||
| 607 | |||
| 608 | static int m5mols_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, | ||
| 609 | struct v4l2_mbus_frame_desc *fd) | ||
| 610 | { | ||
| 611 | struct m5mols_info *info = to_m5mols(sd); | ||
| 612 | |||
| 613 | if (pad != 0 || fd == NULL) | ||
| 614 | return -EINVAL; | ||
| 615 | |||
| 616 | mutex_lock(&info->lock); | ||
| 617 | /* | ||
| 618 | * .get_frame_desc is only used for compressed formats, | ||
| 619 | * thus we always return the capture frame parameters here. | ||
| 620 | */ | ||
| 621 | fd->entry[0].length = info->cap.buf_size; | ||
| 622 | fd->entry[0].pixelcode = info->ffmt[M5MOLS_RESTYPE_CAPTURE].code; | ||
| 623 | mutex_unlock(&info->lock); | ||
| 624 | |||
| 625 | fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; | ||
| 626 | fd->num_entries = 1; | ||
| 627 | |||
| 628 | return 0; | ||
| 629 | } | ||
| 630 | |||
| 631 | static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, | ||
| 632 | struct v4l2_mbus_frame_desc *fd) | ||
| 633 | { | ||
| 634 | struct m5mols_info *info = to_m5mols(sd); | ||
| 635 | struct v4l2_mbus_framefmt *mf = &info->ffmt[M5MOLS_RESTYPE_CAPTURE]; | ||
| 636 | |||
| 637 | if (pad != 0 || fd == NULL) | ||
| 638 | return -EINVAL; | ||
| 639 | |||
| 640 | fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; | ||
| 641 | fd->num_entries = 1; | ||
| 642 | fd->entry[0].length = clamp_t(u32, fd->entry[0].length, | ||
| 643 | mf->width * mf->height, | ||
| 644 | M5MOLS_MAIN_JPEG_SIZE_MAX); | ||
| 645 | mutex_lock(&info->lock); | ||
| 646 | info->cap.buf_size = fd->entry[0].length; | ||
| 647 | mutex_unlock(&info->lock); | ||
| 648 | |||
| 649 | return 0; | ||
| 650 | } | ||
| 651 | |||
| 652 | |||
| 653 | static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, | ||
| 654 | struct v4l2_subdev_state *sd_state, | ||
| 655 | struct v4l2_subdev_mbus_code_enum *code) | ||
| 656 | { | ||
| 657 | if (!code || code->index >= SIZE_DEFAULT_FFMT) | ||
| 658 | return -EINVAL; | ||
| 659 | |||
| 660 | code->code = m5mols_default_ffmt[code->index].code; | ||
| 661 | |||
| 662 | return 0; | ||
| 663 | } | ||
| 664 | |||
| 665 | static const struct v4l2_subdev_pad_ops m5mols_pad_ops = { | ||
| 666 | .enum_mbus_code = m5mols_enum_mbus_code, | ||
| 667 | .get_fmt = m5mols_get_fmt, | ||
| 668 | .set_fmt = m5mols_set_fmt, | ||
| 669 | .get_frame_desc = m5mols_get_frame_desc, | ||
| 670 | .set_frame_desc = m5mols_set_frame_desc, | ||
| 671 | }; | ||
| 672 | |||
| 673 | /** | ||
| 674 | * m5mols_restore_controls - Apply current control values to the registers | ||
| 675 | * @info: M-5MOLS driver data structure | ||
| 676 | * | ||
| 677 | * m5mols_do_scenemode() handles all parameters for which there is yet no | ||
| 678 | * individual control. It should be replaced at some point by setting each | ||
| 679 | * control individually, in required register set up order. | ||
| 680 | */ | ||
| 681 | int m5mols_restore_controls(struct m5mols_info *info) | ||
| 682 | { | ||
| 683 | int ret; | ||
| 684 | |||
| 685 | if (info->ctrl_sync) | ||
| 686 | return 0; | ||
| 687 | |||
| 688 | ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); | ||
| 689 | if (ret) | ||
| 690 | return ret; | ||
| 691 | |||
| 692 | ret = v4l2_ctrl_handler_setup(&info->handle); | ||
| 693 | info->ctrl_sync = !ret; | ||
| 694 | |||
| 695 | return ret; | ||
| 696 | } | ||
| 697 | |||
| 698 | /** | ||
| 699 | * m5mols_start_monitor - Start the monitor mode | ||
| 700 | * @info: M-5MOLS driver data structure | ||
| 701 | * | ||
| 702 | * Before applying the controls setup the resolution and frame rate | ||
| 703 | * in PARAMETER mode, and then switch over to MONITOR mode. | ||
| 704 | */ | ||
| 705 | static int m5mols_start_monitor(struct m5mols_info *info) | ||
| 706 | { | ||
| 707 | struct v4l2_subdev *sd = &info->sd; | ||
| 708 | int ret; | ||
| 709 | |||
| 710 | ret = m5mols_set_mode(info, REG_PARAMETER); | ||
| 711 | if (!ret) | ||
| 712 | ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); | ||
| 713 | if (!ret) | ||
| 714 | ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); | ||
| 715 | if (!ret) | ||
| 716 | ret = m5mols_set_mode(info, REG_MONITOR); | ||
| 717 | if (!ret) | ||
| 718 | ret = m5mols_restore_controls(info); | ||
| 719 | |||
| 720 | return ret; | ||
| 721 | } | ||
| 722 | |||
| 723 | static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) | ||
| 724 | { | ||
| 725 | struct m5mols_info *info = to_m5mols(sd); | ||
| 726 | u32 code; | ||
| 727 | int ret; | ||
| 728 | |||
| 729 | mutex_lock(&info->lock); | ||
| 730 | code = info->ffmt[info->res_type].code; | ||
| 731 | |||
| 732 | if (enable) { | ||
| 733 | if (is_code(code, M5MOLS_RESTYPE_MONITOR)) | ||
| 734 | ret = m5mols_start_monitor(info); | ||
| 735 | else if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) | ||
| 736 | ret = m5mols_start_capture(info); | ||
| 737 | else | ||
| 738 | ret = -EINVAL; | ||
| 739 | } else { | ||
| 740 | ret = m5mols_set_mode(info, REG_PARAMETER); | ||
| 741 | } | ||
| 742 | |||
| 743 | mutex_unlock(&info->lock); | ||
| 744 | return ret; | ||
| 745 | } | ||
| 746 | |||
| 747 | static const struct v4l2_subdev_video_ops m5mols_video_ops = { | ||
| 748 | .s_stream = m5mols_s_stream, | ||
| 749 | }; | ||
| 750 | |||
| 751 | static int m5mols_sensor_power(struct m5mols_info *info, bool enable) | ||
| 752 | { | ||
| 753 | struct v4l2_subdev *sd = &info->sd; | ||
| 754 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 755 | int ret; | ||
| 756 | |||
| 757 | if (info->power == enable) | ||
| 758 | return 0; | ||
| 759 | |||
| 760 | if (enable) { | ||
| 761 | if (info->set_power) { | ||
| 762 | ret = info->set_power(&client->dev, 1); | ||
| 763 | if (ret) | ||
| 764 | return ret; | ||
| 765 | } | ||
| 766 | |||
| 767 | ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); | ||
| 768 | if (ret) { | ||
| 769 | if (info->set_power) | ||
| 770 | info->set_power(&client->dev, 0); | ||
| 771 | return ret; | ||
| 772 | } | ||
| 773 | |||
| 774 | gpiod_set_value(info->reset, 0); | ||
| 775 | info->power = 1; | ||
| 776 | |||
| 777 | return ret; | ||
| 778 | } | ||
| 779 | |||
| 780 | ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); | ||
| 781 | if (ret) | ||
| 782 | return ret; | ||
| 783 | |||
| 784 | if (info->set_power) | ||
| 785 | info->set_power(&client->dev, 0); | ||
| 786 | |||
| 787 | gpiod_set_value(info->reset, 1); | ||
| 788 | |||
| 789 | info->isp_ready = 0; | ||
| 790 | info->power = 0; | ||
| 791 | |||
| 792 | return ret; | ||
| 793 | } | ||
| 794 | |||
| 795 | /* m5mols_update_fw - optional firmware update routine */ | ||
| 796 | int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, | ||
| 797 | int (*set_power)(struct m5mols_info *, bool)) | ||
| 798 | { | ||
| 799 | return 0; | ||
| 800 | } | ||
| 801 | |||
| 802 | /** | ||
| 803 | * m5mols_fw_start - M-5MOLS internal ARM controller initialization | ||
| 804 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 805 | * | ||
| 806 | * Execute the M-5MOLS internal ARM controller initialization sequence. | ||
| 807 | * This function should be called after the supply voltage has been | ||
| 808 | * applied and before any requests to the device are made. | ||
| 809 | */ | ||
| 810 | static int m5mols_fw_start(struct v4l2_subdev *sd) | ||
| 811 | { | ||
| 812 | struct m5mols_info *info = to_m5mols(sd); | ||
| 813 | int ret; | ||
| 814 | |||
| 815 | atomic_set(&info->irq_done, 0); | ||
| 816 | /* Wait until I2C slave is initialized in Flash Writer mode */ | ||
| 817 | ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, | ||
| 818 | M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); | ||
| 819 | if (!ret) | ||
| 820 | ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); | ||
| 821 | if (!ret) | ||
| 822 | ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); | ||
| 823 | if (ret < 0) | ||
| 824 | return ret; | ||
| 825 | |||
| 826 | info->isp_ready = 1; | ||
| 827 | |||
| 828 | ret = m5mols_get_version(sd); | ||
| 829 | if (!ret) | ||
| 830 | ret = m5mols_update_fw(sd, m5mols_sensor_power); | ||
| 831 | if (ret) | ||
| 832 | return ret; | ||
| 833 | |||
| 834 | v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); | ||
| 835 | |||
| 836 | ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); | ||
| 837 | if (!ret) | ||
| 838 | ret = m5mols_enable_interrupt(sd, | ||
| 839 | REG_INT_AF | REG_INT_CAPTURE); | ||
| 840 | |||
| 841 | return ret; | ||
| 842 | } | ||
| 843 | |||
| 844 | /* Execute the lens soft-landing algorithm */ | ||
| 845 | static int m5mols_auto_focus_stop(struct m5mols_info *info) | ||
| 846 | { | ||
| 847 | int ret; | ||
| 848 | |||
| 849 | ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); | ||
| 850 | if (!ret) | ||
| 851 | ret = m5mols_write(&info->sd, AF_MODE, REG_AF_POWEROFF); | ||
| 852 | if (!ret) | ||
| 853 | ret = m5mols_busy_wait(&info->sd, SYSTEM_STATUS, REG_AF_IDLE, | ||
| 854 | 0xff, -1); | ||
| 855 | return ret; | ||
| 856 | } | ||
| 857 | |||
| 858 | /** | ||
| 859 | * m5mols_s_power - Main sensor power control function | ||
| 860 | * @sd: sub-device, as pointed by struct v4l2_subdev | ||
| 861 | * @on: if true, powers on the device; powers off otherwise. | ||
| 862 | * | ||
| 863 | * To prevent breaking the lens when the sensor is powered off the Soft-Landing | ||
| 864 | * algorithm is called where available. The Soft-Landing algorithm availability | ||
| 865 | * dependends on the firmware provider. | ||
| 866 | */ | ||
| 867 | static int m5mols_s_power(struct v4l2_subdev *sd, int on) | ||
| 868 | { | ||
| 869 | struct m5mols_info *info = to_m5mols(sd); | ||
| 870 | int ret; | ||
| 871 | |||
| 872 | mutex_lock(&info->lock); | ||
| 873 | |||
| 874 | if (on) { | ||
| 875 | ret = m5mols_sensor_power(info, true); | ||
| 876 | if (!ret) | ||
| 877 | ret = m5mols_fw_start(sd); | ||
| 878 | } else { | ||
| 879 | if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { | ||
| 880 | ret = m5mols_set_mode(info, REG_MONITOR); | ||
| 881 | if (!ret) | ||
| 882 | ret = m5mols_auto_focus_stop(info); | ||
| 883 | if (ret < 0) | ||
| 884 | v4l2_warn(sd, "Soft landing lens failed\n"); | ||
| 885 | } | ||
| 886 | ret = m5mols_sensor_power(info, false); | ||
| 887 | |||
| 888 | info->ctrl_sync = 0; | ||
| 889 | } | ||
| 890 | |||
| 891 | mutex_unlock(&info->lock); | ||
| 892 | return ret; | ||
| 893 | } | ||
| 894 | |||
| 895 | static int m5mols_log_status(struct v4l2_subdev *sd) | ||
| 896 | { | ||
| 897 | struct m5mols_info *info = to_m5mols(sd); | ||
| 898 | |||
| 899 | v4l2_ctrl_handler_log_status(&info->handle, sd->name); | ||
| 900 | |||
| 901 | return 0; | ||
| 902 | } | ||
| 903 | |||
| 904 | static const struct v4l2_subdev_core_ops m5mols_core_ops = { | ||
| 905 | .s_power = m5mols_s_power, | ||
| 906 | .log_status = m5mols_log_status, | ||
| 907 | }; | ||
| 908 | |||
| 909 | /* | ||
| 910 | * V4L2 subdev internal operations | ||
| 911 | */ | ||
| 912 | static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
| 913 | { | ||
| 914 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, | ||
| 915 | fh->state, | ||
| 916 | 0); | ||
| 917 | |||
| 918 | *format = m5mols_default_ffmt[0]; | ||
| 919 | return 0; | ||
| 920 | } | ||
| 921 | |||
| 922 | static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { | ||
| 923 | .open = m5mols_open, | ||
| 924 | }; | ||
| 925 | |||
| 926 | static const struct v4l2_subdev_ops m5mols_ops = { | ||
| 927 | .core = &m5mols_core_ops, | ||
| 928 | .pad = &m5mols_pad_ops, | ||
| 929 | .video = &m5mols_video_ops, | ||
| 930 | }; | ||
| 931 | |||
| 932 | static irqreturn_t m5mols_irq_handler(int irq, void *data) | ||
| 933 | { | ||
| 934 | struct m5mols_info *info = to_m5mols(data); | ||
| 935 | |||
| 936 | atomic_set(&info->irq_done, 1); | ||
| 937 | wake_up_interruptible(&info->irq_waitq); | ||
| 938 | |||
| 939 | return IRQ_HANDLED; | ||
| 940 | } | ||
| 941 | |||
| 942 | static int m5mols_probe(struct i2c_client *client) | ||
| 943 | { | ||
| 944 | const struct m5mols_platform_data *pdata = client->dev.platform_data; | ||
| 945 | struct m5mols_info *info; | ||
| 946 | struct v4l2_subdev *sd; | ||
| 947 | int ret; | ||
| 948 | |||
| 949 | if (pdata == NULL) { | ||
| 950 | dev_err(&client->dev, "No platform data\n"); | ||
| 951 | return -EINVAL; | ||
| 952 | } | ||
| 953 | |||
| 954 | if (!client->irq) { | ||
| 955 | dev_err(&client->dev, "Interrupt not assigned\n"); | ||
| 956 | return -EINVAL; | ||
| 957 | } | ||
| 958 | |||
| 959 | info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | ||
| 960 | if (!info) | ||
| 961 | return -ENOMEM; | ||
| 962 | |||
| 963 | /* This asserts reset, descriptor shall have polarity specified */ | ||
| 964 | info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); | ||
| 965 | if (IS_ERR(info->reset)) | ||
| 966 | return PTR_ERR(info->reset); | ||
| 967 | /* Notice: the "N" in M5MOLS_NRST implies active low */ | ||
| 968 | gpiod_set_consumer_name(info->reset, "M5MOLS_NRST"); | ||
| 969 | |||
| 970 | info->pdata = pdata; | ||
| 971 | info->set_power = pdata->set_power; | ||
| 972 | |||
| 973 | ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), | ||
| 974 | supplies); | ||
| 975 | if (ret) { | ||
| 976 | dev_err(&client->dev, "Failed to get regulators: %d\n", ret); | ||
| 977 | return ret; | ||
| 978 | } | ||
| 979 | |||
| 980 | sd = &info->sd; | ||
| 981 | v4l2_i2c_subdev_init(sd, client, &m5mols_ops); | ||
| 982 | /* Static name; NEVER use in new drivers! */ | ||
| 983 | strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); | ||
| 984 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 985 | |||
| 986 | sd->internal_ops = &m5mols_subdev_internal_ops; | ||
| 987 | info->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 988 | ret = media_entity_pads_init(&sd->entity, 1, &info->pad); | ||
| 989 | if (ret < 0) | ||
| 990 | return ret; | ||
| 991 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | ||
| 992 | |||
| 993 | init_waitqueue_head(&info->irq_waitq); | ||
| 994 | mutex_init(&info->lock); | ||
| 995 | |||
| 996 | ret = devm_request_irq(&client->dev, client->irq, m5mols_irq_handler, | ||
| 997 | IRQF_TRIGGER_RISING, MODULE_NAME, sd); | ||
| 998 | if (ret) { | ||
| 999 | dev_err(&client->dev, "Interrupt request failed: %d\n", ret); | ||
| 1000 | goto error; | ||
| 1001 | } | ||
| 1002 | info->res_type = M5MOLS_RESTYPE_MONITOR; | ||
| 1003 | info->ffmt[0] = m5mols_default_ffmt[0]; | ||
| 1004 | info->ffmt[1] = m5mols_default_ffmt[1]; | ||
| 1005 | |||
| 1006 | ret = m5mols_sensor_power(info, true); | ||
| 1007 | if (ret) | ||
| 1008 | goto error; | ||
| 1009 | |||
| 1010 | ret = m5mols_fw_start(sd); | ||
| 1011 | if (!ret) | ||
| 1012 | ret = m5mols_init_controls(sd); | ||
| 1013 | |||
| 1014 | ret = m5mols_sensor_power(info, false); | ||
| 1015 | if (!ret) | ||
| 1016 | return 0; | ||
| 1017 | error: | ||
| 1018 | media_entity_cleanup(&sd->entity); | ||
| 1019 | return ret; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | static void m5mols_remove(struct i2c_client *client) | ||
| 1023 | { | ||
| 1024 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
| 1025 | |||
| 1026 | v4l2_device_unregister_subdev(sd); | ||
| 1027 | v4l2_ctrl_handler_free(sd->ctrl_handler); | ||
| 1028 | media_entity_cleanup(&sd->entity); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | static const struct i2c_device_id m5mols_id[] = { | ||
| 1032 | { MODULE_NAME, 0 }, | ||
| 1033 | { }, | ||
| 1034 | }; | ||
| 1035 | MODULE_DEVICE_TABLE(i2c, m5mols_id); | ||
| 1036 | |||
| 1037 | static struct i2c_driver m5mols_i2c_driver = { | ||
| 1038 | .driver = { | ||
| 1039 | .name = MODULE_NAME, | ||
| 1040 | }, | ||
| 1041 | .probe_new = m5mols_probe, | ||
| 1042 | .remove = m5mols_remove, | ||
| 1043 | .id_table = m5mols_id, | ||
| 1044 | }; | ||
| 1045 | |||
| 1046 | module_i2c_driver(m5mols_i2c_driver); | ||
| 1047 | |||
| 1048 | MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); | ||
| 1049 | MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); | ||
| 1050 | MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); | ||
| 1051 | MODULE_LICENSE("GPL"); | ||
| @@ -1,359 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
| 2 | /* | ||
| 3 | * Register map for M-5MOLS 8M Pixel camera sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | ||
| 6 | * Author: HeungJun Kim <riverful.kim@samsung.com> | ||
| 7 | * | ||
| 8 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | ||
| 9 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef M5MOLS_REG_H | ||
| 13 | #define M5MOLS_REG_H | ||
| 14 | |||
| 15 | #define M5MOLS_I2C_MAX_SIZE 4 | ||
| 16 | #define M5MOLS_BYTE_READ 0x01 | ||
| 17 | #define M5MOLS_BYTE_WRITE 0x02 | ||
| 18 | |||
| 19 | #define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) | ||
| 20 | #define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) | ||
| 21 | #define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) | ||
| 22 | #define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) | ||
| 23 | |||
| 24 | /* | ||
| 25 | * Category section register | ||
| 26 | * | ||
| 27 | * The category means set including relevant command of M-5MOLS. | ||
| 28 | */ | ||
| 29 | #define CAT_SYSTEM 0x00 | ||
| 30 | #define CAT_PARAM 0x01 | ||
| 31 | #define CAT_MONITOR 0x02 | ||
| 32 | #define CAT_AE 0x03 | ||
| 33 | #define CAT_WB 0x06 | ||
| 34 | #define CAT_EXIF 0x07 | ||
| 35 | #define CAT_FD 0x09 | ||
| 36 | #define CAT_LENS 0x0a | ||
| 37 | #define CAT_CAPT_PARM 0x0b | ||
| 38 | #define CAT_CAPT_CTRL 0x0c | ||
| 39 | #define CAT_FLASH 0x0f /* related to FW, revisions, booting */ | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Category 0 - SYSTEM mode | ||
| 43 | * | ||
| 44 | * The SYSTEM mode in the M-5MOLS means area available to handle with the whole | ||
| 45 | * & all-round system of sensor. It deals with version/interrupt/setting mode & | ||
| 46 | * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by | ||
| 47 | * packaging & manufacturer, even the customer and project code. And the | ||
| 48 | * function details may vary among them. The version information helps to | ||
| 49 | * determine what methods shall be used in the driver. | ||
| 50 | * | ||
| 51 | * There is many registers between customer version address and awb one. For | ||
| 52 | * more specific contents, see definition if file m5mols.h. | ||
| 53 | */ | ||
| 54 | #define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) | ||
| 55 | #define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) | ||
| 56 | #define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) | ||
| 57 | #define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) | ||
| 58 | #define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) | ||
| 59 | #define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) | ||
| 60 | |||
| 61 | #define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) | ||
| 62 | #define REG_SYSINIT 0x00 /* SYSTEM mode */ | ||
| 63 | #define REG_PARAMETER 0x01 /* PARAMETER mode */ | ||
| 64 | #define REG_MONITOR 0x02 /* MONITOR mode */ | ||
| 65 | #define REG_CAPTURE 0x03 /* CAPTURE mode */ | ||
| 66 | |||
| 67 | #define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) | ||
| 68 | #define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) | ||
| 69 | #define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ | ||
| 70 | #define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ | ||
| 71 | #define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ | ||
| 72 | /* SYSTEM mode status */ | ||
| 73 | #define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) | ||
| 74 | |||
| 75 | /* Interrupt pending register */ | ||
| 76 | #define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) | ||
| 77 | /* interrupt enable register */ | ||
| 78 | #define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) | ||
| 79 | #define REG_INT_MODE (1 << 0) | ||
| 80 | #define REG_INT_AF (1 << 1) | ||
| 81 | #define REG_INT_ZOOM (1 << 2) | ||
| 82 | #define REG_INT_CAPTURE (1 << 3) | ||
| 83 | #define REG_INT_FRAMESYNC (1 << 4) | ||
| 84 | #define REG_INT_FD (1 << 5) | ||
| 85 | #define REG_INT_LENS_INIT (1 << 6) | ||
| 86 | #define REG_INT_SOUND (1 << 7) | ||
| 87 | #define REG_INT_MASK 0x0f | ||
| 88 | |||
| 89 | /* | ||
| 90 | * category 1 - PARAMETER mode | ||
| 91 | * | ||
| 92 | * This category supports function of camera features of M-5MOLS. It means we | ||
| 93 | * can handle with preview(MONITOR) resolution size/frame per second/interface | ||
| 94 | * between the sensor and the Application Processor/even the image effect. | ||
| 95 | */ | ||
| 96 | |||
| 97 | /* Resolution in the MONITOR mode */ | ||
| 98 | #define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) | ||
| 99 | |||
| 100 | /* Frame rate */ | ||
| 101 | #define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) | ||
| 102 | #define REG_FPS_30 0x02 | ||
| 103 | |||
| 104 | /* Video bus between the sensor and a host processor */ | ||
| 105 | #define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) | ||
| 106 | #define REG_INTERFACE_MIPI 0x02 | ||
| 107 | |||
| 108 | /* Image effects */ | ||
| 109 | #define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) | ||
| 110 | #define REG_EFFECT_OFF 0x00 | ||
| 111 | #define REG_EFFECT_NEGA 0x01 | ||
| 112 | #define REG_EFFECT_EMBOSS 0x06 | ||
| 113 | #define REG_EFFECT_OUTLINE 0x07 | ||
| 114 | #define REG_EFFECT_WATERCOLOR 0x08 | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Category 2 - MONITOR mode | ||
| 118 | * | ||
| 119 | * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another | ||
| 120 | * mode named "Preview", but this preview mode is used at the case specific | ||
| 121 | * vider-recording mode. This mmode supports only YUYV format. On the other | ||
| 122 | * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are | ||
| 123 | * another options like zoom/color effect(different with effect in PARAMETER | ||
| 124 | * mode)/anti hand shaking algorithm. | ||
| 125 | */ | ||
| 126 | |||
| 127 | /* Target digital zoom position */ | ||
| 128 | #define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) | ||
| 129 | |||
| 130 | /* CR value for color effect */ | ||
| 131 | #define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) | ||
| 132 | /* CB value for color effect */ | ||
| 133 | #define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) | ||
| 134 | #define REG_CFIXB_SEPIA 0xd8 | ||
| 135 | #define REG_CFIXR_SEPIA 0x18 | ||
| 136 | |||
| 137 | #define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) | ||
| 138 | #define REG_COLOR_EFFECT_OFF 0x00 | ||
| 139 | #define REG_COLOR_EFFECT_ON 0x01 | ||
| 140 | |||
| 141 | /* Chroma enable */ | ||
| 142 | #define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) | ||
| 143 | /* Chroma level */ | ||
| 144 | #define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) | ||
| 145 | #define REG_CHROMA_OFF 0x00 | ||
| 146 | #define REG_CHROMA_ON 0x01 | ||
| 147 | |||
| 148 | /* Sharpness on/off */ | ||
| 149 | #define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) | ||
| 150 | /* Sharpness level */ | ||
| 151 | #define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) | ||
| 152 | #define REG_EDGE_OFF 0x00 | ||
| 153 | #define REG_EDGE_ON 0x01 | ||
| 154 | |||
| 155 | /* Set color tone (contrast) */ | ||
| 156 | #define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Category 3 - Auto Exposure | ||
| 160 | * | ||
| 161 | * The M-5MOLS exposure capbility is detailed as which is similar to digital | ||
| 162 | * camera. This category supports AE locking/various AE mode(range of exposure) | ||
| 163 | * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the | ||
| 164 | * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be | ||
| 165 | * different. So, this category also provide getting the max/min values. And, | ||
| 166 | * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. | ||
| 167 | */ | ||
| 168 | |||
| 169 | /* Auto Exposure locking */ | ||
| 170 | #define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) | ||
| 171 | #define REG_AE_UNLOCK 0x00 | ||
| 172 | #define REG_AE_LOCK 0x01 | ||
| 173 | |||
| 174 | /* Auto Exposure algorithm mode */ | ||
| 175 | #define AE_MODE I2C_REG(CAT_AE, 0x01, 1) | ||
| 176 | #define REG_AE_OFF 0x00 /* AE off */ | ||
| 177 | #define REG_AE_ALL 0x01 /* calc AE in all block integral */ | ||
| 178 | #define REG_AE_CENTER 0x03 /* calc AE in center weighted */ | ||
| 179 | #define REG_AE_SPOT 0x06 /* calc AE in specific spot */ | ||
| 180 | |||
| 181 | #define AE_ISO I2C_REG(CAT_AE, 0x05, 1) | ||
| 182 | #define REG_ISO_AUTO 0x00 | ||
| 183 | #define REG_ISO_50 0x01 | ||
| 184 | #define REG_ISO_100 0x02 | ||
| 185 | #define REG_ISO_200 0x03 | ||
| 186 | #define REG_ISO_400 0x04 | ||
| 187 | #define REG_ISO_800 0x05 | ||
| 188 | |||
| 189 | /* EV (scenemode) preset for MONITOR */ | ||
| 190 | #define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) | ||
| 191 | /* EV (scenemode) preset for CAPTURE */ | ||
| 192 | #define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) | ||
| 193 | #define REG_SCENE_NORMAL 0x00 | ||
| 194 | #define REG_SCENE_PORTRAIT 0x01 | ||
| 195 | #define REG_SCENE_LANDSCAPE 0x02 | ||
| 196 | #define REG_SCENE_SPORTS 0x03 | ||
| 197 | #define REG_SCENE_PARTY_INDOOR 0x04 | ||
| 198 | #define REG_SCENE_BEACH_SNOW 0x05 | ||
| 199 | #define REG_SCENE_SUNSET 0x06 | ||
| 200 | #define REG_SCENE_DAWN_DUSK 0x07 | ||
| 201 | #define REG_SCENE_FALL 0x08 | ||
| 202 | #define REG_SCENE_NIGHT 0x09 | ||
| 203 | #define REG_SCENE_AGAINST_LIGHT 0x0a | ||
| 204 | #define REG_SCENE_FIRE 0x0b | ||
| 205 | #define REG_SCENE_TEXT 0x0c | ||
| 206 | #define REG_SCENE_CANDLE 0x0d | ||
| 207 | |||
| 208 | /* Manual gain in MONITOR mode */ | ||
| 209 | #define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) | ||
| 210 | /* Maximum gain in MONITOR mode */ | ||
| 211 | #define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) | ||
| 212 | /* Manual gain in CAPTURE mode */ | ||
| 213 | #define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) | ||
| 214 | |||
| 215 | #define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) | ||
| 216 | #define REG_AE_INDEX_20_NEG 0x00 | ||
| 217 | #define REG_AE_INDEX_15_NEG 0x01 | ||
| 218 | #define REG_AE_INDEX_10_NEG 0x02 | ||
| 219 | #define REG_AE_INDEX_05_NEG 0x03 | ||
| 220 | #define REG_AE_INDEX_00 0x04 | ||
| 221 | #define REG_AE_INDEX_05_POS 0x05 | ||
| 222 | #define REG_AE_INDEX_10_POS 0x06 | ||
| 223 | #define REG_AE_INDEX_15_POS 0x07 | ||
| 224 | #define REG_AE_INDEX_20_POS 0x08 | ||
| 225 | |||
| 226 | /* | ||
| 227 | * Category 6 - White Balance | ||
| 228 | */ | ||
| 229 | |||
| 230 | /* Auto Whitebalance locking */ | ||
| 231 | #define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) | ||
| 232 | #define REG_AWB_UNLOCK 0x00 | ||
| 233 | #define REG_AWB_LOCK 0x01 | ||
| 234 | |||
| 235 | #define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) | ||
| 236 | #define REG_AWB_AUTO 0x01 /* AWB off */ | ||
| 237 | #define REG_AWB_PRESET 0x02 /* AWB preset */ | ||
| 238 | |||
| 239 | /* Manual WB (preset) */ | ||
| 240 | #define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) | ||
| 241 | #define REG_AWB_INCANDESCENT 0x01 | ||
| 242 | #define REG_AWB_FLUORESCENT_1 0x02 | ||
| 243 | #define REG_AWB_FLUORESCENT_2 0x03 | ||
| 244 | #define REG_AWB_DAYLIGHT 0x04 | ||
| 245 | #define REG_AWB_CLOUDY 0x05 | ||
| 246 | #define REG_AWB_SHADE 0x06 | ||
| 247 | #define REG_AWB_HORIZON 0x07 | ||
| 248 | #define REG_AWB_LEDLIGHT 0x09 | ||
| 249 | |||
| 250 | /* | ||
| 251 | * Category 7 - EXIF information | ||
| 252 | */ | ||
| 253 | #define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) | ||
| 254 | #define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) | ||
| 255 | #define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) | ||
| 256 | #define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) | ||
| 257 | #define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) | ||
| 258 | #define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) | ||
| 259 | #define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) | ||
| 260 | #define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) | ||
| 261 | #define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) | ||
| 262 | #define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) | ||
| 263 | #define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) | ||
| 264 | #define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) | ||
| 265 | #define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) | ||
| 266 | #define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) | ||
| 267 | |||
| 268 | /* | ||
| 269 | * Category 9 - Face Detection | ||
| 270 | */ | ||
| 271 | #define FD_CTL I2C_REG(CAT_FD, 0x00, 1) | ||
| 272 | #define BIT_FD_EN 0 | ||
| 273 | #define BIT_FD_DRAW_FACE_FRAME 4 | ||
| 274 | #define BIT_FD_DRAW_SMILE_LVL 6 | ||
| 275 | #define REG_FD(shift) (1 << shift) | ||
| 276 | #define REG_FD_OFF 0x0 | ||
| 277 | |||
| 278 | /* | ||
| 279 | * Category A - Lens Parameter | ||
| 280 | */ | ||
| 281 | #define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) | ||
| 282 | #define REG_AF_NORMAL 0x00 /* Normal AF, one time */ | ||
| 283 | #define REG_AF_MACRO 0x01 /* Macro AF, one time */ | ||
| 284 | #define REG_AF_POWEROFF 0x07 | ||
| 285 | |||
| 286 | #define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) | ||
| 287 | #define REG_AF_STOP 0x00 | ||
| 288 | #define REG_AF_EXE_AUTO 0x01 | ||
| 289 | #define REG_AF_EXE_CAF 0x02 | ||
| 290 | |||
| 291 | #define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) | ||
| 292 | #define REG_AF_FAIL 0x00 | ||
| 293 | #define REG_AF_SUCCESS 0x02 | ||
| 294 | #define REG_AF_IDLE 0x04 | ||
| 295 | #define REG_AF_BUSY 0x05 | ||
| 296 | |||
| 297 | #define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Category B - CAPTURE Parameter | ||
| 301 | */ | ||
| 302 | #define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) | ||
| 303 | #define REG_YUV422 0x00 | ||
| 304 | #define REG_BAYER10 0x05 | ||
| 305 | #define REG_BAYER8 0x06 | ||
| 306 | #define REG_JPEG 0x10 | ||
| 307 | |||
| 308 | #define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) | ||
| 309 | #define CAPP_JPEG_SIZE_MAX I2C_REG(CAT_CAPT_PARM, 0x0f, 4) | ||
| 310 | #define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) | ||
| 311 | |||
| 312 | #define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) | ||
| 313 | #define REG_MCC_OFF 0x00 | ||
| 314 | #define REG_MCC_NORMAL 0x01 | ||
| 315 | |||
| 316 | #define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) | ||
| 317 | #define REG_WDR_OFF 0x00 | ||
| 318 | #define REG_WDR_ON 0x01 | ||
| 319 | #define REG_WDR_AUTO 0x02 | ||
| 320 | |||
| 321 | #define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) | ||
| 322 | #define REG_LIGHT_OFF 0x00 | ||
| 323 | #define REG_LIGHT_ON 0x01 | ||
| 324 | #define REG_LIGHT_AUTO 0x02 | ||
| 325 | |||
| 326 | #define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) | ||
| 327 | #define REG_FLASH_OFF 0x00 | ||
| 328 | #define REG_FLASH_ON 0x01 | ||
| 329 | #define REG_FLASH_AUTO 0x02 | ||
| 330 | |||
| 331 | /* | ||
| 332 | * Category C - CAPTURE Control | ||
| 333 | */ | ||
| 334 | #define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) | ||
| 335 | #define REG_CAP_NONE 0x00 | ||
| 336 | #define REG_CAP_ANTI_SHAKE 0x02 | ||
| 337 | |||
| 338 | /* Select single- or multi-shot capture */ | ||
| 339 | #define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) | ||
| 340 | |||
| 341 | #define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) | ||
| 342 | #define REG_CAP_START_MAIN 0x01 | ||
| 343 | #define REG_CAP_START_THUMB 0x03 | ||
| 344 | |||
| 345 | #define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) | ||
| 346 | #define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) | ||
| 347 | |||
| 348 | /* | ||
| 349 | * Category F - Flash | ||
| 350 | * | ||
| 351 | * This mode provides functions about internal flash stuff and system startup. | ||
| 352 | */ | ||
| 353 | |||
| 354 | /* Starts internal ARM core booting after power-up */ | ||
| 355 | #define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) | ||
| 356 | #define REG_START_ARM_BOOT 0x01 /* write value */ | ||
| 357 | #define REG_IN_FLASH_MODE 0x00 /* read value */ | ||
| 358 | |||
| 359 | #endif /* M5MOLS_REG_H */ | ||
| @@ -1122,6 +1122,7 @@ err_async: · 1121 unmodified lines | |||
| 1122 | static void max9286_v4l2_unregister(struct max9286_priv *priv) | 1122 | static void max9286_v4l2_unregister(struct max9286_priv *priv) |
| 1123 | { | 1123 | { |
| 1124 | fwnode_handle_put(priv->sd.fwnode); | 1124 | fwnode_handle_put(priv->sd.fwnode); |
| 1125 | v4l2_ctrl_handler_free(&priv->ctrls); | ||
| 1125 | v4l2_async_unregister_subdev(&priv->sd); | 1126 | v4l2_async_unregister_subdev(&priv->sd); |
| 1126 | max9286_v4l2_notifier_unregister(priv); | 1127 | max9286_v4l2_notifier_unregister(priv); |
| 1127 | } | 1128 | } |
| @@ -1,891 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* | ||
| 3 | * Driver for MT9M032 CMOS Image Sensor from Micron | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010-2011 Lund Engineering | ||
| 6 | * Contact: Gil Lund <gwlund@lundeng.com> | ||
| 7 | * Author: Martin Hostettler <martin@neutronstar.dyndns.org> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/i2c.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/math64.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/mutex.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/v4l2-mediabus.h> | ||
| 19 | |||
| 20 | #include <media/media-entity.h> | ||
| 21 | #include <media/i2c/mt9m032.h> | ||
| 22 | #include <media/v4l2-ctrls.h> | ||
| 23 | #include <media/v4l2-device.h> | ||
| 24 | #include <media/v4l2-subdev.h> | ||
| 25 | |||
| 26 | #include "aptina-pll.h" | ||
| 27 | |||
| 28 | /* | ||
| 29 | * width and height include active boundary and black parts | ||
| 30 | * | ||
| 31 | * column 0- 15 active boundary | ||
| 32 | * column 16-1455 image | ||
| 33 | * column 1456-1471 active boundary | ||
| 34 | * column 1472-1599 black | ||
| 35 | * | ||
| 36 | * row 0- 51 black | ||
| 37 | * row 53- 59 active boundary | ||
| 38 | * row 60-1139 image | ||
| 39 | * row 1140-1147 active boundary | ||
| 40 | * row 1148-1151 black | ||
| 41 | */ | ||
| 42 | |||
| 43 | #define MT9M032_PIXEL_ARRAY_WIDTH 1600 | ||
| 44 | #define MT9M032_PIXEL_ARRAY_HEIGHT 1152 | ||
| 45 | |||
| 46 | #define MT9M032_CHIP_VERSION 0x00 | ||
| 47 | #define MT9M032_CHIP_VERSION_VALUE 0x1402 | ||
| 48 | #define MT9M032_ROW_START 0x01 | ||
| 49 | #define MT9M032_ROW_START_MIN 0 | ||
| 50 | #define MT9M032_ROW_START_MAX 1152 | ||
| 51 | #define MT9M032_ROW_START_DEF 60 | ||
| 52 | #define MT9M032_COLUMN_START 0x02 | ||
| 53 | #define MT9M032_COLUMN_START_MIN 0 | ||
| 54 | #define MT9M032_COLUMN_START_MAX 1600 | ||
| 55 | #define MT9M032_COLUMN_START_DEF 16 | ||
| 56 | #define MT9M032_ROW_SIZE 0x03 | ||
| 57 | #define MT9M032_ROW_SIZE_MIN 32 | ||
| 58 | #define MT9M032_ROW_SIZE_MAX 1152 | ||
| 59 | #define MT9M032_ROW_SIZE_DEF 1080 | ||
| 60 | #define MT9M032_COLUMN_SIZE 0x04 | ||
| 61 | #define MT9M032_COLUMN_SIZE_MIN 32 | ||
| 62 | #define MT9M032_COLUMN_SIZE_MAX 1600 | ||
| 63 | #define MT9M032_COLUMN_SIZE_DEF 1440 | ||
| 64 | #define MT9M032_HBLANK 0x05 | ||
| 65 | #define MT9M032_VBLANK 0x06 | ||
| 66 | #define MT9M032_VBLANK_MAX 0x7ff | ||
| 67 | #define MT9M032_SHUTTER_WIDTH_HIGH 0x08 | ||
| 68 | #define MT9M032_SHUTTER_WIDTH_LOW 0x09 | ||
| 69 | #define MT9M032_SHUTTER_WIDTH_MIN 1 | ||
| 70 | #define MT9M032_SHUTTER_WIDTH_MAX 1048575 | ||
| 71 | #define MT9M032_SHUTTER_WIDTH_DEF 1943 | ||
| 72 | #define MT9M032_PIX_CLK_CTRL 0x0a | ||
| 73 | #define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 | ||
| 74 | #define MT9M032_RESTART 0x0b | ||
| 75 | #define MT9M032_RESET 0x0d | ||
| 76 | #define MT9M032_PLL_CONFIG1 0x11 | ||
| 77 | #define MT9M032_PLL_CONFIG1_PREDIV_MASK 0x3f | ||
| 78 | #define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 | ||
| 79 | #define MT9M032_READ_MODE1 0x1e | ||
| 80 | #define MT9M032_READ_MODE1_OUTPUT_BAD_FRAMES (1 << 13) | ||
| 81 | #define MT9M032_READ_MODE1_MAINTAIN_FRAME_RATE (1 << 12) | ||
| 82 | #define MT9M032_READ_MODE1_XOR_LINE_VALID (1 << 11) | ||
| 83 | #define MT9M032_READ_MODE1_CONT_LINE_VALID (1 << 10) | ||
| 84 | #define MT9M032_READ_MODE1_INVERT_TRIGGER (1 << 9) | ||
| 85 | #define MT9M032_READ_MODE1_SNAPSHOT (1 << 8) | ||
| 86 | #define MT9M032_READ_MODE1_GLOBAL_RESET (1 << 7) | ||
| 87 | #define MT9M032_READ_MODE1_BULB_EXPOSURE (1 << 6) | ||
| 88 | #define MT9M032_READ_MODE1_INVERT_STROBE (1 << 5) | ||
| 89 | #define MT9M032_READ_MODE1_STROBE_ENABLE (1 << 4) | ||
| 90 | #define MT9M032_READ_MODE1_STROBE_START_TRIG1 (0 << 2) | ||
| 91 | #define MT9M032_READ_MODE1_STROBE_START_EXP (1 << 2) | ||
| 92 | #define MT9M032_READ_MODE1_STROBE_START_SHUTTER (2 << 2) | ||
| 93 | #define MT9M032_READ_MODE1_STROBE_START_TRIG2 (3 << 2) | ||
| 94 | #define MT9M032_READ_MODE1_STROBE_END_TRIG1 (0 << 0) | ||
| 95 | #define MT9M032_READ_MODE1_STROBE_END_EXP (1 << 0) | ||
| 96 | #define MT9M032_READ_MODE1_STROBE_END_SHUTTER (2 << 0) | ||
| 97 | #define MT9M032_READ_MODE1_STROBE_END_TRIG2 (3 << 0) | ||
| 98 | #define MT9M032_READ_MODE2 0x20 | ||
| 99 | #define MT9M032_READ_MODE2_VFLIP_SHIFT 15 | ||
| 100 | #define MT9M032_READ_MODE2_HFLIP_SHIFT 14 | ||
| 101 | #define MT9M032_READ_MODE2_ROW_BLC 0x40 | ||
| 102 | #define MT9M032_GAIN_GREEN1 0x2b | ||
| 103 | #define MT9M032_GAIN_BLUE 0x2c | ||
| 104 | #define MT9M032_GAIN_RED 0x2d | ||
| 105 | #define MT9M032_GAIN_GREEN2 0x2e | ||
| 106 | |||
| 107 | /* write only */ | ||
| 108 | #define MT9M032_GAIN_ALL 0x35 | ||
| 109 | #define MT9M032_GAIN_DIGITAL_MASK 0x7f | ||
| 110 | #define MT9M032_GAIN_DIGITAL_SHIFT 8 | ||
| 111 | #define MT9M032_GAIN_AMUL_SHIFT 6 | ||
| 112 | #define MT9M032_GAIN_ANALOG_MASK 0x3f | ||
| 113 | #define MT9M032_FORMATTER1 0x9e | ||
| 114 | #define MT9M032_FORMATTER1_PLL_P1_6 (1 << 8) | ||
| 115 | #define MT9M032_FORMATTER1_PARALLEL (1 << 12) | ||
| 116 | #define MT9M032_FORMATTER2 0x9f | ||
| 117 | #define MT9M032_FORMATTER2_DOUT_EN 0x1000 | ||
| 118 | #define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 | ||
| 119 | |||
| 120 | /* | ||
| 121 | * The available MT9M032 datasheet is missing documentation for register 0x10 | ||
| 122 | * MT9P031 seems to be close enough, so use constants from that datasheet for | ||
| 123 | * now. | ||
| 124 | * But keep the name MT9P031 to remind us, that this isn't really confirmed | ||
| 125 | * for this sensor. | ||
| 126 | */ | ||
| 127 | #define MT9P031_PLL_CONTROL 0x10 | ||
| 128 | #define MT9P031_PLL_CONTROL_PWROFF 0x0050 | ||
| 129 | #define MT9P031_PLL_CONTROL_PWRON 0x0051 | ||
| 130 | #define MT9P031_PLL_CONTROL_USEPLL 0x0052 | ||
| 131 | |||
| 132 | struct mt9m032 { | ||
| 133 | struct v4l2_subdev subdev; | ||
| 134 | struct media_pad pad; | ||
| 135 | struct mt9m032_platform_data *pdata; | ||
| 136 | |||
| 137 | unsigned int pix_clock; | ||
| 138 | |||
| 139 | struct v4l2_ctrl_handler ctrls; | ||
| 140 | struct { | ||
| 141 | struct v4l2_ctrl *hflip; | ||
| 142 | struct v4l2_ctrl *vflip; | ||
| 143 | }; | ||
| 144 | |||
| 145 | struct mutex lock; /* Protects streaming, format, interval and crop */ | ||
| 146 | |||
| 147 | bool streaming; | ||
| 148 | |||
| 149 | struct v4l2_mbus_framefmt format; | ||
| 150 | struct v4l2_rect crop; | ||
| 151 | struct v4l2_fract frame_interval; | ||
| 152 | }; | ||
| 153 | |||
| 154 | #define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) | ||
| 155 | #define to_dev(sensor) \ | ||
| 156 | (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) | ||
| 157 | |||
| 158 | static int mt9m032_read(struct i2c_client *client, u8 reg) | ||
| 159 | { | ||
| 160 | return i2c_smbus_read_word_swapped(client, reg); | ||
| 161 | } | ||
| 162 | |||
| 163 | static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) | ||
| 164 | { | ||
| 165 | return i2c_smbus_write_word_swapped(client, reg, data); | ||
| 166 | } | ||
| 167 | |||
| 168 | static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) | ||
| 169 | { | ||
| 170 | unsigned int effective_width; | ||
| 171 | u32 ns; | ||
| 172 | |||
| 173 | effective_width = width + 716; /* empirical value */ | ||
| 174 | ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); | ||
| 175 | dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); | ||
| 176 | return ns; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int mt9m032_update_timing(struct mt9m032 *sensor, | ||
| 180 | struct v4l2_fract *interval) | ||
| 181 | { | ||
| 182 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 183 | struct v4l2_rect *crop = &sensor->crop; | ||
| 184 | unsigned int min_vblank; | ||
| 185 | unsigned int vblank; | ||
| 186 | u32 row_time; | ||
| 187 | |||
| 188 | if (!interval) | ||
| 189 | interval = &sensor->frame_interval; | ||
| 190 | |||
| 191 | row_time = mt9m032_row_time(sensor, crop->width); | ||
| 192 | |||
| 193 | vblank = div_u64(1000000000ULL * interval->numerator, | ||
| 194 | (u64)row_time * interval->denominator) | ||
| 195 | - crop->height; | ||
| 196 | |||
| 197 | if (vblank > MT9M032_VBLANK_MAX) { | ||
| 198 | /* hardware limits to 11 bit values */ | ||
| 199 | interval->denominator = 1000; | ||
| 200 | interval->numerator = | ||
| 201 | div_u64((crop->height + MT9M032_VBLANK_MAX) * | ||
| 202 | (u64)row_time * interval->denominator, | ||
| 203 | 1000000000ULL); | ||
| 204 | vblank = div_u64(1000000000ULL * interval->numerator, | ||
| 205 | (u64)row_time * interval->denominator) | ||
| 206 | - crop->height; | ||
| 207 | } | ||
| 208 | /* enforce minimal 1.6ms blanking time. */ | ||
| 209 | min_vblank = 1600000 / row_time; | ||
| 210 | vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); | ||
| 211 | |||
| 212 | return mt9m032_write(client, MT9M032_VBLANK, vblank); | ||
| 213 | } | ||
| 214 | |||
| 215 | static int mt9m032_update_geom_timing(struct mt9m032 *sensor) | ||
| 216 | { | ||
| 217 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 218 | int ret; | ||
| 219 | |||
| 220 | ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, | ||
| 221 | sensor->crop.width - 1); | ||
| 222 | if (!ret) | ||
| 223 | ret = mt9m032_write(client, MT9M032_ROW_SIZE, | ||
| 224 | sensor->crop.height - 1); | ||
| 225 | if (!ret) | ||
| 226 | ret = mt9m032_write(client, MT9M032_COLUMN_START, | ||
| 227 | sensor->crop.left); | ||
| 228 | if (!ret) | ||
| 229 | ret = mt9m032_write(client, MT9M032_ROW_START, | ||
| 230 | sensor->crop.top); | ||
| 231 | if (!ret) | ||
| 232 | ret = mt9m032_update_timing(sensor, NULL); | ||
| 233 | return ret; | ||
| 234 | } | ||
| 235 | |||
| 236 | static int update_formatter2(struct mt9m032 *sensor, bool streaming) | ||
| 237 | { | ||
| 238 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 239 | u16 reg_val = MT9M032_FORMATTER2_DOUT_EN | ||
| 240 | | 0x0070; /* parts reserved! */ | ||
| 241 | /* possibly for changing to 14-bit mode */ | ||
| 242 | |||
| 243 | if (streaming) | ||
| 244 | reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ | ||
| 245 | |||
| 246 | return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); | ||
| 247 | } | ||
| 248 | |||
| 249 | static int mt9m032_setup_pll(struct mt9m032 *sensor) | ||
| 250 | { | ||
| 251 | static const struct aptina_pll_limits limits = { | ||
| 252 | .ext_clock_min = 8000000, | ||
| 253 | .ext_clock_max = 16500000, | ||
| 254 | .int_clock_min = 2000000, | ||
| 255 | .int_clock_max = 24000000, | ||
| 256 | .out_clock_min = 322000000, | ||
| 257 | .out_clock_max = 693000000, | ||
| 258 | .pix_clock_max = 99000000, | ||
| 259 | .n_min = 1, | ||
| 260 | .n_max = 64, | ||
| 261 | .m_min = 16, | ||
| 262 | .m_max = 255, | ||
| 263 | .p1_min = 6, | ||
| 264 | .p1_max = 7, | ||
| 265 | }; | ||
| 266 | |||
| 267 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 268 | struct mt9m032_platform_data *pdata = sensor->pdata; | ||
| 269 | struct aptina_pll pll; | ||
| 270 | u16 reg_val; | ||
| 271 | int ret; | ||
| 272 | |||
| 273 | pll.ext_clock = pdata->ext_clock; | ||
| 274 | pll.pix_clock = pdata->pix_clock; | ||
| 275 | |||
| 276 | ret = aptina_pll_calculate(&client->dev, &limits, &pll); | ||
| 277 | if (ret < 0) | ||
| 278 | return ret; | ||
| 279 | |||
| 280 | sensor->pix_clock = pdata->pix_clock; | ||
| 281 | |||
| 282 | ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, | ||
| 283 | (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) | | ||
| 284 | ((pll.n - 1) & MT9M032_PLL_CONFIG1_PREDIV_MASK)); | ||
| 285 | if (!ret) | ||
| 286 | ret = mt9m032_write(client, MT9P031_PLL_CONTROL, | ||
| 287 | MT9P031_PLL_CONTROL_PWRON | | ||
| 288 | MT9P031_PLL_CONTROL_USEPLL); | ||
| 289 | if (!ret) /* more reserved, Continuous, Master Mode */ | ||
| 290 | ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8000 | | ||
| 291 | MT9M032_READ_MODE1_STROBE_START_EXP | | ||
| 292 | MT9M032_READ_MODE1_STROBE_END_SHUTTER); | ||
| 293 | if (!ret) { | ||
| 294 | reg_val = (pll.p1 == 6 ? MT9M032_FORMATTER1_PLL_P1_6 : 0) | ||
| 295 | | MT9M032_FORMATTER1_PARALLEL | 0x001e; /* 14-bit */ | ||
| 296 | ret = mt9m032_write(client, MT9M032_FORMATTER1, reg_val); | ||
| 297 | } | ||
| 298 | |||
| 299 | return ret; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* ----------------------------------------------------------------------------- | ||
| 303 | * Subdev pad operations | ||
| 304 | */ | ||
| 305 | |||
| 306 | static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, | ||
| 307 | struct v4l2_subdev_state *sd_state, | ||
| 308 | struct v4l2_subdev_mbus_code_enum *code) | ||
| 309 | { | ||
| 310 | if (code->index != 0) | ||
| 311 | return -EINVAL; | ||
| 312 | |||
| 313 | code->code = MEDIA_BUS_FMT_Y8_1X8; | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, | ||
| 318 | struct v4l2_subdev_state *sd_state, | ||
| 319 | struct v4l2_subdev_frame_size_enum *fse) | ||
| 320 | { | ||
| 321 | if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8) | ||
| 322 | return -EINVAL; | ||
| 323 | |||
| 324 | fse->min_width = MT9M032_COLUMN_SIZE_DEF; | ||
| 325 | fse->max_width = MT9M032_COLUMN_SIZE_DEF; | ||
| 326 | fse->min_height = MT9M032_ROW_SIZE_DEF; | ||
| 327 | fse->max_height = MT9M032_ROW_SIZE_DEF; | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * __mt9m032_get_pad_crop() - get crop rect | ||
| 334 | * @sensor: pointer to the sensor struct | ||
| 335 | * @sd_state: v4l2_subdev_state for getting the try crop rect from | ||
| 336 | * @which: select try or active crop rect | ||
| 337 | * | ||
| 338 | * Returns a pointer the current active or fh relative try crop rect | ||
| 339 | */ | ||
| 340 | static struct v4l2_rect * | ||
| 341 | __mt9m032_get_pad_crop(struct mt9m032 *sensor, | ||
| 342 | struct v4l2_subdev_state *sd_state, | ||
| 343 | enum v4l2_subdev_format_whence which) | ||
| 344 | { | ||
| 345 | switch (which) { | ||
| 346 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 347 | return v4l2_subdev_get_try_crop(&sensor->subdev, sd_state, 0); | ||
| 348 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 349 | return &sensor->crop; | ||
| 350 | default: | ||
| 351 | return NULL; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * __mt9m032_get_pad_format() - get format | ||
| 357 | * @sensor: pointer to the sensor struct | ||
| 358 | * @sd_state: v4l2_subdev_state for getting the try format from | ||
| 359 | * @which: select try or active format | ||
| 360 | * | ||
| 361 | * Returns a pointer the current active or fh relative try format | ||
| 362 | */ | ||
| 363 | static struct v4l2_mbus_framefmt * | ||
| 364 | __mt9m032_get_pad_format(struct mt9m032 *sensor, | ||
| 365 | struct v4l2_subdev_state *sd_state, | ||
| 366 | enum v4l2_subdev_format_whence which) | ||
| 367 | { | ||
| 368 | switch (which) { | ||
| 369 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 370 | return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, | ||
| 371 | 0); | ||
| 372 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 373 | return &sensor->format; | ||
| 374 | default: | ||
| 375 | return NULL; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, | ||
| 380 | struct v4l2_subdev_state *sd_state, | ||
| 381 | struct v4l2_subdev_format *fmt) | ||
| 382 | { | ||
| 383 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 384 | |||
| 385 | mutex_lock(&sensor->lock); | ||
| 386 | fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); | ||
| 387 | mutex_unlock(&sensor->lock); | ||
| 388 | |||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, | ||
| 393 | struct v4l2_subdev_state *sd_state, | ||
| 394 | struct v4l2_subdev_format *fmt) | ||
| 395 | { | ||
| 396 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 397 | int ret; | ||
| 398 | |||
| 399 | mutex_lock(&sensor->lock); | ||
| 400 | |||
| 401 | if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
| 402 | ret = -EBUSY; | ||
| 403 | goto done; | ||
| 404 | } | ||
| 405 | |||
| 406 | /* Scaling is not supported, the format is thus fixed. */ | ||
| 407 | fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); | ||
| 408 | ret = 0; | ||
| 409 | |||
| 410 | done: | ||
| 411 | mutex_unlock(&sensor->lock); | ||
| 412 | return ret; | ||
| 413 | } | ||
| 414 | |||
| 415 | static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, | ||
| 416 | struct v4l2_subdev_state *sd_state, | ||
| 417 | struct v4l2_subdev_selection *sel) | ||
| 418 | { | ||
| 419 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 420 | |||
| 421 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 422 | return -EINVAL; | ||
| 423 | |||
| 424 | mutex_lock(&sensor->lock); | ||
| 425 | sel->r = *__mt9m032_get_pad_crop(sensor, sd_state, sel->which); | ||
| 426 | mutex_unlock(&sensor->lock); | ||
| 427 | |||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, | ||
| 432 | struct v4l2_subdev_state *sd_state, | ||
| 433 | struct v4l2_subdev_selection *sel) | ||
| 434 | { | ||
| 435 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 436 | struct v4l2_mbus_framefmt *format; | ||
| 437 | struct v4l2_rect *__crop; | ||
| 438 | struct v4l2_rect rect; | ||
| 439 | int ret = 0; | ||
| 440 | |||
| 441 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 442 | return -EINVAL; | ||
| 443 | |||
| 444 | mutex_lock(&sensor->lock); | ||
| 445 | |||
| 446 | if (sensor->streaming && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
| 447 | ret = -EBUSY; | ||
| 448 | goto done; | ||
| 449 | } | ||
| 450 | |||
| 451 | /* Clamp the crop rectangle boundaries and align them to a multiple of 2 | ||
| 452 | * pixels to ensure a GRBG Bayer pattern. | ||
| 453 | */ | ||
| 454 | rect.left = clamp(ALIGN(sel->r.left, 2), MT9M032_COLUMN_START_MIN, | ||
| 455 | MT9M032_COLUMN_START_MAX); | ||
| 456 | rect.top = clamp(ALIGN(sel->r.top, 2), MT9M032_ROW_START_MIN, | ||
| 457 | MT9M032_ROW_START_MAX); | ||
| 458 | rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), | ||
| 459 | MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); | ||
| 460 | rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), | ||
| 461 | MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); | ||
| 462 | |||
| 463 | rect.width = min_t(unsigned int, rect.width, | ||
| 464 | MT9M032_PIXEL_ARRAY_WIDTH - rect.left); | ||
| 465 | rect.height = min_t(unsigned int, rect.height, | ||
| 466 | MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); | ||
| 467 | |||
| 468 | __crop = __mt9m032_get_pad_crop(sensor, sd_state, sel->which); | ||
| 469 | |||
| 470 | if (rect.width != __crop->width || rect.height != __crop->height) { | ||
| 471 | /* Reset the output image size if the crop rectangle size has | ||
| 472 | * been modified. | ||
| 473 | */ | ||
| 474 | format = __mt9m032_get_pad_format(sensor, sd_state, | ||
| 475 | sel->which); | ||
| 476 | format->width = rect.width; | ||
| 477 | format->height = rect.height; | ||
| 478 | } | ||
| 479 | |||
| 480 | *__crop = rect; | ||
| 481 | sel->r = rect; | ||
| 482 | |||
| 483 | if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
| 484 | ret = mt9m032_update_geom_timing(sensor); | ||
| 485 | |||
| 486 | done: | ||
| 487 | mutex_unlock(&sensor->lock); | ||
| 488 | return ret; | ||
| 489 | } | ||
| 490 | |||
| 491 | static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, | ||
| 492 | struct v4l2_subdev_frame_interval *fi) | ||
| 493 | { | ||
| 494 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 495 | |||
| 496 | mutex_lock(&sensor->lock); | ||
| 497 | memset(fi, 0, sizeof(*fi)); | ||
| 498 | fi->interval = sensor->frame_interval; | ||
| 499 | mutex_unlock(&sensor->lock); | ||
| 500 | |||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | |||
| 504 | static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, | ||
| 505 | struct v4l2_subdev_frame_interval *fi) | ||
| 506 | { | ||
| 507 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 508 | int ret; | ||
| 509 | |||
| 510 | mutex_lock(&sensor->lock); | ||
| 511 | |||
| 512 | if (sensor->streaming) { | ||
| 513 | ret = -EBUSY; | ||
| 514 | goto done; | ||
| 515 | } | ||
| 516 | |||
| 517 | /* Avoid divisions by 0. */ | ||
| 518 | if (fi->interval.denominator == 0) | ||
| 519 | fi->interval.denominator = 1; | ||
| 520 | |||
| 521 | ret = mt9m032_update_timing(sensor, &fi->interval); | ||
| 522 | if (!ret) | ||
| 523 | sensor->frame_interval = fi->interval; | ||
| 524 | |||
| 525 | done: | ||
| 526 | mutex_unlock(&sensor->lock); | ||
| 527 | return ret; | ||
| 528 | } | ||
| 529 | |||
| 530 | static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) | ||
| 531 | { | ||
| 532 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 533 | int ret; | ||
| 534 | |||
| 535 | mutex_lock(&sensor->lock); | ||
| 536 | ret = update_formatter2(sensor, streaming); | ||
| 537 | if (!ret) | ||
| 538 | sensor->streaming = streaming; | ||
| 539 | mutex_unlock(&sensor->lock); | ||
| 540 | |||
| 541 | return ret; | ||
| 542 | } | ||
| 543 | |||
| 544 | /* ----------------------------------------------------------------------------- | ||
| 545 | * V4L2 subdev core operations | ||
| 546 | */ | ||
| 547 | |||
| 548 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
| 549 | static int mt9m032_g_register(struct v4l2_subdev *sd, | ||
| 550 | struct v4l2_dbg_register *reg) | ||
| 551 | { | ||
| 552 | struct mt9m032 *sensor = to_mt9m032(sd); | ||
| 553 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 554 | int val; | ||
| 555 | |||
| 556 | if (reg->reg > 0xff) | ||
| 557 | return -EINVAL; | ||
| 558 | |||
| 559 | val = mt9m032_read(client, reg->reg); | ||
| 560 | if (val < 0) | ||
| 561 | return -EIO; | ||
| 562 | |||
| 563 | reg->size = 2; | ||
| 564 | reg->val = val; | ||
| 565 | |||
| 566 | return 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | static int mt9m032_s_register(struct v4l2_subdev *sd, | ||
| 570 | const struct v4l2_dbg_register *reg) | ||
| 571 | { | ||
| 572 | struct mt9m032 *sensor = to_mt9m032(sd); | ||
| 573 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 574 | |||
| 575 | if (reg->reg > 0xff) | ||
| 576 | return -EINVAL; | ||
| 577 | |||
| 578 | return mt9m032_write(client, reg->reg, reg->val); | ||
| 579 | } | ||
| 580 | #endif | ||
| 581 | |||
| 582 | /* ----------------------------------------------------------------------------- | ||
| 583 | * V4L2 subdev control operations | ||
| 584 | */ | ||
| 585 | |||
| 586 | static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) | ||
| 587 | { | ||
| 588 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 589 | int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) | ||
| 590 | | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) | ||
| 591 | | MT9M032_READ_MODE2_ROW_BLC | ||
| 592 | | 0x0007; | ||
| 593 | |||
| 594 | return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); | ||
| 595 | } | ||
| 596 | |||
| 597 | static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) | ||
| 598 | { | ||
| 599 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 600 | int digital_gain_val; /* in 1/8th (0..127) */ | ||
| 601 | int analog_mul; /* 0 or 1 */ | ||
| 602 | int analog_gain_val; /* in 1/16th. (0..63) */ | ||
| 603 | u16 reg_val; | ||
| 604 | |||
| 605 | digital_gain_val = 51; /* from setup example */ | ||
| 606 | |||
| 607 | if (val < 63) { | ||
| 608 | analog_mul = 0; | ||
| 609 | analog_gain_val = val; | ||
| 610 | } else { | ||
| 611 | analog_mul = 1; | ||
| 612 | analog_gain_val = val / 2; | ||
| 613 | } | ||
| 614 | |||
| 615 | /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ | ||
| 616 | /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ | ||
| 617 | |||
| 618 | reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) | ||
| 619 | << MT9M032_GAIN_DIGITAL_SHIFT) | ||
| 620 | | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) | ||
| 621 | | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); | ||
| 622 | |||
| 623 | return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); | ||
| 624 | } | ||
| 625 | |||
| 626 | static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) | ||
| 627 | { | ||
| 628 | if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { | ||
| 629 | /* round because of multiplier used for values >= 63 */ | ||
| 630 | ctrl->val &= ~1; | ||
| 631 | } | ||
| 632 | |||
| 633 | return 0; | ||
| 634 | } | ||
| 635 | |||
| 636 | static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) | ||
| 637 | { | ||
| 638 | struct mt9m032 *sensor = | ||
| 639 | container_of(ctrl->handler, struct mt9m032, ctrls); | ||
| 640 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); | ||
| 641 | int ret; | ||
| 642 | |||
| 643 | switch (ctrl->id) { | ||
| 644 | case V4L2_CID_GAIN: | ||
| 645 | return mt9m032_set_gain(sensor, ctrl->val); | ||
| 646 | |||
| 647 | case V4L2_CID_HFLIP: | ||
| 648 | /* case V4L2_CID_VFLIP: -- In the same cluster */ | ||
| 649 | return update_read_mode2(sensor, sensor->vflip->val, | ||
| 650 | sensor->hflip->val); | ||
| 651 | |||
| 652 | case V4L2_CID_EXPOSURE: | ||
| 653 | ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, | ||
| 654 | (ctrl->val >> 16) & 0xffff); | ||
| 655 | if (ret < 0) | ||
| 656 | return ret; | ||
| 657 | |||
| 658 | return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, | ||
| 659 | ctrl->val & 0xffff); | ||
| 660 | } | ||
| 661 | |||
| 662 | return 0; | ||
| 663 | } | ||
| 664 | |||
| 665 | static const struct v4l2_ctrl_ops mt9m032_ctrl_ops = { | ||
| 666 | .s_ctrl = mt9m032_set_ctrl, | ||
| 667 | .try_ctrl = mt9m032_try_ctrl, | ||
| 668 | }; | ||
| 669 | |||
| 670 | /* -------------------------------------------------------------------------- */ | ||
| 671 | |||
| 672 | static const struct v4l2_subdev_core_ops mt9m032_core_ops = { | ||
| 673 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
| 674 | .g_register = mt9m032_g_register, | ||
| 675 | .s_register = mt9m032_s_register, | ||
| 676 | #endif | ||
| 677 | }; | ||
| 678 | |||
| 679 | static const struct v4l2_subdev_video_ops mt9m032_video_ops = { | ||
| 680 | .s_stream = mt9m032_s_stream, | ||
| 681 | .g_frame_interval = mt9m032_get_frame_interval, | ||
| 682 | .s_frame_interval = mt9m032_set_frame_interval, | ||
| 683 | }; | ||
| 684 | |||
| 685 | static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { | ||
| 686 | .enum_mbus_code = mt9m032_enum_mbus_code, | ||
| 687 | .enum_frame_size = mt9m032_enum_frame_size, | ||
| 688 | .get_fmt = mt9m032_get_pad_format, | ||
| 689 | .set_fmt = mt9m032_set_pad_format, | ||
| 690 | .set_selection = mt9m032_set_pad_selection, | ||
| 691 | .get_selection = mt9m032_get_pad_selection, | ||
| 692 | }; | ||
| 693 | |||
| 694 | static const struct v4l2_subdev_ops mt9m032_ops = { | ||
| 695 | .core = &mt9m032_core_ops, | ||
| 696 | .video = &mt9m032_video_ops, | ||
| 697 | .pad = &mt9m032_pad_ops, | ||
| 698 | }; | ||
| 699 | |||
| 700 | /* ----------------------------------------------------------------------------- | ||
| 701 | * Driver initialization and probing | ||
| 702 | */ | ||
| 703 | |||
| 704 | static int mt9m032_probe(struct i2c_client *client) | ||
| 705 | { | ||
| 706 | struct mt9m032_platform_data *pdata = client->dev.platform_data; | ||
| 707 | struct i2c_adapter *adapter = client->adapter; | ||
| 708 | struct mt9m032 *sensor; | ||
| 709 | int chip_version; | ||
| 710 | int ret; | ||
| 711 | |||
| 712 | if (pdata == NULL) { | ||
| 713 | dev_err(&client->dev, "No platform data\n"); | ||
| 714 | return -EINVAL; | ||
| 715 | } | ||
| 716 | |||
| 717 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { | ||
| 718 | dev_warn(&client->dev, | ||
| 719 | "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); | ||
| 720 | return -EIO; | ||
| 721 | } | ||
| 722 | |||
| 723 | if (!client->dev.platform_data) | ||
| 724 | return -ENODEV; | ||
| 725 | |||
| 726 | sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); | ||
| 727 | if (sensor == NULL) | ||
| 728 | return -ENOMEM; | ||
| 729 | |||
| 730 | mutex_init(&sensor->lock); | ||
| 731 | |||
| 732 | sensor->pdata = pdata; | ||
| 733 | |||
| 734 | v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); | ||
| 735 | sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 736 | |||
| 737 | chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); | ||
| 738 | if (chip_version != MT9M032_CHIP_VERSION_VALUE) { | ||
| 739 | dev_err(&client->dev, "MT9M032 not detected, wrong version " | ||
| 740 | "0x%04x\n", chip_version); | ||
| 741 | ret = -ENODEV; | ||
| 742 | goto error_sensor; | ||
| 743 | } | ||
| 744 | |||
| 745 | dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", | ||
| 746 | client->addr); | ||
| 747 | |||
| 748 | sensor->frame_interval.numerator = 1; | ||
| 749 | sensor->frame_interval.denominator = 30; | ||
| 750 | |||
| 751 | sensor->crop.left = MT9M032_COLUMN_START_DEF; | ||
| 752 | sensor->crop.top = MT9M032_ROW_START_DEF; | ||
| 753 | sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; | ||
| 754 | sensor->crop.height = MT9M032_ROW_SIZE_DEF; | ||
| 755 | |||
| 756 | sensor->format.width = sensor->crop.width; | ||
| 757 | sensor->format.height = sensor->crop.height; | ||
| 758 | sensor->format.code = MEDIA_BUS_FMT_Y8_1X8; | ||
| 759 | sensor->format.field = V4L2_FIELD_NONE; | ||
| 760 | sensor->format.colorspace = V4L2_COLORSPACE_SRGB; | ||
| 761 | |||
| 762 | v4l2_ctrl_handler_init(&sensor->ctrls, 5); | ||
| 763 | |||
| 764 | v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, | ||
| 765 | V4L2_CID_GAIN, 0, 127, 1, 64); | ||
| 766 | |||
| 767 | sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, | ||
| 768 | &mt9m032_ctrl_ops, | ||
| 769 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
| 770 | sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, | ||
| 771 | &mt9m032_ctrl_ops, | ||
| 772 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
| 773 | |||
| 774 | v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, | ||
| 775 | V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, | ||
| 776 | MT9M032_SHUTTER_WIDTH_MAX, 1, | ||
| 777 | MT9M032_SHUTTER_WIDTH_DEF); | ||
| 778 | v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, | ||
| 779 | V4L2_CID_PIXEL_RATE, pdata->pix_clock, | ||
| 780 | pdata->pix_clock, 1, pdata->pix_clock); | ||
| 781 | |||
| 782 | if (sensor->ctrls.error) { | ||
| 783 | ret = sensor->ctrls.error; | ||
| 784 | dev_err(&client->dev, "control initialization error %d\n", ret); | ||
| 785 | goto error_ctrl; | ||
| 786 | } | ||
| 787 | |||
| 788 | v4l2_ctrl_cluster(2, &sensor->hflip); | ||
| 789 | |||
| 790 | sensor->subdev.ctrl_handler = &sensor->ctrls; | ||
| 791 | sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; | ||
| 792 | sensor->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 793 | ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); | ||
| 794 | if (ret < 0) | ||
| 795 | goto error_ctrl; | ||
| 796 | |||
| 797 | ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ | ||
| 798 | if (ret < 0) | ||
| 799 | goto error_entity; | ||
| 800 | ret = mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ | ||
| 801 | if (ret < 0) | ||
| 802 | goto error_entity; | ||
| 803 | |||
| 804 | ret = mt9m032_setup_pll(sensor); | ||
| 805 | if (ret < 0) | ||
| 806 | goto error_entity; | ||
| 807 | usleep_range(10000, 11000); | ||
| 808 | |||
| 809 | ret = v4l2_ctrl_handler_setup(&sensor->ctrls); | ||
| 810 | if (ret < 0) | ||
| 811 | goto error_entity; | ||
| 812 | |||
| 813 | /* SIZE */ | ||
| 814 | ret = mt9m032_update_geom_timing(sensor); | ||
| 815 | if (ret < 0) | ||
| 816 | goto error_entity; | ||
| 817 | |||
| 818 | ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ | ||
| 819 | if (ret < 0) | ||
| 820 | goto error_entity; | ||
| 821 | ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ | ||
| 822 | if (ret < 0) | ||
| 823 | goto error_entity; | ||
| 824 | ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ | ||
| 825 | if (ret < 0) | ||
| 826 | goto error_entity; | ||
| 827 | ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ | ||
| 828 | if (ret < 0) | ||
| 829 | goto error_entity; | ||
| 830 | if (sensor->pdata->invert_pixclock) { | ||
| 831 | ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, | ||
| 832 | MT9M032_PIX_CLK_CTRL_INV_PIXCLK); | ||
| 833 | if (ret < 0) | ||
| 834 | goto error_entity; | ||
| 835 | } | ||
| 836 | |||
| 837 | ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ | ||
| 838 | if (ret < 0) | ||
| 839 | goto error_entity; | ||
| 840 | msleep(100); | ||
| 841 | ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ | ||
| 842 | if (ret < 0) | ||
| 843 | goto error_entity; | ||
| 844 | msleep(100); | ||
| 845 | ret = update_formatter2(sensor, false); | ||
| 846 | if (ret < 0) | ||
| 847 | goto error_entity; | ||
| 848 | |||
| 849 | return ret; | ||
| 850 | |||
| 851 | error_entity: | ||
| 852 | media_entity_cleanup(&sensor->subdev.entity); | ||
| 853 | error_ctrl: | ||
| 854 | v4l2_ctrl_handler_free(&sensor->ctrls); | ||
| 855 | error_sensor: | ||
| 856 | mutex_destroy(&sensor->lock); | ||
| 857 | return ret; | ||
| 858 | } | ||
| 859 | |||
| 860 | static void mt9m032_remove(struct i2c_client *client) | ||
| 861 | { | ||
| 862 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
| 863 | struct mt9m032 *sensor = to_mt9m032(subdev); | ||
| 864 | |||
| 865 | v4l2_device_unregister_subdev(subdev); | ||
| 866 | v4l2_ctrl_handler_free(&sensor->ctrls); | ||
| 867 | media_entity_cleanup(&subdev->entity); | ||
| 868 | mutex_destroy(&sensor->lock); | ||
| 869 | } | ||
| 870 | |||
| 871 | static const struct i2c_device_id mt9m032_id_table[] = { | ||
| 872 | { MT9M032_NAME, 0 }, | ||
| 873 | { } | ||
| 874 | }; | ||
| 875 | |||
| 876 | MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); | ||
| 877 | |||
| 878 | static struct i2c_driver mt9m032_i2c_driver = { | ||
| 879 | .driver = { | ||
| 880 | .name = MT9M032_NAME, | ||
| 881 | }, | ||
| 882 | .probe_new = mt9m032_probe, | ||
| 883 | .remove = mt9m032_remove, | ||
| 884 | .id_table = mt9m032_id_table, | ||
| 885 | }; | ||
| 886 | |||
| 887 | module_i2c_driver(mt9m032_i2c_driver); | ||
| 888 | |||
| 889 | MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>"); | ||
| 890 | MODULE_DESCRIPTION("MT9M032 camera sensor driver"); | ||
| 891 | MODULE_LICENSE("GPL v2"); | ||
| @@ -1,992 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* | ||
| 3 | * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 6 | * | ||
| 7 | * Based on the MT9M001 driver, | ||
| 8 | * | ||
| 9 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/i2c.h> | ||
| 14 | #include <linux/log2.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/regulator/consumer.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/videodev2.h> | ||
| 19 | #include <linux/v4l2-mediabus.h> | ||
| 20 | |||
| 21 | #include <media/i2c/mt9t001.h> | ||
| 22 | #include <media/v4l2-ctrls.h> | ||
| 23 | #include <media/v4l2-device.h> | ||
| 24 | #include <media/v4l2-subdev.h> | ||
| 25 | |||
| 26 | #define MT9T001_PIXEL_ARRAY_HEIGHT 1568 | ||
| 27 | #define MT9T001_PIXEL_ARRAY_WIDTH 2112 | ||
| 28 | |||
| 29 | #define MT9T001_CHIP_VERSION 0x00 | ||
| 30 | #define MT9T001_CHIP_ID 0x1621 | ||
| 31 | #define MT9T001_ROW_START 0x01 | ||
| 32 | #define MT9T001_ROW_START_MIN 0 | ||
| 33 | #define MT9T001_ROW_START_DEF 20 | ||
| 34 | #define MT9T001_ROW_START_MAX 1534 | ||
| 35 | #define MT9T001_COLUMN_START 0x02 | ||
| 36 | #define MT9T001_COLUMN_START_MIN 0 | ||
| 37 | #define MT9T001_COLUMN_START_DEF 32 | ||
| 38 | #define MT9T001_COLUMN_START_MAX 2046 | ||
| 39 | #define MT9T001_WINDOW_HEIGHT 0x03 | ||
| 40 | #define MT9T001_WINDOW_HEIGHT_MIN 1 | ||
| 41 | #define MT9T001_WINDOW_HEIGHT_DEF 1535 | ||
| 42 | #define MT9T001_WINDOW_HEIGHT_MAX 1567 | ||
| 43 | #define MT9T001_WINDOW_WIDTH 0x04 | ||
| 44 | #define MT9T001_WINDOW_WIDTH_MIN 1 | ||
| 45 | #define MT9T001_WINDOW_WIDTH_DEF 2047 | ||
| 46 | #define MT9T001_WINDOW_WIDTH_MAX 2111 | ||
| 47 | #define MT9T001_HORIZONTAL_BLANKING 0x05 | ||
| 48 | #define MT9T001_HORIZONTAL_BLANKING_MIN 21 | ||
| 49 | #define MT9T001_HORIZONTAL_BLANKING_MAX 1023 | ||
| 50 | #define MT9T001_VERTICAL_BLANKING 0x06 | ||
| 51 | #define MT9T001_VERTICAL_BLANKING_MIN 3 | ||
| 52 | #define MT9T001_VERTICAL_BLANKING_MAX 1023 | ||
| 53 | #define MT9T001_OUTPUT_CONTROL 0x07 | ||
| 54 | #define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) | ||
| 55 | #define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) | ||
| 56 | #define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) | ||
| 57 | #define MT9T001_OUTPUT_CONTROL_DEF 0x0002 | ||
| 58 | #define MT9T001_SHUTTER_WIDTH_HIGH 0x08 | ||
| 59 | #define MT9T001_SHUTTER_WIDTH_LOW 0x09 | ||
| 60 | #define MT9T001_SHUTTER_WIDTH_MIN 1 | ||
| 61 | #define MT9T001_SHUTTER_WIDTH_DEF 1561 | ||
| 62 | #define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) | ||
| 63 | #define MT9T001_PIXEL_CLOCK 0x0a | ||
| 64 | #define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) | ||
| 65 | #define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) | ||
| 66 | #define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 | ||
| 67 | #define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) | ||
| 68 | #define MT9T001_FRAME_RESTART 0x0b | ||
| 69 | #define MT9T001_SHUTTER_DELAY 0x0c | ||
| 70 | #define MT9T001_SHUTTER_DELAY_MAX 2047 | ||
| 71 | #define MT9T001_RESET 0x0d | ||
| 72 | #define MT9T001_READ_MODE1 0x1e | ||
| 73 | #define MT9T001_READ_MODE_SNAPSHOT (1 << 8) | ||
| 74 | #define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) | ||
| 75 | #define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) | ||
| 76 | #define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) | ||
| 77 | #define MT9T001_READ_MODE2 0x20 | ||
| 78 | #define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) | ||
| 79 | #define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) | ||
| 80 | #define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) | ||
| 81 | #define MT9T001_READ_MODE3 0x21 | ||
| 82 | #define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) | ||
| 83 | #define MT9T001_READ_MODE_GHST_CTL (1 << 1) | ||
| 84 | #define MT9T001_ROW_ADDRESS_MODE 0x22 | ||
| 85 | #define MT9T001_ROW_SKIP_MASK (7 << 0) | ||
| 86 | #define MT9T001_ROW_BIN_MASK (3 << 3) | ||
| 87 | #define MT9T001_ROW_BIN_SHIFT 3 | ||
| 88 | #define MT9T001_COLUMN_ADDRESS_MODE 0x23 | ||
| 89 | #define MT9T001_COLUMN_SKIP_MASK (7 << 0) | ||
| 90 | #define MT9T001_COLUMN_BIN_MASK (3 << 3) | ||
| 91 | #define MT9T001_COLUMN_BIN_SHIFT 3 | ||
| 92 | #define MT9T001_GREEN1_GAIN 0x2b | ||
| 93 | #define MT9T001_BLUE_GAIN 0x2c | ||
| 94 | #define MT9T001_RED_GAIN 0x2d | ||
| 95 | #define MT9T001_GREEN2_GAIN 0x2e | ||
| 96 | #define MT9T001_TEST_DATA 0x32 | ||
| 97 | #define MT9T001_GLOBAL_GAIN 0x35 | ||
| 98 | #define MT9T001_GLOBAL_GAIN_MIN 8 | ||
| 99 | #define MT9T001_GLOBAL_GAIN_MAX 1024 | ||
| 100 | #define MT9T001_BLACK_LEVEL 0x49 | ||
| 101 | #define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b | ||
| 102 | #define MT9T001_BLC_DELTA_THRESHOLDS 0x5d | ||
| 103 | #define MT9T001_CAL_THRESHOLDS 0x5f | ||
| 104 | #define MT9T001_GREEN1_OFFSET 0x60 | ||
| 105 | #define MT9T001_GREEN2_OFFSET 0x61 | ||
| 106 | #define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 | ||
| 107 | #define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) | ||
| 108 | #define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) | ||
| 109 | #define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) | ||
| 110 | #define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) | ||
| 111 | #define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) | ||
| 112 | #define MT9T001_RED_OFFSET 0x63 | ||
| 113 | #define MT9T001_BLUE_OFFSET 0x64 | ||
| 114 | |||
| 115 | struct mt9t001 { | ||
| 116 | struct v4l2_subdev subdev; | ||
| 117 | struct media_pad pad; | ||
| 118 | |||
| 119 | struct clk *clk; | ||
| 120 | struct regulator_bulk_data regulators[2]; | ||
| 121 | |||
| 122 | struct mutex power_lock; /* lock to protect power_count */ | ||
| 123 | int power_count; | ||
| 124 | |||
| 125 | struct v4l2_mbus_framefmt format; | ||
| 126 | struct v4l2_rect crop; | ||
| 127 | |||
| 128 | struct v4l2_ctrl_handler ctrls; | ||
| 129 | struct v4l2_ctrl *gains[4]; | ||
| 130 | |||
| 131 | u16 output_control; | ||
| 132 | u16 black_level; | ||
| 133 | }; | ||
| 134 | |||
| 135 | static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) | ||
| 136 | { | ||
| 137 | return container_of(sd, struct mt9t001, subdev); | ||
| 138 | } | ||
| 139 | |||
| 140 | static int mt9t001_read(struct i2c_client *client, u8 reg) | ||
| 141 | { | ||
| 142 | return i2c_smbus_read_word_swapped(client, reg); | ||
| 143 | } | ||
| 144 | |||
| 145 | static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) | ||
| 146 | { | ||
| 147 | return i2c_smbus_write_word_swapped(client, reg, data); | ||
| 148 | } | ||
| 149 | |||
| 150 | static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, | ||
| 151 | u16 set) | ||
| 152 | { | ||
| 153 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
| 154 | u16 value = (mt9t001->output_control & ~clear) | set; | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | if (value == mt9t001->output_control) | ||
| 158 | return 0; | ||
| 159 | |||
| 160 | ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); | ||
| 161 | if (ret < 0) | ||
| 162 | return ret; | ||
| 163 | |||
| 164 | mt9t001->output_control = value; | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int mt9t001_reset(struct mt9t001 *mt9t001) | ||
| 169 | { | ||
| 170 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
| 171 | int ret; | ||
| 172 | |||
| 173 | /* Reset the chip and stop data read out */ | ||
| 174 | ret = mt9t001_write(client, MT9T001_RESET, 1); | ||
| 175 | if (ret < 0) | ||
| 176 | return ret; | ||
| 177 | |||
| 178 | ret = mt9t001_write(client, MT9T001_RESET, 0); | ||
| 179 | if (ret < 0) | ||
| 180 | return ret; | ||
| 181 | |||
| 182 | mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; | ||
| 183 | |||
| 184 | return mt9t001_set_output_control(mt9t001, | ||
| 185 | MT9T001_OUTPUT_CONTROL_CHIP_ENABLE, | ||
| 186 | 0); | ||
| 187 | } | ||
| 188 | |||
| 189 | static int mt9t001_power_on(struct mt9t001 *mt9t001) | ||
| 190 | { | ||
| 191 | int ret; | ||
| 192 | |||
| 193 | /* Bring up the supplies */ | ||
| 194 | ret = regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators), | ||
| 195 | mt9t001->regulators); | ||
| 196 | if (ret < 0) | ||
| 197 | return ret; | ||
| 198 | |||
| 199 | /* Enable clock */ | ||
| 200 | ret = clk_prepare_enable(mt9t001->clk); | ||
| 201 | if (ret < 0) | ||
| 202 | regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), | ||
| 203 | mt9t001->regulators); | ||
| 204 | |||
| 205 | return ret; | ||
| 206 | } | ||
| 207 | |||
| 208 | static void mt9t001_power_off(struct mt9t001 *mt9t001) | ||
| 209 | { | ||
| 210 | regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), | ||
| 211 | mt9t001->regulators); | ||
| 212 | |||
| 213 | clk_disable_unprepare(mt9t001->clk); | ||
| 214 | } | ||
| 215 | |||
| 216 | static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) | ||
| 217 | { | ||
| 218 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
| 219 | int ret; | ||
| 220 | |||
| 221 | if (!on) { | ||
| 222 | mt9t001_power_off(mt9t001); | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | ret = mt9t001_power_on(mt9t001); | ||
| 227 | if (ret < 0) | ||
| 228 | return ret; | ||
| 229 | |||
| 230 | ret = mt9t001_reset(mt9t001); | ||
| 231 | if (ret < 0) { | ||
| 232 | dev_err(&client->dev, "Failed to reset the camera\n"); | ||
| 233 | goto e_power; | ||
| 234 | } | ||
| 235 | |||
| 236 | ret = v4l2_ctrl_handler_setup(&mt9t001->ctrls); | ||
| 237 | if (ret < 0) { | ||
| 238 | dev_err(&client->dev, "Failed to set up control handlers\n"); | ||
| 239 | goto e_power; | ||
| 240 | } | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | |||
| 244 | e_power: | ||
| 245 | mt9t001_power_off(mt9t001); | ||
| 246 | |||
| 247 | return ret; | ||
| 248 | } | ||
| 249 | |||
| 250 | /* ----------------------------------------------------------------------------- | ||
| 251 | * V4L2 subdev video operations | ||
| 252 | */ | ||
| 253 | |||
| 254 | static struct v4l2_mbus_framefmt * | ||
| 255 | __mt9t001_get_pad_format(struct mt9t001 *mt9t001, | ||
| 256 | struct v4l2_subdev_state *sd_state, | ||
| 257 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
| 258 | { | ||
| 259 | switch (which) { | ||
| 260 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 261 | return v4l2_subdev_get_try_format(&mt9t001->subdev, sd_state, | ||
| 262 | pad); | ||
| 263 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 264 | return &mt9t001->format; | ||
| 265 | default: | ||
| 266 | return NULL; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | static struct v4l2_rect * | ||
| 271 | __mt9t001_get_pad_crop(struct mt9t001 *mt9t001, | ||
| 272 | struct v4l2_subdev_state *sd_state, | ||
| 273 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
| 274 | { | ||
| 275 | switch (which) { | ||
| 276 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 277 | return v4l2_subdev_get_try_crop(&mt9t001->subdev, sd_state, | ||
| 278 | pad); | ||
| 279 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 280 | return &mt9t001->crop; | ||
| 281 | default: | ||
| 282 | return NULL; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) | ||
| 287 | { | ||
| 288 | const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; | ||
| 289 | struct i2c_client *client = v4l2_get_subdevdata(subdev); | ||
| 290 | struct mt9t001_platform_data *pdata = client->dev.platform_data; | ||
| 291 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 292 | struct v4l2_mbus_framefmt *format = &mt9t001->format; | ||
| 293 | struct v4l2_rect *crop = &mt9t001->crop; | ||
| 294 | unsigned int hratio; | ||
| 295 | unsigned int vratio; | ||
| 296 | int ret; | ||
| 297 | |||
| 298 | if (!enable) | ||
| 299 | return mt9t001_set_output_control(mt9t001, mode, 0); | ||
| 300 | |||
| 301 | /* Configure the pixel clock polarity */ | ||
| 302 | if (pdata->clk_pol) { | ||
| 303 | ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, | ||
| 304 | MT9T001_PIXEL_CLOCK_INVERT); | ||
| 305 | if (ret < 0) | ||
| 306 | return ret; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* Configure the window size and row/column bin */ | ||
| 310 | hratio = DIV_ROUND_CLOSEST(crop->width, format->width); | ||
| 311 | vratio = DIV_ROUND_CLOSEST(crop->height, format->height); | ||
| 312 | |||
| 313 | ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); | ||
| 314 | if (ret < 0) | ||
| 315 | return ret; | ||
| 316 | |||
| 317 | ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); | ||
| 318 | if (ret < 0) | ||
| 319 | return ret; | ||
| 320 | |||
| 321 | ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); | ||
| 322 | if (ret < 0) | ||
| 323 | return ret; | ||
| 324 | |||
| 325 | ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); | ||
| 326 | if (ret < 0) | ||
| 327 | return ret; | ||
| 328 | |||
| 329 | ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); | ||
| 330 | if (ret < 0) | ||
| 331 | return ret; | ||
| 332 | |||
| 333 | ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); | ||
| 334 | if (ret < 0) | ||
| 335 | return ret; | ||
| 336 | |||
| 337 | /* Switch to master "normal" mode */ | ||
| 338 | return mt9t001_set_output_control(mt9t001, 0, mode); | ||
| 339 | } | ||
| 340 | |||
| 341 | static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, | ||
| 342 | struct v4l2_subdev_state *sd_state, | ||
| 343 | struct v4l2_subdev_mbus_code_enum *code) | ||
| 344 | { | ||
| 345 | if (code->index > 0) | ||
| 346 | return -EINVAL; | ||
| 347 | |||
| 348 | code->code = MEDIA_BUS_FMT_SGRBG10_1X10; | ||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, | ||
| 353 | struct v4l2_subdev_state *sd_state, | ||
| 354 | struct v4l2_subdev_frame_size_enum *fse) | ||
| 355 | { | ||
| 356 | if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) | ||
| 357 | return -EINVAL; | ||
| 358 | |||
| 359 | fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; | ||
| 360 | fse->max_width = fse->min_width; | ||
| 361 | fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; | ||
| 362 | fse->max_height = fse->min_height; | ||
| 363 | |||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | |||
| 367 | static int mt9t001_get_format(struct v4l2_subdev *subdev, | ||
| 368 | struct v4l2_subdev_state *sd_state, | ||
| 369 | struct v4l2_subdev_format *format) | ||
| 370 | { | ||
| 371 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 372 | |||
| 373 | format->format = *__mt9t001_get_pad_format(mt9t001, sd_state, | ||
| 374 | format->pad, | ||
| 375 | format->which); | ||
| 376 | return 0; | ||
| 377 | } | ||
| 378 | |||
| 379 | static int mt9t001_set_format(struct v4l2_subdev *subdev, | ||
| 380 | struct v4l2_subdev_state *sd_state, | ||
| 381 | struct v4l2_subdev_format *format) | ||
| 382 | { | ||
| 383 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 384 | struct v4l2_mbus_framefmt *__format; | ||
| 385 | struct v4l2_rect *__crop; | ||
| 386 | unsigned int width; | ||
| 387 | unsigned int height; | ||
| 388 | unsigned int hratio; | ||
| 389 | unsigned int vratio; | ||
| 390 | |||
| 391 | __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, format->pad, | ||
| 392 | format->which); | ||
| 393 | |||
| 394 | /* Clamp the width and height to avoid dividing by zero. */ | ||
| 395 | width = clamp_t(unsigned int, ALIGN(format->format.width, 2), | ||
| 396 | max_t(unsigned int, __crop->width / 8, | ||
| 397 | MT9T001_WINDOW_HEIGHT_MIN + 1), | ||
| 398 | __crop->width); | ||
| 399 | height = clamp_t(unsigned int, ALIGN(format->format.height, 2), | ||
| 400 | max_t(unsigned int, __crop->height / 8, | ||
| 401 | MT9T001_WINDOW_HEIGHT_MIN + 1), | ||
| 402 | __crop->height); | ||
| 403 | |||
| 404 | hratio = DIV_ROUND_CLOSEST(__crop->width, width); | ||
| 405 | vratio = DIV_ROUND_CLOSEST(__crop->height, height); | ||
| 406 | |||
| 407 | __format = __mt9t001_get_pad_format(mt9t001, sd_state, format->pad, | ||
| 408 | format->which); | ||
| 409 | __format->width = __crop->width / hratio; | ||
| 410 | __format->height = __crop->height / vratio; | ||
| 411 | |||
| 412 | format->format = *__format; | ||
| 413 | |||
| 414 | return 0; | ||
| 415 | } | ||
| 416 | |||
| 417 | static int mt9t001_get_selection(struct v4l2_subdev *subdev, | ||
| 418 | struct v4l2_subdev_state *sd_state, | ||
| 419 | struct v4l2_subdev_selection *sel) | ||
| 420 | { | ||
| 421 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 422 | |||
| 423 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 424 | return -EINVAL; | ||
| 425 | |||
| 426 | sel->r = *__mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, | ||
| 427 | sel->which); | ||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | static int mt9t001_set_selection(struct v4l2_subdev *subdev, | ||
| 432 | struct v4l2_subdev_state *sd_state, | ||
| 433 | struct v4l2_subdev_selection *sel) | ||
| 434 | { | ||
| 435 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 436 | struct v4l2_mbus_framefmt *__format; | ||
| 437 | struct v4l2_rect *__crop; | ||
| 438 | struct v4l2_rect rect; | ||
| 439 | |||
| 440 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 441 | return -EINVAL; | ||
| 442 | |||
| 443 | /* Clamp the crop rectangle boundaries and align them to a multiple of 2 | ||
| 444 | * pixels. | ||
| 445 | */ | ||
| 446 | rect.left = clamp(ALIGN(sel->r.left, 2), | ||
| 447 | MT9T001_COLUMN_START_MIN, | ||
| 448 | MT9T001_COLUMN_START_MAX); | ||
| 449 | rect.top = clamp(ALIGN(sel->r.top, 2), | ||
| 450 | MT9T001_ROW_START_MIN, | ||
| 451 | MT9T001_ROW_START_MAX); | ||
| 452 | rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), | ||
| 453 | MT9T001_WINDOW_WIDTH_MIN + 1, | ||
| 454 | MT9T001_WINDOW_WIDTH_MAX + 1); | ||
| 455 | rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), | ||
| 456 | MT9T001_WINDOW_HEIGHT_MIN + 1, | ||
| 457 | MT9T001_WINDOW_HEIGHT_MAX + 1); | ||
| 458 | |||
| 459 | rect.width = min_t(unsigned int, rect.width, | ||
| 460 | MT9T001_PIXEL_ARRAY_WIDTH - rect.left); | ||
| 461 | rect.height = min_t(unsigned int, rect.height, | ||
| 462 | MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); | ||
| 463 | |||
| 464 | __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, | ||
| 465 | sel->which); | ||
| 466 | |||
| 467 | if (rect.width != __crop->width || rect.height != __crop->height) { | ||
| 468 | /* Reset the output image size if the crop rectangle size has | ||
| 469 | * been modified. | ||
| 470 | */ | ||
| 471 | __format = __mt9t001_get_pad_format(mt9t001, sd_state, | ||
| 472 | sel->pad, | ||
| 473 | sel->which); | ||
| 474 | __format->width = rect.width; | ||
| 475 | __format->height = rect.height; | ||
| 476 | } | ||
| 477 | |||
| 478 | *__crop = rect; | ||
| 479 | sel->r = rect; | ||
| 480 | |||
| 481 | return 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | /* ----------------------------------------------------------------------------- | ||
| 485 | * V4L2 subdev control operations | ||
| 486 | */ | ||
| 487 | |||
| 488 | #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) | ||
| 489 | #define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) | ||
| 490 | #define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) | ||
| 491 | #define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) | ||
| 492 | |||
| 493 | #define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) | ||
| 494 | #define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) | ||
| 495 | #define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) | ||
| 496 | #define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) | ||
| 497 | |||
| 498 | static u16 mt9t001_gain_value(s32 *gain) | ||
| 499 | { | ||
| 500 | /* Gain is controlled by 2 analog stages and a digital stage. Valid | ||
| 501 | * values for the 3 stages are | ||
| 502 | * | ||
| 503 | * Stage Min Max Step | ||
| 504 | * ------------------------------------------ | ||
| 505 | * First analog stage x1 x2 1 | ||
| 506 | * Second analog stage x1 x4 0.125 | ||
| 507 | * Digital stage x1 x16 0.125 | ||
| 508 | * | ||
| 509 | * To minimize noise, the gain stages should be used in the second | ||
| 510 | * analog stage, first analog stage, digital stage order. Gain from a | ||
| 511 | * previous stage should be pushed to its maximum value before the next | ||
| 512 | * stage is used. | ||
| 513 | */ | ||
| 514 | if (*gain <= 32) | ||
| 515 | return *gain; | ||
| 516 | |||
| 517 | if (*gain <= 64) { | ||
| 518 | *gain &= ~1; | ||
| 519 | return (1 << 6) | (*gain >> 1); | ||
| 520 | } | ||
| 521 | |||
| 522 | *gain &= ~7; | ||
| 523 | return ((*gain - 64) << 5) | (1 << 6) | 32; | ||
| 524 | } | ||
| 525 | |||
| 526 | static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) | ||
| 527 | { | ||
| 528 | return mt9t001_set_output_control(mt9t001, | ||
| 529 | freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, | ||
| 530 | freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); | ||
| 531 | } | ||
| 532 | |||
| 533 | static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 534 | { | ||
| 535 | static const u8 gains[4] = { | ||
| 536 | MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, | ||
| 537 | MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN | ||
| 538 | }; | ||
| 539 | |||
| 540 | struct mt9t001 *mt9t001 = | ||
| 541 | container_of(ctrl->handler, struct mt9t001, ctrls); | ||
| 542 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
| 543 | unsigned int count; | ||
| 544 | unsigned int i; | ||
| 545 | u16 value; | ||
| 546 | int ret; | ||
| 547 | |||
| 548 | switch (ctrl->id) { | ||
| 549 | case V4L2_CID_GAIN_RED: | ||
| 550 | case V4L2_CID_GAIN_GREEN_RED: | ||
| 551 | case V4L2_CID_GAIN_GREEN_BLUE: | ||
| 552 | case V4L2_CID_GAIN_BLUE: | ||
| 553 | |||
| 554 | /* Disable control updates if more than one control has changed | ||
| 555 | * in the cluster. | ||
| 556 | */ | ||
| 557 | for (i = 0, count = 0; i < 4; ++i) { | ||
| 558 | struct v4l2_ctrl *gain = mt9t001->gains[i]; | ||
| 559 | |||
| 560 | if (gain->val != gain->cur.val) | ||
| 561 | count++; | ||
| 562 | } | ||
| 563 | |||
| 564 | if (count > 1) { | ||
| 565 | ret = mt9t001_ctrl_freeze(mt9t001, true); | ||
| 566 | if (ret < 0) | ||
| 567 | return ret; | ||
| 568 | } | ||
| 569 | |||
| 570 | /* Update the gain controls. */ | ||
| 571 | for (i = 0; i < 4; ++i) { | ||
| 572 | struct v4l2_ctrl *gain = mt9t001->gains[i]; | ||
| 573 | |||
| 574 | if (gain->val == gain->cur.val) | ||
| 575 | continue; | ||
| 576 | |||
| 577 | value = mt9t001_gain_value(&gain->val); | ||
| 578 | ret = mt9t001_write(client, gains[i], value); | ||
| 579 | if (ret < 0) { | ||
| 580 | mt9t001_ctrl_freeze(mt9t001, false); | ||
| 581 | return ret; | ||
| 582 | } | ||
| 583 | } | ||
| 584 | |||
| 585 | /* Enable control updates. */ | ||
| 586 | if (count > 1) { | ||
| 587 | ret = mt9t001_ctrl_freeze(mt9t001, false); | ||
| 588 | if (ret < 0) | ||
| 589 | return ret; | ||
| 590 | } | ||
| 591 | |||
| 592 | break; | ||
| 593 | |||
| 594 | case V4L2_CID_EXPOSURE: | ||
| 595 | ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, | ||
| 596 | ctrl->val & 0xffff); | ||
| 597 | if (ret < 0) | ||
| 598 | return ret; | ||
| 599 | |||
| 600 | return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, | ||
| 601 | ctrl->val >> 16); | ||
| 602 | |||
| 603 | case V4L2_CID_TEST_PATTERN: | ||
| 604 | return mt9t001_set_output_control(mt9t001, | ||
| 605 | ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, | ||
| 606 | ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); | ||
| 607 | |||
| 608 | case V4L2_CID_TEST_PATTERN_COLOR: | ||
| 609 | return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); | ||
| 610 | |||
| 611 | case V4L2_CID_BLACK_LEVEL_AUTO: | ||
| 612 | value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; | ||
| 613 | ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, | ||
| 614 | value); | ||
| 615 | if (ret < 0) | ||
| 616 | return ret; | ||
| 617 | |||
| 618 | mt9t001->black_level = value; | ||
| 619 | break; | ||
| 620 | |||
| 621 | case V4L2_CID_BLACK_LEVEL_OFFSET: | ||
| 622 | ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); | ||
| 623 | if (ret < 0) | ||
| 624 | return ret; | ||
| 625 | |||
| 626 | ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); | ||
| 627 | if (ret < 0) | ||
| 628 | return ret; | ||
| 629 | |||
| 630 | ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); | ||
| 631 | if (ret < 0) | ||
| 632 | return ret; | ||
| 633 | |||
| 634 | return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); | ||
| 635 | |||
| 636 | case V4L2_CID_BLACK_LEVEL_CALIBRATE: | ||
| 637 | return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, | ||
| 638 | MT9T001_BLACK_LEVEL_RECALCULATE | | ||
| 639 | mt9t001->black_level); | ||
| 640 | } | ||
| 641 | |||
| 642 | return 0; | ||
| 643 | } | ||
| 644 | |||
| 645 | static const struct v4l2_ctrl_ops mt9t001_ctrl_ops = { | ||
| 646 | .s_ctrl = mt9t001_s_ctrl, | ||
| 647 | }; | ||
| 648 | |||
| 649 | static const char * const mt9t001_test_pattern_menu[] = { | ||
| 650 | "Disabled", | ||
| 651 | "Enabled", | ||
| 652 | }; | ||
| 653 | |||
| 654 | static const struct v4l2_ctrl_config mt9t001_ctrls[] = { | ||
| 655 | { | ||
| 656 | .ops = &mt9t001_ctrl_ops, | ||
| 657 | .id = V4L2_CID_TEST_PATTERN_COLOR, | ||
| 658 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 659 | .name = "Test Pattern Color", | ||
| 660 | .min = 0, | ||
| 661 | .max = 1023, | ||
| 662 | .step = 1, | ||
| 663 | .def = 0, | ||
| 664 | .flags = 0, | ||
| 665 | }, { | ||
| 666 | .ops = &mt9t001_ctrl_ops, | ||
| 667 | .id = V4L2_CID_BLACK_LEVEL_AUTO, | ||
| 668 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
| 669 | .name = "Black Level, Auto", | ||
| 670 | .min = 0, | ||
| 671 | .max = 1, | ||
| 672 | .step = 1, | ||
| 673 | .def = 1, | ||
| 674 | .flags = 0, | ||
| 675 | }, { | ||
| 676 | .ops = &mt9t001_ctrl_ops, | ||
| 677 | .id = V4L2_CID_BLACK_LEVEL_OFFSET, | ||
| 678 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 679 | .name = "Black Level, Offset", | ||
| 680 | .min = -256, | ||
| 681 | .max = 255, | ||
| 682 | .step = 1, | ||
| 683 | .def = 32, | ||
| 684 | .flags = 0, | ||
| 685 | }, { | ||
| 686 | .ops = &mt9t001_ctrl_ops, | ||
| 687 | .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, | ||
| 688 | .type = V4L2_CTRL_TYPE_BUTTON, | ||
| 689 | .name = "Black Level, Calibrate", | ||
| 690 | .min = 0, | ||
| 691 | .max = 0, | ||
| 692 | .step = 0, | ||
| 693 | .def = 0, | ||
| 694 | .flags = V4L2_CTRL_FLAG_WRITE_ONLY, | ||
| 695 | }, | ||
| 696 | }; | ||
| 697 | |||
| 698 | static const struct v4l2_ctrl_config mt9t001_gains[] = { | ||
| 699 | { | ||
| 700 | .ops = &mt9t001_ctrl_ops, | ||
| 701 | .id = V4L2_CID_GAIN_RED, | ||
| 702 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 703 | .name = "Gain, Red", | ||
| 704 | .min = MT9T001_GLOBAL_GAIN_MIN, | ||
| 705 | .max = MT9T001_GLOBAL_GAIN_MAX, | ||
| 706 | .step = 1, | ||
| 707 | .def = MT9T001_GLOBAL_GAIN_MIN, | ||
| 708 | .flags = 0, | ||
| 709 | }, { | ||
| 710 | .ops = &mt9t001_ctrl_ops, | ||
| 711 | .id = V4L2_CID_GAIN_GREEN_RED, | ||
| 712 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 713 | .name = "Gain, Green (R)", | ||
| 714 | .min = MT9T001_GLOBAL_GAIN_MIN, | ||
| 715 | .max = MT9T001_GLOBAL_GAIN_MAX, | ||
| 716 | .step = 1, | ||
| 717 | .def = MT9T001_GLOBAL_GAIN_MIN, | ||
| 718 | .flags = 0, | ||
| 719 | }, { | ||
| 720 | .ops = &mt9t001_ctrl_ops, | ||
| 721 | .id = V4L2_CID_GAIN_GREEN_BLUE, | ||
| 722 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 723 | .name = "Gain, Green (B)", | ||
| 724 | .min = MT9T001_GLOBAL_GAIN_MIN, | ||
| 725 | .max = MT9T001_GLOBAL_GAIN_MAX, | ||
| 726 | .step = 1, | ||
| 727 | .def = MT9T001_GLOBAL_GAIN_MIN, | ||
| 728 | .flags = 0, | ||
| 729 | }, { | ||
| 730 | .ops = &mt9t001_ctrl_ops, | ||
| 731 | .id = V4L2_CID_GAIN_BLUE, | ||
| 732 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 733 | .name = "Gain, Blue", | ||
| 734 | .min = MT9T001_GLOBAL_GAIN_MIN, | ||
| 735 | .max = MT9T001_GLOBAL_GAIN_MAX, | ||
| 736 | .step = 1, | ||
| 737 | .def = MT9T001_GLOBAL_GAIN_MIN, | ||
| 738 | .flags = 0, | ||
| 739 | }, | ||
| 740 | }; | ||
| 741 | |||
| 742 | /* ----------------------------------------------------------------------------- | ||
| 743 | * V4L2 subdev core operations | ||
| 744 | */ | ||
| 745 | |||
| 746 | static int mt9t001_set_power(struct v4l2_subdev *subdev, int on) | ||
| 747 | { | ||
| 748 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 749 | int ret = 0; | ||
| 750 | |||
| 751 | mutex_lock(&mt9t001->power_lock); | ||
| 752 | |||
| 753 | /* If the power count is modified from 0 to != 0 or from != 0 to 0, | ||
| 754 | * update the power state. | ||
| 755 | */ | ||
| 756 | if (mt9t001->power_count == !on) { | ||
| 757 | ret = __mt9t001_set_power(mt9t001, !!on); | ||
| 758 | if (ret < 0) | ||
| 759 | goto out; | ||
| 760 | } | ||
| 761 | |||
| 762 | /* Update the power count. */ | ||
| 763 | mt9t001->power_count += on ? 1 : -1; | ||
| 764 | WARN_ON(mt9t001->power_count < 0); | ||
| 765 | |||
| 766 | out: | ||
| 767 | mutex_unlock(&mt9t001->power_lock); | ||
| 768 | return ret; | ||
| 769 | } | ||
| 770 | |||
| 771 | /* ----------------------------------------------------------------------------- | ||
| 772 | * V4L2 subdev internal operations | ||
| 773 | */ | ||
| 774 | |||
| 775 | static int mt9t001_registered(struct v4l2_subdev *subdev) | ||
| 776 | { | ||
| 777 | struct i2c_client *client = v4l2_get_subdevdata(subdev); | ||
| 778 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 779 | s32 data; | ||
| 780 | int ret; | ||
| 781 | |||
| 782 | ret = mt9t001_power_on(mt9t001); | ||
| 783 | if (ret < 0) { | ||
| 784 | dev_err(&client->dev, "MT9T001 power up failed\n"); | ||
| 785 | return ret; | ||
| 786 | } | ||
| 787 | |||
| 788 | /* Read out the chip version register */ | ||
| 789 | data = mt9t001_read(client, MT9T001_CHIP_VERSION); | ||
| 790 | mt9t001_power_off(mt9t001); | ||
| 791 | |||
| 792 | if (data != MT9T001_CHIP_ID) { | ||
| 793 | dev_err(&client->dev, | ||
| 794 | "MT9T001 not detected, wrong version 0x%04x\n", data); | ||
| 795 | return -ENODEV; | ||
| 796 | } | ||
| 797 | |||
| 798 | dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", | ||
| 799 | client->addr); | ||
| 800 | |||
| 801 | return 0; | ||
| 802 | } | ||
| 803 | |||
| 804 | static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | ||
| 805 | { | ||
| 806 | struct v4l2_mbus_framefmt *format; | ||
| 807 | struct v4l2_rect *crop; | ||
| 808 | |||
| 809 | crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); | ||
| 810 | crop->left = MT9T001_COLUMN_START_DEF; | ||
| 811 | crop->top = MT9T001_ROW_START_DEF; | ||
| 812 | crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; | ||
| 813 | crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; | ||
| 814 | |||
| 815 | format = v4l2_subdev_get_try_format(subdev, fh->state, 0); | ||
| 816 | format->code = MEDIA_BUS_FMT_SGRBG10_1X10; | ||
| 817 | format->width = MT9T001_WINDOW_WIDTH_DEF + 1; | ||
| 818 | format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; | ||
| 819 | format->field = V4L2_FIELD_NONE; | ||
| 820 | format->colorspace = V4L2_COLORSPACE_SRGB; | ||
| 821 | |||
| 822 | return mt9t001_set_power(subdev, 1); | ||
| 823 | } | ||
| 824 | |||
| 825 | static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | ||
| 826 | { | ||
| 827 | return mt9t001_set_power(subdev, 0); | ||
| 828 | } | ||
| 829 | |||
| 830 | static const struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = { | ||
| 831 | .s_power = mt9t001_set_power, | ||
| 832 | }; | ||
| 833 | |||
| 834 | static const struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { | ||
| 835 | .s_stream = mt9t001_s_stream, | ||
| 836 | }; | ||
| 837 | |||
| 838 | static const struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { | ||
| 839 | .enum_mbus_code = mt9t001_enum_mbus_code, | ||
| 840 | .enum_frame_size = mt9t001_enum_frame_size, | ||
| 841 | .get_fmt = mt9t001_get_format, | ||
| 842 | .set_fmt = mt9t001_set_format, | ||
| 843 | .get_selection = mt9t001_get_selection, | ||
| 844 | .set_selection = mt9t001_set_selection, | ||
| 845 | }; | ||
| 846 | |||
| 847 | static const struct v4l2_subdev_ops mt9t001_subdev_ops = { | ||
| 848 | .core = &mt9t001_subdev_core_ops, | ||
| 849 | .video = &mt9t001_subdev_video_ops, | ||
| 850 | .pad = &mt9t001_subdev_pad_ops, | ||
| 851 | }; | ||
| 852 | |||
| 853 | static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { | ||
| 854 | .registered = mt9t001_registered, | ||
| 855 | .open = mt9t001_open, | ||
| 856 | .close = mt9t001_close, | ||
| 857 | }; | ||
| 858 | |||
| 859 | static int mt9t001_probe(struct i2c_client *client) | ||
| 860 | { | ||
| 861 | struct mt9t001_platform_data *pdata = client->dev.platform_data; | ||
| 862 | struct mt9t001 *mt9t001; | ||
| 863 | unsigned int i; | ||
| 864 | int ret; | ||
| 865 | |||
| 866 | if (pdata == NULL) { | ||
| 867 | dev_err(&client->dev, "No platform data\n"); | ||
| 868 | return -EINVAL; | ||
| 869 | } | ||
| 870 | |||
| 871 | if (!i2c_check_functionality(client->adapter, | ||
| 872 | I2C_FUNC_SMBUS_WORD_DATA)) { | ||
| 873 | dev_warn(&client->adapter->dev, | ||
| 874 | "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); | ||
| 875 | return -EIO; | ||
| 876 | } | ||
| 877 | |||
| 878 | mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL); | ||
| 879 | if (!mt9t001) | ||
| 880 | return -ENOMEM; | ||
| 881 | |||
| 882 | mutex_init(&mt9t001->power_lock); | ||
| 883 | mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; | ||
| 884 | |||
| 885 | mt9t001->regulators[0].supply = "vdd"; | ||
| 886 | mt9t001->regulators[1].supply = "vaa"; | ||
| 887 | |||
| 888 | ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators); | ||
| 889 | if (ret < 0) { | ||
| 890 | dev_err(&client->dev, "Unable to get regulators\n"); | ||
| 891 | return ret; | ||
| 892 | } | ||
| 893 | |||
| 894 | mt9t001->clk = devm_clk_get(&client->dev, NULL); | ||
| 895 | if (IS_ERR(mt9t001->clk)) { | ||
| 896 | dev_err(&client->dev, "Unable to get clock\n"); | ||
| 897 | return PTR_ERR(mt9t001->clk); | ||
| 898 | } | ||
| 899 | |||
| 900 | v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + | ||
| 901 | ARRAY_SIZE(mt9t001_gains) + 4); | ||
| 902 | |||
| 903 | v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, | ||
| 904 | V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, | ||
| 905 | MT9T001_SHUTTER_WIDTH_MAX, 1, | ||
| 906 | MT9T001_SHUTTER_WIDTH_DEF); | ||
| 907 | v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, | ||
| 908 | V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); | ||
| 909 | v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, | ||
| 910 | V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, | ||
| 911 | 1, pdata->ext_clk); | ||
| 912 | v4l2_ctrl_new_std_menu_items(&mt9t001->ctrls, &mt9t001_ctrl_ops, | ||
| 913 | V4L2_CID_TEST_PATTERN, | ||
| 914 | ARRAY_SIZE(mt9t001_test_pattern_menu) - 1, 0, | ||
| 915 | 0, mt9t001_test_pattern_menu); | ||
| 916 | |||
| 917 | for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) | ||
| 918 | v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); | ||
| 919 | |||
| 920 | for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) | ||
| 921 | mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, | ||
| 922 | &mt9t001_gains[i], NULL); | ||
| 923 | |||
| 924 | v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); | ||
| 925 | |||
| 926 | mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; | ||
| 927 | |||
| 928 | if (mt9t001->ctrls.error) { | ||
| 929 | printk(KERN_INFO "%s: control initialization error %d\n", | ||
| 930 | __func__, mt9t001->ctrls.error); | ||
| 931 | ret = -EINVAL; | ||
| 932 | goto done; | ||
| 933 | } | ||
| 934 | |||
| 935 | mt9t001->crop.left = MT9T001_COLUMN_START_DEF; | ||
| 936 | mt9t001->crop.top = MT9T001_ROW_START_DEF; | ||
| 937 | mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; | ||
| 938 | mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; | ||
| 939 | |||
| 940 | mt9t001->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; | ||
| 941 | mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; | ||
| 942 | mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; | ||
| 943 | mt9t001->format.field = V4L2_FIELD_NONE; | ||
| 944 | mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; | ||
| 945 | |||
| 946 | v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); | ||
| 947 | mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; | ||
| 948 | mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 949 | |||
| 950 | mt9t001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; | ||
| 951 | mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 952 | ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); | ||
| 953 | |||
| 954 | done: | ||
| 955 | if (ret < 0) { | ||
| 956 | v4l2_ctrl_handler_free(&mt9t001->ctrls); | ||
| 957 | media_entity_cleanup(&mt9t001->subdev.entity); | ||
| 958 | } | ||
| 959 | |||
| 960 | return ret; | ||
| 961 | } | ||
| 962 | |||
| 963 | static void mt9t001_remove(struct i2c_client *client) | ||
| 964 | { | ||
| 965 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
| 966 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
| 967 | |||
| 968 | v4l2_ctrl_handler_free(&mt9t001->ctrls); | ||
| 969 | v4l2_device_unregister_subdev(subdev); | ||
| 970 | media_entity_cleanup(&subdev->entity); | ||
| 971 | } | ||
| 972 | |||
| 973 | static const struct i2c_device_id mt9t001_id[] = { | ||
| 974 | { "mt9t001", 0 }, | ||
| 975 | { } | ||
| 976 | }; | ||
| 977 | MODULE_DEVICE_TABLE(i2c, mt9t001_id); | ||
| 978 | |||
| 979 | static struct i2c_driver mt9t001_driver = { | ||
| 980 | .driver = { | ||
| 981 | .name = "mt9t001", | ||
| 982 | }, | ||
| 983 | .probe_new = mt9t001_probe, | ||
| 984 | .remove = mt9t001_remove, | ||
| 985 | .id_table = mt9t001_id, | ||
| 986 | }; | ||
| 987 | |||
| 988 | module_i2c_driver(mt9t001_driver); | ||
| 989 | |||
| 990 | MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); | ||
| 991 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
| 992 | MODULE_LICENSE("GPL"); | ||
| @@ -1,821 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /* | ||
| 3 | * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. | ||
| 6 | * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> | ||
| 7 | * | ||
| 8 | * Initial register configuration based on a driver authored by | ||
| 9 | * HeungJun Kim <riverful.kim@samsung.com>. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/delay.h> | ||
| 13 | #include <linux/gpio/consumer.h> | ||
| 14 | #include <linux/i2c.h> | ||
| 15 | #include <linux/slab.h> | ||
| 16 | #include <linux/regulator/consumer.h> | ||
| 17 | #include <media/i2c/noon010pc30.h> | ||
| 18 | #include <linux/videodev2.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <media/v4l2-ctrls.h> | ||
| 21 | #include <media/v4l2-device.h> | ||
| 22 | #include <media/v4l2-mediabus.h> | ||
| 23 | #include <media/v4l2-subdev.h> | ||
| 24 | |||
| 25 | static int debug; | ||
| 26 | module_param(debug, int, 0644); | ||
| 27 | MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); | ||
| 28 | |||
| 29 | #define MODULE_NAME "NOON010PC30" | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Register offsets within a page | ||
| 33 | * b15..b8 - page id, b7..b0 - register address | ||
| 34 | */ | ||
| 35 | #define POWER_CTRL_REG 0x0001 | ||
| 36 | #define PAGEMODE_REG 0x03 | ||
| 37 | #define DEVICE_ID_REG 0x0004 | ||
| 38 | #define NOON010PC30_ID 0x86 | ||
| 39 | #define VDO_CTL_REG(n) (0x0010 + (n)) | ||
| 40 | #define SYNC_CTL_REG 0x0012 | ||
| 41 | /* Window size and position */ | ||
| 42 | #define WIN_ROWH_REG 0x0013 | ||
| 43 | #define WIN_ROWL_REG 0x0014 | ||
| 44 | #define WIN_COLH_REG 0x0015 | ||
| 45 | #define WIN_COLL_REG 0x0016 | ||
| 46 | #define WIN_HEIGHTH_REG 0x0017 | ||
| 47 | #define WIN_HEIGHTL_REG 0x0018 | ||
| 48 | #define WIN_WIDTHH_REG 0x0019 | ||
| 49 | #define WIN_WIDTHL_REG 0x001A | ||
| 50 | #define HBLANKH_REG 0x001B | ||
| 51 | #define HBLANKL_REG 0x001C | ||
| 52 | #define VSYNCH_REG 0x001D | ||
| 53 | #define VSYNCL_REG 0x001E | ||
| 54 | /* VSYNC control */ | ||
| 55 | #define VS_CTL_REG(n) (0x00A1 + (n)) | ||
| 56 | /* page 1 */ | ||
| 57 | #define ISP_CTL_REG(n) (0x0110 + (n)) | ||
| 58 | #define YOFS_REG 0x0119 | ||
| 59 | #define DARK_YOFS_REG 0x011A | ||
| 60 | #define SAT_CTL_REG 0x0120 | ||
| 61 | #define BSAT_REG 0x0121 | ||
| 62 | #define RSAT_REG 0x0122 | ||
| 63 | /* Color correction */ | ||
| 64 | #define CMC_CTL_REG 0x0130 | ||
| 65 | #define CMC_OFSGH_REG 0x0133 | ||
| 66 | #define CMC_OFSGL_REG 0x0135 | ||
| 67 | #define CMC_SIGN_REG 0x0136 | ||
| 68 | #define CMC_GOFS_REG 0x0137 | ||
| 69 | #define CMC_COEF_REG(n) (0x0138 + (n)) | ||
| 70 | #define CMC_OFS_REG(n) (0x0141 + (n)) | ||
| 71 | /* Gamma correction */ | ||
| 72 | #define GMA_CTL_REG 0x0160 | ||
| 73 | #define GMA_COEF_REG(n) (0x0161 + (n)) | ||
| 74 | /* Lens Shading */ | ||
| 75 | #define LENS_CTRL_REG 0x01D0 | ||
| 76 | #define LENS_XCEN_REG 0x01D1 | ||
| 77 | #define LENS_YCEN_REG 0x01D2 | ||
| 78 | #define LENS_RC_REG 0x01D3 | ||
| 79 | #define LENS_GC_REG 0x01D4 | ||
| 80 | #define LENS_BC_REG 0x01D5 | ||
| 81 | #define L_AGON_REG 0x01D6 | ||
| 82 | #define L_AGOFF_REG 0x01D7 | ||
| 83 | /* Page 3 - Auto Exposure */ | ||
| 84 | #define AE_CTL_REG(n) (0x0310 + (n)) | ||
| 85 | #define AE_CTL9_REG 0x032C | ||
| 86 | #define AE_CTL10_REG 0x032D | ||
| 87 | #define AE_YLVL_REG 0x031C | ||
| 88 | #define AE_YTH_REG(n) (0x031D + (n)) | ||
| 89 | #define AE_WGT_REG 0x0326 | ||
| 90 | #define EXP_TIMEH_REG 0x0333 | ||
| 91 | #define EXP_TIMEM_REG 0x0334 | ||
| 92 | #define EXP_TIMEL_REG 0x0335 | ||
| 93 | #define EXP_MMINH_REG 0x0336 | ||
| 94 | #define EXP_MMINL_REG 0x0337 | ||
| 95 | #define EXP_MMAXH_REG 0x0338 | ||
| 96 | #define EXP_MMAXM_REG 0x0339 | ||
| 97 | #define EXP_MMAXL_REG 0x033A | ||
| 98 | /* Page 4 - Auto White Balance */ | ||
| 99 | #define AWB_CTL_REG(n) (0x0410 + (n)) | ||
| 100 | #define AWB_ENABE 0x80 | ||
| 101 | #define AWB_WGHT_REG 0x0419 | ||
| 102 | #define BGAIN_PAR_REG(n) (0x044F + (n)) | ||
| 103 | /* Manual white balance, when AWB_CTL2[0]=1 */ | ||
| 104 | #define MWB_RGAIN_REG 0x0466 | ||
| 105 | #define MWB_BGAIN_REG 0x0467 | ||
| 106 | |||
| 107 | /* The token to mark an array end */ | ||
| 108 | #define REG_TERM 0xFFFF | ||
| 109 | |||
| 110 | struct noon010_format { | ||
| 111 | u32 code; | ||
| 112 | enum v4l2_colorspace colorspace; | ||
| 113 | u16 ispctl1_reg; | ||
| 114 | }; | ||
| 115 | |||
| 116 | struct noon010_frmsize { | ||
| 117 | u16 width; | ||
| 118 | u16 height; | ||
| 119 | int vid_ctl1; | ||
| 120 | }; | ||
| 121 | |||
| 122 | static const char * const noon010_supply_name[] = { | ||
| 123 | "vdd_core", "vddio", "vdda" | ||
| 124 | }; | ||
| 125 | |||
| 126 | #define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) | ||
| 127 | |||
| 128 | struct noon010_info { | ||
| 129 | struct v4l2_subdev sd; | ||
| 130 | struct media_pad pad; | ||
| 131 | struct v4l2_ctrl_handler hdl; | ||
| 132 | struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; | ||
| 133 | struct gpio_desc *reset; | ||
| 134 | struct gpio_desc *stby; | ||
| 135 | |||
| 136 | /* Protects the struct members below */ | ||
| 137 | struct mutex lock; | ||
| 138 | |||
| 139 | const struct noon010_format *curr_fmt; | ||
| 140 | const struct noon010_frmsize *curr_win; | ||
| 141 | unsigned int apply_new_cfg:1; | ||
| 142 | unsigned int streaming:1; | ||
| 143 | unsigned int hflip:1; | ||
| 144 | unsigned int vflip:1; | ||
| 145 | unsigned int power:1; | ||
| 146 | u8 i2c_reg_page; | ||
| 147 | }; | ||
| 148 | |||
| 149 | struct i2c_regval { | ||
| 150 | u16 addr; | ||
| 151 | u16 val; | ||
| 152 | }; | ||
| 153 | |||
| 154 | /* Supported resolutions. */ | ||
| 155 | static const struct noon010_frmsize noon010_sizes[] = { | ||
| 156 | { | ||
| 157 | .width = 352, | ||
| 158 | .height = 288, | ||
| 159 | .vid_ctl1 = 0, | ||
| 160 | }, { | ||
| 161 | .width = 176, | ||
| 162 | .height = 144, | ||
| 163 | .vid_ctl1 = 0x10, | ||
| 164 | }, { | ||
| 165 | .width = 88, | ||
| 166 | .height = 72, | ||
| 167 | .vid_ctl1 = 0x20, | ||
| 168 | }, | ||
| 169 | }; | ||
| 170 | |||
| 171 | /* Supported pixel formats. */ | ||
| 172 | static const struct noon010_format noon010_formats[] = { | ||
| 173 | { | ||
| 174 | .code = MEDIA_BUS_FMT_YUYV8_2X8, | ||
| 175 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 176 | .ispctl1_reg = 0x03, | ||
| 177 | }, { | ||
| 178 | .code = MEDIA_BUS_FMT_YVYU8_2X8, | ||
| 179 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 180 | .ispctl1_reg = 0x02, | ||
| 181 | }, { | ||
| 182 | .code = MEDIA_BUS_FMT_VYUY8_2X8, | ||
| 183 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 184 | .ispctl1_reg = 0, | ||
| 185 | }, { | ||
| 186 | .code = MEDIA_BUS_FMT_UYVY8_2X8, | ||
| 187 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 188 | .ispctl1_reg = 0x01, | ||
| 189 | }, { | ||
| 190 | .code = MEDIA_BUS_FMT_RGB565_2X8_BE, | ||
| 191 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 192 | .ispctl1_reg = 0x40, | ||
| 193 | }, | ||
| 194 | }; | ||
| 195 | |||
| 196 | static const struct i2c_regval noon010_base_regs[] = { | ||
| 197 | { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, | ||
| 198 | /* Color corection and saturation */ | ||
| 199 | { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, | ||
| 200 | { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, | ||
| 201 | { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, | ||
| 202 | { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, | ||
| 203 | { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, | ||
| 204 | { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, | ||
| 205 | { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, | ||
| 206 | { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, | ||
| 207 | { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, | ||
| 208 | { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, | ||
| 209 | { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, | ||
| 210 | { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, | ||
| 211 | { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, | ||
| 212 | { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, | ||
| 213 | /* Automatic white balance */ | ||
| 214 | { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, | ||
| 215 | { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, | ||
| 216 | /* Auto exposure */ | ||
| 217 | { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, | ||
| 218 | { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, | ||
| 219 | { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, | ||
| 220 | { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, | ||
| 221 | { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, | ||
| 222 | { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, | ||
| 223 | { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, | ||
| 224 | /* Lens shading compensation */ | ||
| 225 | { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, | ||
| 226 | { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, | ||
| 227 | { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, | ||
| 228 | { REG_TERM, 0 }, | ||
| 229 | }; | ||
| 230 | |||
| 231 | static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) | ||
| 232 | { | ||
| 233 | return container_of(sd, struct noon010_info, sd); | ||
| 234 | } | ||
| 235 | |||
| 236 | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | ||
| 237 | { | ||
| 238 | return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; | ||
| 239 | } | ||
| 240 | |||
| 241 | static inline int set_i2c_page(struct noon010_info *info, | ||
| 242 | struct i2c_client *client, unsigned int reg) | ||
| 243 | { | ||
| 244 | u32 page = reg >> 8 & 0xFF; | ||
| 245 | int ret = 0; | ||
| 246 | |||
| 247 | if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { | ||
| 248 | ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); | ||
| 249 | if (!ret) | ||
| 250 | info->i2c_reg_page = page; | ||
| 251 | } | ||
| 252 | return ret; | ||
| 253 | } | ||
| 254 | |||
| 255 | static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) | ||
| 256 | { | ||
| 257 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 258 | struct noon010_info *info = to_noon010(sd); | ||
| 259 | int ret = set_i2c_page(info, client, reg_addr); | ||
| 260 | |||
| 261 | if (ret) | ||
| 262 | return ret; | ||
| 263 | return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); | ||
| 264 | } | ||
| 265 | |||
| 266 | static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) | ||
| 267 | { | ||
| 268 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 269 | struct noon010_info *info = to_noon010(sd); | ||
| 270 | int ret = set_i2c_page(info, client, reg_addr); | ||
| 271 | |||
| 272 | if (ret) | ||
| 273 | return ret; | ||
| 274 | return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); | ||
| 275 | } | ||
| 276 | |||
| 277 | static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, | ||
| 278 | const struct i2c_regval *msg) | ||
| 279 | { | ||
| 280 | while (msg->addr != REG_TERM) { | ||
| 281 | int ret = cam_i2c_write(sd, msg->addr, msg->val); | ||
| 282 | |||
| 283 | if (ret) | ||
| 284 | return ret; | ||
| 285 | msg++; | ||
| 286 | } | ||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | /* Device reset and sleep mode control */ | ||
| 291 | static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) | ||
| 292 | { | ||
| 293 | struct noon010_info *info = to_noon010(sd); | ||
| 294 | u8 reg = sleep ? 0xF1 : 0xF0; | ||
| 295 | int ret = 0; | ||
| 296 | |||
| 297 | if (reset) { | ||
| 298 | ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); | ||
| 299 | udelay(20); | ||
| 300 | } | ||
| 301 | if (!ret) { | ||
| 302 | ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); | ||
| 303 | if (reset && !ret) | ||
| 304 | info->i2c_reg_page = -1; | ||
| 305 | } | ||
| 306 | return ret; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* Automatic white balance control */ | ||
| 310 | static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) | ||
| 311 | { | ||
| 312 | int ret; | ||
| 313 | |||
| 314 | ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); | ||
| 315 | if (!ret) | ||
| 316 | ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); | ||
| 317 | return ret; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Called with struct noon010_info.lock mutex held */ | ||
| 321 | static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) | ||
| 322 | { | ||
| 323 | struct noon010_info *info = to_noon010(sd); | ||
| 324 | int reg, ret; | ||
| 325 | |||
| 326 | reg = cam_i2c_read(sd, VDO_CTL_REG(1)); | ||
| 327 | if (reg < 0) | ||
| 328 | return reg; | ||
| 329 | |||
| 330 | reg &= 0x7C; | ||
| 331 | if (hflip) | ||
| 332 | reg |= 0x01; | ||
| 333 | if (vflip) | ||
| 334 | reg |= 0x02; | ||
| 335 | |||
| 336 | ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); | ||
| 337 | if (!ret) { | ||
| 338 | info->hflip = hflip; | ||
| 339 | info->vflip = vflip; | ||
| 340 | } | ||
| 341 | return ret; | ||
| 342 | } | ||
| 343 | |||
| 344 | /* Configure resolution and color format */ | ||
| 345 | static int noon010_set_params(struct v4l2_subdev *sd) | ||
| 346 | { | ||
| 347 | struct noon010_info *info = to_noon010(sd); | ||
| 348 | |||
| 349 | int ret = cam_i2c_write(sd, VDO_CTL_REG(0), | ||
| 350 | info->curr_win->vid_ctl1); | ||
| 351 | if (ret) | ||
| 352 | return ret; | ||
| 353 | return cam_i2c_write(sd, ISP_CTL_REG(0), | ||
| 354 | info->curr_fmt->ispctl1_reg); | ||
| 355 | } | ||
| 356 | |||
| 357 | /* Find nearest matching image pixel size. */ | ||
| 358 | static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, | ||
| 359 | const struct noon010_frmsize **size) | ||
| 360 | { | ||
| 361 | unsigned int min_err = ~0; | ||
| 362 | int i = ARRAY_SIZE(noon010_sizes); | ||
| 363 | const struct noon010_frmsize *fsize = &noon010_sizes[0], | ||
| 364 | *match = NULL; | ||
| 365 | |||
| 366 | while (i--) { | ||
| 367 | int err = abs(fsize->width - mf->width) | ||
| 368 | + abs(fsize->height - mf->height); | ||
| 369 | |||
| 370 | if (err < min_err) { | ||
| 371 | min_err = err; | ||
| 372 | match = fsize; | ||
| 373 | } | ||
| 374 | fsize++; | ||
| 375 | } | ||
| 376 | if (match) { | ||
| 377 | mf->width = match->width; | ||
| 378 | mf->height = match->height; | ||
| 379 | if (size) | ||
| 380 | *size = match; | ||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | return -EINVAL; | ||
| 384 | } | ||
| 385 | |||
| 386 | /* Called with info.lock mutex held */ | ||
| 387 | static int power_enable(struct noon010_info *info) | ||
| 388 | { | ||
| 389 | int ret; | ||
| 390 | |||
| 391 | if (info->power) { | ||
| 392 | v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); | ||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | |||
| 396 | /* Assert standby: line should be flagged active low in descriptor */ | ||
| 397 | if (info->stby) | ||
| 398 | gpiod_set_value(info->stby, 1); | ||
| 399 | |||
| 400 | /* Assert reset: line should be flagged active low in descriptor */ | ||
| 401 | if (info->reset) | ||
| 402 | gpiod_set_value(info->reset, 1); | ||
| 403 | |||
| 404 | ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); | ||
| 405 | if (ret) | ||
| 406 | return ret; | ||
| 407 | |||
| 408 | /* De-assert reset and standby */ | ||
| 409 | if (info->reset) { | ||
| 410 | msleep(50); | ||
| 411 | gpiod_set_value(info->reset, 0); | ||
| 412 | } | ||
| 413 | if (info->stby) { | ||
| 414 | udelay(1000); | ||
| 415 | gpiod_set_value(info->stby, 0); | ||
| 416 | } | ||
| 417 | /* Cycle reset: assert and deassert */ | ||
| 418 | if (info->reset) { | ||
| 419 | udelay(1000); | ||
| 420 | gpiod_set_value(info->reset, 1); | ||
| 421 | msleep(100); | ||
| 422 | gpiod_set_value(info->reset, 0); | ||
| 423 | msleep(20); | ||
| 424 | } | ||
| 425 | info->power = 1; | ||
| 426 | |||
| 427 | v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); | ||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | /* Called with info.lock mutex held */ | ||
| 432 | static int power_disable(struct noon010_info *info) | ||
| 433 | { | ||
| 434 | int ret; | ||
| 435 | |||
| 436 | if (!info->power) { | ||
| 437 | v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); | ||
| 438 | return 0; | ||
| 439 | } | ||
| 440 | |||
| 441 | ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); | ||
| 442 | if (ret) | ||
| 443 | return ret; | ||
| 444 | |||
| 445 | /* Assert standby and reset */ | ||
| 446 | if (info->stby) | ||
| 447 | gpiod_set_value(info->stby, 1); | ||
| 448 | |||
| 449 | if (info->reset) | ||
| 450 | gpiod_set_value(info->reset, 1); | ||
| 451 | |||
| 452 | info->power = 0; | ||
| 453 | |||
| 454 | v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); | ||
| 455 | |||
| 456 | return 0; | ||
| 457 | } | ||
| 458 | |||
| 459 | static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 460 | { | ||
| 461 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
| 462 | struct noon010_info *info = to_noon010(sd); | ||
| 463 | int ret = 0; | ||
| 464 | |||
| 465 | v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", | ||
| 466 | __func__, ctrl->id, ctrl->val); | ||
| 467 | |||
| 468 | mutex_lock(&info->lock); | ||
| 469 | /* | ||
| 470 | * If the device is not powered up by the host driver do | ||
| 471 | * not apply any controls to H/W at this time. Instead | ||
| 472 | * the controls will be restored right after power-up. | ||
| 473 | */ | ||
| 474 | if (!info->power) | ||
| 475 | goto unlock; | ||
| 476 | |||
| 477 | switch (ctrl->id) { | ||
| 478 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
| 479 | ret = noon010_enable_autowhitebalance(sd, ctrl->val); | ||
| 480 | break; | ||
| 481 | case V4L2_CID_BLUE_BALANCE: | ||
| 482 | ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); | ||
| 483 | break; | ||
| 484 | case V4L2_CID_RED_BALANCE: | ||
| 485 | ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); | ||
| 486 | break; | ||
| 487 | default: | ||
| 488 | ret = -EINVAL; | ||
| 489 | } | ||
| 490 | unlock: | ||
| 491 | mutex_unlock(&info->lock); | ||
| 492 | return ret; | ||
| 493 | } | ||
| 494 | |||
| 495 | static int noon010_enum_mbus_code(struct v4l2_subdev *sd, | ||
| 496 | struct v4l2_subdev_state *sd_state, | ||
| 497 | struct v4l2_subdev_mbus_code_enum *code) | ||
| 498 | { | ||
| 499 | if (code->index >= ARRAY_SIZE(noon010_formats)) | ||
| 500 | return -EINVAL; | ||
| 501 | |||
| 502 | code->code = noon010_formats[code->index].code; | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | static int noon010_get_fmt(struct v4l2_subdev *sd, | ||
| 507 | struct v4l2_subdev_state *sd_state, | ||
| 508 | struct v4l2_subdev_format *fmt) | ||
| 509 | { | ||
| 510 | struct noon010_info *info = to_noon010(sd); | ||
| 511 | struct v4l2_mbus_framefmt *mf; | ||
| 512 | |||
| 513 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
| 514 | if (sd_state) { | ||
| 515 | mf = v4l2_subdev_get_try_format(sd, sd_state, 0); | ||
| 516 | fmt->format = *mf; | ||
| 517 | } | ||
| 518 | return 0; | ||
| 519 | } | ||
| 520 | mf = &fmt->format; | ||
| 521 | |||
| 522 | mutex_lock(&info->lock); | ||
| 523 | mf->width = info->curr_win->width; | ||
| 524 | mf->height = info->curr_win->height; | ||
| 525 | mf->code = info->curr_fmt->code; | ||
| 526 | mf->colorspace = info->curr_fmt->colorspace; | ||
| 527 | mf->field = V4L2_FIELD_NONE; | ||
| 528 | |||
| 529 | mutex_unlock(&info->lock); | ||
| 530 | return 0; | ||
| 531 | } | ||
| 532 | |||
| 533 | /* Return nearest media bus frame format. */ | ||
| 534 | static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, | ||
| 535 | struct v4l2_mbus_framefmt *mf) | ||
| 536 | { | ||
| 537 | int i = ARRAY_SIZE(noon010_formats); | ||
| 538 | |||
| 539 | while (--i) | ||
| 540 | if (mf->code == noon010_formats[i].code) | ||
| 541 | break; | ||
| 542 | mf->code = noon010_formats[i].code; | ||
| 543 | |||
| 544 | return &noon010_formats[i]; | ||
| 545 | } | ||
| 546 | |||
| 547 | static int noon010_set_fmt(struct v4l2_subdev *sd, | ||
| 548 | struct v4l2_subdev_state *sd_state, | ||
| 549 | struct v4l2_subdev_format *fmt) | ||
| 550 | { | ||
| 551 | struct noon010_info *info = to_noon010(sd); | ||
| 552 | const struct noon010_frmsize *size = NULL; | ||
| 553 | const struct noon010_format *nf; | ||
| 554 | struct v4l2_mbus_framefmt *mf; | ||
| 555 | int ret = 0; | ||
| 556 | |||
| 557 | nf = noon010_try_fmt(sd, &fmt->format); | ||
| 558 | noon010_try_frame_size(&fmt->format, &size); | ||
| 559 | fmt->format.colorspace = V4L2_COLORSPACE_JPEG; | ||
| 560 | fmt->format.field = V4L2_FIELD_NONE; | ||
| 561 | |||
| 562 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
| 563 | if (sd_state) { | ||
| 564 | mf = v4l2_subdev_get_try_format(sd, sd_state, 0); | ||
| 565 | *mf = fmt->format; | ||
| 566 | } | ||
| 567 | return 0; | ||
| 568 | } | ||
| 569 | mutex_lock(&info->lock); | ||
| 570 | if (!info->streaming) { | ||
| 571 | info->apply_new_cfg = 1; | ||
| 572 | info->curr_fmt = nf; | ||
| 573 | info->curr_win = size; | ||
| 574 | } else { | ||
| 575 | ret = -EBUSY; | ||
| 576 | } | ||
| 577 | mutex_unlock(&info->lock); | ||
| 578 | return ret; | ||
| 579 | } | ||
| 580 | |||
| 581 | /* Called with struct noon010_info.lock mutex held */ | ||
| 582 | static int noon010_base_config(struct v4l2_subdev *sd) | ||
| 583 | { | ||
| 584 | int ret = noon010_bulk_write_reg(sd, noon010_base_regs); | ||
| 585 | if (!ret) | ||
| 586 | ret = noon010_set_params(sd); | ||
| 587 | if (!ret) | ||
| 588 | ret = noon010_set_flip(sd, 1, 0); | ||
| 589 | |||
| 590 | return ret; | ||
| 591 | } | ||
| 592 | |||
| 593 | static int noon010_s_power(struct v4l2_subdev *sd, int on) | ||
| 594 | { | ||
| 595 | struct noon010_info *info = to_noon010(sd); | ||
| 596 | int ret; | ||
| 597 | |||
| 598 | mutex_lock(&info->lock); | ||
| 599 | if (on) { | ||
| 600 | ret = power_enable(info); | ||
| 601 | if (!ret) | ||
| 602 | ret = noon010_base_config(sd); | ||
| 603 | } else { | ||
| 604 | noon010_power_ctrl(sd, false, true); | ||
| 605 | ret = power_disable(info); | ||
| 606 | } | ||
| 607 | mutex_unlock(&info->lock); | ||
| 608 | |||
| 609 | /* Restore the controls state */ | ||
| 610 | if (!ret && on) | ||
| 611 | ret = v4l2_ctrl_handler_setup(&info->hdl); | ||
| 612 | |||
| 613 | return ret; | ||
| 614 | } | ||
| 615 | |||
| 616 | static int noon010_s_stream(struct v4l2_subdev *sd, int on) | ||
| 617 | { | ||
| 618 | struct noon010_info *info = to_noon010(sd); | ||
| 619 | int ret = 0; | ||
| 620 | |||
| 621 | mutex_lock(&info->lock); | ||
| 622 | if (!info->streaming != !on) { | ||
| 623 | ret = noon010_power_ctrl(sd, false, !on); | ||
| 624 | if (!ret) | ||
| 625 | info->streaming = on; | ||
| 626 | } | ||
| 627 | if (!ret && on && info->apply_new_cfg) { | ||
| 628 | ret = noon010_set_params(sd); | ||
| 629 | if (!ret) | ||
| 630 | info->apply_new_cfg = 0; | ||
| 631 | } | ||
| 632 | mutex_unlock(&info->lock); | ||
| 633 | return ret; | ||
| 634 | } | ||
| 635 | |||
| 636 | static int noon010_log_status(struct v4l2_subdev *sd) | ||
| 637 | { | ||
| 638 | struct noon010_info *info = to_noon010(sd); | ||
| 639 | |||
| 640 | v4l2_ctrl_handler_log_status(&info->hdl, sd->name); | ||
| 641 | return 0; | ||
| 642 | } | ||
| 643 | |||
| 644 | static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
| 645 | { | ||
| 646 | struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, | ||
| 647 | fh->state, | ||
| 648 | 0); | ||
| 649 | |||
| 650 | mf->width = noon010_sizes[0].width; | ||
| 651 | mf->height = noon010_sizes[0].height; | ||
| 652 | mf->code = noon010_formats[0].code; | ||
| 653 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
| 654 | mf->field = V4L2_FIELD_NONE; | ||
| 655 | return 0; | ||
| 656 | } | ||
| 657 | |||
| 658 | static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { | ||
| 659 | .open = noon010_open, | ||
| 660 | }; | ||
| 661 | |||
| 662 | static const struct v4l2_ctrl_ops noon010_ctrl_ops = { | ||
| 663 | .s_ctrl = noon010_s_ctrl, | ||
| 664 | }; | ||
| 665 | |||
| 666 | static const struct v4l2_subdev_core_ops noon010_core_ops = { | ||
| 667 | .s_power = noon010_s_power, | ||
| 668 | .log_status = noon010_log_status, | ||
| 669 | }; | ||
| 670 | |||
| 671 | static const struct v4l2_subdev_pad_ops noon010_pad_ops = { | ||
| 672 | .enum_mbus_code = noon010_enum_mbus_code, | ||
| 673 | .get_fmt = noon010_get_fmt, | ||
| 674 | .set_fmt = noon010_set_fmt, | ||
| 675 | }; | ||
| 676 | |||
| 677 | static const struct v4l2_subdev_video_ops noon010_video_ops = { | ||
| 678 | .s_stream = noon010_s_stream, | ||
| 679 | }; | ||
| 680 | |||
| 681 | static const struct v4l2_subdev_ops noon010_ops = { | ||
| 682 | .core = &noon010_core_ops, | ||
| 683 | .pad = &noon010_pad_ops, | ||
| 684 | .video = &noon010_video_ops, | ||
| 685 | }; | ||
| 686 | |||
| 687 | /* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ | ||
| 688 | static int noon010_detect(struct i2c_client *client, struct noon010_info *info) | ||
| 689 | { | ||
| 690 | int ret; | ||
| 691 | |||
| 692 | ret = power_enable(info); | ||
| 693 | if (ret) | ||
| 694 | return ret; | ||
| 695 | |||
| 696 | ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); | ||
| 697 | if (ret < 0) | ||
| 698 | dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); | ||
| 699 | |||
| 700 | power_disable(info); | ||
| 701 | |||
| 702 | return ret == NOON010PC30_ID ? 0 : -ENODEV; | ||
| 703 | } | ||
| 704 | |||
| 705 | static int noon010_probe(struct i2c_client *client) | ||
| 706 | { | ||
| 707 | struct noon010_info *info; | ||
| 708 | struct v4l2_subdev *sd; | ||
| 709 | const struct noon010pc30_platform_data *pdata | ||
| 710 | = client->dev.platform_data; | ||
| 711 | int ret; | ||
| 712 | int i; | ||
| 713 | |||
| 714 | if (!pdata) { | ||
| 715 | dev_err(&client->dev, "No platform data!\n"); | ||
| 716 | return -EIO; | ||
| 717 | } | ||
| 718 | |||
| 719 | info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | ||
| 720 | if (!info) | ||
| 721 | return -ENOMEM; | ||
| 722 | |||
| 723 | mutex_init(&info->lock); | ||
| 724 | sd = &info->sd; | ||
| 725 | v4l2_i2c_subdev_init(sd, client, &noon010_ops); | ||
| 726 | /* Static name; NEVER use in new drivers! */ | ||
| 727 | strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); | ||
| 728 | |||
| 729 | sd->internal_ops = &noon010_subdev_internal_ops; | ||
| 730 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 731 | |||
| 732 | v4l2_ctrl_handler_init(&info->hdl, 3); | ||
| 733 | |||
| 734 | v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, | ||
| 735 | V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); | ||
| 736 | v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, | ||
| 737 | V4L2_CID_RED_BALANCE, 0, 127, 1, 64); | ||
| 738 | v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, | ||
| 739 | V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); | ||
| 740 | |||
| 741 | sd->ctrl_handler = &info->hdl; | ||
| 742 | |||
| 743 | ret = info->hdl.error; | ||
| 744 | if (ret) | ||
| 745 | goto np_err; | ||
| 746 | |||
| 747 | info->i2c_reg_page = -1; | ||
| 748 | info->curr_fmt = &noon010_formats[0]; | ||
| 749 | info->curr_win = &noon010_sizes[0]; | ||
| 750 | |||
| 751 | /* Request reset asserted so we get put into reset */ | ||
| 752 | info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); | ||
| 753 | if (IS_ERR(info->reset)) { | ||
| 754 | ret = PTR_ERR(info->reset); | ||
| 755 | goto np_err; | ||
| 756 | } | ||
| 757 | gpiod_set_consumer_name(info->reset, "NOON010PC30 NRST"); | ||
| 758 | |||
| 759 | /* Request standby asserted so we get put into standby */ | ||
| 760 | info->stby = devm_gpiod_get(&client->dev, "standby", GPIOD_OUT_HIGH); | ||
| 761 | if (IS_ERR(info->stby)) { | ||
| 762 | ret = PTR_ERR(info->stby); | ||
| 763 | goto np_err; | ||
| 764 | } | ||
| 765 | gpiod_set_consumer_name(info->reset, "NOON010PC30 STBY"); | ||
| 766 | |||
| 767 | for (i = 0; i < NOON010_NUM_SUPPLIES; i++) | ||
| 768 | info->supply[i].supply = noon010_supply_name[i]; | ||
| 769 | |||
| 770 | ret = devm_regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, | ||
| 771 | info->supply); | ||
| 772 | if (ret) | ||
| 773 | goto np_err; | ||
| 774 | |||
| 775 | info->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 776 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | ||
| 777 | ret = media_entity_pads_init(&sd->entity, 1, &info->pad); | ||
| 778 | if (ret < 0) | ||
| 779 | goto np_err; | ||
| 780 | |||
| 781 | ret = noon010_detect(client, info); | ||
| 782 | if (!ret) | ||
| 783 | return 0; | ||
| 784 | |||
| 785 | np_err: | ||
| 786 | v4l2_ctrl_handler_free(&info->hdl); | ||
| 787 | v4l2_device_unregister_subdev(sd); | ||
| 788 | return ret; | ||
| 789 | } | ||
| 790 | |||
| 791 | static void noon010_remove(struct i2c_client *client) | ||
| 792 | { | ||
| 793 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
| 794 | struct noon010_info *info = to_noon010(sd); | ||
| 795 | |||
| 796 | v4l2_device_unregister_subdev(sd); | ||
| 797 | v4l2_ctrl_handler_free(&info->hdl); | ||
| 798 | media_entity_cleanup(&sd->entity); | ||
| 799 | } | ||
| 800 | |||
| 801 | static const struct i2c_device_id noon010_id[] = { | ||
| 802 | { MODULE_NAME, 0 }, | ||
| 803 | { }, | ||
| 804 | }; | ||
| 805 | MODULE_DEVICE_TABLE(i2c, noon010_id); | ||
| 806 | |||
| 807 | |||
| 808 | static struct i2c_driver noon010_i2c_driver = { | ||
| 809 | .driver = { | ||
| 810 | .name = MODULE_NAME | ||
| 811 | }, | ||
| 812 | .probe_new = noon010_probe, | ||
| 813 | .remove = noon010_remove, | ||
| 814 | .id_table = noon010_id, | ||
| 815 | }; | ||
| 816 | |||
| 817 | module_i2c_driver(noon010_i2c_driver); | ||
| 818 | |||
| 819 | MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); | ||
| 820 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
| 821 | MODULE_LICENSE("GPL"); | ||
| @@ -243,7 +243,6 @@ static const struct ov13b10_reg mipi_data_rate_1120mbps[] = { · 242 unmodified lines | |||
| 243 | {0x5047, 0xa4}, | 243 | {0x5047, 0xa4}, |
| 244 | {0x5048, 0x20}, | 244 | {0x5048, 0x20}, |
| 245 | {0x5049, 0xa4}, | 245 | {0x5049, 0xa4}, |
| 246 | {0x0100, 0x01}, | ||
| 247 | }; | 246 | }; |
| 248 | 247 | ||
| 249 | static const struct ov13b10_reg mode_4208x3120_regs[] = { | 248 | static const struct ov13b10_reg mode_4208x3120_regs[] = { |
| @@ -589,6 +588,9 @@ struct ov13b10 { · 339 unmodified lines | |||
| 589 | 588 | ||
| 590 | /* Streaming on/off */ | 589 | /* Streaming on/off */ |
| 591 | bool streaming; | 590 | bool streaming; |
| 591 | |||
| 592 | /* True if the device has been identified */ | ||
| 593 | bool identified; | ||
| 592 | }; | 594 | }; |
| 593 | 595 | ||
| 594 | #define to_ov13b10(_sd) container_of(_sd, struct ov13b10, sd) | 596 | #define to_ov13b10(_sd) container_of(_sd, struct ov13b10, sd) |
| @@ -1023,12 +1025,42 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, · 428 unmodified lines | |||
| 1023 | return 0; | 1025 | return 0; |
| 1024 | } | 1026 | } |
| 1025 | 1027 | ||
| 1028 | /* Verify chip ID */ | ||
| 1029 | static int ov13b10_identify_module(struct ov13b10 *ov13b) | ||
| 1030 | { | ||
| 1031 | struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); | ||
| 1032 | int ret; | ||
| 1033 | u32 val; | ||
| 1034 | |||
| 1035 | if (ov13b->identified) | ||
| 1036 | return 0; | ||
| 1037 | |||
| 1038 | ret = ov13b10_read_reg(ov13b, OV13B10_REG_CHIP_ID, | ||
| 1039 | OV13B10_REG_VALUE_24BIT, &val); | ||
| 1040 | if (ret) | ||
| 1041 | return ret; | ||
| 1042 | |||
| 1043 | if (val != OV13B10_CHIP_ID) { | ||
| 1044 | dev_err(&client->dev, "chip id mismatch: %x!=%x\n", | ||
| 1045 | OV13B10_CHIP_ID, val); | ||
| 1046 | return -EIO; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | ov13b->identified = true; | ||
| 1050 | |||
| 1051 | return 0; | ||
| 1052 | } | ||
| 1053 | |||
| 1026 | static int ov13b10_start_streaming(struct ov13b10 *ov13b) | 1054 | static int ov13b10_start_streaming(struct ov13b10 *ov13b) |
| 1027 | { | 1055 | { |
| 1028 | struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); | 1056 | struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); |
| 1029 | const struct ov13b10_reg_list *reg_list; | 1057 | const struct ov13b10_reg_list *reg_list; |
| 1030 | int ret, link_freq_index; | 1058 | int ret, link_freq_index; |
| 1031 | 1059 | ||
| 1060 | ret = ov13b10_identify_module(ov13b); | ||
| 1061 | if (ret) | ||
| 1062 | return ret; | ||
| 1063 | |||
| 1032 | /* Get out of from software reset */ | 1064 | /* Get out of from software reset */ |
| 1033 | ret = ov13b10_write_reg(ov13b, OV13B10_REG_SOFTWARE_RST, | 1065 | ret = ov13b10_write_reg(ov13b, OV13B10_REG_SOFTWARE_RST, |
| 1034 | OV13B10_REG_VALUE_08BIT, OV13B10_SOFTWARE_RST); | 1066 | OV13B10_REG_VALUE_08BIT, OV13B10_SOFTWARE_RST); |
| @@ -1144,27 +1176,6 @@ error: · 109 unmodified lines | |||
| 1144 | return ret; | 1176 | return ret; |
| 1145 | } | 1177 | } |
| 1146 | 1178 | ||
| 1147 | /* Verify chip ID */ | ||
| 1148 | static int ov13b10_identify_module(struct ov13b10 *ov13b) | ||
| 1149 | { | ||
| 1150 | struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); | ||
| 1151 | int ret; | ||
| 1152 | u32 val; | ||
| 1153 | |||
| 1154 | ret = ov13b10_read_reg(ov13b, OV13B10_REG_CHIP_ID, | ||
| 1155 | OV13B10_REG_VALUE_24BIT, &val); | ||
| 1156 | if (ret) | ||
| 1157 | return ret; | ||
| 1158 | |||
| 1159 | if (val != OV13B10_CHIP_ID) { | ||
| 1160 | dev_err(&client->dev, "chip id mismatch: %x!=%x\n", | ||
| 1161 | OV13B10_CHIP_ID, val); | ||
| 1162 | return -EIO; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | return 0; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | static const struct v4l2_subdev_video_ops ov13b10_video_ops = { | 1179 | static const struct v4l2_subdev_video_ops ov13b10_video_ops = { |
| 1169 | .s_stream = ov13b10_set_stream, | 1180 | .s_stream = ov13b10_set_stream, |
| 1170 | }; | 1181 | }; |
| @@ -1379,6 +1390,7 @@ out_err: · 208 unmodified lines | |||
| 1379 | static int ov13b10_probe(struct i2c_client *client) | 1390 | static int ov13b10_probe(struct i2c_client *client) |
| 1380 | { | 1391 | { |
| 1381 | struct ov13b10 *ov13b; | 1392 | struct ov13b10 *ov13b; |
| 1393 | bool full_power; | ||
| 1382 | int ret; | 1394 | int ret; |
| 1383 | 1395 | ||
| 1384 | /* Check HW config */ | 1396 | /* Check HW config */ |
| @@ -1395,11 +1407,14 @@ static int ov13b10_probe(struct i2c_client *client) · 10 unmodified lines | |||
| 1395 | /* Initialize subdev */ | 1407 | /* Initialize subdev */ |
| 1396 | v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); | 1408 | v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); |
| 1397 | 1409 | ||
| 1398 | /* Check module identity */ | 1410 | full_power = acpi_dev_state_d0(&client->dev); |
| 1399 | ret = ov13b10_identify_module(ov13b); | 1411 | if (full_power) { |
| 1400 | if (ret) { | 1412 | /* Check module identity */ |
| 1401 | dev_err(&client->dev, "failed to find sensor: %d\n", ret); | 1413 | ret = ov13b10_identify_module(ov13b); |
| 1402 | return ret; | 1414 | if (ret) { |
| 1415 | dev_err(&client->dev, "failed to find sensor: %d\n", ret); | ||
| 1416 | return ret; | ||
| 1417 | } | ||
| 1403 | } | 1418 | } |
| 1404 | 1419 | ||
| 1405 | /* Set default mode to max resolution */ | 1420 | /* Set default mode to max resolution */ |
| @@ -1431,7 +1446,10 @@ static int ov13b10_probe(struct i2c_client *client) · 25 unmodified lines | |||
| 1431 | * Device is already turned on by i2c-core with ACPI domain PM. | 1446 | * Device is already turned on by i2c-core with ACPI domain PM. |
| 1432 | * Enable runtime PM and turn off the device. | 1447 | * Enable runtime PM and turn off the device. |
| 1433 | */ | 1448 | */ |
| 1434 | pm_runtime_set_active(&client->dev); | 1449 | |
| 1450 | /* Set the device's state to active if it's in D0 state. */ | ||
| 1451 | if (full_power) | ||
| 1452 | pm_runtime_set_active(&client->dev); | ||
| 1435 | pm_runtime_enable(&client->dev); | 1453 | pm_runtime_enable(&client->dev); |
| 1436 | pm_runtime_idle(&client->dev); | 1454 | pm_runtime_idle(&client->dev); |
| 1437 | 1455 | ||
| @@ -1480,6 +1498,7 @@ static struct i2c_driver ov13b10_i2c_driver = { · 42 unmodified lines | |||
| 1480 | }, | 1498 | }, |
| 1481 | .probe_new = ov13b10_probe, | 1499 | .probe_new = ov13b10_probe, |
| 1482 | .remove = ov13b10_remove, | 1500 | .remove = ov13b10_remove, |
| 1501 | .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, | ||
| 1483 | }; | 1502 | }; |
| 1484 | 1503 | ||
| 1485 | module_i2c_driver(ov13b10_i2c_driver); | 1504 | module_i2c_driver(ov13b10_i2c_driver); |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | |||
| 17 | #include <media/media-entity.h> | 17 | #include <media/media-entity.h> |
| 18 | #include <media/v4l2-async.h> | 18 | #include <media/v4l2-async.h> |
| 19 | #include <media/v4l2-ctrls.h> | 19 | #include <media/v4l2-ctrls.h> |
| 20 | #include <media/v4l2-fwnode.h> | ||
| 20 | #include <media/v4l2-subdev.h> | 21 | #include <media/v4l2-subdev.h> |
| 21 | 22 | ||
| 22 | #define CHIP_ID 0x2685 | 23 | #define CHIP_ID 0x2685 |
| @@ -55,6 +56,9 @@ · 32 unmodified lines | |||
| 55 | #define OV2685_REG_VALUE_16BIT 2 | 56 | #define OV2685_REG_VALUE_16BIT 2 |
| 56 | #define OV2685_REG_VALUE_24BIT 3 | 57 | #define OV2685_REG_VALUE_24BIT 3 |
| 57 | 58 | ||
| 59 | #define OV2685_NATIVE_WIDTH 1616 | ||
| 60 | #define OV2685_NATIVE_HEIGHT 1216 | ||
| 61 | |||
| 58 | #define OV2685_LANES 1 | 62 | #define OV2685_LANES 1 |
| 59 | #define OV2685_BITS_PER_SAMPLE 10 | 63 | #define OV2685_BITS_PER_SAMPLE 10 |
| 60 | 64 | ||
| @@ -77,6 +81,7 @@ struct ov2685_mode { · 16 unmodified lines | |||
| 77 | u32 exp_def; | 81 | u32 exp_def; |
| 78 | u32 hts_def; | 82 | u32 hts_def; |
| 79 | u32 vts_def; | 83 | u32 vts_def; |
| 84 | const struct v4l2_rect *analog_crop; | ||
| 80 | const struct regval *reg_list; | 85 | const struct regval *reg_list; |
| 81 | }; | 86 | }; |
| 82 | 87 | ||
| @@ -230,6 +235,13 @@ static const int ov2685_test_pattern_val[] = { · 147 unmodified lines | |||
| 230 | OV2685_TEST_PATTERN_COLOR_SQUARE, | 235 | OV2685_TEST_PATTERN_COLOR_SQUARE, |
| 231 | }; | 236 | }; |
| 232 | 237 | ||
| 238 | static const struct v4l2_rect ov2685_analog_crop = { | ||
| 239 | .left = 8, | ||
| 240 | .top = 8, | ||
| 241 | .width = 1600, | ||
| 242 | .height = 1200, | ||
| 243 | }; | ||
| 244 | |||
| 233 | static const struct ov2685_mode supported_modes[] = { | 245 | static const struct ov2685_mode supported_modes[] = { |
| 234 | { | 246 | { |
| 235 | .width = 1600, | 247 | .width = 1600, |
| @@ -237,6 +249,7 @@ static const struct ov2685_mode supported_modes[] = { · 1 unmodified line | |||
| 237 | .exp_def = 0x04ee, | 249 | .exp_def = 0x04ee, |
| 238 | .hts_def = 0x06a4, | 250 | .hts_def = 0x06a4, |
| 239 | .vts_def = 0x050e, | 251 | .vts_def = 0x050e, |
| 252 | .analog_crop = &ov2685_analog_crop, | ||
| 240 | .reg_list = ov2685_1600x1200_regs, | 253 | .reg_list = ov2685_1600x1200_regs, |
| 241 | }, | 254 | }, |
| 242 | }; | 255 | }; |
| @@ -383,6 +396,53 @@ static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd, · 140 unmodified lines | |||
| 383 | return 0; | 396 | return 0; |
| 384 | } | 397 | } |
| 385 | 398 | ||
| 399 | static const struct v4l2_rect * | ||
| 400 | __ov2685_get_pad_crop(struct ov2685 *ov2685, | ||
| 401 | struct v4l2_subdev_state *state, unsigned int pad, | ||
| 402 | enum v4l2_subdev_format_whence which) | ||
| 403 | { | ||
| 404 | const struct ov2685_mode *mode = ov2685->cur_mode; | ||
| 405 | |||
| 406 | switch (which) { | ||
| 407 | case V4L2_SUBDEV_FORMAT_TRY: | ||
| 408 | return v4l2_subdev_get_try_crop(&ov2685->subdev, state, pad); | ||
| 409 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
| 410 | return mode->analog_crop; | ||
| 411 | } | ||
| 412 | |||
| 413 | return NULL; | ||
| 414 | } | ||
| 415 | |||
| 416 | static int ov2685_get_selection(struct v4l2_subdev *sd, | ||
| 417 | struct v4l2_subdev_state *sd_state, | ||
| 418 | struct v4l2_subdev_selection *sel) | ||
| 419 | { | ||
| 420 | struct ov2685 *ov2685 = to_ov2685(sd); | ||
| 421 | |||
| 422 | switch (sel->target) { | ||
| 423 | case V4L2_SEL_TGT_CROP: | ||
| 424 | mutex_lock(&ov2685->mutex); | ||
| 425 | sel->r = *__ov2685_get_pad_crop(ov2685, sd_state, sel->pad, | ||
| 426 | sel->which); | ||
| 427 | mutex_unlock(&ov2685->mutex); | ||
| 428 | break; | ||
| 429 | case V4L2_SEL_TGT_NATIVE_SIZE: | ||
| 430 | case V4L2_SEL_TGT_CROP_BOUNDS: | ||
| 431 | sel->r.top = 0; | ||
| 432 | sel->r.left = 0; | ||
| 433 | sel->r.width = OV2685_NATIVE_WIDTH; | ||
| 434 | sel->r.height = OV2685_NATIVE_HEIGHT; | ||
| 435 | break; | ||
| 436 | case V4L2_SEL_TGT_CROP_DEFAULT: | ||
| 437 | sel->r = ov2685_analog_crop; | ||
| 438 | break; | ||
| 439 | default: | ||
| 440 | return -EINVAL; | ||
| 441 | } | ||
| 442 | |||
| 443 | return 0; | ||
| 444 | } | ||
| 445 | |||
| 386 | /* Calculate the delay in us by clock rate and clock cycles */ | 446 | /* Calculate the delay in us by clock rate and clock cycles */ |
| 387 | static inline u32 ov2685_cal_delay(u32 cycles) | 447 | static inline u32 ov2685_cal_delay(u32 cycles) |
| 388 | { | 448 | { |
| @@ -419,8 +479,10 @@ static int __ov2685_power_on(struct ov2685 *ov2685) · 30 unmodified lines | |||
| 419 | * writing register before .s_stream() as a workaround | 479 | * writing register before .s_stream() as a workaround |
| 420 | */ | 480 | */ |
| 421 | ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list); | 481 | ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list); |
| 422 | if (ret) | 482 | if (ret) { |
| 483 | dev_err(dev, "Failed to set regs for power on\n"); | ||
| 423 | goto disable_supplies; | 484 | goto disable_supplies; |
| 485 | } | ||
| 424 | 486 | ||
| 425 | return 0; | 487 | return 0; |
| 426 | 488 | ||
| @@ -589,6 +651,8 @@ static const struct v4l2_subdev_pad_ops ov2685_pad_ops = { · 162 unmodified lines | |||
| 589 | .enum_frame_size = ov2685_enum_frame_sizes, | 651 | .enum_frame_size = ov2685_enum_frame_sizes, |
| 590 | .get_fmt = ov2685_get_fmt, | 652 | .get_fmt = ov2685_get_fmt, |
| 591 | .set_fmt = ov2685_set_fmt, | 653 | .set_fmt = ov2685_set_fmt, |
| 654 | .get_selection = ov2685_get_selection, | ||
| 655 | .set_selection = ov2685_get_selection, | ||
| 592 | }; | 656 | }; |
| 593 | 657 | ||
| 594 | static const struct v4l2_subdev_ops ov2685_subdev_ops = { | 658 | static const struct v4l2_subdev_ops ov2685_subdev_ops = { |
| @@ -611,13 +675,14 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) · 16 unmodified lines | |||
| 611 | const struct ov2685_mode *mode; | 675 | const struct ov2685_mode *mode; |
| 612 | struct v4l2_ctrl_handler *handler; | 676 | struct v4l2_ctrl_handler *handler; |
| 613 | struct v4l2_ctrl *ctrl; | 677 | struct v4l2_ctrl *ctrl; |
| 678 | struct v4l2_fwnode_device_properties props; | ||
| 614 | u64 exposure_max; | 679 | u64 exposure_max; |
| 615 | u32 pixel_rate, h_blank; | 680 | u32 pixel_rate, h_blank; |
| 616 | int ret; | 681 | int ret; |
| 617 | 682 | ||
| 618 | handler = &ov2685->ctrl_handler; | 683 | handler = &ov2685->ctrl_handler; |
| 619 | mode = ov2685->cur_mode; | 684 | mode = ov2685->cur_mode; |
| 620 | ret = v4l2_ctrl_handler_init(handler, 8); | 685 | ret = v4l2_ctrl_handler_init(handler, 10); |
| 621 | if (ret) | 686 | if (ret) |
| 622 | return ret; | 687 | return ret; |
| 623 | handler->lock = &ov2685->mutex; | 688 | handler->lock = &ov2685->mutex; |
| @@ -659,6 +724,15 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) · 35 unmodified lines | |||
| 659 | ARRAY_SIZE(ov2685_test_pattern_menu) - 1, | 724 | ARRAY_SIZE(ov2685_test_pattern_menu) - 1, |
| 660 | 0, 0, ov2685_test_pattern_menu); | 725 | 0, 0, ov2685_test_pattern_menu); |
| 661 | 726 | ||
| 727 | /* set properties from fwnode (e.g. rotation, orientation) */ | ||
| 728 | ret = v4l2_fwnode_device_parse(&ov2685->client->dev, &props); | ||
| 729 | if (ret) | ||
| 730 | goto err_free_handler; | ||
| 731 | |||
| 732 | ret = v4l2_ctrl_new_fwnode_properties(handler, &ov2685_ctrl_ops, &props); | ||
| 733 | if (ret) | ||
| 734 | goto err_free_handler; | ||
| 735 | |||
| 662 | if (handler->error) { | 736 | if (handler->error) { |
| 663 | ret = handler->error; | 737 | ret = handler->error; |
| 664 | dev_err(&ov2685->client->dev, | 738 | dev_err(&ov2685->client->dev, |
| @@ -733,7 +807,7 @@ static int ov2685_probe(struct i2c_client *client) · 68 unmodified lines | |||
| 733 | if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) | 807 | if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) |
| 734 | dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); | 808 | dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); |
| 735 | 809 | ||
| 736 | ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); | 810 | ov2685->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); |
| 737 | if (IS_ERR(ov2685->reset_gpio)) { | 811 | if (IS_ERR(ov2685->reset_gpio)) { |
| 738 | dev_err(dev, "Failed to get reset-gpios\n"); | 812 | dev_err(dev, "Failed to get reset-gpios\n"); |
| 739 | return -EINVAL; | 813 | return -EINVAL; |
| @@ -58,6 +58,7 @@ · 57 unmodified lines | |||
| 58 | #define OV5647_REG_MIPI_CTRL00 0x4800 | 58 | #define OV5647_REG_MIPI_CTRL00 0x4800 |
| 59 | #define OV5647_REG_MIPI_CTRL14 0x4814 | 59 | #define OV5647_REG_MIPI_CTRL14 0x4814 |
| 60 | #define OV5647_REG_AWB 0x5001 | 60 | #define OV5647_REG_AWB 0x5001 |
| 61 | #define OV5647_REG_ISPCTRL3D 0x503d | ||
| 61 | 62 | ||
| 62 | #define REG_TERM 0xfffe | 63 | #define REG_TERM 0xfffe |
| 63 | #define VAL_TERM 0xfe | 64 | #define VAL_TERM 0xfe |
| @@ -116,6 +117,20 @@ static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) · 52 unmodified lines | |||
| 116 | return container_of(sd, struct ov5647, sd); | 117 | return container_of(sd, struct ov5647, sd); |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 120 | static const char * const ov5647_test_pattern_menu[] = { | ||
| 121 | "Disabled", | ||
| 122 | "Color Bars", | ||
| 123 | "Color Squares", | ||
| 124 | "Random Data", | ||
| 125 | }; | ||
| 126 | |||
| 127 | static const u8 ov5647_test_pattern_val[] = { | ||
| 128 | 0x00, /* Disabled */ | ||
| 129 | 0x80, /* Color Bars */ | ||
| 130 | 0x82, /* Color Squares */ | ||
| 131 | 0x81, /* Random Data */ | ||
| 132 | }; | ||
| 133 | |||
| 119 | static const struct regval_list sensor_oe_disable_regs[] = { | 134 | static const struct regval_list sensor_oe_disable_regs[] = { |
| 120 | {0x3000, 0x00}, | 135 | {0x3000, 0x00}, |
| 121 | {0x3001, 0x00}, | 136 | {0x3001, 0x00}, |
| @@ -614,23 +629,29 @@ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) · 492 unmodified lines | |||
| 614 | 629 | ||
| 615 | static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) | 630 | static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) |
| 616 | { | 631 | { |
| 617 | unsigned char data_w[2] = { reg >> 8, reg & 0xff }; | ||
| 618 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 632 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| 633 | u8 buf[2] = { reg >> 8, reg & 0xff }; | ||
| 634 | struct i2c_msg msg[2]; | ||
| 619 | int ret; | 635 | int ret; |
| 620 | 636 | ||
| 621 | ret = i2c_master_send(client, data_w, 2); | 637 | msg[0].addr = client->addr; |
| 622 | if (ret < 0) { | 638 | msg[0].flags = client->flags; |
| 623 | dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", | 639 | msg[0].buf = buf; |
| 624 | __func__, reg); | 640 | msg[0].len = sizeof(buf); |
| 625 | return ret; | 641 | |
| 642 | msg[1].addr = client->addr; | ||
| 643 | msg[1].flags = client->flags | I2C_M_RD; | ||
| 644 | msg[1].buf = buf; | ||
| 645 | msg[1].len = 1; | ||
| 646 | |||
| 647 | ret = i2c_transfer(client->adapter, msg, 2); | ||
| 648 | if (ret != 2) { | ||
| 649 | dev_err(&client->dev, "%s: i2c read error, reg: %x = %d\n", | ||
| 650 | __func__, reg, ret); | ||
| 651 | return ret >= 0 ? -EINVAL : ret; | ||
| 626 | } | 652 | } |
| 627 | 653 | ||
| 628 | ret = i2c_master_recv(client, val, 1); | 654 | *val = buf[0]; |
| 629 | if (ret < 0) { | ||
| 630 | dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", | ||
| 631 | __func__, reg); | ||
| 632 | return ret; | ||
| 633 | } | ||
| 634 | 655 | ||
| 635 | return 0; | 656 | return 0; |
| 636 | } | 657 | } |
| @@ -1242,6 +1263,10 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) · 605 unmodified lines | |||
| 1242 | ret = ov5647_write16(sd, OV5647_REG_VTS_HI, | 1263 | ret = ov5647_write16(sd, OV5647_REG_VTS_HI, |
| 1243 | sensor->mode->format.height + ctrl->val); | 1264 | sensor->mode->format.height + ctrl->val); |
| 1244 | break; | 1265 | break; |
| 1266 | case V4L2_CID_TEST_PATTERN: | ||
| 1267 | ret = ov5647_write(sd, OV5647_REG_ISPCTRL3D, | ||
| 1268 | ov5647_test_pattern_val[ctrl->val]); | ||
| 1269 | break; | ||
| 1245 | 1270 | ||
| 1246 | /* Read-only, but we adjust it based on mode. */ | 1271 | /* Read-only, but we adjust it based on mode. */ |
| 1247 | case V4L2_CID_PIXEL_RATE: | 1272 | case V4L2_CID_PIXEL_RATE: |
| @@ -1270,7 +1295,7 @@ static int ov5647_init_controls(struct ov5647 *sensor) · 22 unmodified lines | |||
| 1270 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); | 1295 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); |
| 1271 | int hblank, exposure_max, exposure_def; | 1296 | int hblank, exposure_max, exposure_def; |
| 1272 | 1297 | ||
| 1273 | v4l2_ctrl_handler_init(&sensor->ctrls, 8); | 1298 | v4l2_ctrl_handler_init(&sensor->ctrls, 9); |
| 1274 | 1299 | ||
| 1275 | v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, | 1300 | v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, |
| 1276 | V4L2_CID_AUTOGAIN, 0, 1, 1, 0); | 1301 | V4L2_CID_AUTOGAIN, 0, 1, 1, 0); |
| @@ -1314,6 +1339,11 @@ static int ov5647_init_controls(struct ov5647 *sensor) · 37 unmodified lines | |||
| 1314 | sensor->mode->vts - | 1339 | sensor->mode->vts - |
| 1315 | sensor->mode->format.height); | 1340 | sensor->mode->format.height); |
| 1316 | 1341 | ||
| 1342 | v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &ov5647_ctrl_ops, | ||
| 1343 | V4L2_CID_TEST_PATTERN, | ||
| 1344 | ARRAY_SIZE(ov5647_test_pattern_menu) - 1, | ||
| 1345 | 0, 0, ov5647_test_pattern_menu); | ||
| 1346 | |||
| 1317 | if (sensor->ctrls.error) | 1347 | if (sensor->ctrls.error) |
| 1318 | goto handler_free; | 1348 | goto handler_free; |
| 1319 | 1349 | ||
| @@ -29,6 +29,12 @@ · 28 unmodified lines | |||
| 29 | #define OV5670_REG_SOFTWARE_RST 0x0103 | 29 | #define OV5670_REG_SOFTWARE_RST 0x0103 |
| 30 | #define OV5670_SOFTWARE_RST 0x01 | 30 | #define OV5670_SOFTWARE_RST 0x01 |
| 31 | 31 | ||
| 32 | #define OV5670_MIPI_SC_CTRL0_REG 0x3018 | ||
| 33 | #define OV5670_MIPI_SC_CTRL0_LANES(v) ((((v) - 1) << 5) & \ | ||
| 34 | GENMASK(7, 5)) | ||
| 35 | #define OV5670_MIPI_SC_CTRL0_MIPI_EN BIT(4) | ||
| 36 | #define OV5670_MIPI_SC_CTRL0_RESERVED BIT(1) | ||
| 37 | |||
| 32 | /* vertical-timings from sensor */ | 38 | /* vertical-timings from sensor */ |
| 33 | #define OV5670_REG_VTS 0x380e | 39 | #define OV5670_REG_VTS 0x380e |
| 34 | #define OV5670_VTS_30FPS 0x0808 /* default for 30 fps */ | 40 | #define OV5670_VTS_30FPS 0x0808 /* default for 30 fps */ |
| @@ -92,7 +98,6 @@ struct ov5670_reg_list { · 57 unmodified lines | |||
| 92 | }; | 98 | }; |
| 93 | 99 | ||
| 94 | struct ov5670_link_freq_config { | 100 | struct ov5670_link_freq_config { |
| 95 | u32 pixel_rate; | ||
| 96 | const struct ov5670_reg_list reg_list; | 101 | const struct ov5670_reg_list reg_list; |
| 97 | }; | 102 | }; |
| 98 | 103 | ||
| @@ -163,7 +168,6 @@ static const struct ov5670_reg mode_2592x1944_regs[] = { · 64 unmodified lines | |||
| 163 | {0x3005, 0xf0}, | 168 | {0x3005, 0xf0}, |
| 164 | {0x3007, 0x00}, | 169 | {0x3007, 0x00}, |
| 165 | {0x3015, 0x0f}, | 170 | {0x3015, 0x0f}, |
| 166 | {0x3018, 0x32}, | ||
| 167 | {0x301a, 0xf0}, | 171 | {0x301a, 0xf0}, |
| 168 | {0x301b, 0xf0}, | 172 | {0x301b, 0xf0}, |
| 169 | {0x301c, 0xf0}, | 173 | {0x301c, 0xf0}, |
| @@ -429,7 +433,6 @@ static const struct ov5670_reg mode_1296x972_regs[] = { · 259 unmodified lines | |||
| 429 | {0x3005, 0xf0}, | 433 | {0x3005, 0xf0}, |
| 430 | {0x3007, 0x00}, | 434 | {0x3007, 0x00}, |
| 431 | {0x3015, 0x0f}, | 435 | {0x3015, 0x0f}, |
| 432 | {0x3018, 0x32}, | ||
| 433 | {0x301a, 0xf0}, | 436 | {0x301a, 0xf0}, |
| 434 | {0x301b, 0xf0}, | 437 | {0x301b, 0xf0}, |
| 435 | {0x301c, 0xf0}, | 438 | {0x301c, 0xf0}, |
| @@ -695,7 +698,6 @@ static const struct ov5670_reg mode_648x486_regs[] = { · 259 unmodified lines | |||
| 695 | {0x3005, 0xf0}, | 698 | {0x3005, 0xf0}, |
| 696 | {0x3007, 0x00}, | 699 | {0x3007, 0x00}, |
| 697 | {0x3015, 0x0f}, | 700 | {0x3015, 0x0f}, |
| 698 | {0x3018, 0x32}, | ||
| 699 | {0x301a, 0xf0}, | 701 | {0x301a, 0xf0}, |
| 700 | {0x301b, 0xf0}, | 702 | {0x301b, 0xf0}, |
| 701 | {0x301c, 0xf0}, | 703 | {0x301c, 0xf0}, |
| @@ -961,7 +963,6 @@ static const struct ov5670_reg mode_2560x1440_regs[] = { · 259 unmodified lines | |||
| 961 | {0x3005, 0xf0}, | 963 | {0x3005, 0xf0}, |
| 962 | {0x3007, 0x00}, | 964 | {0x3007, 0x00}, |
| 963 | {0x3015, 0x0f}, | 965 | {0x3015, 0x0f}, |
| 964 | {0x3018, 0x32}, | ||
| 965 | {0x301a, 0xf0}, | 966 | {0x301a, 0xf0}, |
| 966 | {0x301b, 0xf0}, | 967 | {0x301b, 0xf0}, |
| 967 | {0x301c, 0xf0}, | 968 | {0x301c, 0xf0}, |
| @@ -1226,7 +1227,6 @@ static const struct ov5670_reg mode_1280x720_regs[] = { · 258 unmodified lines | |||
| 1226 | {0x3005, 0xf0}, | 1227 | {0x3005, 0xf0}, |
| 1227 | {0x3007, 0x00}, | 1228 | {0x3007, 0x00}, |
| 1228 | {0x3015, 0x0f}, | 1229 | {0x3015, 0x0f}, |
| 1229 | {0x3018, 0x32}, | ||
| 1230 | {0x301a, 0xf0}, | 1230 | {0x301a, 0xf0}, |
| 1231 | {0x301b, 0xf0}, | 1231 | {0x301b, 0xf0}, |
| 1232 | {0x301c, 0xf0}, | 1232 | {0x301c, 0xf0}, |
| @@ -1492,7 +1492,6 @@ static const struct ov5670_reg mode_640x360_regs[] = { · 259 unmodified lines | |||
| 1492 | {0x3005, 0xf0}, | 1492 | {0x3005, 0xf0}, |
| 1493 | {0x3007, 0x00}, | 1493 | {0x3007, 0x00}, |
| 1494 | {0x3015, 0x0f}, | 1494 | {0x3015, 0x0f}, |
| 1495 | {0x3018, 0x32}, | ||
| 1496 | {0x301a, 0xf0}, | 1495 | {0x301a, 0xf0}, |
| 1497 | {0x301b, 0xf0}, | 1496 | {0x301b, 0xf0}, |
| 1498 | {0x301c, 0xf0}, | 1497 | {0x301c, 0xf0}, |
| @@ -1762,8 +1761,6 @@ static const char * const ov5670_test_pattern_menu[] = { · 263 unmodified lines | |||
| 1762 | #define OV5670_LINK_FREQ_422MHZ_INDEX 0 | 1761 | #define OV5670_LINK_FREQ_422MHZ_INDEX 0 |
| 1763 | static const struct ov5670_link_freq_config link_freq_configs[] = { | 1762 | static const struct ov5670_link_freq_config link_freq_configs[] = { |
| 1764 | { | 1763 | { |
| 1765 | /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ | ||
| 1766 | .pixel_rate = (OV5670_LINK_FREQ_422MHZ * 2 * 2) / 10, | ||
| 1767 | .reg_list = { | 1764 | .reg_list = { |
| 1768 | .num_of_regs = ARRAY_SIZE(mipi_data_rate_840mbps), | 1765 | .num_of_regs = ARRAY_SIZE(mipi_data_rate_840mbps), |
| 1769 | .regs = mipi_data_rate_840mbps, | 1766 | .regs = mipi_data_rate_840mbps, |
| @@ -1859,6 +1856,7 @@ static const struct ov5670_mode supported_modes[] = { · 89 unmodified lines | |||
| 1859 | struct ov5670 { | 1856 | struct ov5670 { |
| 1860 | struct v4l2_subdev sd; | 1857 | struct v4l2_subdev sd; |
| 1861 | struct media_pad pad; | 1858 | struct media_pad pad; |
| 1859 | struct v4l2_fwnode_endpoint endpoint; | ||
| 1862 | 1860 | ||
| 1863 | struct v4l2_ctrl_handler ctrl_handler; | 1861 | struct v4l2_ctrl_handler ctrl_handler; |
| 1864 | /* V4L2 Controls */ | 1862 | /* V4L2 Controls */ |
| @@ -2101,9 +2099,13 @@ static const struct v4l2_ctrl_ops ov5670_ctrl_ops = { · 236 unmodified lines | |||
| 2101 | /* Initialize control handlers */ | 2099 | /* Initialize control handlers */ |
| 2102 | static int ov5670_init_controls(struct ov5670 *ov5670) | 2100 | static int ov5670_init_controls(struct ov5670 *ov5670) |
| 2103 | { | 2101 | { |
| 2102 | struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = | ||
| 2103 | &ov5670->endpoint.bus.mipi_csi2; | ||
| 2104 | struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); | 2104 | struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); |
| 2105 | struct v4l2_fwnode_device_properties props; | 2105 | struct v4l2_fwnode_device_properties props; |
| 2106 | struct v4l2_ctrl_handler *ctrl_hdlr; | 2106 | struct v4l2_ctrl_handler *ctrl_hdlr; |
| 2107 | unsigned int lanes_count; | ||
| 2108 | s64 mipi_pixel_rate; | ||
| 2107 | s64 vblank_max; | 2109 | s64 vblank_max; |
| 2108 | s64 vblank_def; | 2110 | s64 vblank_def; |
| 2109 | s64 vblank_min; | 2111 | s64 vblank_min; |
| @@ -2124,12 +2126,15 @@ static int ov5670_init_controls(struct ov5670 *ov5670) · 14 unmodified lines | |||
| 2124 | ov5670->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; | 2126 | ov5670->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| 2125 | 2127 | ||
| 2126 | /* By default, V4L2_CID_PIXEL_RATE is read only */ | 2128 | /* By default, V4L2_CID_PIXEL_RATE is read only */ |
| 2129 | lanes_count = bus_mipi_csi2->num_data_lanes; | ||
| 2130 | mipi_pixel_rate = OV5670_LINK_FREQ_422MHZ * 2 * lanes_count / 10; | ||
| 2131 | |||
| 2127 | ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, | 2132 | ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, |
| 2128 | V4L2_CID_PIXEL_RATE, | 2133 | V4L2_CID_PIXEL_RATE, |
| 2129 | link_freq_configs[0].pixel_rate, | 2134 | mipi_pixel_rate, |
| 2130 | link_freq_configs[0].pixel_rate, | 2135 | mipi_pixel_rate, |
| 2131 | 1, | 2136 | 1, |
| 2132 | link_freq_configs[0].pixel_rate); | 2137 | mipi_pixel_rate); |
| 2133 | 2138 | ||
| 2134 | vblank_max = OV5670_VTS_MAX - ov5670->cur_mode->height; | 2139 | vblank_max = OV5670_VTS_MAX - ov5670->cur_mode->height; |
| 2135 | vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height; | 2140 | vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height; |
| @@ -2288,8 +2293,13 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, · 152 unmodified lines | |||
| 2288 | struct v4l2_subdev_format *fmt) | 2293 | struct v4l2_subdev_format *fmt) |
| 2289 | { | 2294 | { |
| 2290 | struct ov5670 *ov5670 = to_ov5670(sd); | 2295 | struct ov5670 *ov5670 = to_ov5670(sd); |
| 2296 | struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = | ||
| 2297 | &ov5670->endpoint.bus.mipi_csi2; | ||
| 2291 | const struct ov5670_mode *mode; | 2298 | const struct ov5670_mode *mode; |
| 2299 | unsigned int lanes_count; | ||
| 2300 | s64 mipi_pixel_rate; | ||
| 2292 | s32 vblank_def; | 2301 | s32 vblank_def; |
| 2302 | s64 link_freq; | ||
| 2293 | s32 h_blank; | 2303 | s32 h_blank; |
| 2294 | 2304 | ||
| 2295 | mutex_lock(&ov5670->mutex); | 2305 | mutex_lock(&ov5670->mutex); |
| @@ -2306,9 +2316,14 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, · 10 unmodified lines | |||
| 2306 | } else { | 2316 | } else { |
| 2307 | ov5670->cur_mode = mode; | 2317 | ov5670->cur_mode = mode; |
| 2308 | __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); | 2318 | __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); |
| 2319 | |||
| 2320 | lanes_count = bus_mipi_csi2->num_data_lanes; | ||
| 2321 | link_freq = link_freq_menu_items[mode->link_freq_index]; | ||
| 2322 | /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ | ||
| 2323 | mipi_pixel_rate = div_s64(link_freq * 2 * lanes_count, 10); | ||
| 2309 | __v4l2_ctrl_s_ctrl_int64( | 2324 | __v4l2_ctrl_s_ctrl_int64( |
| 2310 | ov5670->pixel_rate, | 2325 | ov5670->pixel_rate, |
| 2311 | link_freq_configs[mode->link_freq_index].pixel_rate); | 2326 | mipi_pixel_rate); |
| 2312 | /* Update limits and set FPS to default */ | 2327 | /* Update limits and set FPS to default */ |
| 2313 | vblank_def = ov5670->cur_mode->vts_def - | 2328 | vblank_def = ov5670->cur_mode->vts_def - |
| 2314 | ov5670->cur_mode->height; | 2329 | ov5670->cur_mode->height; |
| @@ -2361,6 +2376,19 @@ static int ov5670_identify_module(struct ov5670 *ov5670) · 46 unmodified lines | |||
| 2361 | return 0; | 2376 | return 0; |
| 2362 | } | 2377 | } |
| 2363 | 2378 | ||
| 2379 | static int ov5670_mipi_configure(struct ov5670 *ov5670) | ||
| 2380 | { | ||
| 2381 | struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = | ||
| 2382 | &ov5670->endpoint.bus.mipi_csi2; | ||
| 2383 | unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; | ||
| 2384 | |||
| 2385 | return ov5670_write_reg(ov5670, OV5670_MIPI_SC_CTRL0_REG, | ||
| 2386 | OV5670_REG_VALUE_08BIT, | ||
| 2387 | OV5670_MIPI_SC_CTRL0_LANES(lanes_count) | | ||
| 2388 | OV5670_MIPI_SC_CTRL0_MIPI_EN | | ||
| 2389 | OV5670_MIPI_SC_CTRL0_RESERVED); | ||
| 2390 | } | ||
| 2391 | |||
| 2364 | /* Prepare streaming by writing default values and customized values */ | 2392 | /* Prepare streaming by writing default values and customized values */ |
| 2365 | static int ov5670_start_streaming(struct ov5670 *ov5670) | 2393 | static int ov5670_start_streaming(struct ov5670 *ov5670) |
| 2366 | { | 2394 | { |
| @@ -2399,6 +2427,12 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) · 32 unmodified lines | |||
| 2399 | return ret; | 2427 | return ret; |
| 2400 | } | 2428 | } |
| 2401 | 2429 | ||
| 2430 | ret = ov5670_mipi_configure(ov5670); | ||
| 2431 | if (ret) { | ||
| 2432 | dev_err(&client->dev, "%s failed to configure MIPI\n", __func__); | ||
| 2433 | return ret; | ||
| 2434 | } | ||
| 2435 | |||
| 2402 | ret = __v4l2_ctrl_handler_setup(ov5670->sd.ctrl_handler); | 2436 | ret = __v4l2_ctrl_handler_setup(ov5670->sd.ctrl_handler); |
| 2403 | if (ret) | 2437 | if (ret) |
| 2404 | return ret; | 2438 | return ret; |
| @@ -2647,23 +2681,20 @@ static int ov5670_gpio_probe(struct ov5670 *ov5670) · 242 unmodified lines | |||
| 2647 | 2681 | ||
| 2648 | static int ov5670_probe(struct i2c_client *client) | 2682 | static int ov5670_probe(struct i2c_client *client) |
| 2649 | { | 2683 | { |
| 2684 | struct fwnode_handle *handle; | ||
| 2650 | struct ov5670 *ov5670; | 2685 | struct ov5670 *ov5670; |
| 2651 | const char *err_msg; | ||
| 2652 | u32 input_clk = 0; | 2686 | u32 input_clk = 0; |
| 2653 | bool full_power; | 2687 | bool full_power; |
| 2654 | int ret; | 2688 | int ret; |
| 2655 | 2689 | ||
| 2656 | ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL); | 2690 | ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL); |
| 2657 | if (!ov5670) { | 2691 | if (!ov5670) |
| 2658 | ret = -ENOMEM; | 2692 | return -ENOMEM; |
| 2659 | err_msg = "devm_kzalloc() error"; | ||
| 2660 | goto error_print; | ||
| 2661 | } | ||
| 2662 | 2693 | ||
| 2663 | ov5670->xvclk = devm_clk_get(&client->dev, NULL); | 2694 | ov5670->xvclk = devm_clk_get_optional(&client->dev, NULL); |
| 2664 | if (!IS_ERR_OR_NULL(ov5670->xvclk)) | 2695 | if (!IS_ERR_OR_NULL(ov5670->xvclk)) |
| 2665 | input_clk = clk_get_rate(ov5670->xvclk); | 2696 | input_clk = clk_get_rate(ov5670->xvclk); |
| 2666 | else if (PTR_ERR(ov5670->xvclk) == -ENOENT) | 2697 | else if (!ov5670->xvclk || PTR_ERR(ov5670->xvclk) == -ENOENT) |
| 2667 | device_property_read_u32(&client->dev, "clock-frequency", | 2698 | device_property_read_u32(&client->dev, "clock-frequency", |
| 2668 | &input_clk); | 2699 | &input_clk); |
| 2669 | else | 2700 | else |
| @@ -2680,29 +2711,38 @@ static int ov5670_probe(struct i2c_client *client) · 10 unmodified lines | |||
| 2680 | v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); | 2711 | v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); |
| 2681 | 2712 | ||
| 2682 | ret = ov5670_regulators_probe(ov5670); | 2713 | ret = ov5670_regulators_probe(ov5670); |
| 2683 | if (ret) { | 2714 | if (ret) |
| 2684 | err_msg = "Regulators probe failed"; | 2715 | return dev_err_probe(&client->dev, ret, "Regulators probe failed\n"); |
| 2685 | goto error_print; | ||
| 2686 | } | ||
| 2687 | 2716 | ||
| 2688 | ret = ov5670_gpio_probe(ov5670); | 2717 | ret = ov5670_gpio_probe(ov5670); |
| 2689 | if (ret) { | 2718 | if (ret) |
| 2690 | err_msg = "GPIO probe failed"; | 2719 | return dev_err_probe(&client->dev, ret, "GPIO probe failed\n"); |
| 2691 | goto error_print; | 2720 | |
| 2692 | } | 2721 | /* Graph Endpoint */ |
| 2722 | handle = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); | ||
| 2723 | if (!handle) | ||
| 2724 | return dev_err_probe(&client->dev, -ENXIO, "Endpoint for node get failed\n"); | ||
| 2725 | |||
| 2726 | ov5670->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; | ||
| 2727 | ov5670->endpoint.bus.mipi_csi2.num_data_lanes = 2; | ||
| 2728 | |||
| 2729 | ret = v4l2_fwnode_endpoint_alloc_parse(handle, &ov5670->endpoint); | ||
| 2730 | fwnode_handle_put(handle); | ||
| 2731 | if (ret) | ||
| 2732 | return dev_err_probe(&client->dev, ret, "Endpoint parse failed\n"); | ||
| 2693 | 2733 | ||
| 2694 | full_power = acpi_dev_state_d0(&client->dev); | 2734 | full_power = acpi_dev_state_d0(&client->dev); |
| 2695 | if (full_power) { | 2735 | if (full_power) { |
| 2696 | ret = ov5670_runtime_resume(&client->dev); | 2736 | ret = ov5670_runtime_resume(&client->dev); |
| 2697 | if (ret) { | 2737 | if (ret) { |
| 2698 | err_msg = "Power up failed"; | 2738 | dev_err_probe(&client->dev, ret, "Power up failed\n"); |
| 2699 | goto error_print; | 2739 | goto error_endpoint; |
| 2700 | } | 2740 | } |
| 2701 | 2741 | ||
| 2702 | /* Check module identity */ | 2742 | /* Check module identity */ |
| 2703 | ret = ov5670_identify_module(ov5670); | 2743 | ret = ov5670_identify_module(ov5670); |
| 2704 | if (ret) { | 2744 | if (ret) { |
| 2705 | err_msg = "ov5670_identify_module() error"; | 2745 | dev_err_probe(&client->dev, ret, "ov5670_identify_module() error\n"); |
| 2706 | goto error_power_off; | 2746 | goto error_power_off; |
| 2707 | } | 2747 | } |
| 2708 | } | 2748 | } |
| @@ -2714,7 +2754,7 @@ static int ov5670_probe(struct i2c_client *client) · 5 unmodified lines | |||
| 2714 | 2754 | ||
| 2715 | ret = ov5670_init_controls(ov5670); | 2755 | ret = ov5670_init_controls(ov5670); |
| 2716 | if (ret) { | 2756 | if (ret) { |
| 2717 | err_msg = "ov5670_init_controls() error"; | 2757 | dev_err_probe(&client->dev, ret, "ov5670_init_controls() error\n"); |
| 2718 | goto error_mutex_destroy; | 2758 | goto error_mutex_destroy; |
| 2719 | } | 2759 | } |
| 2720 | 2760 | ||
| @@ -2727,7 +2767,7 @@ static int ov5670_probe(struct i2c_client *client) · 6 unmodified lines | |||
| 2727 | ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; | 2767 | ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; |
| 2728 | ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad); | 2768 | ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad); |
| 2729 | if (ret) { | 2769 | if (ret) { |
| 2730 | err_msg = "media_entity_pads_init() error"; | 2770 | dev_err_probe(&client->dev, ret, "media_entity_pads_init() error\n"); |
| 2731 | goto error_handler_free; | 2771 | goto error_handler_free; |
| 2732 | } | 2772 | } |
| 2733 | 2773 | ||
| @@ -2741,7 +2781,7 @@ static int ov5670_probe(struct i2c_client *client) · 7 unmodified lines | |||
| 2741 | /* Async register for subdev */ | 2781 | /* Async register for subdev */ |
| 2742 | ret = v4l2_async_register_subdev_sensor(&ov5670->sd); | 2782 | ret = v4l2_async_register_subdev_sensor(&ov5670->sd); |
| 2743 | if (ret < 0) { | 2783 | if (ret < 0) { |
| 2744 | err_msg = "v4l2_async_register_subdev() error"; | 2784 | dev_err_probe(&client->dev, ret, "v4l2_async_register_subdev() error\n"); |
| 2745 | goto error_pm_disable; | 2785 | goto error_pm_disable; |
| 2746 | } | 2786 | } |
| 2747 | 2787 | ||
| @@ -2764,8 +2804,8 @@ error_power_off: · 16 unmodified lines | |||
| 2764 | if (full_power) | 2804 | if (full_power) |
| 2765 | ov5670_runtime_suspend(&client->dev); | 2805 | ov5670_runtime_suspend(&client->dev); |
| 2766 | 2806 | ||
| 2767 | error_print: | 2807 | error_endpoint: |
| 2768 | dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret); | 2808 | v4l2_fwnode_endpoint_free(&ov5670->endpoint); |
| 2769 | 2809 | ||
| 2770 | return ret; | 2810 | return ret; |
| 2771 | } | 2811 | } |
| @@ -2782,6 +2822,8 @@ static void ov5670_remove(struct i2c_client *client) · 10 unmodified lines | |||
| 2782 | 2822 | ||
| 2783 | pm_runtime_disable(&client->dev); | 2823 | pm_runtime_disable(&client->dev); |
| 2784 | ov5670_runtime_suspend(&client->dev); | 2824 | ov5670_runtime_suspend(&client->dev); |
| 2825 | |||
| 2826 | v4l2_fwnode_endpoint_free(&ov5670->endpoint); | ||
| 2785 | } | 2827 | } |
| 2786 | 2828 | ||
| 2787 | static const struct dev_pm_ops ov5670_pm_ops = { | 2829 | static const struct dev_pm_ops ov5670_pm_ops = { |
| @@ -1894,14 +1894,9 @@ static int ov7670_probe(struct i2c_client *client) · 1893 unmodified lines | |||
| 1894 | info->pclk_hb_disable = true; | 1894 | info->pclk_hb_disable = true; |
| 1895 | } | 1895 | } |
| 1896 | 1896 | ||
| 1897 | info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */ | 1897 | info->clk = devm_clk_get_optional(&client->dev, "xclk"); |
| 1898 | if (IS_ERR(info->clk)) { | 1898 | if (IS_ERR(info->clk)) |
| 1899 | ret = PTR_ERR(info->clk); | 1899 | return PTR_ERR(info->clk); |
| 1900 | if (ret == -ENOENT) | ||
| 1901 | info->clk = NULL; | ||
| 1902 | else | ||
| 1903 | return ret; | ||
| 1904 | } | ||
| 1905 | 1900 | ||
| 1906 | ret = ov7670_init_gpio(client, info); | 1901 | ret = ov7670_init_gpio(client, info); |
| 1907 | if (ret) | 1902 | if (ret) |
| @@ -1709,46 +1709,6 @@ static int ov8856_identify_module(struct ov8856 *ov8856) · 1708 unmodified lines | |||
| 1709 | return -ENXIO; | 1709 | return -ENXIO; |
| 1710 | } | 1710 | } |
| 1711 | 1711 | ||
| 1712 | ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, | ||
| 1713 | OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); | ||
| 1714 | if (ret) | ||
| 1715 | return ret; | ||
| 1716 | |||
| 1717 | ret = ov8856_write_reg(ov8856, OV8856_OTP_MODE_CTRL, | ||
| 1718 | OV8856_REG_VALUE_08BIT, OV8856_OTP_MODE_AUTO); | ||
| 1719 | if (ret) { | ||
| 1720 | dev_err(&client->dev, "failed to set otp mode"); | ||
| 1721 | return ret; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | ret = ov8856_write_reg(ov8856, OV8856_OTP_LOAD_CTRL, | ||
| 1725 | OV8856_REG_VALUE_08BIT, | ||
| 1726 | OV8856_OTP_LOAD_CTRL_ENABLE); | ||
| 1727 | if (ret) { | ||
| 1728 | dev_err(&client->dev, "failed to enable load control"); | ||
| 1729 | return ret; | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | ret = ov8856_read_reg(ov8856, OV8856_MODULE_REVISION, | ||
| 1733 | OV8856_REG_VALUE_08BIT, &val); | ||
| 1734 | if (ret) { | ||
| 1735 | dev_err(&client->dev, "failed to read module revision"); | ||
| 1736 | return ret; | ||
| 1737 | } | ||
| 1738 | |||
| 1739 | dev_info(&client->dev, "OV8856 revision %x (%s) at address 0x%02x\n", | ||
| 1740 | val, | ||
| 1741 | val == OV8856_2A_MODULE ? "2A" : | ||
| 1742 | val == OV8856_1B_MODULE ? "1B" : "unknown revision", | ||
| 1743 | client->addr); | ||
| 1744 | |||
| 1745 | ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, | ||
| 1746 | OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY); | ||
| 1747 | if (ret) { | ||
| 1748 | dev_err(&client->dev, "failed to exit streaming mode"); | ||
| 1749 | return ret; | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | ov8856->identified = true; | 1712 | ov8856->identified = true; |
| 1753 | 1713 | ||
| 1754 | return 0; | 1714 | return 0; |
| @@ -1,1652 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /* | ||
| 3 | * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor | ||
| 4 | * with embedded SoC ISP. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2011, Samsung Electronics Co., Ltd. | ||
| 7 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
| 8 | * | ||
| 9 | * Based on a driver authored by Dongsoo Nathaniel Kim. | ||
| 10 | * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/clk.h> | ||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/gpio.h> | ||
| 16 | #include <linux/i2c.h> | ||
| 17 | #include <linux/media.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/regulator/consumer.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | |||
| 22 | #include <media/media-entity.h> | ||
| 23 | #include <media/v4l2-ctrls.h> | ||
| 24 | #include <media/v4l2-device.h> | ||
| 25 | #include <media/v4l2-subdev.h> | ||
| 26 | #include <media/v4l2-mediabus.h> | ||
| 27 | #include <media/i2c/s5k6aa.h> | ||
| 28 | |||
| 29 | static int debug; | ||
| 30 | module_param(debug, int, 0644); | ||
| 31 | |||
| 32 | #define DRIVER_NAME "S5K6AA" | ||
| 33 | |||
| 34 | /* The token to indicate array termination */ | ||
| 35 | #define S5K6AA_TERM 0xffff | ||
| 36 | #define S5K6AA_OUT_WIDTH_DEF 640 | ||
| 37 | #define S5K6AA_OUT_HEIGHT_DEF 480 | ||
| 38 | #define S5K6AA_WIN_WIDTH_MAX 1280 | ||
| 39 | #define S5K6AA_WIN_HEIGHT_MAX 1024 | ||
| 40 | #define S5K6AA_WIN_WIDTH_MIN 8 | ||
| 41 | #define S5K6AA_WIN_HEIGHT_MIN 8 | ||
| 42 | |||
| 43 | /* | ||
| 44 | * H/W register Interface (0xD0000000 - 0xD0000FFF) | ||
| 45 | */ | ||
| 46 | #define AHB_MSB_ADDR_PTR 0xfcfc | ||
| 47 | #define GEN_REG_OFFSH 0xd000 | ||
| 48 | #define REG_CMDWR_ADDRH 0x0028 | ||
| 49 | #define REG_CMDWR_ADDRL 0x002a | ||
| 50 | #define REG_CMDRD_ADDRH 0x002c | ||
| 51 | #define REG_CMDRD_ADDRL 0x002e | ||
| 52 | #define REG_CMDBUF0_ADDR 0x0f12 | ||
| 53 | #define REG_CMDBUF1_ADDR 0x0f10 | ||
| 54 | |||
| 55 | /* | ||
| 56 | * Host S/W Register interface (0x70000000 - 0x70002000) | ||
| 57 | * The value of the two most significant address bytes is 0x7000, | ||
| 58 | * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. | ||
| 59 | */ | ||
| 60 | #define HOST_SWIF_OFFSH 0x7000 | ||
| 61 | |||
| 62 | /* Initialization parameters */ | ||
| 63 | /* Master clock frequency in KHz */ | ||
| 64 | #define REG_I_INCLK_FREQ_L 0x01b8 | ||
| 65 | #define REG_I_INCLK_FREQ_H 0x01ba | ||
| 66 | #define MIN_MCLK_FREQ_KHZ 6000U | ||
| 67 | #define MAX_MCLK_FREQ_KHZ 27000U | ||
| 68 | #define REG_I_USE_NPVI_CLOCKS 0x01c6 | ||
| 69 | #define REG_I_USE_NMIPI_CLOCKS 0x01c8 | ||
| 70 | |||
| 71 | /* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ | ||
| 72 | #define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) | ||
| 73 | #define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) | ||
| 74 | #define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) | ||
| 75 | #define SYS_PLL_OUT_FREQ (48000000 / 4000) | ||
| 76 | #define PCLK_FREQ_MIN (24000000 / 4000) | ||
| 77 | #define PCLK_FREQ_MAX (48000000 / 4000) | ||
| 78 | #define REG_I_INIT_PARAMS_UPDATED 0x01e0 | ||
| 79 | #define REG_I_ERROR_INFO 0x01e2 | ||
| 80 | |||
| 81 | /* General purpose parameters */ | ||
| 82 | #define REG_USER_BRIGHTNESS 0x01e4 | ||
| 83 | #define REG_USER_CONTRAST 0x01e6 | ||
| 84 | #define REG_USER_SATURATION 0x01e8 | ||
| 85 | #define REG_USER_SHARPBLUR 0x01ea | ||
| 86 | |||
| 87 | #define REG_G_SPEC_EFFECTS 0x01ee | ||
| 88 | #define REG_G_ENABLE_PREV 0x01f0 | ||
| 89 | #define REG_G_ENABLE_PREV_CHG 0x01f2 | ||
| 90 | #define REG_G_NEW_CFG_SYNC 0x01f8 | ||
| 91 | #define REG_G_PREVZOOM_IN_WIDTH 0x020a | ||
| 92 | #define REG_G_PREVZOOM_IN_HEIGHT 0x020c | ||
| 93 | #define REG_G_PREVZOOM_IN_XOFFS 0x020e | ||
| 94 | #define REG_G_PREVZOOM_IN_YOFFS 0x0210 | ||
| 95 | #define REG_G_INPUTS_CHANGE_REQ 0x021a | ||
| 96 | #define REG_G_ACTIVE_PREV_CFG 0x021c | ||
| 97 | #define REG_G_PREV_CFG_CHG 0x021e | ||
| 98 | #define REG_G_PREV_OPEN_AFTER_CH 0x0220 | ||
| 99 | #define REG_G_PREV_CFG_ERROR 0x0222 | ||
| 100 | |||
| 101 | /* Preview control section. n = 0...4. */ | ||
| 102 | #define PREG(n, x) ((n) * 0x26 + x) | ||
| 103 | #define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) | ||
| 104 | #define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) | ||
| 105 | #define REG_P_FMT(n) PREG(n, 0x0246) | ||
| 106 | #define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) | ||
| 107 | #define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) | ||
| 108 | #define REG_P_PVI_MASK(n) PREG(n, 0x024c) | ||
| 109 | #define REG_P_CLK_INDEX(n) PREG(n, 0x024e) | ||
| 110 | #define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) | ||
| 111 | #define FR_RATE_DYNAMIC 0 | ||
| 112 | #define FR_RATE_FIXED 1 | ||
| 113 | #define FR_RATE_FIXED_ACCURATE 2 | ||
| 114 | #define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) | ||
| 115 | #define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ | ||
| 116 | #define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ | ||
| 117 | /* Frame period in 0.1 ms units */ | ||
| 118 | #define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) | ||
| 119 | #define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) | ||
| 120 | /* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ | ||
| 121 | #define US_TO_FR_TIME(__t) ((__t) / 100) | ||
| 122 | #define S5K6AA_MIN_FR_TIME 33300 /* us */ | ||
| 123 | #define S5K6AA_MAX_FR_TIME 650000 /* us */ | ||
| 124 | #define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ | ||
| 125 | /* The below 5 registers are for "device correction" values */ | ||
| 126 | #define REG_P_COLORTEMP(n) PREG(n, 0x025e) | ||
| 127 | #define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) | ||
| 128 | |||
| 129 | /* Extended image property controls */ | ||
| 130 | /* Exposure time in 10 us units */ | ||
| 131 | #define REG_SF_USR_EXPOSURE_L 0x03c6 | ||
| 132 | #define REG_SF_USR_EXPOSURE_H 0x03c8 | ||
| 133 | #define REG_SF_USR_EXPOSURE_CHG 0x03ca | ||
| 134 | #define REG_SF_USR_TOT_GAIN 0x03cc | ||
| 135 | #define REG_SF_USR_TOT_GAIN_CHG 0x03ce | ||
| 136 | #define REG_SF_RGAIN 0x03d0 | ||
| 137 | #define REG_SF_RGAIN_CHG 0x03d2 | ||
| 138 | #define REG_SF_GGAIN 0x03d4 | ||
| 139 | #define REG_SF_GGAIN_CHG 0x03d6 | ||
| 140 | #define REG_SF_BGAIN 0x03d8 | ||
| 141 | #define REG_SF_BGAIN_CHG 0x03da | ||
| 142 | #define REG_SF_FLICKER_QUANT 0x03dc | ||
| 143 | #define REG_SF_FLICKER_QUANT_CHG 0x03de | ||
| 144 | |||
| 145 | /* Output interface (parallel/MIPI) setup */ | ||
| 146 | #define REG_OIF_EN_MIPI_LANES 0x03fa | ||
| 147 | #define REG_OIF_EN_PACKETS 0x03fc | ||
| 148 | #define REG_OIF_CFG_CHG 0x03fe | ||
| 149 | |||
| 150 | /* Auto-algorithms enable mask */ | ||
| 151 | #define REG_DBG_AUTOALG_EN 0x0400 | ||
| 152 | #define AALG_ALL_EN_MASK (1 << 0) | ||
| 153 | #define AALG_AE_EN_MASK (1 << 1) | ||
| 154 | #define AALG_DIVLEI_EN_MASK (1 << 2) | ||
| 155 | #define AALG_WB_EN_MASK (1 << 3) | ||
| 156 | #define AALG_FLICKER_EN_MASK (1 << 5) | ||
| 157 | #define AALG_FIT_EN_MASK (1 << 6) | ||
| 158 | #define AALG_WRHW_EN_MASK (1 << 7) | ||
| 159 | |||
| 160 | /* Firmware revision information */ | ||
| 161 | #define REG_FW_APIVER 0x012e | ||
| 162 | #define S5K6AAFX_FW_APIVER 0x0001 | ||
| 163 | #define REG_FW_REVISION 0x0130 | ||
| 164 | |||
| 165 | /* For now we use only one user configuration register set */ | ||
| 166 | #define S5K6AA_MAX_PRESETS 1 | ||
| 167 | |||
| 168 | static const char * const s5k6aa_supply_names[] = { | ||
| 169 | "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ | ||
| 170 | "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ | ||
| 171 | "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) | ||
| 172 | or 2.8V (2.6V to 3.0) */ | ||
| 173 | "vddio", /* I/O supply 1.8V (1.65V to 1.95V) | ||
| 174 | or 2.8V (2.5V to 3.1V) */ | ||
| 175 | }; | ||
| 176 | #define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) | ||
| 177 | |||
| 178 | enum s5k6aa_gpio_id { | ||
| 179 | STBY, | ||
| 180 | RSET, | ||
| 181 | GPIO_NUM, | ||
| 182 | }; | ||
| 183 | |||
| 184 | struct s5k6aa_regval { | ||
| 185 | u16 addr; | ||
| 186 | u16 val; | ||
| 187 | }; | ||
| 188 | |||
| 189 | struct s5k6aa_pixfmt { | ||
| 190 | u32 code; | ||
| 191 | u32 colorspace; | ||
| 192 | /* REG_P_FMT(x) register value */ | ||
| 193 | u16 reg_p_fmt; | ||
| 194 | }; | ||
| 195 | |||
| 196 | struct s5k6aa_preset { | ||
| 197 | /* output pixel format and resolution */ | ||
| 198 | struct v4l2_mbus_framefmt mbus_fmt; | ||
| 199 | u8 clk_id; | ||
| 200 | u8 index; | ||
| 201 | }; | ||
| 202 | |||
| 203 | struct s5k6aa_ctrls { | ||
| 204 | struct v4l2_ctrl_handler handler; | ||
| 205 | /* Auto / manual white balance cluster */ | ||
| 206 | struct v4l2_ctrl *awb; | ||
| 207 | struct v4l2_ctrl *gain_red; | ||
| 208 | struct v4l2_ctrl *gain_blue; | ||
| 209 | struct v4l2_ctrl *gain_green; | ||
| 210 | /* Mirror cluster */ | ||
| 211 | struct v4l2_ctrl *hflip; | ||
| 212 | struct v4l2_ctrl *vflip; | ||
| 213 | /* Auto exposure / manual exposure and gain cluster */ | ||
| 214 | struct v4l2_ctrl *auto_exp; | ||
| 215 | struct v4l2_ctrl *exposure; | ||
| 216 | struct v4l2_ctrl *gain; | ||
| 217 | }; | ||
| 218 | |||
| 219 | struct s5k6aa_interval { | ||
| 220 | u16 reg_fr_time; | ||
| 221 | struct v4l2_fract interval; | ||
| 222 | /* Maximum rectangle for the interval */ | ||
| 223 | struct v4l2_frmsize_discrete size; | ||
| 224 | }; | ||
| 225 | |||
| 226 | struct s5k6aa { | ||
| 227 | struct v4l2_subdev sd; | ||
| 228 | struct media_pad pad; | ||
| 229 | |||
| 230 | enum v4l2_mbus_type bus_type; | ||
| 231 | u8 mipi_lanes; | ||
| 232 | |||
| 233 | int (*s_power)(int enable); | ||
| 234 | struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; | ||
| 235 | struct s5k6aa_gpio gpio[GPIO_NUM]; | ||
| 236 | |||
| 237 | /* external master clock frequency */ | ||
| 238 | unsigned long mclk_frequency; | ||
| 239 | /* ISP internal master clock frequency */ | ||
| 240 | u16 clk_fop; | ||
| 241 | /* output pixel clock frequency range */ | ||
| 242 | u16 pclk_fmin; | ||
| 243 | u16 pclk_fmax; | ||
| 244 | |||
| 245 | unsigned int inv_hflip:1; | ||
| 246 | unsigned int inv_vflip:1; | ||
| 247 | |||
| 248 | /* protects the struct members below */ | ||
| 249 | struct mutex lock; | ||
| 250 | |||
| 251 | /* sensor matrix scan window */ | ||
| 252 | struct v4l2_rect ccd_rect; | ||
| 253 | |||
| 254 | struct s5k6aa_ctrls ctrls; | ||
| 255 | struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; | ||
| 256 | struct s5k6aa_preset *preset; | ||
| 257 | const struct s5k6aa_interval *fiv; | ||
| 258 | |||
| 259 | unsigned int streaming:1; | ||
| 260 | unsigned int apply_cfg:1; | ||
| 261 | unsigned int apply_crop:1; | ||
| 262 | unsigned int power; | ||
| 263 | }; | ||
| 264 | |||
| 265 | static struct s5k6aa_regval s5k6aa_analog_config[] = { | ||
| 266 | /* Analog settings */ | ||
| 267 | { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, | ||
| 268 | { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, | ||
| 269 | { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, | ||
| 270 | { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, | ||
| 271 | { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, | ||
| 272 | { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, | ||
| 273 | { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, | ||
| 274 | { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, | ||
| 275 | { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, | ||
| 276 | { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, | ||
| 277 | { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, | ||
| 278 | { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, | ||
| 279 | { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, | ||
| 280 | }; | ||
| 281 | |||
| 282 | /* TODO: Add RGB888 and Bayer format */ | ||
| 283 | static const struct s5k6aa_pixfmt s5k6aa_formats[] = { | ||
| 284 | { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, | ||
| 285 | /* range 16-240 */ | ||
| 286 | { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, | ||
| 287 | { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, | ||
| 288 | }; | ||
| 289 | |||
| 290 | static const struct s5k6aa_interval s5k6aa_intervals[] = { | ||
| 291 | { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ | ||
| 292 | { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ | ||
| 293 | { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ | ||
| 294 | { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ | ||
| 295 | { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ | ||
| 296 | }; | ||
| 297 | |||
| 298 | #define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ | ||
| 299 | |||
| 300 | static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) | ||
| 301 | { | ||
| 302 | return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; | ||
| 303 | } | ||
| 304 | |||
| 305 | static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) | ||
| 306 | { | ||
| 307 | return container_of(sd, struct s5k6aa, sd); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Set initial values for all preview presets */ | ||
| 311 | static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) | ||
| 312 | { | ||
| 313 | struct s5k6aa_preset *preset = &s5k6aa->presets[0]; | ||
| 314 | int i; | ||
| 315 | |||
| 316 | for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { | ||
| 317 | preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; | ||
| 318 | preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; | ||
| 319 | preset->mbus_fmt.code = s5k6aa_formats[0].code; | ||
| 320 | preset->index = i; | ||
| 321 | preset->clk_id = 0; | ||
| 322 | preset++; | ||
| 323 | } | ||
| 324 | |||
| 325 | s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; | ||
| 326 | s5k6aa->preset = &s5k6aa->presets[0]; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) | ||
| 330 | { | ||
| 331 | u8 wbuf[2] = {addr >> 8, addr & 0xFF}; | ||
| 332 | struct i2c_msg msg[2]; | ||
| 333 | u8 rbuf[2]; | ||
| 334 | int ret; | ||
| 335 | |||
| 336 | msg[0].addr = client->addr; | ||
| 337 | msg[0].flags = 0; | ||
| 338 | msg[0].len = 2; | ||
| 339 | msg[0].buf = wbuf; | ||
| 340 | |||
| 341 | msg[1].addr = client->addr; | ||
| 342 | msg[1].flags = I2C_M_RD; | ||
| 343 | msg[1].len = 2; | ||
| 344 | msg[1].buf = rbuf; | ||
| 345 | |||
| 346 | ret = i2c_transfer(client->adapter, msg, 2); | ||
| 347 | *val = be16_to_cpu(*((__be16 *)rbuf)); | ||
| 348 | |||
| 349 | v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); | ||
| 350 | |||
| 351 | return ret == 2 ? 0 : ret; | ||
| 352 | } | ||
| 353 | |||
| 354 | static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) | ||
| 355 | { | ||
| 356 | u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; | ||
| 357 | |||
| 358 | int ret = i2c_master_send(client, buf, 4); | ||
| 359 | v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); | ||
| 360 | |||
| 361 | return ret == 4 ? 0 : ret; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* The command register write, assumes Command_Wr_addH = 0x7000. */ | ||
| 365 | static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) | ||
| 366 | { | ||
| 367 | int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); | ||
| 368 | if (ret) | ||
| 369 | return ret; | ||
| 370 | return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); | ||
| 371 | } | ||
| 372 | |||
| 373 | /* The command register read, assumes Command_Rd_addH = 0x7000. */ | ||
| 374 | static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) | ||
| 375 | { | ||
| 376 | int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); | ||
| 377 | if (ret) | ||
| 378 | return ret; | ||
| 379 | return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); | ||
| 380 | } | ||
| 381 | |||
| 382 | static int s5k6aa_write_array(struct v4l2_subdev *sd, | ||
| 383 | const struct s5k6aa_regval *msg) | ||
| 384 | { | ||
| 385 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 386 | u16 addr_incr = 0; | ||
| 387 | int ret = 0; | ||
| 388 | |||
| 389 | while (msg->addr != S5K6AA_TERM) { | ||
| 390 | if (addr_incr != 2) | ||
| 391 | ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, | ||
| 392 | msg->addr); | ||
| 393 | if (ret) | ||
| 394 | break; | ||
| 395 | ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); | ||
| 396 | if (ret) | ||
| 397 | break; | ||
| 398 | /* Assume that msg->addr is always less than 0xfffc */ | ||
| 399 | addr_incr = (msg + 1)->addr - msg->addr; | ||
| 400 | msg++; | ||
| 401 | } | ||
| 402 | |||
| 403 | return ret; | ||
| 404 | } | ||
| 405 | |||
| 406 | /* Configure the AHB high address bytes for GTG registers access */ | ||
| 407 | static int s5k6aa_set_ahb_address(struct i2c_client *client) | ||
| 408 | { | ||
| 409 | int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); | ||
| 410 | if (ret) | ||
| 411 | return ret; | ||
| 412 | ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); | ||
| 413 | if (ret) | ||
| 414 | return ret; | ||
| 415 | return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); | ||
| 416 | } | ||
| 417 | |||
| 418 | /** | ||
| 419 | * s5k6aa_configure_pixel_clocks - apply ISP main clock/PLL configuration | ||
| 420 | * @s5k6aa: pointer to &struct s5k6aa describing the device | ||
| 421 | * | ||
| 422 | * Configure the internal ISP PLL for the required output frequency. | ||
| 423 | * Locking: called with s5k6aa.lock mutex held. | ||
| 424 | */ | ||
| 425 | static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) | ||
| 426 | { | ||
| 427 | struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 428 | unsigned long fmclk = s5k6aa->mclk_frequency / 1000; | ||
| 429 | u16 status; | ||
| 430 | int ret; | ||
| 431 | |||
| 432 | if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, | ||
| 433 | "Invalid clock frequency: %ld\n", fmclk)) | ||
| 434 | return -EINVAL; | ||
| 435 | |||
| 436 | s5k6aa->pclk_fmin = PCLK_FREQ_MIN; | ||
| 437 | s5k6aa->pclk_fmax = PCLK_FREQ_MAX; | ||
| 438 | s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; | ||
| 439 | |||
| 440 | /* External input clock frequency in kHz */ | ||
| 441 | ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); | ||
| 442 | if (!ret) | ||
| 443 | ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); | ||
| 444 | if (!ret) | ||
| 445 | ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); | ||
| 446 | /* Internal PLL frequency */ | ||
| 447 | if (!ret) | ||
| 448 | ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); | ||
| 449 | if (!ret) | ||
| 450 | ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), | ||
| 451 | s5k6aa->pclk_fmin); | ||
| 452 | if (!ret) | ||
| 453 | ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), | ||
| 454 | s5k6aa->pclk_fmax); | ||
| 455 | if (!ret) | ||
| 456 | ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); | ||
| 457 | if (!ret) | ||
| 458 | ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); | ||
| 459 | |||
| 460 | return ret ? ret : (status ? -EINVAL : 0); | ||
| 461 | } | ||
| 462 | |||
| 463 | /* Set horizontal and vertical image flipping */ | ||
| 464 | static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) | ||
| 465 | { | ||
| 466 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 467 | int index = s5k6aa->preset->index; | ||
| 468 | |||
| 469 | unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; | ||
| 470 | unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); | ||
| 471 | |||
| 472 | return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); | ||
| 473 | } | ||
| 474 | |||
| 475 | /* Configure auto/manual white balance and R/G/B gains */ | ||
| 476 | static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) | ||
| 477 | { | ||
| 478 | struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 479 | struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; | ||
| 480 | u16 reg; | ||
| 481 | |||
| 482 | int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); | ||
| 483 | |||
| 484 | if (!ret && !awb) { | ||
| 485 | ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); | ||
| 486 | if (!ret) | ||
| 487 | ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); | ||
| 488 | if (ret) | ||
| 489 | return ret; | ||
| 490 | |||
| 491 | ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); | ||
| 492 | if (!ret) | ||
| 493 | ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); | ||
| 494 | if (ret) | ||
| 495 | return ret; | ||
| 496 | |||
| 497 | ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); | ||
| 498 | if (!ret) | ||
| 499 | ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); | ||
| 500 | } | ||
| 501 | if (!ret) { | ||
| 502 | reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; | ||
| 503 | ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); | ||
| 504 | } | ||
| 505 | |||
| 506 | return ret; | ||
| 507 | } | ||
| 508 | |||
| 509 | /* Program FW with exposure time, 'exposure' in us units */ | ||
| 510 | static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) | ||
| 511 | { | ||
| 512 | unsigned int time = exposure / 10; | ||
| 513 | |||
| 514 | int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); | ||
| 515 | if (!ret) | ||
| 516 | ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); | ||
| 517 | if (ret) | ||
| 518 | return ret; | ||
| 519 | return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); | ||
| 520 | } | ||
| 521 | |||
| 522 | static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) | ||
| 523 | { | ||
| 524 | int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); | ||
| 525 | if (ret) | ||
| 526 | return ret; | ||
| 527 | return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); | ||
| 528 | } | ||
| 529 | |||
| 530 | /* Set auto/manual exposure and total gain */ | ||
| 531 | static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) | ||
| 532 | { | ||
| 533 | struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 534 | unsigned int exp_time = s5k6aa->ctrls.exposure->val; | ||
| 535 | u16 auto_alg; | ||
| 536 | |||
| 537 | int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); | ||
| 538 | if (ret) | ||
| 539 | return ret; | ||
| 540 | |||
| 541 | v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", | ||
| 542 | exp_time, value, auto_alg); | ||
| 543 | |||
| 544 | if (value == V4L2_EXPOSURE_AUTO) { | ||
| 545 | auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; | ||
| 546 | } else { | ||
| 547 | ret = s5k6aa_set_user_exposure(c, exp_time); | ||
| 548 | if (ret) | ||
| 549 | return ret; | ||
| 550 | ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); | ||
| 551 | if (ret) | ||
| 552 | return ret; | ||
| 553 | auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); | ||
| 554 | } | ||
| 555 | |||
| 556 | return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); | ||
| 557 | } | ||
| 558 | |||
| 559 | static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) | ||
| 560 | { | ||
| 561 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 562 | u16 auto_alg; | ||
| 563 | int ret; | ||
| 564 | |||
| 565 | ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); | ||
| 566 | if (ret) | ||
| 567 | return ret; | ||
| 568 | |||
| 569 | if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { | ||
| 570 | auto_alg |= AALG_FLICKER_EN_MASK; | ||
| 571 | } else { | ||
| 572 | auto_alg &= ~AALG_FLICKER_EN_MASK; | ||
| 573 | /* The V4L2_CID_LINE_FREQUENCY control values match | ||
| 574 | * the register values */ | ||
| 575 | ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); | ||
| 576 | if (ret) | ||
| 577 | return ret; | ||
| 578 | ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); | ||
| 579 | if (ret) | ||
| 580 | return ret; | ||
| 581 | } | ||
| 582 | |||
| 583 | return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); | ||
| 584 | } | ||
| 585 | |||
| 586 | static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) | ||
| 587 | { | ||
| 588 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 589 | static const struct v4l2_control colorfx[] = { | ||
| 590 | { V4L2_COLORFX_NONE, 0 }, | ||
| 591 | { V4L2_COLORFX_BW, 1 }, | ||
| 592 | { V4L2_COLORFX_NEGATIVE, 2 }, | ||
| 593 | { V4L2_COLORFX_SEPIA, 3 }, | ||
| 594 | { V4L2_COLORFX_SKY_BLUE, 4 }, | ||
| 595 | { V4L2_COLORFX_SKETCH, 5 }, | ||
| 596 | }; | ||
| 597 | int i; | ||
| 598 | |||
| 599 | for (i = 0; i < ARRAY_SIZE(colorfx); i++) { | ||
| 600 | if (colorfx[i].id == val) | ||
| 601 | return s5k6aa_write(client, REG_G_SPEC_EFFECTS, | ||
| 602 | colorfx[i].value); | ||
| 603 | } | ||
| 604 | return -EINVAL; | ||
| 605 | } | ||
| 606 | |||
| 607 | static int s5k6aa_preview_config_status(struct i2c_client *client) | ||
| 608 | { | ||
| 609 | u16 error = 0; | ||
| 610 | int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); | ||
| 611 | |||
| 612 | v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); | ||
| 613 | return ret ? ret : (error ? -EINVAL : 0); | ||
| 614 | } | ||
| 615 | |||
| 616 | static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, | ||
| 617 | struct v4l2_mbus_framefmt *mf) | ||
| 618 | { | ||
| 619 | unsigned int i; | ||
| 620 | |||
| 621 | for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) | ||
| 622 | if (mf->colorspace == s5k6aa_formats[i].colorspace && | ||
| 623 | mf->code == s5k6aa_formats[i].code) | ||
| 624 | return i; | ||
| 625 | return 0; | ||
| 626 | } | ||
| 627 | |||
| 628 | static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, | ||
| 629 | struct s5k6aa_preset *preset) | ||
| 630 | { | ||
| 631 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 632 | int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); | ||
| 633 | int ret; | ||
| 634 | |||
| 635 | ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), | ||
| 636 | preset->mbus_fmt.width); | ||
| 637 | if (!ret) | ||
| 638 | ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), | ||
| 639 | preset->mbus_fmt.height); | ||
| 640 | if (!ret) | ||
| 641 | ret = s5k6aa_write(client, REG_P_FMT(preset->index), | ||
| 642 | s5k6aa_formats[fmt_index].reg_p_fmt); | ||
| 643 | return ret; | ||
| 644 | } | ||
| 645 | |||
| 646 | static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) | ||
| 647 | { | ||
| 648 | struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 649 | struct v4l2_rect *r = &s5k6aa->ccd_rect; | ||
| 650 | int ret; | ||
| 651 | |||
| 652 | ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); | ||
| 653 | if (!ret) | ||
| 654 | ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); | ||
| 655 | if (!ret) | ||
| 656 | ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); | ||
| 657 | if (!ret) | ||
| 658 | ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); | ||
| 659 | if (!ret) | ||
| 660 | ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); | ||
| 661 | if (!ret) | ||
| 662 | s5k6aa->apply_crop = 0; | ||
| 663 | |||
| 664 | return ret; | ||
| 665 | } | ||
| 666 | |||
| 667 | /** | ||
| 668 | * s5k6aa_configure_video_bus - configure the video output interface | ||
| 669 | * @s5k6aa: pointer to &struct s5k6aa describing the device | ||
| 670 | * @bus_type: video bus type: parallel or MIPI-CSI | ||
| 671 | * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) | ||
| 672 | * | ||
| 673 | * Note: Only parallel bus operation has been tested. | ||
| 674 | */ | ||
| 675 | static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, | ||
| 676 | enum v4l2_mbus_type bus_type, int nlanes) | ||
| 677 | { | ||
| 678 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 679 | u16 cfg = 0; | ||
| 680 | int ret; | ||
| 681 | |||
| 682 | /* | ||
| 683 | * TODO: The sensor is supposed to support BT.601 and BT.656 | ||
| 684 | * but there is nothing indicating how to switch between both | ||
| 685 | * in the datasheet. For now default BT.601 interface is assumed. | ||
| 686 | */ | ||
| 687 | if (bus_type == V4L2_MBUS_CSI2_DPHY) | ||
| 688 | cfg = nlanes; | ||
| 689 | else if (bus_type != V4L2_MBUS_PARALLEL) | ||
| 690 | return -EINVAL; | ||
| 691 | |||
| 692 | ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); | ||
| 693 | if (ret) | ||
| 694 | return ret; | ||
| 695 | return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); | ||
| 696 | } | ||
| 697 | |||
| 698 | /* This function should be called when switching to new user configuration set*/ | ||
| 699 | static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, | ||
| 700 | int cid) | ||
| 701 | { | ||
| 702 | unsigned long end = jiffies + msecs_to_jiffies(timeout); | ||
| 703 | u16 reg = 1; | ||
| 704 | int ret; | ||
| 705 | |||
| 706 | ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); | ||
| 707 | if (!ret) | ||
| 708 | ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); | ||
| 709 | if (!ret) | ||
| 710 | ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); | ||
| 711 | if (timeout == 0) | ||
| 712 | return ret; | ||
| 713 | |||
| 714 | while (ret >= 0 && time_is_after_jiffies(end)) { | ||
| 715 | ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); | ||
| 716 | if (!reg) | ||
| 717 | return 0; | ||
| 718 | usleep_range(1000, 5000); | ||
| 719 | } | ||
| 720 | return ret ? ret : -ETIMEDOUT; | ||
| 721 | } | ||
| 722 | |||
| 723 | /** | ||
| 724 | * s5k6aa_set_prev_config - write user preview register set | ||
| 725 | * @s5k6aa: pointer to &struct s5k6aa describing the device | ||
| 726 | * @preset: s5kaa preset to be applied | ||
| 727 | * | ||
| 728 | * Configure output resolution and color format, pixel clock | ||
| 729 | * frequency range, device frame rate type and frame period range. | ||
| 730 | */ | ||
| 731 | static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, | ||
| 732 | struct s5k6aa_preset *preset) | ||
| 733 | { | ||
| 734 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 735 | int idx = preset->index; | ||
| 736 | u16 frame_rate_q; | ||
| 737 | int ret; | ||
| 738 | |||
| 739 | if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) | ||
| 740 | frame_rate_q = FR_RATE_Q_BEST_FRRATE; | ||
| 741 | else | ||
| 742 | frame_rate_q = FR_RATE_Q_BEST_QUALITY; | ||
| 743 | |||
| 744 | ret = s5k6aa_set_output_framefmt(s5k6aa, preset); | ||
| 745 | if (!ret) | ||
| 746 | ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), | ||
| 747 | s5k6aa->pclk_fmax); | ||
| 748 | if (!ret) | ||
| 749 | ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), | ||
| 750 | s5k6aa->pclk_fmin); | ||
| 751 | if (!ret) | ||
| 752 | ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), | ||
| 753 | preset->clk_id); | ||
| 754 | if (!ret) | ||
| 755 | ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), | ||
| 756 | FR_RATE_DYNAMIC); | ||
| 757 | if (!ret) | ||
| 758 | ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), | ||
| 759 | frame_rate_q); | ||
| 760 | if (!ret) | ||
| 761 | ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), | ||
| 762 | s5k6aa->fiv->reg_fr_time + 33); | ||
| 763 | if (!ret) | ||
| 764 | ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), | ||
| 765 | s5k6aa->fiv->reg_fr_time - 33); | ||
| 766 | if (!ret) | ||
| 767 | ret = s5k6aa_new_config_sync(client, 250, idx); | ||
| 768 | if (!ret) | ||
| 769 | ret = s5k6aa_preview_config_status(client); | ||
| 770 | if (!ret) | ||
| 771 | s5k6aa->apply_cfg = 0; | ||
| 772 | |||
| 773 | v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", | ||
| 774 | s5k6aa->fiv->reg_fr_time, ret); | ||
| 775 | return ret; | ||
| 776 | } | ||
| 777 | |||
| 778 | /** | ||
| 779 | * s5k6aa_initialize_isp - basic ISP MCU initialization | ||
| 780 | * @sd: pointer to V4L2 sub-device descriptor | ||
| 781 | * | ||
| 782 | * Configure AHB addresses for registers read/write; configure PLLs for | ||
| 783 | * required output pixel clock. The ISP power supply needs to be already | ||
| 784 | * enabled, with an optional H/W reset. | ||
| 785 | * Locking: called with s5k6aa.lock mutex held. | ||
| 786 | */ | ||
| 787 | static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) | ||
| 788 | { | ||
| 789 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 790 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 791 | int ret; | ||
| 792 | |||
| 793 | s5k6aa->apply_crop = 1; | ||
| 794 | s5k6aa->apply_cfg = 1; | ||
| 795 | msleep(100); | ||
| 796 | |||
| 797 | ret = s5k6aa_set_ahb_address(client); | ||
| 798 | if (ret) | ||
| 799 | return ret; | ||
| 800 | ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, | ||
| 801 | s5k6aa->mipi_lanes); | ||
| 802 | if (ret) | ||
| 803 | return ret; | ||
| 804 | ret = s5k6aa_write_array(sd, s5k6aa_analog_config); | ||
| 805 | if (ret) | ||
| 806 | return ret; | ||
| 807 | msleep(20); | ||
| 808 | |||
| 809 | return s5k6aa_configure_pixel_clocks(s5k6aa); | ||
| 810 | } | ||
| 811 | |||
| 812 | static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) | ||
| 813 | { | ||
| 814 | if (!gpio_is_valid(priv->gpio[id].gpio)) | ||
| 815 | return 0; | ||
| 816 | gpio_set_value(priv->gpio[id].gpio, !!val); | ||
| 817 | return 1; | ||
| 818 | } | ||
| 819 | |||
| 820 | static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) | ||
| 821 | { | ||
| 822 | return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); | ||
| 823 | } | ||
| 824 | |||
| 825 | static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) | ||
| 826 | { | ||
| 827 | return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); | ||
| 828 | } | ||
| 829 | |||
| 830 | static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) | ||
| 831 | { | ||
| 832 | int ret; | ||
| 833 | |||
| 834 | ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); | ||
| 835 | if (ret) | ||
| 836 | return ret; | ||
| 837 | if (s5k6aa_gpio_deassert(s5k6aa, STBY)) | ||
| 838 | usleep_range(150, 200); | ||
| 839 | |||
| 840 | if (s5k6aa->s_power) | ||
| 841 | ret = s5k6aa->s_power(1); | ||
| 842 | usleep_range(4000, 5000); | ||
| 843 | |||
| 844 | if (s5k6aa_gpio_deassert(s5k6aa, RSET)) | ||
| 845 | msleep(20); | ||
| 846 | |||
| 847 | return ret; | ||
| 848 | } | ||
| 849 | |||
| 850 | static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) | ||
| 851 | { | ||
| 852 | int ret; | ||
| 853 | |||
| 854 | if (s5k6aa_gpio_assert(s5k6aa, RSET)) | ||
| 855 | usleep_range(100, 150); | ||
| 856 | |||
| 857 | if (s5k6aa->s_power) { | ||
| 858 | ret = s5k6aa->s_power(0); | ||
| 859 | if (ret) | ||
| 860 | return ret; | ||
| 861 | } | ||
| 862 | if (s5k6aa_gpio_assert(s5k6aa, STBY)) | ||
| 863 | usleep_range(50, 100); | ||
| 864 | s5k6aa->streaming = 0; | ||
| 865 | |||
| 866 | return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); | ||
| 867 | } | ||
| 868 | |||
| 869 | /* | ||
| 870 | * V4L2 subdev core and video operations | ||
| 871 | */ | ||
| 872 | static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) | ||
| 873 | { | ||
| 874 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 875 | int ret = 0; | ||
| 876 | |||
| 877 | mutex_lock(&s5k6aa->lock); | ||
| 878 | |||
| 879 | if (s5k6aa->power == !on) { | ||
| 880 | if (on) { | ||
| 881 | ret = __s5k6aa_power_on(s5k6aa); | ||
| 882 | if (!ret) | ||
| 883 | ret = s5k6aa_initialize_isp(sd); | ||
| 884 | } else { | ||
| 885 | ret = __s5k6aa_power_off(s5k6aa); | ||
| 886 | } | ||
| 887 | |||
| 888 | if (!ret) | ||
| 889 | s5k6aa->power += on ? 1 : -1; | ||
| 890 | } | ||
| 891 | |||
| 892 | mutex_unlock(&s5k6aa->lock); | ||
| 893 | |||
| 894 | if (!on || ret || s5k6aa->power != 1) | ||
| 895 | return ret; | ||
| 896 | |||
| 897 | return v4l2_ctrl_handler_setup(sd->ctrl_handler); | ||
| 898 | } | ||
| 899 | |||
| 900 | static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) | ||
| 901 | { | ||
| 902 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 903 | int ret = 0; | ||
| 904 | |||
| 905 | ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); | ||
| 906 | if (!ret) | ||
| 907 | ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); | ||
| 908 | if (!ret) | ||
| 909 | s5k6aa->streaming = enable; | ||
| 910 | |||
| 911 | return ret; | ||
| 912 | } | ||
| 913 | |||
| 914 | static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) | ||
| 915 | { | ||
| 916 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 917 | int ret = 0; | ||
| 918 | |||
| 919 | mutex_lock(&s5k6aa->lock); | ||
| 920 | |||
| 921 | if (s5k6aa->streaming == !on) { | ||
| 922 | if (!ret && s5k6aa->apply_cfg) | ||
| 923 | ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); | ||
| 924 | if (s5k6aa->apply_crop) | ||
| 925 | ret = s5k6aa_set_input_params(s5k6aa); | ||
| 926 | if (!ret) | ||
| 927 | ret = __s5k6aa_stream(s5k6aa, !!on); | ||
| 928 | } | ||
| 929 | mutex_unlock(&s5k6aa->lock); | ||
| 930 | |||
| 931 | return ret; | ||
| 932 | } | ||
| 933 | |||
| 934 | static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, | ||
| 935 | struct v4l2_subdev_frame_interval *fi) | ||
| 936 | { | ||
| 937 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 938 | |||
| 939 | mutex_lock(&s5k6aa->lock); | ||
| 940 | fi->interval = s5k6aa->fiv->interval; | ||
| 941 | mutex_unlock(&s5k6aa->lock); | ||
| 942 | |||
| 943 | return 0; | ||
| 944 | } | ||
| 945 | |||
| 946 | static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, | ||
| 947 | struct v4l2_subdev_frame_interval *fi) | ||
| 948 | { | ||
| 949 | struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; | ||
| 950 | const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; | ||
| 951 | unsigned int err, min_err = UINT_MAX; | ||
| 952 | unsigned int i, fr_time; | ||
| 953 | |||
| 954 | if (fi->interval.denominator == 0) | ||
| 955 | return -EINVAL; | ||
| 956 | |||
| 957 | fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; | ||
| 958 | |||
| 959 | for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { | ||
| 960 | const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; | ||
| 961 | |||
| 962 | if (mbus_fmt->width > iv->size.width || | ||
| 963 | mbus_fmt->height > iv->size.height) | ||
| 964 | continue; | ||
| 965 | |||
| 966 | err = abs(iv->reg_fr_time - fr_time); | ||
| 967 | if (err < min_err) { | ||
| 968 | fiv = iv; | ||
| 969 | min_err = err; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | s5k6aa->fiv = fiv; | ||
| 973 | |||
| 974 | v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", | ||
| 975 | fiv->reg_fr_time * 100); | ||
| 976 | return 0; | ||
| 977 | } | ||
| 978 | |||
| 979 | static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, | ||
| 980 | struct v4l2_subdev_frame_interval *fi) | ||
| 981 | { | ||
| 982 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 983 | int ret; | ||
| 984 | |||
| 985 | v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", | ||
| 986 | fi->interval.numerator, fi->interval.denominator); | ||
| 987 | |||
| 988 | mutex_lock(&s5k6aa->lock); | ||
| 989 | ret = __s5k6aa_set_frame_interval(s5k6aa, fi); | ||
| 990 | s5k6aa->apply_cfg = 1; | ||
| 991 | |||
| 992 | mutex_unlock(&s5k6aa->lock); | ||
| 993 | return ret; | ||
| 994 | } | ||
| 995 | |||
| 996 | /* | ||
| 997 | * V4L2 subdev pad level and video operations | ||
| 998 | */ | ||
| 999 | static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, | ||
| 1000 | struct v4l2_subdev_state *sd_state, | ||
| 1001 | struct v4l2_subdev_frame_interval_enum *fie) | ||
| 1002 | { | ||
| 1003 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1004 | const struct s5k6aa_interval *fi; | ||
| 1005 | int ret = 0; | ||
| 1006 | |||
| 1007 | if (fie->index >= ARRAY_SIZE(s5k6aa_intervals)) | ||
| 1008 | return -EINVAL; | ||
| 1009 | |||
| 1010 | v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, | ||
| 1011 | S5K6AA_WIN_WIDTH_MAX, 1, | ||
| 1012 | &fie->height, S5K6AA_WIN_HEIGHT_MIN, | ||
| 1013 | S5K6AA_WIN_HEIGHT_MAX, 1, 0); | ||
| 1014 | |||
| 1015 | mutex_lock(&s5k6aa->lock); | ||
| 1016 | fi = &s5k6aa_intervals[fie->index]; | ||
| 1017 | if (fie->width > fi->size.width || fie->height > fi->size.height) | ||
| 1018 | ret = -EINVAL; | ||
| 1019 | else | ||
| 1020 | fie->interval = fi->interval; | ||
| 1021 | mutex_unlock(&s5k6aa->lock); | ||
| 1022 | |||
| 1023 | return ret; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, | ||
| 1027 | struct v4l2_subdev_state *sd_state, | ||
| 1028 | struct v4l2_subdev_mbus_code_enum *code) | ||
| 1029 | { | ||
| 1030 | if (code->index >= ARRAY_SIZE(s5k6aa_formats)) | ||
| 1031 | return -EINVAL; | ||
| 1032 | |||
| 1033 | code->code = s5k6aa_formats[code->index].code; | ||
| 1034 | return 0; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, | ||
| 1038 | struct v4l2_subdev_state *sd_state, | ||
| 1039 | struct v4l2_subdev_frame_size_enum *fse) | ||
| 1040 | { | ||
| 1041 | int i = ARRAY_SIZE(s5k6aa_formats); | ||
| 1042 | |||
| 1043 | if (fse->index > 0) | ||
| 1044 | return -EINVAL; | ||
| 1045 | |||
| 1046 | while (--i) | ||
| 1047 | if (fse->code == s5k6aa_formats[i].code) | ||
| 1048 | break; | ||
| 1049 | |||
| 1050 | fse->code = s5k6aa_formats[i].code; | ||
| 1051 | fse->min_width = S5K6AA_WIN_WIDTH_MIN; | ||
| 1052 | fse->max_width = S5K6AA_WIN_WIDTH_MAX; | ||
| 1053 | fse->max_height = S5K6AA_WIN_HEIGHT_MIN; | ||
| 1054 | fse->min_height = S5K6AA_WIN_HEIGHT_MAX; | ||
| 1055 | |||
| 1056 | return 0; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | static struct v4l2_rect * | ||
| 1060 | __s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, | ||
| 1061 | struct v4l2_subdev_state *sd_state, | ||
| 1062 | enum v4l2_subdev_format_whence which) | ||
| 1063 | { | ||
| 1064 | if (which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
| 1065 | return &s5k6aa->ccd_rect; | ||
| 1066 | |||
| 1067 | WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY); | ||
| 1068 | return v4l2_subdev_get_try_crop(&s5k6aa->sd, sd_state, 0); | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | static void s5k6aa_try_format(struct s5k6aa *s5k6aa, | ||
| 1072 | struct v4l2_mbus_framefmt *mf) | ||
| 1073 | { | ||
| 1074 | unsigned int index; | ||
| 1075 | |||
| 1076 | v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, | ||
| 1077 | S5K6AA_WIN_WIDTH_MAX, 1, | ||
| 1078 | &mf->height, S5K6AA_WIN_HEIGHT_MIN, | ||
| 1079 | S5K6AA_WIN_HEIGHT_MAX, 1, 0); | ||
| 1080 | |||
| 1081 | if (mf->colorspace != V4L2_COLORSPACE_JPEG && | ||
| 1082 | mf->colorspace != V4L2_COLORSPACE_REC709) | ||
| 1083 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
| 1084 | |||
| 1085 | index = s5k6aa_get_pixfmt_index(s5k6aa, mf); | ||
| 1086 | |||
| 1087 | mf->colorspace = s5k6aa_formats[index].colorspace; | ||
| 1088 | mf->code = s5k6aa_formats[index].code; | ||
| 1089 | mf->field = V4L2_FIELD_NONE; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | static int s5k6aa_get_fmt(struct v4l2_subdev *sd, | ||
| 1093 | struct v4l2_subdev_state *sd_state, | ||
| 1094 | struct v4l2_subdev_format *fmt) | ||
| 1095 | { | ||
| 1096 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1097 | struct v4l2_mbus_framefmt *mf; | ||
| 1098 | |||
| 1099 | memset(fmt->reserved, 0, sizeof(fmt->reserved)); | ||
| 1100 | |||
| 1101 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
| 1102 | mf = v4l2_subdev_get_try_format(sd, sd_state, 0); | ||
| 1103 | fmt->format = *mf; | ||
| 1104 | return 0; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | mutex_lock(&s5k6aa->lock); | ||
| 1108 | fmt->format = s5k6aa->preset->mbus_fmt; | ||
| 1109 | mutex_unlock(&s5k6aa->lock); | ||
| 1110 | |||
| 1111 | return 0; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | static int s5k6aa_set_fmt(struct v4l2_subdev *sd, | ||
| 1115 | struct v4l2_subdev_state *sd_state, | ||
| 1116 | struct v4l2_subdev_format *fmt) | ||
| 1117 | { | ||
| 1118 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1119 | struct s5k6aa_preset *preset = s5k6aa->preset; | ||
| 1120 | struct v4l2_mbus_framefmt *mf; | ||
| 1121 | struct v4l2_rect *crop; | ||
| 1122 | int ret = 0; | ||
| 1123 | |||
| 1124 | mutex_lock(&s5k6aa->lock); | ||
| 1125 | s5k6aa_try_format(s5k6aa, &fmt->format); | ||
| 1126 | |||
| 1127 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
| 1128 | mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); | ||
| 1129 | crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); | ||
| 1130 | } else { | ||
| 1131 | if (s5k6aa->streaming) { | ||
| 1132 | ret = -EBUSY; | ||
| 1133 | } else { | ||
| 1134 | mf = &preset->mbus_fmt; | ||
| 1135 | crop = &s5k6aa->ccd_rect; | ||
| 1136 | s5k6aa->apply_cfg = 1; | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | if (ret == 0) { | ||
| 1141 | struct v4l2_subdev_frame_interval fiv = { | ||
| 1142 | .interval = {0, 1} | ||
| 1143 | }; | ||
| 1144 | |||
| 1145 | *mf = fmt->format; | ||
| 1146 | /* | ||
| 1147 | * Make sure the crop window is valid, i.e. its size is | ||
| 1148 | * greater than the output window, as the ISP supports | ||
| 1149 | * only down-scaling. | ||
| 1150 | */ | ||
| 1151 | crop->width = clamp_t(unsigned int, crop->width, mf->width, | ||
| 1152 | S5K6AA_WIN_WIDTH_MAX); | ||
| 1153 | crop->height = clamp_t(unsigned int, crop->height, mf->height, | ||
| 1154 | S5K6AA_WIN_HEIGHT_MAX); | ||
| 1155 | crop->left = clamp_t(unsigned int, crop->left, 0, | ||
| 1156 | S5K6AA_WIN_WIDTH_MAX - crop->width); | ||
| 1157 | crop->top = clamp_t(unsigned int, crop->top, 0, | ||
| 1158 | S5K6AA_WIN_HEIGHT_MAX - crop->height); | ||
| 1159 | |||
| 1160 | /* Reset to minimum possible frame interval */ | ||
| 1161 | ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); | ||
| 1162 | } | ||
| 1163 | mutex_unlock(&s5k6aa->lock); | ||
| 1164 | |||
| 1165 | return ret; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | static int s5k6aa_get_selection(struct v4l2_subdev *sd, | ||
| 1169 | struct v4l2_subdev_state *sd_state, | ||
| 1170 | struct v4l2_subdev_selection *sel) | ||
| 1171 | { | ||
| 1172 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1173 | struct v4l2_rect *rect; | ||
| 1174 | |||
| 1175 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 1176 | return -EINVAL; | ||
| 1177 | |||
| 1178 | memset(sel->reserved, 0, sizeof(sel->reserved)); | ||
| 1179 | |||
| 1180 | mutex_lock(&s5k6aa->lock); | ||
| 1181 | rect = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); | ||
| 1182 | sel->r = *rect; | ||
| 1183 | mutex_unlock(&s5k6aa->lock); | ||
| 1184 | |||
| 1185 | v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", | ||
| 1186 | rect->left, rect->top, rect->width, rect->height); | ||
| 1187 | |||
| 1188 | return 0; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | static int s5k6aa_set_selection(struct v4l2_subdev *sd, | ||
| 1192 | struct v4l2_subdev_state *sd_state, | ||
| 1193 | struct v4l2_subdev_selection *sel) | ||
| 1194 | { | ||
| 1195 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1196 | struct v4l2_mbus_framefmt *mf; | ||
| 1197 | unsigned int max_x, max_y; | ||
| 1198 | struct v4l2_rect *crop_r; | ||
| 1199 | |||
| 1200 | if (sel->target != V4L2_SEL_TGT_CROP) | ||
| 1201 | return -EINVAL; | ||
| 1202 | |||
| 1203 | mutex_lock(&s5k6aa->lock); | ||
| 1204 | crop_r = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); | ||
| 1205 | |||
| 1206 | if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
| 1207 | mf = &s5k6aa->preset->mbus_fmt; | ||
| 1208 | s5k6aa->apply_crop = 1; | ||
| 1209 | } else { | ||
| 1210 | mf = v4l2_subdev_get_try_format(sd, sd_state, 0); | ||
| 1211 | } | ||
| 1212 | v4l_bound_align_image(&sel->r.width, mf->width, | ||
| 1213 | S5K6AA_WIN_WIDTH_MAX, 1, | ||
| 1214 | &sel->r.height, mf->height, | ||
| 1215 | S5K6AA_WIN_HEIGHT_MAX, 1, 0); | ||
| 1216 | |||
| 1217 | max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1; | ||
| 1218 | max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1; | ||
| 1219 | |||
| 1220 | sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x); | ||
| 1221 | sel->r.top = clamp_t(unsigned int, sel->r.top, 0, max_y); | ||
| 1222 | |||
| 1223 | *crop_r = sel->r; | ||
| 1224 | |||
| 1225 | mutex_unlock(&s5k6aa->lock); | ||
| 1226 | |||
| 1227 | v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", | ||
| 1228 | crop_r->left, crop_r->top, crop_r->width, crop_r->height); | ||
| 1229 | |||
| 1230 | return 0; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { | ||
| 1234 | .enum_mbus_code = s5k6aa_enum_mbus_code, | ||
| 1235 | .enum_frame_size = s5k6aa_enum_frame_size, | ||
| 1236 | .enum_frame_interval = s5k6aa_enum_frame_interval, | ||
| 1237 | .get_fmt = s5k6aa_get_fmt, | ||
| 1238 | .set_fmt = s5k6aa_set_fmt, | ||
| 1239 | .get_selection = s5k6aa_get_selection, | ||
| 1240 | .set_selection = s5k6aa_set_selection, | ||
| 1241 | }; | ||
| 1242 | |||
| 1243 | static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { | ||
| 1244 | .g_frame_interval = s5k6aa_g_frame_interval, | ||
| 1245 | .s_frame_interval = s5k6aa_s_frame_interval, | ||
| 1246 | .s_stream = s5k6aa_s_stream, | ||
| 1247 | }; | ||
| 1248 | |||
| 1249 | /* | ||
| 1250 | * V4L2 subdev controls | ||
| 1251 | */ | ||
| 1252 | |||
| 1253 | static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) | ||
| 1254 | { | ||
| 1255 | struct v4l2_subdev *sd = ctrl_to_sd(ctrl); | ||
| 1256 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 1257 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1258 | int idx, err = 0; | ||
| 1259 | |||
| 1260 | v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); | ||
| 1261 | |||
| 1262 | mutex_lock(&s5k6aa->lock); | ||
| 1263 | /* | ||
| 1264 | * If the device is not powered up by the host driver do | ||
| 1265 | * not apply any controls to H/W at this time. Instead | ||
| 1266 | * the controls will be restored right after power-up. | ||
| 1267 | */ | ||
| 1268 | if (s5k6aa->power == 0) | ||
| 1269 | goto unlock; | ||
| 1270 | idx = s5k6aa->preset->index; | ||
| 1271 | |||
| 1272 | switch (ctrl->id) { | ||
| 1273 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
| 1274 | err = s5k6aa_set_awb(s5k6aa, ctrl->val); | ||
| 1275 | break; | ||
| 1276 | |||
| 1277 | case V4L2_CID_BRIGHTNESS: | ||
| 1278 | err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); | ||
| 1279 | break; | ||
| 1280 | |||
| 1281 | case V4L2_CID_COLORFX: | ||
| 1282 | err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); | ||
| 1283 | break; | ||
| 1284 | |||
| 1285 | case V4L2_CID_CONTRAST: | ||
| 1286 | err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); | ||
| 1287 | break; | ||
| 1288 | |||
| 1289 | case V4L2_CID_EXPOSURE_AUTO: | ||
| 1290 | err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); | ||
| 1291 | break; | ||
| 1292 | |||
| 1293 | case V4L2_CID_HFLIP: | ||
| 1294 | err = s5k6aa_set_mirror(s5k6aa, ctrl->val); | ||
| 1295 | if (err) | ||
| 1296 | break; | ||
| 1297 | err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); | ||
| 1298 | break; | ||
| 1299 | |||
| 1300 | case V4L2_CID_POWER_LINE_FREQUENCY: | ||
| 1301 | err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); | ||
| 1302 | break; | ||
| 1303 | |||
| 1304 | case V4L2_CID_SATURATION: | ||
| 1305 | err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); | ||
| 1306 | break; | ||
| 1307 | |||
| 1308 | case V4L2_CID_SHARPNESS: | ||
| 1309 | err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); | ||
| 1310 | break; | ||
| 1311 | |||
| 1312 | case V4L2_CID_WHITE_BALANCE_TEMPERATURE: | ||
| 1313 | err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); | ||
| 1314 | if (err) | ||
| 1315 | break; | ||
| 1316 | err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); | ||
| 1317 | break; | ||
| 1318 | } | ||
| 1319 | unlock: | ||
| 1320 | mutex_unlock(&s5k6aa->lock); | ||
| 1321 | return err; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { | ||
| 1325 | .s_ctrl = s5k6aa_s_ctrl, | ||
| 1326 | }; | ||
| 1327 | |||
| 1328 | static int s5k6aa_log_status(struct v4l2_subdev *sd) | ||
| 1329 | { | ||
| 1330 | v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); | ||
| 1331 | return 0; | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | #define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) | ||
| 1335 | #define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) | ||
| 1336 | #define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) | ||
| 1337 | |||
| 1338 | static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { | ||
| 1339 | { | ||
| 1340 | .ops = &s5k6aa_ctrl_ops, | ||
| 1341 | .id = V4L2_CID_RED_GAIN, | ||
| 1342 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 1343 | .name = "Gain, Red", | ||
| 1344 | .min = 0, | ||
| 1345 | .max = 256, | ||
| 1346 | .def = 127, | ||
| 1347 | .step = 1, | ||
| 1348 | }, { | ||
| 1349 | .ops = &s5k6aa_ctrl_ops, | ||
| 1350 | .id = V4L2_CID_GREEN_GAIN, | ||
| 1351 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 1352 | .name = "Gain, Green", | ||
| 1353 | .min = 0, | ||
| 1354 | .max = 256, | ||
| 1355 | .def = 127, | ||
| 1356 | .step = 1, | ||
| 1357 | }, { | ||
| 1358 | .ops = &s5k6aa_ctrl_ops, | ||
| 1359 | .id = V4L2_CID_BLUE_GAIN, | ||
| 1360 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
| 1361 | .name = "Gain, Blue", | ||
| 1362 | .min = 0, | ||
| 1363 | .max = 256, | ||
| 1364 | .def = 127, | ||
| 1365 | .step = 1, | ||
| 1366 | }, | ||
| 1367 | }; | ||
| 1368 | |||
| 1369 | static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) | ||
| 1370 | { | ||
| 1371 | const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; | ||
| 1372 | struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; | ||
| 1373 | struct v4l2_ctrl_handler *hdl = &ctrls->handler; | ||
| 1374 | |||
| 1375 | int ret = v4l2_ctrl_handler_init(hdl, 16); | ||
| 1376 | if (ret) | ||
| 1377 | return ret; | ||
| 1378 | /* Auto white balance cluster */ | ||
| 1379 | ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, | ||
| 1380 | 0, 1, 1, 1); | ||
| 1381 | ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); | ||
| 1382 | ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); | ||
| 1383 | ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); | ||
| 1384 | v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); | ||
| 1385 | |||
| 1386 | ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
| 1387 | ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
| 1388 | v4l2_ctrl_cluster(2, &ctrls->hflip); | ||
| 1389 | |||
| 1390 | ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, | ||
| 1391 | V4L2_CID_EXPOSURE_AUTO, | ||
| 1392 | V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); | ||
| 1393 | /* Exposure time: x 1 us */ | ||
| 1394 | ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, | ||
| 1395 | 0, 6000000U, 1, 100000U); | ||
| 1396 | /* Total gain: 256 <=> 1x */ | ||
| 1397 | ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, | ||
| 1398 | 0, 256, 1, 256); | ||
| 1399 | v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); | ||
| 1400 | |||
| 1401 | v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, | ||
| 1402 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, | ||
| 1403 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO); | ||
| 1404 | |||
| 1405 | v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, | ||
| 1406 | V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); | ||
| 1407 | |||
| 1408 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, | ||
| 1409 | 0, 256, 1, 0); | ||
| 1410 | |||
| 1411 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); | ||
| 1412 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); | ||
| 1413 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); | ||
| 1414 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); | ||
| 1415 | |||
| 1416 | if (hdl->error) { | ||
| 1417 | ret = hdl->error; | ||
| 1418 | v4l2_ctrl_handler_free(hdl); | ||
| 1419 | return ret; | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | s5k6aa->sd.ctrl_handler = hdl; | ||
| 1423 | return 0; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | /* | ||
| 1427 | * V4L2 subdev internal operations | ||
| 1428 | */ | ||
| 1429 | static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
| 1430 | { | ||
| 1431 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, | ||
| 1432 | fh->state, | ||
| 1433 | 0); | ||
| 1434 | struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); | ||
| 1435 | |||
| 1436 | format->colorspace = s5k6aa_formats[0].colorspace; | ||
| 1437 | format->code = s5k6aa_formats[0].code; | ||
| 1438 | format->width = S5K6AA_OUT_WIDTH_DEF; | ||
| 1439 | format->height = S5K6AA_OUT_HEIGHT_DEF; | ||
| 1440 | format->field = V4L2_FIELD_NONE; | ||
| 1441 | |||
| 1442 | crop->width = S5K6AA_WIN_WIDTH_MAX; | ||
| 1443 | crop->height = S5K6AA_WIN_HEIGHT_MAX; | ||
| 1444 | crop->left = 0; | ||
| 1445 | crop->top = 0; | ||
| 1446 | |||
| 1447 | return 0; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) | ||
| 1451 | { | ||
| 1452 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 1453 | u16 api_ver = 0, fw_rev = 0; | ||
| 1454 | |||
| 1455 | int ret = s5k6aa_set_ahb_address(client); | ||
| 1456 | |||
| 1457 | if (!ret) | ||
| 1458 | ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); | ||
| 1459 | if (!ret) | ||
| 1460 | ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); | ||
| 1461 | if (ret) { | ||
| 1462 | v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); | ||
| 1463 | return ret; | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", | ||
| 1467 | api_ver, fw_rev); | ||
| 1468 | |||
| 1469 | return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | static int s5k6aa_registered(struct v4l2_subdev *sd) | ||
| 1473 | { | ||
| 1474 | struct s5k6aa *s5k6aa = to_s5k6aa(sd); | ||
| 1475 | int ret; | ||
| 1476 | |||
| 1477 | mutex_lock(&s5k6aa->lock); | ||
| 1478 | ret = __s5k6aa_power_on(s5k6aa); | ||
| 1479 | if (!ret) { | ||
| 1480 | msleep(100); | ||
| 1481 | ret = s5k6aa_check_fw_revision(s5k6aa); | ||
| 1482 | __s5k6aa_power_off(s5k6aa); | ||
| 1483 | } | ||
| 1484 | mutex_unlock(&s5k6aa->lock); | ||
| 1485 | |||
| 1486 | return ret; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { | ||
| 1490 | .registered = s5k6aa_registered, | ||
| 1491 | .open = s5k6aa_open, | ||
| 1492 | }; | ||
| 1493 | |||
| 1494 | static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { | ||
| 1495 | .s_power = s5k6aa_set_power, | ||
| 1496 | .log_status = s5k6aa_log_status, | ||
| 1497 | }; | ||
| 1498 | |||
| 1499 | static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { | ||
| 1500 | .core = &s5k6aa_core_ops, | ||
| 1501 | .pad = &s5k6aa_pad_ops, | ||
| 1502 | .video = &s5k6aa_video_ops, | ||
| 1503 | }; | ||
| 1504 | |||
| 1505 | /* | ||
| 1506 | * GPIO setup | ||
| 1507 | */ | ||
| 1508 | |||
| 1509 | static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, | ||
| 1510 | const struct s5k6aa_platform_data *pdata) | ||
| 1511 | { | ||
| 1512 | struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); | ||
| 1513 | const struct s5k6aa_gpio *gpio; | ||
| 1514 | unsigned long flags; | ||
| 1515 | int ret; | ||
| 1516 | |||
| 1517 | s5k6aa->gpio[STBY].gpio = -EINVAL; | ||
| 1518 | s5k6aa->gpio[RSET].gpio = -EINVAL; | ||
| 1519 | |||
| 1520 | gpio = &pdata->gpio_stby; | ||
| 1521 | if (gpio_is_valid(gpio->gpio)) { | ||
| 1522 | flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) | ||
| 1523 | | GPIOF_EXPORT; | ||
| 1524 | ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags, | ||
| 1525 | "S5K6AA_STBY"); | ||
| 1526 | if (ret < 0) | ||
| 1527 | return ret; | ||
| 1528 | |||
| 1529 | s5k6aa->gpio[STBY] = *gpio; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | gpio = &pdata->gpio_reset; | ||
| 1533 | if (gpio_is_valid(gpio->gpio)) { | ||
| 1534 | flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) | ||
| 1535 | | GPIOF_EXPORT; | ||
| 1536 | ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags, | ||
| 1537 | "S5K6AA_RST"); | ||
| 1538 | if (ret < 0) | ||
| 1539 | return ret; | ||
| 1540 | |||
| 1541 | s5k6aa->gpio[RSET] = *gpio; | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | return 0; | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | static int s5k6aa_probe(struct i2c_client *client) | ||
| 1548 | { | ||
| 1549 | const struct s5k6aa_platform_data *pdata = client->dev.platform_data; | ||
| 1550 | struct v4l2_subdev *sd; | ||
| 1551 | struct s5k6aa *s5k6aa; | ||
| 1552 | int i, ret; | ||
| 1553 | |||
| 1554 | if (pdata == NULL) { | ||
| 1555 | dev_err(&client->dev, "Platform data not specified\n"); | ||
| 1556 | return -EINVAL; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | if (pdata->mclk_frequency == 0) { | ||
| 1560 | dev_err(&client->dev, "MCLK frequency not specified\n"); | ||
| 1561 | return -EINVAL; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); | ||
| 1565 | if (!s5k6aa) | ||
| 1566 | return -ENOMEM; | ||
| 1567 | |||
| 1568 | mutex_init(&s5k6aa->lock); | ||
| 1569 | |||
| 1570 | s5k6aa->mclk_frequency = pdata->mclk_frequency; | ||
| 1571 | s5k6aa->bus_type = pdata->bus_type; | ||
| 1572 | s5k6aa->mipi_lanes = pdata->nlanes; | ||
| 1573 | s5k6aa->s_power = pdata->set_power; | ||
| 1574 | s5k6aa->inv_hflip = pdata->horiz_flip; | ||
| 1575 | s5k6aa->inv_vflip = pdata->vert_flip; | ||
| 1576 | |||
| 1577 | sd = &s5k6aa->sd; | ||
| 1578 | v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); | ||
| 1579 | /* Static name; NEVER use in new drivers! */ | ||
| 1580 | strscpy(sd->name, DRIVER_NAME, sizeof(sd->name)); | ||
| 1581 | |||
| 1582 | sd->internal_ops = &s5k6aa_subdev_internal_ops; | ||
| 1583 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
| 1584 | |||
| 1585 | s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
| 1586 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | ||
| 1587 | ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad); | ||
| 1588 | if (ret) | ||
| 1589 | return ret; | ||
| 1590 | |||
| 1591 | ret = s5k6aa_configure_gpios(s5k6aa, pdata); | ||
| 1592 | if (ret) | ||
| 1593 | goto out_err; | ||
| 1594 | |||
| 1595 | for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) | ||
| 1596 | s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; | ||
| 1597 | |||
| 1598 | ret = devm_regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, | ||
| 1599 | s5k6aa->supplies); | ||
| 1600 | if (ret) { | ||
| 1601 | dev_err(&client->dev, "Failed to get regulators\n"); | ||
| 1602 | goto out_err; | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | ret = s5k6aa_initialize_ctrls(s5k6aa); | ||
| 1606 | if (ret) | ||
| 1607 | goto out_err; | ||
| 1608 | |||
| 1609 | s5k6aa_presets_data_init(s5k6aa); | ||
| 1610 | |||
| 1611 | s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; | ||
| 1612 | s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; | ||
| 1613 | s5k6aa->ccd_rect.left = 0; | ||
| 1614 | s5k6aa->ccd_rect.top = 0; | ||
| 1615 | |||
| 1616 | return 0; | ||
| 1617 | |||
| 1618 | out_err: | ||
| 1619 | media_entity_cleanup(&s5k6aa->sd.entity); | ||
| 1620 | return ret; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | static void s5k6aa_remove(struct i2c_client *client) | ||
| 1624 | { | ||
| 1625 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
| 1626 | |||
| 1627 | v4l2_device_unregister_subdev(sd); | ||
| 1628 | v4l2_ctrl_handler_free(sd->ctrl_handler); | ||
| 1629 | media_entity_cleanup(&sd->entity); | ||
| 1630 | } | ||
| 1631 | |||
| 1632 | static const struct i2c_device_id s5k6aa_id[] = { | ||
| 1633 | { DRIVER_NAME, 0 }, | ||
| 1634 | { }, | ||
| 1635 | }; | ||
| 1636 | MODULE_DEVICE_TABLE(i2c, s5k6aa_id); | ||
| 1637 | |||
| 1638 | |||
| 1639 | static struct i2c_driver s5k6aa_i2c_driver = { | ||
| 1640 | .driver = { | ||
| 1641 | .name = DRIVER_NAME | ||
| 1642 | }, | ||
| 1643 | .probe_new = s5k6aa_probe, | ||
| 1644 | .remove = s5k6aa_remove, | ||
| 1645 | .id_table = s5k6aa_id, | ||
| 1646 | }; | ||
| 1647 | |||
| 1648 | module_i2c_driver(s5k6aa_i2c_driver); | ||
| 1649 | |||
| 1650 | MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); | ||
| 1651 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
| 1652 | MODULE_LICENSE("GPL"); | ||
| @@ -1,762 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /* | ||
| 3 | * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Samsung Electronics Co., Ltd | ||
| 6 | * Author: Sylwester Nawrocki, s.nawrocki@samsung.com | ||
| 7 | * | ||
| 8 | * Based on original driver authored by Dongsoo Nathaniel Kim | ||
| 9 | * and HeungJun Kim <riverful.kim@samsung.com>. | ||
| 10 | * | ||
| 11 | * Based on mt9v011 Micron Digital Image Sensor driver | ||
| 12 | * Copyright (c) 2009 Mauro Carvalho Chehab | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/i2c.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <media/v4l2-device.h> | ||
| 20 | #include <media/v4l2-subdev.h> | ||
| 21 | #include <media/v4l2-mediabus.h> | ||
| 22 | #include <media/v4l2-ctrls.h> | ||
| 23 | #include <media/i2c/sr030pc30.h> | ||
| 24 | |||
| 25 | static int debug; | ||
| 26 | module_param(debug, int, 0644); | ||
| 27 | |||
| 28 | #define MODULE_NAME "SR030PC30" | ||
| 29 | |||
| 30 | /* | ||
| 31 | * Register offsets within a page | ||
| 32 | * b15..b8 - page id, b7..b0 - register address | ||
| 33 | */ | ||
| 34 | #define POWER_CTRL_REG 0x0001 | ||
| 35 | #define PAGEMODE_REG 0x03 | ||
| 36 | #define DEVICE_ID_REG 0x0004 | ||
| 37 | #define NOON010PC30_ID 0x86 | ||
| 38 | #define SR030PC30_ID 0x8C | ||
| 39 | #define VDO_CTL1_REG 0x0010 | ||
| 40 | #define SUBSAMPL_NONE_VGA 0 | ||
| 41 | #define SUBSAMPL_QVGA 0x10 | ||
| 42 | #define SUBSAMPL_QQVGA 0x20 | ||
| 43 | #define VDO_CTL2_REG 0x0011 | ||
| 44 | #define SYNC_CTL_REG 0x0012 | ||
| 45 | #define WIN_ROWH_REG 0x0020 | ||
| 46 | #define WIN_ROWL_REG 0x0021 | ||
| 47 | #define WIN_COLH_REG 0x0022 | ||
| 48 | #define WIN_COLL_REG 0x0023 | ||
| 49 | #define WIN_HEIGHTH_REG 0x0024 | ||
| 50 | #define WIN_HEIGHTL_REG 0x0025 | ||
| 51 | #define WIN_WIDTHH_REG 0x0026 | ||
| 52 | #define WIN_WIDTHL_REG 0x0027 | ||
| 53 | #define HBLANKH_REG 0x0040 | ||
| 54 | #define HBLANKL_REG 0x0041 | ||
| 55 | #define VSYNCH_REG 0x0042 | ||
| 56 | #define VSYNCL_REG 0x0043 | ||
| 57 | /* page 10 */ | ||
| 58 | #define ISP_CTL_REG(n) (0x1010 + (n)) | ||
| 59 | #define YOFS_REG 0x1040 | ||
| 60 | #define DARK_YOFS_REG 0x1041 | ||
| 61 | #define AG_ABRTH_REG 0x1050 | ||
| 62 | #define SAT_CTL_REG 0x1060 | ||
| 63 | #define BSAT_REG 0x1061 | ||
| 64 | #define RSAT_REG 0x1062 | ||
| 65 | #define AG_SAT_TH_REG 0x1063 | ||
| 66 | /* page 11 */ | ||
| 67 | #define ZLPF_CTRL_REG 0x1110 | ||
| 68 | #define ZLPF_CTRL2_REG 0x1112 | ||
| 69 | #define ZLPF_AGH_THR_REG 0x1121 | ||
| 70 | #define ZLPF_THR_REG 0x1160 | ||
| 71 | #define ZLPF_DYN_THR_REG 0x1160 | ||
| 72 | /* page 12 */ | ||
| 73 | #define YCLPF_CTL1_REG 0x1240 | ||
| 74 | #define YCLPF_CTL2_REG 0x1241 | ||
| 75 | #define YCLPF_THR_REG 0x1250 | ||
| 76 | #define BLPF_CTL_REG 0x1270 | ||
| 77 | #define BLPF_THR1_REG 0x1274 | ||
| 78 | #define BLPF_THR2_REG 0x1275 | ||
| 79 | /* page 14 - Lens Shading Compensation */ | ||
| 80 | #define LENS_CTRL_REG 0x1410 | ||
| 81 | #define LENS_XCEN_REG 0x1420 | ||
| 82 | #define LENS_YCEN_REG 0x1421 | ||
| 83 | #define LENS_R_COMP_REG 0x1422 | ||
| 84 | #define LENS_G_COMP_REG 0x1423 | ||
| 85 | #define LENS_B_COMP_REG 0x1424 | ||
| 86 | /* page 15 - Color correction */ | ||
| 87 | #define CMC_CTL_REG 0x1510 | ||
| 88 | #define CMC_OFSGH_REG 0x1514 | ||
| 89 | #define CMC_OFSGL_REG 0x1516 | ||
| 90 | #define CMC_SIGN_REG 0x1517 | ||
| 91 | /* Color correction coefficients */ | ||
| 92 | #define CMC_COEF_REG(n) (0x1530 + (n)) | ||
| 93 | /* Color correction offset coefficients */ | ||
| 94 | #define CMC_OFS_REG(n) (0x1540 + (n)) | ||
| 95 | /* page 16 - Gamma correction */ | ||
| 96 | #define GMA_CTL_REG 0x1610 | ||
| 97 | /* Gamma correction coefficients 0.14 */ | ||
| 98 | #define GMA_COEF_REG(n) (0x1630 + (n)) | ||
| 99 | /* page 20 - Auto Exposure */ | ||
| 100 | #define AE_CTL1_REG 0x2010 | ||
| 101 | #define AE_CTL2_REG 0x2011 | ||
| 102 | #define AE_FRM_CTL_REG 0x2020 | ||
| 103 | #define AE_FINE_CTL_REG(n) (0x2028 + (n)) | ||
| 104 | #define EXP_TIMEH_REG 0x2083 | ||
| 105 | #define EXP_TIMEM_REG 0x2084 | ||
| 106 | #define EXP_TIMEL_REG 0x2085 | ||
| 107 | #define EXP_MMINH_REG 0x2086 | ||
| 108 | #define EXP_MMINL_REG 0x2087 | ||
| 109 | #define EXP_MMAXH_REG 0x2088 | ||
| 110 | #define EXP_MMAXM_REG 0x2089 | ||
| 111 | #define EXP_MMAXL_REG 0x208A | ||
| 112 | /* page 22 - Auto White Balance */ | ||
| 113 | #define AWB_CTL1_REG 0x2210 | ||
| 114 | #define AWB_ENABLE 0x80 | ||
| 115 | #define AWB_CTL2_REG 0x2211 | ||
| 116 | #define MWB_ENABLE 0x01 | ||
| 117 | /* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ | ||
| 118 | #define AWB_RGAIN_REG 0x2280 | ||
| 119 | #define AWB_GGAIN_REG 0x2281 | ||
| 120 | #define AWB_BGAIN_REG 0x2282 | ||
| 121 | #define AWB_RMAX_REG 0x2283 | ||
| 122 | #define AWB_RMIN_REG 0x2284 | ||
| 123 | #define AWB_BMAX_REG 0x2285 | ||
| 124 | #define AWB_BMIN_REG 0x2286 | ||
| 125 | /* R, B gain range in bright light conditions */ | ||
| 126 | #define AWB_RMAXB_REG 0x2287 | ||
| 127 | #define AWB_RMINB_REG 0x2288 | ||
| 128 | #define AWB_BMAXB_REG 0x2289 | ||
| 129 | #define AWB_BMINB_REG 0x228A | ||
| 130 | /* manual white balance, when AWB_CTL2[0]=1 */ | ||
| 131 | #define MWB_RGAIN_REG 0x22B2 | ||
| 132 | #define MWB_BGAIN_REG 0x22B3 | ||
| 133 | /* the token to mark an array end */ | ||
| 134 | #define REG_TERM 0xFFFF | ||
| 135 | |||
| 136 | /* Minimum and maximum exposure time in ms */ | ||
| 137 | #define EXPOS_MIN_MS 1 | ||
| 138 | #define EXPOS_MAX_MS 125 | ||
| 139 | |||
| 140 | struct sr030pc30_info { | ||
| 141 | struct v4l2_subdev sd; | ||
| 142 | struct v4l2_ctrl_handler hdl; | ||
| 143 | const struct sr030pc30_platform_data *pdata; | ||
| 144 | const struct sr030pc30_format *curr_fmt; | ||
| 145 | const struct sr030pc30_frmsize *curr_win; | ||
| 146 | unsigned int hflip:1; | ||
| 147 | unsigned int vflip:1; | ||
| 148 | unsigned int sleep:1; | ||
| 149 | struct { | ||
| 150 | /* auto whitebalance control cluster */ | ||
| 151 | struct v4l2_ctrl *awb; | ||
| 152 | struct v4l2_ctrl *red; | ||
| 153 | struct v4l2_ctrl *blue; | ||
| 154 | }; | ||
| 155 | struct { | ||
| 156 | /* auto exposure control cluster */ | ||
| 157 | struct v4l2_ctrl *autoexp; | ||
| 158 | struct v4l2_ctrl *exp; | ||
| 159 | }; | ||
| 160 | u8 i2c_reg_page; | ||
| 161 | }; | ||
| 162 | |||
| 163 | struct sr030pc30_format { | ||
| 164 | u32 code; | ||
| 165 | enum v4l2_colorspace colorspace; | ||
| 166 | u16 ispctl1_reg; | ||
| 167 | }; | ||
| 168 | |||
| 169 | struct sr030pc30_frmsize { | ||
| 170 | u16 width; | ||
| 171 | u16 height; | ||
| 172 | int vid_ctl1; | ||
| 173 | }; | ||
| 174 | |||
| 175 | struct i2c_regval { | ||
| 176 | u16 addr; | ||
| 177 | u16 val; | ||
| 178 | }; | ||
| 179 | |||
| 180 | /* supported resolutions */ | ||
| 181 | static const struct sr030pc30_frmsize sr030pc30_sizes[] = { | ||
| 182 | { | ||
| 183 | .width = 640, | ||
| 184 | .height = 480, | ||
| 185 | .vid_ctl1 = SUBSAMPL_NONE_VGA, | ||
| 186 | }, { | ||
| 187 | .width = 320, | ||
| 188 | .height = 240, | ||
| 189 | .vid_ctl1 = SUBSAMPL_QVGA, | ||
| 190 | }, { | ||
| 191 | .width = 160, | ||
| 192 | .height = 120, | ||
| 193 | .vid_ctl1 = SUBSAMPL_QQVGA, | ||
| 194 | }, | ||
| 195 | }; | ||
| 196 | |||
| 197 | /* supported pixel formats */ | ||
| 198 | static const struct sr030pc30_format sr030pc30_formats[] = { | ||
| 199 | { | ||
| 200 | .code = MEDIA_BUS_FMT_YUYV8_2X8, | ||
| 201 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
| 202 | .ispctl1_reg = 0x03, | ||
| 203 | }, { | ||
| 204 | .code = MEDIA_BUS_FMT_YVYU8_2X8, | ||
558 more lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 855 lines aren’t shown here.
This diff is large: its 326 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 157 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 595 lines aren’t shown here.
This diff is large: its 142 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 153 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 456 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 377 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 57 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 74 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 564 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 167 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 103 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 303 lines aren’t shown here.
This diff is large: its 60 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 454 lines aren’t shown here.
This diff is large: its 145 lines aren’t shown here.
This diff is large: its 222 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 372 lines aren’t shown here.
This diff is large: its 1021 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 104 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 120 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 385 lines aren’t shown here.
This diff is large: its 282 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 274 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 164 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 617 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 540 lines aren’t shown here.
This diff is large: its 395 lines aren’t shown here.
This diff is large: its 530 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 651 lines aren’t shown here.
This diff is large: its 859 lines aren’t shown here.
This diff is large: its 868 lines aren’t shown here.
This diff is large: its 419 lines aren’t shown here.
This diff is large: its 1513 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 124 lines aren’t shown here.
This diff is large: its 103 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 139 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 205 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 93 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 52 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 80 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 57 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 127 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 76 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 119 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 222 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 58 lines aren’t shown here.
This diff is large: its 152 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 146 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 163 lines aren’t shown here.
This diff is large: its 318 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 140 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 198 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 416 lines aren’t shown here.
This diff is large: its 1236 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 492 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 1573 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 710 lines aren’t shown here.
This diff is large: its 228 lines aren’t shown here.
This diff is large: its 341 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 343 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 306 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 155 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 324 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 108 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 109 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 368 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 38 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 60 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.