Merge branch 'renesas-drivers-2016-05-17-v4.6/iccom' into v4.6/rcar-3.3.x
* renesas-drivers-2016-05-17-v4.6/iccom:
hwspinlock: rcar: Add support for R-Car Gen3 Hardware Spinlock
DT: hwspinlock: Add binding documentation for R-Car Gen3 hwspinlock
arm64: renesas: Enable RPMSG
rpmsg: DMA map sgs passed to virtio
virtio: Add dma variants of virtqueue_add_in and outbuf
virtio_ring: Add option for DMA mapped sgs in virtqueue_add
virtio_ring: Break out vring descriptor setup code
clk: renesas: r8a7796: Add MFIS clock
clk: renesas: r8a7795: Add MFIS clock
diff --git a/Documentation/devicetree/bindings/display/renesas,du.txt b/Documentation/devicetree/bindings/display/renesas,du.txt
index 0d30e42..8515658 100644
--- a/Documentation/devicetree/bindings/display/renesas,du.txt
+++ b/Documentation/devicetree/bindings/display/renesas,du.txt
@@ -9,6 +9,7 @@
- "renesas,du-r8a7793" for R8A7793 (R-Car M2-N) compatible DU
- "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU
- "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
+ - "renesas,du-r8a7796" for R8A7796 (R-Car M3) compatible DU
- reg: A list of base address and length of each memory resource, one for
each entry in the reg-names property.
@@ -50,6 +51,7 @@
R8A7793 (M2-N) DPAD LVDS 0 - -
R8A7794 (E2) DPAD 0 DPAD 1 - -
R8A7795 (H3) DPAD HDMI 0 HDMI 1 LVDS
+ R8A7796 (M3) DPAD HDMI LVDS -
Example: R8A7790 (R-Car H2) DU
diff --git a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
index 5b902ac..0e2065b 100644
--- a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
+++ b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.txt
@@ -1,6 +1,6 @@
* Renesas R-Car DMA Controller Device Tree bindings
-Renesas R-Car Generation 2 SoCs have multiple multi-channel DMA
+Renesas R-Car Generation 2 and 3 SoCs have multiple multi-channel DMA
controller instances named DMAC capable of serving multiple clients. Channels
can be dedicated to specific clients or shared between a large number of
clients.
@@ -22,6 +22,7 @@
- "renesas,dmac-r8a7793" (R-Car M2-N)
- "renesas,dmac-r8a7794" (R-Car E2)
- "renesas,dmac-r8a7795" (R-Car H3)
+ - "renesas,dmac-r8a7796" (R-Car M3)
- reg: base address and length of the registers block for the DMAC
diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
index e7780a1..4987ff6 100644
--- a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
+++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
@@ -8,6 +8,7 @@
- "renesas,r8a7793-usb-dmac" (R-Car M2-N)
- "renesas,r8a7794-usb-dmac" (R-Car E2)
- "renesas,r8a7795-usb-dmac" (R-Car H3)
+ - "renesas,r8a7796-usb-dmac" (R-Car M3)
- reg: base address and length of the registers block for the DMAC
- interrupts: interrupt specifiers for the DMAC, one for each entry in
interrupt-names.
diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
index f60e2f4..4303b67 100644
--- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
+++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt
@@ -10,6 +10,7 @@
- "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) compatible GPIO controller.
- "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller.
- "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller.
+ - "renesas,gpio-r8a7796": for R8A7796 (R-Car M3) compatible GPIO controller.
- "renesas,gpio-rcar": for generic R-Car GPIO controller.
- reg: Base address and length of each memory resource used by the GPIO
diff --git a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
index cf8bfc9..239632a 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
@@ -11,6 +11,7 @@
"renesas,i2c-r8a7793"
"renesas,i2c-r8a7794"
"renesas,i2c-r8a7795"
+ "renesas,i2c-r8a7796"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt specifier.
@@ -19,6 +20,9 @@
- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
property indicates the default frequency 100 kHz.
- clocks: clock specifier.
+- dmas: Must contain a list of two references to DMA specifiers, one for
+ transmission, and one for reception.
+- dma-names: Must contain a list of two DMA names, "tx" and "rx".
- i2c-scl-falling-time-ns: see i2c.txt
- i2c-scl-internal-delay-ns: see i2c.txt
diff --git a/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.txt b/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.txt
index 3ed027c..98ae900 100644
--- a/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.txt
+++ b/Documentation/devicetree/bindings/iommu/renesas,ipmmu-vmsa.txt
@@ -16,6 +16,7 @@
- "renesas,ipmmu-r8a7793" for the R8A7793 (R-Car M2-N) IPMMU.
- "renesas,ipmmu-r8a7794" for the R8A7794 (R-Car E2) IPMMU.
- "renesas,ipmmu-r8a7795" for the R8A7795 (R-Car H3) IPMMU.
+ - "renesas,ipmmu-r8a7796" for the R8A7796 (R-Car M3-W) IPMMU.
- "renesas,ipmmu-vmsa" for generic R-Car Gen2 VMSA-compatible IPMMU.
- reg: Base address and size of the IPMMU registers.
diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt
index 6a4e61c..285048b 100644
--- a/Documentation/devicetree/bindings/media/rcar_vin.txt
+++ b/Documentation/devicetree/bindings/media/rcar_vin.txt
@@ -6,6 +6,7 @@
channel which can be either RGB, YUYV or BT656.
- compatible: Must be one or more of the following
+ - "renesas,vin-r8a7796" for the R8A7796 device
- "renesas,vin-r8a7795" for the R8A7795 device
- "renesas,vin-r8a7794" for the R8A7794 device
- "renesas,vin-r8a7793" for the R8A7793 device
diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
index 0f610d4..56e259f 100644
--- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
@@ -23,6 +23,9 @@
"renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
+ "renesas,mmc-r8a7795" - MMC IP on R8A7795 SoC
+ "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
+ "renesas,mmc-r8a7796" - MMC IP on R8A7796 SoC
Optional properties:
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
diff --git a/Documentation/devicetree/bindings/net/renesas,ravb.txt b/Documentation/devicetree/bindings/net/renesas,ravb.txt
index c8ac222..86e3e10 100644
--- a/Documentation/devicetree/bindings/net/renesas,ravb.txt
+++ b/Documentation/devicetree/bindings/net/renesas,ravb.txt
@@ -10,6 +10,7 @@
"renesas,etheravb-r8a7793" if the device is a part of R8A7793 SoC.
"renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
"renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC.
+ "renesas,etheravb-r8a7796" if the device is a part of R8A7796 SoC.
"renesas,etheravb-rcar-gen2" for generic R-Car Gen 2 compatible interface.
"renesas,etheravb-rcar-gen3" for generic R-Car Gen 3 compatible interface.
@@ -33,7 +34,7 @@
- interrupt-parent: the phandle for the interrupt controller that services
interrupts for this device.
- interrupt-names: A list of interrupt names.
- For the R8A7795 SoC this property is mandatory;
+ For the R8A7795/R8A7796 SoC this property is mandatory;
it should include one entry per channel, named "ch%u",
where %u is the channel number ranging from 0 to 24.
For other SoCs this property is optional; if present
diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt
index 6cf9969..ca1698b 100644
--- a/Documentation/devicetree/bindings/pci/rcar-pci.txt
+++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt
@@ -6,6 +6,7 @@
"renesas,pcie-r8a7791" for the R8A7791 SoC;
"renesas,pcie-r8a7793" for the R8A7793 SoC;
"renesas,pcie-r8a7795" for the R8A7795 SoC;
+ "renesas,pcie-r8a7796" for the R8A7796 SoC;
"renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device.
When compatible with the generic version, nodes must list the
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
index 2281d6c..22b8306 100644
--- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
@@ -6,6 +6,8 @@
Required properties:
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
SoC.
+ "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
+ SoC.
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
When compatible with the generic version, nodes must list the
diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
index 503bce3..e8078b1 100644
--- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
+++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
@@ -31,8 +31,8 @@
- "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART.
- "renesas,scif-r8a7795" for R8A7795 (R-Car H3) SCIF compatible UART.
- "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART.
- - "renesas,scif-r8a7796" for R8A7796 (R-Car M3-W) SCIF compatible UART.
- - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART.
+ - "renesas,scif-r8a7796" for R8A7796 (R-Car M3) SCIF compatible UART.
+ - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3) HSCIF compatible UART.
- "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.
- "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART.
- "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART,
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index c7b29df..15a7316 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -373,6 +373,8 @@
- #clock-cells : it must be 0 if your system has audio_clkout
it must be 1 if your system has audio_clkout0/1/2/3
- clock-frequency : for all audio_clkout0/1/2/3
+- clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn
+ is asynchronizes with lr-clock.
SSI subnode properties:
- interrupts : Should contain SSI interrupt for PIO transfer
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index aa005c1..c2b6251 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -10,6 +10,8 @@
"renesas,msiof-r8a7792" (R-Car V2H)
"renesas,msiof-r8a7793" (R-Car M2-N)
"renesas,msiof-r8a7794" (R-Car E2)
+ "renesas,msiof-r8a7795" (R-Car H3)
+ "renesas,msiof-r8a7796" (R-Car M3)
"renesas,msiof-sh73a0" (SH-Mobile AG5)
- reg : A list of offsets and lengths of the register sets for
the device.
diff --git a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
new file mode 100644
index 0000000..9b38109
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
@@ -0,0 +1,84 @@
+* Renesas R-Car Gen3 Thermal
+
+Required properties:
+- compatible : "renesas,thermal-<soctype>",
+ Examples with soctypes are:
+ - "renesas,thermal-r8a7795" (R-Car H3)
+ - "renesas,thermal-r8a7796" (R-Car M3)
+- reg : Address range of the thermal registers.
+- #thermal-sensor-cells : Please see ./thermal.txt
+
+Option properties:
+
+- interrupts : use interrupt
+
+Example (non interrupt support):
+
+thermal: thermal@0xe6198000 {
+ compatible = "renesas,thermal-r8a7795";
+ reg = <0 0xe6198000 0 0x5c>;
+ };
+
+thermal-zones {
+ cpu_thermal: cpu-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&thermal>;
+ };
+};
+
+Example (interrupt support):
+
+thermal: thermal@0xe6198000 {
+ compatible = "renesas,thermal-r8a7795";
+ reg = <0 0xe6198000 0 0x5c>;
+ interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+ <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+ <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+thermal-zones {
+ cpu_thermal: cpu-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <0>;
+
+ thermal-sensors = <&thermal>;
+ };
+};
+
+* Emergency shutdown for R-CAR Gen3
+Emergency shutdown functionality provides the specific cooling mechanism
+for R-CAR Gen3. In case of high temperature(e.g over 100 degrees),
+it has the ability to rapidly cool down the system.
+
+Required property:
+- polling-delay: The maximum number of milliseconds to wait between polls
+ Type: unsigned when checking temperature for emergency shutdown.
+ Size: one cell
+
+- on-temperature: This value indicates the emergency temperature and invokes
+ Type: unsigned emergency shutdown functionality when exceeding this
+ Size: one cell temperature.
+
+- off-temperature: This value indicates the temperature to disable emergency
+ Type: unsigned shutdown.
+ Size: one cell
+
+- status: Should be "disabled" always.
+ Type: string
+
+- target_cpus: This property indicates which CPU will be targeted for shutdown.
+ Type: phandle
+
+thermal-zones {
+ emergency {
+ polling-delay = <1000>; /* milliseconds */
+ on-temperature = <110000>; /* millicelsius */
+ off-temperature = <95000>; /* millicelsius */
+ target_cpus = <&a57_1>,
+ <&a57_2>,
+ <&a57_3>;
+ status = "disabled";
+ };
+};
diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
index b604056..3b6086e 100644
--- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
+++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
@@ -9,6 +9,7 @@
- "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device
- "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
+ - "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3) compatible device
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
@@ -24,8 +25,9 @@
- renesas,buswait: Integer to use BUSWAIT register
- renesas,enable-gpio: A gpio specifier to check GPIO determining if USB
function should be enabled
- - phys: phandle + phy specifier pair
+ - phys: phandle of *Generic PHY* + phy specifier pair
- phy-names: must be "usb"
+ - usb-phy: phandle of usb phy
- dmas: Must contain a list of references to DMA specifiers.
- dma-names : named "ch%d", where %d is the channel number ranging from zero
to the number of channels (DnFIFOs) minus one.
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 966885c..0b7d857 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -11,6 +11,7 @@
- "renesas,xhci-r8a7791" for r8a7791 SoC
- "renesas,xhci-r8a7793" for r8a7793 SoC
- "renesas,xhci-r8a7795" for r8a7795 SoC
+ - "renesas,xhci-r8a7796" for r8a7796 SoC
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 compatible device
- "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device
- "xhci-platform" (deprecated)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index df6b67e..8aad18a 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -80,6 +80,7 @@
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPUFREQ_DT=y
CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
@@ -190,7 +191,8 @@
CONFIG_SPI_ORION=y
CONFIG_SPI_PL022=y
CONFIG_SPI_QUP=y
-CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SH_MSIOF=y
+CONFIG_SPI_SPIDEV=y
CONFIG_SPMI=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_PINCTRL_MSM8916=y
@@ -207,8 +209,13 @@
CONFIG_SENSORS_LM90=m
CONFIG_SENSORS_INA2XX=m
CONFIG_THERMAL=y
+CONFIG_THERMAL_OF=y
+CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y
+CONFIG_CPU_THERMAL=y
CONFIG_THERMAL_EMULATION=y
CONFIG_EXYNOS_THERMAL=y
+CONFIG_RCAR_GEN3_THERMAL=y
+CONFIG_RCAR_THERMAL_EMS_ENABLED=y
CONFIG_WATCHDOG=y
CONFIG_RENESAS_WDT=y
CONFIG_MFD_SPMI_PMIC=y
@@ -218,6 +225,7 @@
CONFIG_REGULATOR_BD9571MWV=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_HI655X=y
+CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_QCOM_SMD_RPM=y
CONFIG_REGULATOR_QCOM_SPMI=y
CONFIG_REGULATOR_S2MPS11=y
@@ -228,6 +236,10 @@
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_PLATFORM=y
+CONFIG_VIDEO_RCAR_VIN=y
+CONFIG_VIDEO_RCAR_CSI2=y
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7482=y
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_RENESAS_FCP=y
CONFIG_VIDEO_RENESAS_VSP1=y
@@ -266,6 +278,8 @@
CONFIG_USB_MSM_OTG=y
CONFIG_USB_ULPI=y
CONFIG_USB_GADGET=y
+CONFIG_USB_RENESAS_USBHS=y
+CONFIG_USB_RENESAS_USBHS_UDC=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_ARMMMCI=y
@@ -275,6 +289,8 @@
CONFIG_MMC_SDHCI_TEGRA=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SPI=y
+CONFIG_MMC_TMIO_CORE=y
+CONFIG_MMC_SDHI=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_MMC_DW_K3=y
@@ -298,6 +314,7 @@
CONFIG_TEGRA20_APB_DMA=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_RCAR_DMAC=y
+CONFIG_RENESAS_USB_DMAC=y
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VIRTIO_PCI=y
@@ -305,8 +322,11 @@
CONFIG_VIRTIO_MMIO=y
CONFIG_XEN_GNTDEV=y
CONFIG_XEN_GRANT_DEV_ALLOC=y
+CONFIG_STAGING=y
+CONFIG_STAGING_BOARD=y
CONFIG_COMMON_CLK_SCPI=y
CONFIG_COMMON_CLK_CS2000_CP=y
+CONFIG_COMMON_CLK_5P49V5923A=y
CONFIG_CLK_QORIQ=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_MSM_GCC_8916=y
@@ -316,6 +336,8 @@
CONFIG_HI6220_MBOX=y
CONFIG_IPMMU_VMSA=y
CONFIG_ARM_SMMU=y
+CONFIG_PWM=y
+CONFIG_PWM_RCAR=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD=y
CONFIG_QCOM_SMD_RPM=y
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 2dd371d..8667442 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -192,6 +192,13 @@
Adapter driver so that any PWM output can be (mis)used as clock signal
at 50% duty cycle.
+config COMMON_CLK_5P49V5923A
+ tristate "Clock driver for 5P49V5923A programmable clock generator"
+ depends on I2C
+ help
+ If you say yes here you get support for the 5P49V5923A programmable
+ clock generator.
+
config COMMON_CLK_PXA
def_bool COMMON_CLK && ARCH_PXA
---help---
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 6d1e3be..4eb37ec 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -17,6 +17,7 @@
# hardware specific clock types
# please keep this section sorted lexicographically by file/directory path name
+obj-$(CONFIG_COMMON_CLK_5P49V5923A) += clk-5p49v5923a.o
obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
diff --git a/drivers/clk/clk-5p49v5923a.c b/drivers/clk/clk-5p49v5923a.c
new file mode 100644
index 0000000..f273748
--- /dev/null
+++ b/drivers/clk/clk-5p49v5923a.c
@@ -0,0 +1,340 @@
+/*
+ * drivers/gpu/drm/i2c/5p49v5923a.c
+ * This file is programmable clock generator driver.
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This file is based on the drivers/clk/clk-cs2000-cp.c
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+
+#define REF_CLK 1
+#define CLK_MAX 5
+
+#define INPUT_CLK 25000000
+
+#define C5P49_FB_INT_DIV_REG1 0x17
+#define C5P49_FB_INT_DIV_REG0 0x18
+
+/* offset address*/
+#define C5P49_DIV_FRAC_29_22 0x02
+#define C5P49_DIV_FRAC_21_14 0x03
+#define C5P49_DIV_FRAC_13_6 0x04
+#define C5P49_DIV_FRAC_5_0 0x05
+#define C5P49_DIV_INTEGER_11_4 0x0d
+#define C5P49_DIV_INTEGER_3_0 0x0e
+
+#define C5P49_CLK_OE_SHUTDOWN 0x68
+
+#define hw_to_priv(_hw) container_of(_hw, struct clk_5p49_priv, hw)
+#define priv_to_client(priv) (priv->client)
+#define priv_to_dev(priv) (&(priv_to_client(priv)->dev))
+
+struct clk_5p49_priv {
+ struct clk_hw hw;
+ struct i2c_client *client;
+ struct clk *clk_out;
+ unsigned long index;
+ unsigned long clk_rate;
+};
+
+static const struct of_device_id clk_5p49_of_match[] = {
+ { .compatible = "idt,5p49v5923a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, clk_5p49_of_match);
+
+static const struct i2c_device_id clk_5p49_id[] = {
+ { "5p49v5923a",},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, clk_5p49_id);
+
+#define clk_5p49_read(priv, addr) \
+ i2c_smbus_read_byte_data(priv_to_client(priv), \
+ (addr + (0x10 * priv->index)))
+#define clk_5p49_write(priv, addr, val) \
+ i2c_smbus_write_byte_data(priv_to_client(priv), \
+ (addr + (0x10 * priv->index)), val)
+
+static int clk_5p49_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ return 0;
+}
+
+static void clk_5p49_power(struct clk_hw *hw, bool power)
+{
+ struct clk_5p49_priv *priv = hw_to_priv(hw);
+ u8 reg;
+
+ if (power) {
+ reg = i2c_smbus_read_byte_data(priv->client,
+ C5P49_CLK_OE_SHUTDOWN);
+ reg |= (0x80 >> (priv->index - 1));
+ i2c_smbus_write_byte_data(priv->client,
+ C5P49_CLK_OE_SHUTDOWN, reg);
+ } else {
+ reg = i2c_smbus_read_byte_data(priv->client,
+ C5P49_CLK_OE_SHUTDOWN);
+ reg &= ~(0x80 >> (priv->index - 1));
+ i2c_smbus_write_byte_data(priv->client,
+ C5P49_CLK_OE_SHUTDOWN, reg);
+ }
+}
+
+static int clk_5p49_enable(struct clk_hw *hw)
+{
+ clk_5p49_power(hw, true);
+
+ return 0;
+}
+
+static void clk_5p49_disable(struct clk_hw *hw)
+{
+ clk_5p49_power(hw, false);
+}
+
+static unsigned long clk_5p49_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_5p49_priv *priv = hw_to_priv(hw);
+
+ return priv->clk_rate;
+}
+
+static int clk_5p49_div_calculation(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_5p49_priv *priv = hw_to_priv(hw);
+ int integ_div, frac_div, div, vco_div, vco_clk;
+ u32 shift_1kHz = 1000;
+ u8 frac_0, frac_1, frac_2, frac_3;
+
+ vco_div = ((i2c_smbus_read_byte_data(priv->client,
+ C5P49_FB_INT_DIV_REG0) & 0xF0) >> 4)
+ + (i2c_smbus_read_byte_data(priv->client,
+ C5P49_FB_INT_DIV_REG1) << 4);
+
+ clk_5p49_power(hw, false);
+
+ vco_clk = INPUT_CLK * vco_div / shift_1kHz;
+ dev_dbg(&priv->client->dev, "vco clock:%d kHz\n", vco_clk);
+
+ vco_clk = (vco_clk / 2);
+ rate = rate / shift_1kHz;
+
+ integ_div = (vco_clk / rate);
+ div = ((vco_clk * shift_1kHz) / rate);
+ frac_div = div - (integ_div * shift_1kHz);
+
+ if (frac_div > 0x3fffffff)
+ return -EINVAL;
+
+ clk_5p49_write(priv, C5P49_DIV_INTEGER_11_4,
+ ((0x0ff0 & (u16)integ_div) >> 4));
+ clk_5p49_write(priv, C5P49_DIV_INTEGER_3_0,
+ ((0x000f & (u16)integ_div) << 4));
+
+ /* spread = 0.01% */
+ frac_div = frac_div - ((div / (100 * 100 / 1)) / 2);
+ frac_div = ((0x1000000 / shift_1kHz) * frac_div);
+ dev_dbg(&priv->client->dev,
+ "integer:0x%x, fraction:0x%x\n",
+ integ_div, frac_div);
+
+ frac_0 = (frac_div & 0x3fc00000) >> 22;
+ frac_1 = (frac_div & 0x003fc000) >> 14;
+ frac_2 = (frac_div & 0x00003fc0) >> 6;
+ frac_3 = (frac_div & 0x0000003f) << 2;
+
+ clk_5p49_write(priv, C5P49_DIV_FRAC_29_22, frac_0);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_21_14, frac_1);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_13_6, frac_2);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_5_0, frac_3);
+
+ clk_5p49_power(hw, true);
+
+ return 0;
+}
+
+static long clk_5p49_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_5p49_priv *priv = hw_to_priv(hw);
+ int ret;
+
+ priv->clk_rate = 0;
+
+ ret = clk_5p49_div_calculation(hw, rate);
+ if (ret < 0)
+ return ret;
+
+ priv->clk_rate = rate;
+
+ return 0;
+}
+
+static u8 clk_5p49_get_parent(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static const struct clk_ops clk_5p49_ops = {
+ .get_parent = clk_5p49_get_parent,
+ .set_rate = clk_5p49_set_rate,
+ .prepare = clk_5p49_enable,
+ .unprepare = clk_5p49_disable,
+ .recalc_rate = clk_5p49_recalc_rate,
+ .round_rate = clk_5p49_round_rate,
+};
+
+static int clk_5p49_clk_register(struct clk_5p49_priv *priv,
+ struct device_node *np)
+{
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *name;
+ static const char *parent_names[REF_CLK];
+ int ret = 0;
+
+ parent_names[0] = __clk_get_name(of_clk_get(np, 0));
+ name = np->name;
+
+ init.name = name;
+ init.ops = &clk_5p49_ops;
+ init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = ARRAY_SIZE(parent_names);
+
+ priv->hw.init = &init;
+
+ clk = clk_register(NULL, &priv->hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (ret < 0) {
+ clk_unregister(clk);
+ return ret;
+ }
+
+ priv->clk_out = clk;
+
+ return 0;
+}
+
+static int clk_5p49_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct clk_5p49_priv *priv = NULL;
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node, *ch_np;
+ int ret, i, ch = 1; /* ch = 0 reserved.*/
+ u32 probe_cnt = 0;
+
+ for (i = ch; i < CLK_MAX; i++) {
+ char name[20];
+
+ sprintf(name, "5p49v5923a_clk%u", i);
+ ch_np = of_get_child_by_name(np, name);
+ if (!ch_np)
+ continue;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = client;
+ priv->index = i + 1;
+ i2c_set_clientdata(client, priv);
+
+ ret = clk_5p49_clk_register(priv, ch_np);
+ if (ret < 0)
+ return ret;
+ probe_cnt++;
+ }
+
+ if (probe_cnt == 0) {
+ dev_err(dev, "Device tree error.\n");
+ return -EINVAL;
+ }
+
+ dev_info(dev, "Rev.0x%x, probed\n",
+ i2c_smbus_read_byte_data(priv->client, 0x01));
+
+ return 0;
+}
+
+static int clk_5p49_remove(struct i2c_client *client)
+{
+ struct clk_5p49_priv *priv = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+
+ of_clk_del_provider(np);
+
+ clk_unregister(priv->clk_out);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int clk_5p49_suspend(struct device *dev)
+{
+ /* Empty function for now */
+ return 0;
+}
+
+static int clk_5p49_resume(struct device *dev)
+{
+ struct clk_5p49_priv *priv = dev_get_drvdata(dev);
+
+ /* Initialize value */
+ clk_5p49_write(priv, C5P49_DIV_INTEGER_11_4, 1);
+ clk_5p49_write(priv, C5P49_DIV_INTEGER_3_0, 0x40);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_29_22, 0);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_21_14, 0xC3);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_13_6, 0x94);
+ clk_5p49_write(priv, C5P49_DIV_FRAC_5_0, 0xDC);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(clk_5p49_pm_ops,
+ clk_5p49_suspend, clk_5p49_resume);
+#define DEV_PM_OPS (&clk_5p49_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct i2c_driver clk_5p49_driver = {
+ .driver = {
+ .name = "5p49v5923a",
+ .pm = DEV_PM_OPS,
+ .of_match_table = clk_5p49_of_match,
+ },
+ .probe = clk_5p49_probe,
+ .remove = clk_5p49_remove,
+ .id_table = clk_5p49_id,
+};
+
+module_i2c_driver(clk_5p49_driver);
+
+MODULE_DESCRIPTION("5p49v5923a programmable clock generator driver");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index e342565e..dc511e2 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "CPUidle arm: " fmt
#include <linux/cpuidle.h>
+#include <linux/cpufeature.h>
#include <linux/cpumask.h>
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
@@ -19,7 +20,9 @@
#include <linux/of.h>
#include <linux/slab.h>
+#include <asm/cpu.h>
#include <asm/cpuidle.h>
+#include <asm/cputype.h>
#include "dt_idle_states.h"
@@ -78,6 +81,28 @@
}
};
+static int __init arm_idle_driver_init(struct cpuidle_driver *drv, int part_id)
+{
+ struct cpumask *cpumask;
+ int cpu;
+
+ cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+ if (!cpumask)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, cpu);
+ u32 midr = cpuinfo->reg_midr;
+
+ if (MIDR_PARTNUM(midr) == part_id)
+ cpumask_set_cpu(cpu, cpumask);
+ }
+
+ drv->cpumask = cpumask;
+
+ return 0;
+}
+
static const struct of_device_id arm_idle_state_match[] __initconst = {
{ .compatible = "arm,idle-state",
.data = arm_enter_idle_state },
@@ -97,6 +122,11 @@
struct cpuidle_driver *drv = &arm_idle_driver;
struct cpuidle_device *dev;
+ /* Support CPUIdle for Cortex-A57 only */
+ ret = arm_idle_driver_init(drv, ARM_CPU_PART_CORTEX_A57);
+ if (ret)
+ return ret;
+
/*
* Initialize idle states data, starting at index 1.
* This driver is DT only, if no DT idle states are detected (ret == 0)
@@ -117,7 +147,7 @@
* Call arch CPU operations in order to initialize
* idle states suspend back-end specific data
*/
- for_each_possible_cpu(cpu) {
+ for_each_cpu(cpu, drv->cpumask) {
ret = arm_cpuidle_init(cpu);
/*
diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index 6e0685f..55584f1 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -40,7 +40,7 @@
endif
config RCAR_DMAC
- tristate "Renesas R-Car Gen2 DMA Controller"
+ tristate "Renesas R-Car Gen2/3 DMA Controller"
depends on ARCH_RENESAS || COMPILE_TEST
select RENESAS_DMA
help
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 03e0458..bb09330 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -485,6 +485,13 @@
psci_0_2_set_functions();
psci_init_migrate();
+ /*
+ * psci_init_migrate() might fail to get needed information due to
+ * incomplete firmware support.
+ * FIXME: Let's assume that Trusted OS services (e.g DDR training)
+ * always run in CPU0.
+ */
+ resident_cpu = 0;
if (PSCI_VERSION_MAJOR(ver) >= 1) {
psci_init_cpu_suspend();
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index dd9f640..1b8d0dc 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -584,6 +584,10 @@
/* Gen3 GPIO is identical to Gen2. */
.data = &gpio_rcar_info_gen2,
}, {
+ .compatible = "renesas,gpio-r8a7796",
+ /* Gen3 GPIO is identical to Gen2. */
+ .data = &gpio_rcar_info_gen2,
+ }, {
.compatible = "renesas,gpio-rcar",
.data = &gpio_rcar_info_gen1,
}, {
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 4a64481..e40f065 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -245,8 +245,13 @@
spin_lock_irqsave(&hdmi->i2c->lock, flags);
- /* Set Fast Mode speed */
- hdmi_writeb(hdmi, 0x0b, HDMI_I2CM_DIV);
+ if (hdmi->dev_type == RCAR_HDMI) {
+ /* Set Standard Mode speed */
+ hdmi_writeb(hdmi, 0x03, HDMI_I2CM_DIV);
+ } else {
+ /* Set Fast Mode speed */
+ hdmi_writeb(hdmi, 0x0b, HDMI_I2CM_DIV);
+ }
/* Software reset */
hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
@@ -1709,6 +1714,14 @@
hdmi->disabled = false;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
+
+ if (hdmi->dev_type == RCAR_HDMI) {
+ hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+ HDMI_PHY_POL0);
+ dw_hdmi_fb_registered(hdmi);
+ dw_hdmi_i2c_init(hdmi);
+ }
+
mutex_unlock(&hdmi->mutex);
}
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index 1f500a1..4537664 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -65,7 +65,10 @@
gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
if (!gem_obj)
return ERR_PTR(-ENOMEM);
+
cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base);
+ if (!cma_obj->dev)
+ cma_obj->dev = drm->dev;
ret = drm_gem_object_init(drm, gem_obj, size);
if (ret)
@@ -109,7 +112,7 @@
if (IS_ERR(cma_obj))
return cma_obj;
- cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr,
+ cma_obj->vaddr = dma_alloc_wc(cma_obj->dev, size, &cma_obj->paddr,
GFP_KERNEL | __GFP_NOWARN);
if (!cma_obj->vaddr) {
dev_err(drm->dev, "failed to allocate buffer with size %zu\n",
@@ -192,7 +195,7 @@
cma_obj = to_drm_gem_cma_obj(gem_obj);
if (cma_obj->vaddr) {
- dma_free_wc(gem_obj->dev->dev, cma_obj->base.size,
+ dma_free_wc(cma_obj->dev, cma_obj->base.size,
cma_obj->vaddr, cma_obj->paddr);
} else if (gem_obj->import_attach) {
drm_prime_gem_destroy(gem_obj, cma_obj->sgt);
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index d7c7ffa..fa4b977 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -23,6 +23,7 @@
config DRM_RCAR_LVDS
bool "R-Car DU LVDS Encoder Support"
depends on DRM_RCAR_DU
+ select GPIOLIB
help
Enable support for the R-Car Display Unit embedded LVDS encoders.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 2ade186..435c81b 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -3,12 +3,12 @@
rcar_du_encoder.o \
rcar_du_group.o \
rcar_du_kms.o \
- rcar_du_lvdscon.o \
rcar_du_plane.o \
rcar_du_vgacon.o
rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmienc.o
-rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o \
+ rcar_du_lvdscon.o
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index b2da9a3..7ece810 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/mutex.h>
+#include <linux/soc/renesas/rcar_prr.h>
#include <drm/drmP.h>
#include <drm/drm_atomic.h>
@@ -108,6 +109,21 @@
clk_disable_unprepare(rcrtc->clock);
}
+void rcar_du_crtc_vbk_check(struct rcar_du_group *rgrp)
+{
+ int i;
+
+ for (i = 0; i < rgrp->dev->num_crtcs; ++i) {
+ struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[i];
+ struct drm_crtc *crtc = &rcrtc->crtc;
+
+ if (!(rcar_du_crtc_read(rcrtc, DIER) & DIER_VBE))
+ continue;
+
+ drm_crtc_wait_one_vblank(crtc);
+ }
+}
+
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
@@ -124,9 +140,14 @@
for (n = 39; n < 120; n++) {
for (m = 0; m < 4; m++) {
for (fdpll = 1; fdpll < 32; fdpll++) {
- /* 1/2 (FRQSEL=1) for duty rate 50% */
- dpllclk = extclk * (n + 1) / (m + 1)
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ /* 1/2 (FRQSEL=1) for duty rate 50% */
+ dpllclk = extclk * (n + 1) / (m + 1)
/ (fdpll + 1) / 2;
+ else
+ dpllclk = extclk * (n + 1) / (m + 1)
+ / (fdpll + 1);
if (dpllclk >= 400000000)
continue;
@@ -167,20 +188,11 @@
u32 div;
u32 dpll_reg = 0;
struct dpll_info *dpll;
- void __iomem *product_reg;
- bool h3_es1_workaround = false;
dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
if (dpll == NULL)
return;
- /* DU2 DPLL Clock Select bit workaround in R-Car H3(ES1.0) */
- product_reg = ioremap(PRODUCT_REG, 0x04);
- if (((readl(product_reg) & PRODUCT_MASK) == PRODUCT_H3_BIT)
- && ((readl(product_reg) & CUT_ES1_MASK) == CUT_ES1))
- h3_es1_workaround = true;
- iounmap(product_reg);
-
/* Compute the clock divisor and select the internal or external dot
* clock based on the requested frequency.
*/
@@ -195,6 +207,7 @@
unsigned long rate;
u32 extdiv;
+ clk_set_rate(rcrtc->extclock, mode_clock);
extclk = clk_get_rate(rcrtc->extclock);
if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
@@ -216,7 +229,11 @@
dev_dbg(rcrtc->group->dev->dev,
"crtc%u: using external clock\n", rcrtc->index);
if (rcdu->info->dpll_ch & (0x01 << rcrtc->index)) {
- escr = ESCR_DCLKSEL_DCLKIN | 0x01;
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ escr = ESCR_DCLKSEL_DCLKIN | 0x01;
+ else
+ escr = ESCR_DCLKSEL_DCLKIN;
dpll_reg = DPLLCR_CODE | DPLLCR_M(dpll->m) |
DPLLCR_FDPLL(dpll->fdpll) |
DPLLCR_CLKE | DPLLCR_N(dpll->n) |
@@ -224,12 +241,13 @@
if (rcrtc->index == DU_CH_1)
dpll_reg |= (DPLLCR_PLCS1 |
- DPLLCR_INCS_DPLL01_DOTCLKIN13);
+ DPLLCR_INCS_DPLL01_DOTCLKIN13);
if (rcrtc->index == DU_CH_2) {
dpll_reg |= (DPLLCR_PLCS0 |
- DPLLCR_INCS_DPLL01_DOTCLKIN02);
- if (h3_es1_workaround)
- dpll_reg |= (0x01 << 21);
+ DPLLCR_INCS_DPLL01_DOTCLKIN02);
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ dpll_reg |= (0x01 << 21);
}
rcar_du_group_write(rcrtc->group, DPLLCR,
@@ -244,8 +262,9 @@
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */
- value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
- | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
+ | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
+ | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
| DSMR_DIPM_DISP | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value);
@@ -267,7 +286,7 @@
mode->crtc_vsync_start - 1);
rcar_du_crtc_write(rcrtc, VCR, mode->crtc_vtotal - 1);
- rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
+ rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start - 1);
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
}
@@ -764,6 +783,12 @@
return ret;
}
+ ret = RCAR_PRR_INIT();
+ if (ret) {
+ dev_dbg(rcdu->dev, "product register init fail.\n");
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index b52e9be..3476515 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -87,5 +87,6 @@
enum rcar_du_output output);
int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_vbk_check(struct rcar_du_group *rgrp);
#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index ea06b44..6290193 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -23,10 +23,15 @@
#include <linux/wait.h>
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder_slave.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
+#include <drm/rcar_du_drm.h>
+
+#include <media/vsp1.h>
#include "rcar_du_crtc.h"
#include "rcar_du_encoder.h"
@@ -35,6 +40,7 @@
#include "rcar_du_kms.h"
#include "rcar_du_lvdsenc.h"
#include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
/* -----------------------------------------------------------------------------
* Device Information
@@ -144,7 +150,8 @@
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE
- | RCAR_DU_FEATURE_GEN3_REGS,
+ | RCAR_DU_FEATURE_GEN3_REGS
+ | RCAR_DU_FEATURE_DIDSR2_REG,
.num_crtcs = 4,
.routes = {
/* R8A7795 has one RGB output, two HDMI outputs and one
@@ -176,6 +183,38 @@
.vsp_num = 5,
};
+static const struct rcar_du_device_info rcar_du_r8a7796_info = {
+ .gen = 3,
+ .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
+ | RCAR_DU_FEATURE_EXT_CTRL_REGS
+ | RCAR_DU_FEATURE_VSP1_SOURCE
+ | RCAR_DU_FEATURE_GEN3_REGS,
+ .num_crtcs = 3,
+ .routes = {
+ /* R8A7796 has one RGB output, one LVDS output and one
+ * HDMI outputs.
+ */
+ [RCAR_DU_OUTPUT_DPAD0] = {
+ .possible_crtcs = BIT(2),
+ .encoder_type = DRM_MODE_ENCODER_NONE,
+ .port = 0,
+ },
+ [RCAR_DU_OUTPUT_HDMI0] = {
+ .possible_crtcs = BIT(1),
+ .encoder_type = RCAR_DU_ENCODER_HDMI,
+ .port = 1,
+ },
+ [RCAR_DU_OUTPUT_LVDS0] = {
+ .possible_crtcs = BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_LVDS,
+ .port = 2,
+ },
+ },
+ .num_lvds = 1,
+ .dpll_ch = BIT(1),
+ .vsp_num = 5,
+};
+
static const struct of_device_id rcar_du_of_table[] = {
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
@@ -183,6 +222,7 @@
{ .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
+ { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
{ }
};
@@ -445,6 +485,13 @@
rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], false);
}
+static const struct drm_ioctl_desc rcar_du_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(DRM_RCAR_DU_SET_VMUTE, rcar_du_set_vmute,
+ DRM_UNLOCKED | DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF_DRV(DRM_RCAR_DU_SCRSHOT, rcar_du_vsp_write_back,
+ DRM_UNLOCKED | DRM_CONTROL_ALLOW),
+};
+
static const struct file_operations rcar_du_fops = {
.owner = THIS_MODULE,
.open = drm_open,
@@ -466,6 +513,7 @@
.get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank,
+ .gem_create_object = rcar_du_create_object,
.gem_free_object = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
@@ -486,6 +534,8 @@
.date = "20130110",
.major = 1,
.minor = 0,
+ .ioctls = rcar_du_ioctls,
+ .num_ioctls = ARRAY_SIZE(rcar_du_ioctls),
};
/* -----------------------------------------------------------------------------
@@ -555,14 +605,6 @@
}
#endif
-#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
- list_for_each_entry(encoder,
- &rcdu->ddev->mode_config.encoder_list, head) {
- if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
- rcar_du_hdmienc_enable(encoder);
- }
-#endif
-
#ifdef CONFIG_RCAR_DDR_BACKUP
ret = rcar_du_restore_regs(rcdu);
#endif /* CONFIG_RCAR_DDR_BACKUP */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index 6413b7e..267488f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -33,6 +33,7 @@
#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */
#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */
#define RCAR_DU_FEATURE_GEN3_REGS (1 << 3) /* Use Gen3 registers */
+#define RCAR_DU_FEATURE_DIDSR2_REG (1 << 4) /* Has DIDSR2 register */
#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index 55dd3bd..f48fc64 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -1,7 +1,7 @@
/*
* rcar_du_group.c -- R-Car Display Unit Channels Pair
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -101,11 +101,6 @@
rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
rcar_du_group_setup_pins(rgrp);
- if (rcdu->info->gen == 3) {
- rcar_du_group_write(rgrp, DEFR6, DEFR6_CODE |
- DEFR6_ODPM22_DISP);
- rcar_du_group_write(rgrp, DEFR10, DEFR10_CODE | DEFR10_DEFE10);
- }
if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
rcar_du_group_setup_defr8(rgrp);
@@ -113,13 +108,30 @@
/* Configure input dot clock routing. We currently hardcode the
* configuration to routing DOTCLKINn to DUn.
*/
- rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
+ if (rcdu->info->gen < 3) {
+ rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
DIDSR_LCDS_DCLKIN(2) |
DIDSR_LCDS_DCLKIN(1) |
DIDSR_LCDS_DCLKIN(0) |
DIDSR_PDCS_CLK(2, 0) |
DIDSR_PDCS_CLK(1, 0) |
DIDSR_PDCS_CLK(0, 0));
+ } else {
+ if (rgrp->index == 0) {
+ rcar_du_group_write(rgrp,
+ DIDSR, DIDSR_CODE |
+ DIDSR_LCDS0_DCLKIN |
+ DIDSR_PDCS_CLK(1, 0) |
+ DIDSR_PDCS_CLK(0, 0));
+ } else if ((rgrp->index == 1) &&
+ rcar_du_has(rgrp->dev,
+ RCAR_DU_FEATURE_DIDSR2_REG)) {
+ rcar_du_group_write(rgrp,
+ DIDSR, DIDSR_CODE |
+ DIDSR_PDCS_CLK(1, 0) |
+ DIDSR_PDCS_CLK(0, 0));
+ }
+ }
}
if (rcdu->info->gen >= 3)
@@ -171,6 +183,15 @@
static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
{
+ if (!start) {
+ rcar_du_group_write(rgrp, DSYSR,
+ (rcar_du_group_read(rgrp, DSYSR) &
+ ~(DSYSR_DRES | DSYSR_DEN)));
+
+ /* Wait for access stop of vsp */
+ rcar_du_crtc_vbk_check(rgrp);
+ }
+
rcar_du_group_write(rgrp, DSYSR,
(rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
(start ? DSYSR_DEN : DSYSR_DRES));
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
index b03973c..7bda47a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c
@@ -719,6 +719,8 @@
temp = readl(smstpcr);
writel(temp & ~(0x3 << 28), smstpcr);
+ rcar_du_hdmienc_enable(encoder);
+
ret = handle_registers(ip, DO_RESTORE);
if (ret)
pr_err("%s: Failed to restore %s register\n",
@@ -731,8 +733,8 @@
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
const struct drm_bridge_funcs *bfuncs = encoder->bridge->funcs;
- if ((bfuncs) && (bfuncs->post_disable))
- bfuncs->post_disable(encoder->bridge);
+ if ((bfuncs) && (bfuncs->disable))
+ bfuncs->disable(encoder->bridge);
if (hdmienc->renc->lvds)
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
@@ -898,6 +900,16 @@
rcar_du_hdmienc_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
+ if ((mode->hdisplay > 3840) || (mode->vdisplay > 2160))
+ return MODE_BAD;
+
+ if (((mode->hdisplay == 3840) && (mode->vdisplay == 2160))
+ && (mode->vrefresh > 30))
+ return MODE_BAD;
+
+ if (mode->clock > 297000)
+ return MODE_BAD;
+
return MODE_OK;
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index d6d9acb..6c24464 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -108,27 +108,35 @@
{
.fourcc = DRM_FORMAT_RGB332,
.bpp = 8,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_ARGB4444,
.bpp = 16,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_XRGB4444,
.bpp = 16,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_BGR888,
.bpp = 24,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_RGB888,
.bpp = 24,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_BGRA8888,
.bpp = 32,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_BGRX8888,
.bpp = 32,
+ .planes = 1,
}, {
.fourcc = DRM_FORMAT_YVYU,
.bpp = 16,
+ .planes = 1,
},
/* The following formats are not supported on Gen2 and thus have no
* associated .pnmr or .edf settings.
@@ -192,6 +200,35 @@
* Frame buffer
*/
+struct drm_gem_object *rcar_du_create_object(struct drm_device *dev,
+ size_t size)
+{
+ struct rcar_du_device *rcdu = dev->dev_private;
+ struct drm_gem_cma_object *cma_obj;
+ unsigned int i;
+
+ cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+ if (!cma_obj)
+ return ERR_PTR(-ENOMEM);
+
+ /* Use struct device from any FCP in case of VSP1_SOURCE. */
+ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+ for (i = 0; i < rcdu->num_crtcs; ++i) {
+ struct rcar_du_vsp *vsp = &rcdu->vsps[i];
+
+ if (vsp->fcp) {
+ cma_obj->dev = vsp->fcp;
+ break;
+ }
+ }
+
+ if (!cma_obj->dev)
+ dev_warn(rcdu->dev, "unable to locate FCP\n");
+ }
+
+ return &cma_obj->base;
+}
+
int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index 10eb51a..d3b27dc 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -34,6 +34,8 @@
int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+struct drm_gem_object *rcar_du_create_object(struct drm_device *dev,
+ size_t size);
int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
index d4881ee..07f9e3e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
@@ -1,7 +1,7 @@
/*
* rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -17,8 +17,17 @@
struct rcar_du_device;
struct rcar_du_encoder;
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc,
struct device_node *np);
+#else
+static inline int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
+ struct rcar_du_encoder *renc,
+ struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
index eb40f3e..36c532f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -31,6 +32,7 @@
bool enabled;
enum rcar_lvds_input input;
+ int gpio_pd;
};
static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
@@ -107,6 +109,13 @@
/* Turn the PLL on, set it to LVDS normal mode, wait for the startup
* delay and turn the output on.
*/
+
+ /* Turn all the channels on. */
+ rcar_lvds_write(lvds, LVDCR1,
+ LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
+ LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
+ LVDCR1_CLKSTBY_GEN3);
+
lvdcr0 = LVDCR0_PLLON;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
@@ -117,12 +126,6 @@
lvdcr0 |= LVDCR0_LVRES;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-
- /* Turn all the channels on. */
- rcar_lvds_write(lvds, LVDCR1,
- LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
- LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
- LVDCR1_CLKSTBY_GEN3);
}
int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
@@ -134,6 +137,9 @@
if (lvds->enabled)
return 0;
+ if (gpio_is_valid(lvds->gpio_pd))
+ gpio_set_value(lvds->gpio_pd, 1);
+
ret = clk_prepare_enable(lvds->clock);
if (ret < 0)
return ret;
@@ -182,6 +188,9 @@
lvds->enabled = false;
+ if (gpio_is_valid(lvds->gpio_pd))
+ gpio_set_value(lvds->gpio_pd, 0);
+
return 0;
}
@@ -260,6 +269,7 @@
struct rcar_du_lvdsenc *lvds;
unsigned int i;
int ret;
+ char name[16];
for (i = 0; i < rcdu->info->num_lvds; ++i) {
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
@@ -272,12 +282,20 @@
lvds->index = i;
lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
lvds->enabled = false;
+ /* Get optional backlight GPIO */
+ lvds->gpio_pd = of_get_named_gpio(rcdu->dev->of_node,
+ "backlight", 0);
ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
if (ret < 0)
return ret;
rcdu->lvds[i] = lvds;
+
+ sprintf(name, "lvds%u", i);
+ if (gpio_is_valid(lvds->gpio_pd))
+ devm_gpio_request_one(&pdev->dev, lvds->gpio_pd,
+ GPIOF_OUT_INIT_LOW, name);
}
return 0;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index 7a34bf3..63dad00 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -255,6 +255,8 @@
#define DIDSR 0x20028
#define DIDSR_CODE (0x7790 << 16)
+#define DIDSR_LCDS0_DCLKIN (0 << 9)
+#define DIDSR_LCDS0_LVDSIF (1 << 9)
#define DIDSR_LCDS_DCLKIN(n) (0 << (8 + (n) * 2))
#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2))
#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2))
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 9d7e5c9..0d63275 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -1,7 +1,7 @@
/*
* rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -23,7 +23,7 @@
static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
{
- return 0;
+ return drm_add_modes_noedid(connector, 1024, 768);
}
static const struct drm_connector_helper_funcs connector_helper_funcs = {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 8b55e8d..95269b1 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -1,7 +1,7 @@
/*
- * rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor
+ * rcar_du_vsp.c -- R-Car Display Unit VSP-Based Compositor
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -12,12 +12,14 @@
*/
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
+#include <drm/rcar_du_drm.h>
#include <linux/soc/renesas/s2ram_ddr_backup.h>
#include <linux/of_platform.h>
@@ -932,6 +934,12 @@
};
unsigned int i;
+ if (plane->plane.state->crtc->mode.flags
+ & DRM_MODE_FLAG_INTERLACE)
+ cfg.interlaced = true;
+ else
+ cfg.interlaced = false;
+
cfg.src.left = state->state.src_x >> 16;
cfg.src.top = state->state.src_y >> 16;
cfg.src.width = state->state.src_w >> 16;
@@ -965,6 +973,7 @@
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
struct rcar_du_device *rcdu = rplane->vsp->dev;
+ int hdisplay, vdisplay;
if (!state->fb || !state->crtc) {
rstate->format = NULL;
@@ -977,6 +986,19 @@
return -EINVAL;
}
+ hdisplay = state->crtc->mode.hdisplay;
+ vdisplay = state->crtc->mode.vdisplay;
+
+ if ((state->plane->type == DRM_PLANE_TYPE_OVERLAY) &&
+ (((state->crtc_w + state->crtc_x) > hdisplay) ||
+ ((state->crtc_h + state->crtc_y) > vdisplay))) {
+ dev_err(rcdu->dev,
+ "%s: specify (%dx%d) + (%d, %d) < (%dx%d).\n",
+ __func__, state->crtc_w, state->crtc_h, state->crtc_x,
+ state->crtc_y, hdisplay, vdisplay);
+ return -EINVAL;
+ }
+
rstate->format = rcar_du_format_info(state->fb->pixel_format);
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) &&
@@ -1089,6 +1111,162 @@
return 0;
}
+static int rcar_du_async_commit(struct drm_device *dev, u32 crtc_id)
+{
+ int ret, index;
+ struct drm_atomic_state *state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ obj = drm_mode_object_find(dev, crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj)
+ return -EINVAL;
+
+ crtc = obj_to_crtc(obj);
+
+ index = drm_crtc_index(crtc);
+
+ crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc);
+ if (!crtc_state)
+ return -ENOMEM;
+
+ state->crtc_states[index] = crtc_state;
+ state->crtcs[index] = crtc;
+ crtc_state->state = state;
+
+ ret = drm_atomic_commit(state);
+ if (ret != 0) {
+ drm_atomic_helper_crtc_destroy_state(crtc, crtc_state);
+ return ret;
+ }
+
+ return 0;
+}
+
+int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret;
+ struct rcar_du_screen_shot *sh = (struct rcar_du_screen_shot *)data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ struct rcar_du_crtc *rcrtc;
+ struct rcar_du_device *rcdu;
+ const struct drm_display_mode *mode;
+ u32 pixelformat, bpp;
+ unsigned int pitch;
+ dma_addr_t mem[3];
+
+ obj = drm_mode_object_find(dev, sh->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj)
+ return -EINVAL;
+
+ crtc = obj_to_crtc(obj);
+ rcrtc = to_rcar_crtc(crtc);
+ rcdu = rcrtc->group->dev;
+ mode = &rcrtc->crtc.state->adjusted_mode;
+
+ switch (sh->fmt) {
+ case DRM_FORMAT_RGB565:
+ bpp = 16;
+ pixelformat = V4L2_PIX_FMT_RGB565;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ bpp = 16;
+ pixelformat = V4L2_PIX_FMT_ARGB555;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ bpp = 32;
+ pixelformat = V4L2_PIX_FMT_ABGR32;
+ break;
+ default:
+ dev_err(rcdu->dev, "specified format is not supported.\n");
+ return -EINVAL;
+ }
+
+ pitch = mode->hdisplay * bpp / 8;
+
+ mem[0] = sh->buff;
+ mem[1] = 0;
+ mem[2] = 0;
+
+ if ((sh->width != (mode->hdisplay)) ||
+ (sh->height != (mode->vdisplay)))
+ return -EINVAL;
+
+ if ((pitch * mode->vdisplay) > sh->buff_len)
+ return -EINVAL;
+
+ vsp1_du_setup_wb(rcrtc->vsp->vsp, pixelformat, pitch, mem);
+ vsp1_du_wait_wb(rcrtc->vsp->vsp, 2);
+
+ ret = rcar_du_async_commit(dev, sh->crtc_id);
+ if (ret != 0)
+ return ret;
+
+ vsp1_du_wait_wb(rcrtc->vsp->vsp, 1);
+
+ ret = rcar_du_async_commit(dev, sh->crtc_id);
+ if (ret != 0)
+ return ret;
+
+ vsp1_du_wait_wb(rcrtc->vsp->vsp, 0);
+
+ return ret;
+}
+
+int rcar_du_set_vmute(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct rcar_du_vmute *vmute =
+ (struct rcar_du_vmute *)data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ struct rcar_du_crtc *rcrtc;
+ struct drm_atomic_state *state;
+ struct drm_crtc_state *crtc_state;
+ int ret = 0, index;
+
+ dev_dbg(dev->dev, "CRTC[%d], display:%s\n",
+ vmute->crtc_id, vmute->on ? "off":"on");
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ obj = drm_mode_object_find(dev, vmute->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj)
+ return -EINVAL;
+ crtc = obj_to_crtc(obj);
+
+ index = drm_crtc_index(crtc);
+
+ rcrtc = to_rcar_crtc(crtc);
+ crtc_state = drm_atomic_helper_crtc_duplicate_state(crtc);
+ if (!crtc_state)
+ return -ENOMEM;
+
+ state->crtc_states[index] = crtc_state;
+ state->crtcs[index] = crtc;
+ crtc_state->state = state;
+
+ crtc_state->active = true;
+
+ vsp1_du_if_set_mute(rcrtc->vsp->vsp, vmute->on);
+
+ ret = drm_atomic_commit(state);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
@@ -1145,6 +1323,15 @@
vsp->vsp = &pdev->dev;
+ /* Locate FCP device behind the VSP for dma_alloc/free. */
+ np = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
+ if (np) {
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (pdev)
+ vsp->fcp = &pdev->dev;
+ }
+
ret = vsp1_du_init(vsp->vsp);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
index 30b8f8f..8147fdd 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
@@ -29,6 +29,7 @@
struct rcar_du_vsp {
unsigned int index;
struct device *vsp;
+ struct device *fcp;
struct rcar_du_device *dev;
struct rcar_du_vsp_plane *planes;
unsigned int num_planes;
@@ -67,6 +68,10 @@
void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
+int rcar_du_set_vmute(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
#ifdef CONFIG_RCAR_DDR_BACKUP
int rcar_du_vsp_save_regs(struct rcar_du_crtc *crtc);
int rcar_du_vsp_restore_regs(struct rcar_du_crtc *crtc);
@@ -77,6 +82,10 @@
static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
+static inline int rcar_du_set_vmute(struct drm_device *dev, void *data,
+ struct drm_file *file_priv) { return 0; };
+static inline int rcar_du_vsp_write_back(struct drm_device *dev, void *data,
+ struct drm_file *file_priv) { return 0; };
#ifdef CONFIG_RCAR_DDR_BACKUP
static inline int rcar_du_vsp_save_regs(struct rcar_du_crtc *crtc)
{ return 0; };
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2dd40dd..8ffb0d7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -985,6 +985,7 @@
config I2C_RCAR
tristate "Renesas R-Car I2C Controller"
+ depends on HAS_DMA
depends on ARCH_RENESAS || COMPILE_TEST
select I2C_SLAVE
help
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index eeb8786..54c1722 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -2,7 +2,7 @@
* Driver for the Renesas RCar I2C unit
*
* Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
- * Copyright (C) 2011-2015 Renesas Electronics Corporation
+ * Copyright (C) 2011-2016 Renesas Electronics Corporation
*
* Copyright (C) 2012-14 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
@@ -21,6 +21,8 @@
*/
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -44,6 +46,8 @@
#define ICSAR 0x1C /* slave address */
#define ICMAR 0x20 /* master address */
#define ICRXTX 0x24 /* data port */
+#define ICDMAER 0x3c /* DMA enable */
+#define ICFBSCR 0x38 /* first bit setup cycle */
/* ICSCR */
#define SDBS (1 << 3) /* slave data buffer select */
@@ -79,6 +83,16 @@
#define MDR (1 << 1)
#define MAT (1 << 0) /* slave addr xfer done */
+/* ICDMAER */
+#define RSDMAE (1 << 3) /* DMA Slave Received Enable */
+#define TSDMAE (1 << 2) /* DMA Slave Transmitted Enable */
+#define RMDMAE (1 << 1) /* DMA Master Received Enable */
+#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */
+
+/* ICFBSCR */
+#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */
+#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
+
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
@@ -121,6 +135,12 @@
u32 flags;
enum rcar_i2c_type devtype;
struct i2c_client *slave;
+
+ struct resource *res;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ struct scatterlist sg;
+ enum dma_data_direction dma_direction;
};
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
@@ -522,6 +542,118 @@
/*
* interrupt functions
*/
+static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
+{
+ struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
+ ? priv->dma_rx : priv->dma_tx;
+
+ /* Disable DMA Master Received/Transmitted */
+ rcar_i2c_write(priv, ICDMAER, 0);
+
+ /* Reset default delay */
+ rcar_i2c_write(priv, ICFBSCR, TCYC06);
+
+ dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
+ priv->msg->len, priv->dma_direction);
+
+ priv->dma_direction = DMA_NONE;
+}
+
+static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
+{
+ if (priv->dma_direction == DMA_NONE)
+ return;
+ else if (priv->dma_direction == DMA_FROM_DEVICE)
+ dmaengine_terminate_all(priv->dma_rx);
+ else if (priv->dma_direction == DMA_TO_DEVICE)
+ dmaengine_terminate_all(priv->dma_tx);
+
+ rcar_i2c_dma_unmap(priv);
+}
+
+static void rcar_i2c_dma_callback(void *data)
+{
+ struct rcar_i2c_priv *priv = data;
+
+ priv->pos += sg_dma_len(&priv->sg);
+
+ rcar_i2c_dma_unmap(priv);
+}
+
+static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
+{
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ struct i2c_msg *msg = priv->msg;
+ bool read = msg->flags & I2C_M_RD;
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ struct dma_chan *chan = read ? priv->dma_rx : priv->dma_tx;
+ struct dma_async_tx_descriptor *txdesc;
+ dma_addr_t dma_addr;
+ dma_cookie_t cookie;
+ unsigned char *buf;
+ int len;
+
+ /* Do not use DMA if it's not available or for messages < 8 bytes */
+ if (IS_ERR(chan) || msg->len < 8)
+ return;
+
+ if (read) {
+ /*
+ * The last two bytes needs to be fetched using PIO in
+ * order for the STOP phase to work.
+ */
+ buf = priv->msg->buf;
+ len = priv->msg->len - 2;
+ } else {
+ /*
+ * First byte in message was sent using PIO.
+ */
+ buf = priv->msg->buf + 1;
+ len = priv->msg->len - 1;
+ }
+
+ dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_dbg(dev, "dma map failed, using PIO\n");
+ return;
+ }
+
+ sg_dma_len(&priv->sg) = len;
+ sg_dma_address(&priv->sg) = dma_addr;
+
+ priv->dma_direction = dir;
+
+ txdesc = dmaengine_prep_slave_sg(chan, &priv->sg, 1,
+ read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc) {
+ dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
+ rcar_i2c_cleanup_dma(priv);
+ return;
+ }
+
+ txdesc->callback = rcar_i2c_dma_callback;
+ txdesc->callback_param = priv;
+
+ cookie = dmaengine_submit(txdesc);
+ if (dma_submit_error(cookie)) {
+ dev_dbg(dev, "submitting dma failed, using PIO\n");
+ rcar_i2c_cleanup_dma(priv);
+ return;
+ }
+
+ /* Set delay for DMA operations */
+ rcar_i2c_write(priv, ICFBSCR, TCYC17);
+
+ /* Enable DMA Master Received/Transmitted */
+ if (read)
+ rcar_i2c_write(priv, ICDMAER, RMDMAE);
+ else
+ rcar_i2c_write(priv, ICDMAER, TMDMAE);
+
+ dma_async_issue_pending(chan);
+}
+
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
@@ -541,6 +673,12 @@
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
priv->pos++;
+ /*
+ * Try to use DMA to transmit the rest of the data if
+ * address transfer pashe just finished.
+ */
+ if (msr & MAT)
+ rcar_i2c_dma(priv);
} else {
/*
* The last data was pushed to ICRXTX on _PREV_ empty irq.
@@ -575,7 +713,11 @@
return;
if (msr & MAT) {
- /* Address transfer phase finished, but no data at this point. */
+ /*
+ * Address transfer phase finished, but no data at this point.
+ * Try to use DMA to receive data.
+ */
+ rcar_i2c_dma(priv);
} else if (priv->pos < msg->len) {
/* get received data */
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
@@ -707,6 +849,81 @@
return IRQ_HANDLED;
}
+static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
+ enum dma_transfer_direction dir,
+ dma_addr_t port_addr)
+{
+ struct dma_chan *chan;
+ struct dma_slave_config cfg;
+ char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
+ int ret;
+
+ chan = dma_request_chan(dev, chan_name);
+ if (IS_ERR(chan)) {
+ ret = PTR_ERR(chan);
+ dev_dbg(dev, "request_channel failed for %s (%d)\n",
+ chan_name, ret);
+ return chan;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.direction = dir;
+ if (dir == DMA_MEM_TO_DEV) {
+ cfg.dst_addr = port_addr;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else {
+ cfg.src_addr = port_addr;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ }
+
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret) {
+ dev_dbg(dev, "slave_config failed for %s (%d)\n",
+ chan_name, ret);
+ dma_release_channel(chan);
+ return ERR_PTR(ret);
+ }
+
+ dev_dbg(dev, "got DMA channel for %s\n", chan_name);
+ return chan;
+}
+
+static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv,
+ struct i2c_msg *msg)
+{
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ bool read;
+ struct dma_chan *chan;
+ enum dma_transfer_direction dir;
+
+ read = msg->flags & I2C_M_RD;
+
+ chan = read ? priv->dma_rx : priv->dma_tx;
+ if (PTR_ERR(chan) != -EPROBE_DEFER)
+ return;
+
+ dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+ chan = rcar_i2c_request_dma_chan(dev, dir, priv->res->start + ICRXTX);
+
+ if (read)
+ priv->dma_rx = chan;
+ else
+ priv->dma_tx = chan;
+}
+
+static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
+{
+ if (!IS_ERR(priv->dma_tx)) {
+ dma_release_channel(priv->dma_tx);
+ priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
+ }
+
+ if (!IS_ERR(priv->dma_rx)) {
+ dma_release_channel(priv->dma_rx);
+ priv->dma_rx = ERR_PTR(-EPROBE_DEFER);
+ }
+}
+
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num)
@@ -728,6 +945,7 @@
ret = -EOPNOTSUPP;
goto out;
}
+ rcar_i2c_request_dma(priv, msgs + i);
}
/* init first message */
@@ -739,6 +957,7 @@
time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
num * adap->timeout);
if (!time_left) {
+ rcar_i2c_cleanup_dma(priv);
rcar_i2c_init(priv);
ret = -ETIMEDOUT;
} else if (priv->flags & ID_NACK) {
@@ -818,6 +1037,7 @@
{ .compatible = "renesas,i2c-r8a7793", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 },
+ { .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 },
{},
};
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
@@ -826,7 +1046,6 @@
{
struct rcar_i2c_priv *priv;
struct i2c_adapter *adap;
- struct resource *res;
struct device *dev = &pdev->dev;
struct i2c_timings i2c_t;
int irq, ret;
@@ -841,8 +1060,9 @@
return PTR_ERR(priv->clk);
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->io = devm_ioremap_resource(dev, res);
+ priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ priv->io = devm_ioremap_resource(dev, priv->res);
if (IS_ERR(priv->io))
return PTR_ERR(priv->io);
@@ -861,6 +1081,11 @@
i2c_parse_fw_timings(dev, &i2c_t, false);
+ /* Init DMA */
+ sg_init_table(&priv->sg, 1);
+ priv->dma_direction = DMA_NONE;
+ priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
+
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
ret = rcar_i2c_clock_calculate(priv, &i2c_t);
@@ -908,6 +1133,7 @@
struct device *dev = &pdev->dev;
i2c_del_adapter(&priv->adap);
+ rcar_i2c_release_dma(priv);
if (priv->flags & ID_P_PM_BLOCKED)
pm_runtime_put(dev);
pm_runtime_disable(dev);
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index ad08603..295445f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -273,7 +273,6 @@
config IPMMU_VMSA
bool "Renesas VMSA-compatible IPMMU"
- depends on ARM_LPAE
depends on ARCH_RENESAS || COMPILE_TEST
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 993dc50..19acc7d 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -204,6 +204,18 @@
To compile this driver as a module, choose M here: the
module will be called adv7183.
+config VIDEO_ADV7482
+ tristate "Analog Devices ADV7482 HDMI receiver and video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7482 HDMI receiver.
+ Support for the Analog Devices ADV7482 video decoder.
+ Video capture data is the HD digital signal that is
+ received by the Analog Devices ADV7482 HDMI receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7482.
+
config VIDEO_ADV7604
tristate "Analog Devices ADV7604 decoder"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 94f2c99..a39ee28 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -25,6 +25,7 @@
obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
+obj-$(CONFIG_VIDEO_ADV7482) += adv7482.o
obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o
obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o
obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
diff --git a/drivers/media/i2c/adv7482.c b/drivers/media/i2c/adv7482.c
new file mode 100644
index 0000000..0cca208
--- /dev/null
+++ b/drivers/media/i2c/adv7482.c
@@ -0,0 +1,2412 @@
+/*
+ * drivers/media/i2c/adv7482.c
+ * This file is Analog Devices ADV7482 HDMI receiver driver.
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-of.h>
+#include <linux/mutex.h>
+#include <media/soc_camera.h>
+
+#include <linux/delay.h> /* for msleep() */
+
+#define DRIVER_NAME "adv7482"
+
+/****************************************/
+/* ADV7482 I2C slave address definition */
+/****************************************/
+#define ADV7482_I2C_IO 0x70 /* IO Map */
+#define ADV7482_I2C_DPLL 0x26 /* DPLL Map */
+#define ADV7482_I2C_CP 0x22 /* CP Map */
+#define ADV7482_I2C_HDMI 0x34 /* HDMI Map */
+#define ADV7482_I2C_EDID 0x36 /* EDID Map */
+#define ADV7482_I2C_REPEATER 0x32 /* HDMI RX Repeater Map */
+#define ADV7482_I2C_INFOFRAME 0x31 /* HDMI RX InfoFrame Map */
+#define ADV7482_I2C_CEC 0x41 /* CEC Map */
+#define ADV7482_I2C_SDP 0x79 /* SDP Map */
+#define ADV7482_I2C_TXB 0x48 /* CSI-TXB Map */
+#define ADV7482_I2C_TXA 0x4A /* CSI-TXA Map */
+#define ADV7482_I2C_WAIT 0xFE /* Wait x mesec */
+#define ADV7482_I2C_EOR 0xFF /* End Mark */
+
+/****************************************/
+/* ADV7482 IO register definition */
+/****************************************/
+
+/* Revision */
+#define ADV7482_IO_RD_INFO1_REG 0xDF /* chip version register */
+#define ADV7482_IO_RD_INFO2_REG 0xE0 /* chip version register */
+
+#define ADV7482_IO_CP_DATAPATH_REG 0x03 /* datapath ctrl */
+#define ADV7482_IO_CP_COLORSPACE_REG 0x04
+#define ADV7482_IO_CP_VID_STD_REG 0x05 /* Video Standard */
+
+/* Power Management */
+#define ADV7482_IO_PWR_MAN_REG 0x0C /* Power management register */
+#define ADV7482_IO_PWR_ON 0xE0 /* Power on */
+#define ADV7482_IO_PWR_OFF 0x00 /* Power down */
+
+#define ADV7482_HDMI_DDC_PWRDN 0x73 /* Power DDC pads control register */
+#define ADV7482_HDMI_DDC_PWR_ON 0x00 /* Power on */
+#define ADV7482_HDMI_DDC_PWR_OFF 0x01 /* Power down */
+
+#define ADV7482_IO_CP_VID_STD_480I 0x40
+#define ADV7482_IO_CP_VID_STD_576I 0x41
+#define ADV7482_IO_CP_VID_STD_480P 0x4A
+#define ADV7482_IO_CP_VID_STD_576P 0x4B
+#define ADV7482_IO_CP_VID_STD_720P 0x53
+#define ADV7482_IO_CP_VID_STD_1080I 0x54
+#define ADV7482_IO_CP_VID_STD_1080P 0x5E
+#define ADV7482_IO_CP_VID_STD_SVGA56 0x80
+#define ADV7482_IO_CP_VID_STD_SVGA60 0x81
+#define ADV7482_IO_CP_VID_STD_SVGA72 0x82
+#define ADV7482_IO_CP_VID_STD_SVGA75 0x83
+#define ADV7482_IO_CP_VID_STD_SVGA85 0x84
+#define ADV7482_IO_CP_VID_STD_SXGA60 0x85
+#define ADV7482_IO_CP_VID_STD_SXGA75 0x86
+#define ADV7482_IO_CP_VID_STD_VGA60 0x88
+#define ADV7482_IO_CP_VID_STD_VGA72 0x89
+#define ADV7482_IO_CP_VID_STD_VGA75 0x8A
+#define ADV7482_IO_CP_VID_STD_VGA85 0x8B
+#define ADV7482_IO_CP_VID_STD_XGA60 0x8C
+#define ADV7482_IO_CP_VID_STD_XGA70 0x8D
+#define ADV7482_IO_CP_VID_STD_XGA75 0x8E
+#define ADV7482_IO_CP_VID_STD_XGA85 0x8F
+#define ADV7482_IO_CP_VID_STD_UXGA60 0x96
+
+#define ADV7482_IO_CSI4_EN_ENABLE 0x80
+#define ADV7482_IO_CSI4_EN_DISABLE 0x00
+
+#define ADV7482_IO_CSI1_EN_ENABLE 0x40
+#define ADV7482_IO_CSI1_EN_DISABLE 0x00
+
+/****************************************/
+/* ADV7482 CP register definition */
+/****************************************/
+
+/* Contrast Control */
+#define ADV7482_CP_CON_REG 0x3a /* Contrast (unsigned) */
+#define ADV7482_CP_CON_MIN 0 /* Minimum contrast */
+#define ADV7482_CP_CON_DEF 128 /* Default */
+#define ADV7482_CP_CON_MAX 255 /* Maximum contrast */
+
+/* Saturation Control */
+#define ADV7482_CP_SAT_REG 0x3b /* Saturation (unsigned) */
+#define ADV7482_CP_SAT_MIN 0 /* Minimum saturation */
+#define ADV7482_CP_SAT_DEF 128 /* Default */
+#define ADV7482_CP_SAT_MAX 255 /* Maximum saturation */
+
+/* Brightness Control */
+#define ADV7482_CP_BRI_REG 0x3c /* Brightness (signed) */
+#define ADV7482_CP_BRI_MIN -128 /* Luma is -512d */
+#define ADV7482_CP_BRI_DEF 0 /* Luma is 0 */
+#define ADV7482_CP_BRI_MAX 127 /* Luma is 508d */
+
+/* Hue Control */
+#define ADV7482_CP_HUE_REG 0x3d /* Hue (unsigned) */
+#define ADV7482_CP_HUE_MIN 0 /* -90 degree */
+#define ADV7482_CP_HUE_DEF 0 /* -90 degree */
+#define ADV7482_CP_HUE_MAX 255 /* +90 degree */
+
+/* Video adjustment register */
+#define ADV7482_CP_VID_ADJ_REG 0x3e
+/* Video adjustment mask */
+#define ADV7482_CP_VID_ADJ_MASK 0x7F
+/* Enable color controls */
+#define ADV7482_CP_VID_ADJ_ENABLE 0x80
+
+/****************************************/
+/* ADV7482 HDMI register definition */
+/****************************************/
+
+/* HDMI status register */
+#define ADV7482_HDMI_STATUS1_REG 0x07
+/* VERT_FILTER_LOCKED flag */
+#define ADV7482_HDMI_VF_LOCKED_FLG 0x80
+/* DE_REGEN_FILTER_LOCKED flag */
+#define ADV7482_HDMI_DERF_LOCKED_FLG 0x20
+/* LINE_WIDTH[12:8] mask */
+#define ADV7482_HDMI_LWIDTH_MSBS_MASK 0x1F
+
+/* LINE_WIDTH[7:0] register */
+#define ADV7482_HDMI_LWIDTH_REG 0x08
+
+/* FIELD0_HEIGHT[12:8] register */
+#define ADV7482_HDMI_F0HEIGHT_MSBS_REG 0x09
+/* FIELD0_HEIGHT[12:8] mask */
+#define ADV7482_HDMI_F0HEIGHT_MSBS_MASK 0x1F
+
+/* FIELD0_HEIGHT[7:0] register */
+#define ADV7482_HDMI_F0HEIGHT_LSBS_REG 0x0A
+
+/* HDMI status register */
+#define ADV7482_HDMI_STATUS2_REG 0x0B
+/* DEEP_COLOR_MODE[1:0] mask */
+#define ADV7482_HDMI_DCM_MASK 0xC0
+/* HDMI_INTERLACED flag */
+#define ADV7482_HDMI_IP_FLAG 0x20
+/* FIELD1_HEIGHT[12:8] mask */
+#define ADV7482_HDMI_F1HEIGHT_MSBS_MASK 0x1F
+
+/* FIELD1_HEIGHT[7:0] register */
+#define ADV7482_HDMI_F1HEIGHT_REG 0x0C
+
+struct adv7482_sdp_main_info {
+ u8 status_reg_10;
+};
+
+/****************************************/
+/* ADV7482 SDP register definition */
+/****************************************/
+
+#define ADV7482_SDP_MAIN_MAP 0x00
+#define ADV7482_SDP_SUB_MAP1 0x20
+#define ADV7482_SDP_SUB_MAP2 0x40
+
+#define ADV7482_SDP_NO_RO_MAIN_MAP 0x00
+#define ADV7482_SDP_RO_MAIN_MAP 0x01
+#define ADV7482_SDP_RO_SUB_MAP1 0x02
+#define ADV7482_SDP_RO_SUB_MAP2 0x03
+
+#define ADV7482_SDP_MAIN_MAP_RW \
+ (ADV7482_SDP_MAIN_MAP | ADV7482_SDP_NO_RO_MAIN_MAP)
+
+#define ADV7482_SDP_STD_AD_PAL_BG_NTSC_J_SECAM 0x0
+#define ADV7482_SDP_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1
+#define ADV7482_SDP_STD_AD_PAL_N_NTSC_J_SECAM 0x2
+#define ADV7482_SDP_STD_AD_PAL_N_NTSC_M_SECAM 0x3
+#define ADV7482_SDP_STD_NTSC_J 0x4
+#define ADV7482_SDP_STD_NTSC_M 0x5
+#define ADV7482_SDP_STD_PAL60 0x6
+#define ADV7482_SDP_STD_NTSC_443 0x7
+#define ADV7482_SDP_STD_PAL_BG 0x8
+#define ADV7482_SDP_STD_PAL_N 0x9
+#define ADV7482_SDP_STD_PAL_M 0xa
+#define ADV7482_SDP_STD_PAL_M_PED 0xb
+#define ADV7482_SDP_STD_PAL_COMB_N 0xc
+#define ADV7482_SDP_STD_PAL_COMB_N_PED 0xd
+#define ADV7482_SDP_STD_PAL_SECAM 0xe
+#define ADV7482_SDP_STD_PAL_SECAM_PED 0xf
+
+#define ADV7482_SDP_REG_INPUT_CONTROL 0x00
+#define ADV7482_SDP_INPUT_CONTROL_INSEL_MASK 0x0f
+
+#define ADV7482_SDP_REG_INPUT_VIDSEL 0x02
+
+#define ADV7482_SDP_REG_CTRL 0x0e
+
+#define ADV7482_SDP_REG_PWR_MAN 0x0f
+#define ADV7482_SDP_PWR_MAN_ON 0x00
+#define ADV7482_SDP_PWR_MAN_OFF 0x20
+#define ADV7482_SDP_PWR_MAN_RES 0x80
+
+/* Contrast */
+#define ADV7482_SDP_REG_CON 0x08 /*Unsigned */
+#define ADV7482_SDP_CON_MIN 0
+#define ADV7482_SDP_CON_DEF 128
+#define ADV7482_SDP_CON_MAX 255
+/* Brightness*/
+#define ADV7482_SDP_REG_BRI 0x0a /*Signed */
+#define ADV7482_SDP_BRI_MIN -128
+#define ADV7482_SDP_BRI_DEF 0
+#define ADV7482_SDP_BRI_MAX 127
+/* Hue */
+#define ADV7482_SDP_REG_HUE 0x0b /*Signed, inverted */
+#define ADV7482_SDP_HUE_MIN -127
+#define ADV7482_SDP_HUE_DEF 0
+#define ADV7482_SDP_HUE_MAX 128
+
+/* Saturation */
+#define ADV7482_SDP_REG_SD_SAT_CB 0xe3
+#define ADV7482_SDP_REG_SD_SAT_CR 0xe4
+#define ADV7482_SDP_SAT_MIN 0
+#define ADV7482_SDP_SAT_DEF 128
+#define ADV7482_SDP_SAT_MAX 255
+
+#define ADV7482_SDP_INPUT_CVBS_AIN1 0x00
+#define ADV7482_SDP_INPUT_CVBS_AIN2 0x01
+#define ADV7482_SDP_INPUT_CVBS_AIN3 0x02
+#define ADV7482_SDP_INPUT_CVBS_AIN4 0x03
+#define ADV7482_SDP_INPUT_CVBS_AIN5 0x04
+#define ADV7482_SDP_INPUT_CVBS_AIN6 0x05
+#define ADV7482_SDP_INPUT_CVBS_AIN7 0x06
+#define ADV7482_SDP_INPUT_CVBS_AIN8 0x07
+#define ADV7482_SDP_INPUT_SVIDEO_AIN1_AIN2 0x08
+#define ADV7482_SDP_INPUT_SVIDEO_AIN3_AIN4 0x09
+#define ADV7482_SDP_INPUT_SVIDEO_AIN5_AIN6 0x0a
+#define ADV7482_SDP_INPUT_SVIDEO_AIN7_AIN8 0x0b
+#define ADV7482_SDP_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
+#define ADV7482_SDP_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
+#define ADV7482_SDP_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
+
+#define ADV7482_SDP_R_REG_10 0x10
+#define ADV7482_SDP_R_REG_10_IN_LOCK 0x01
+#define ADV7482_SDP_R_REG_10_FSC_LOCK 0x04
+
+#define ADV7482_SDP_R_REG_10_AUTOD_MASK 0x70
+#define ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J 0x00
+#define ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43 0x10
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_M 0x20
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_60 0x30
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G 0x40
+#define ADV7482_SDP_R_REG_10_AUTOD_SECAM 0x50
+#define ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB 0x60
+#define ADV7482_SDP_R_REG_10_AUTOD_SECAM_525 0x70
+
+#define ADV7482_MAX_WIDTH 1920
+#define ADV7482_MAX_HEIGHT 1080
+
+/****************************************/
+/* ADV7482 structure definition */
+/****************************************/
+
+/* Structure for register values */
+struct adv7482_reg_value {
+ u8 addr; /* i2c slave address */
+ u8 reg; /* sub (register) address */
+ u8 value; /* register value */
+};
+
+#define END_REGISTER_TABLE \
+{ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+
+/* Register default values */
+
+static const struct adv7482_reg_value adv7482_sw_reset[] = {
+
+ {ADV7482_I2C_IO, 0xFF, 0xFF}, /* SW reset */
+ {ADV7482_I2C_WAIT, 0x00, 0x05}, /* delay 5 */
+ {ADV7482_I2C_IO, 0x01, 0x76}, /* ADI Required Write */
+ {ADV7482_I2C_IO, 0xF2, 0x01}, /* Enable I2C Read Auto-Increment */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_slave_address[] = {
+
+ /* I2C Slave Address settings */
+ {ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2}, /* DPLL Map */
+ {ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2}, /* CP Map */
+ {ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2}, /* HDMI Map */
+ {ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2}, /* EDID Map */
+ {ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+ /* HDMI RX Repeater Map */
+ {ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+ /* HDMI RX InfoFrame Map */
+ {ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2}, /* CEC Map */
+ {ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2}, /* SDP Map */
+ {ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2}, /* CSI-TXB Map */
+ {ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2}, /* CSI-TXA Map */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+/* Supported Formats For Script Below */
+/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */
+static const struct adv7482_reg_value adv7482_init_txa_4lane[] = {
+ /* I2C Slave Address settings */
+ {ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2}, /* DPLL Map */
+ {ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2}, /* CP Map */
+ {ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2}, /* HDMI Map */
+ {ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2}, /* EDID Map */
+ {ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+ /* HDMI RX Repeater Map */
+ {ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+ /* HDMI RX InfoFrame Map */
+ {ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2}, /* CEC Map */
+ {ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2}, /* SDP Map */
+ {ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2}, /* CSI-TXB Map */
+ {ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2}, /* CSI-TXA Map */
+
+ /* Disable chip powerdown & Enable HDMI Rx block */
+ {ADV7482_I2C_IO, 0x00, 0x40},
+
+ {ADV7482_I2C_REPEATER, 0x40, 0x83}, /* Enable HDCP 1.1 */
+
+ {ADV7482_I2C_HDMI, 0x00, 0x08}, /* Foreground Channel = A */
+ {ADV7482_I2C_HDMI, 0x98, 0xFF}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x99, 0xA3}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x9A, 0x00}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x9B, 0x0A}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x9D, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0xCB, 0x09}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x3D, 0x10}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x3E, 0x7B}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x3F, 0x5E}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x4E, 0xFE}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x4F, 0x18}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x57, 0xA3}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x58, 0x04}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0x85, 0x10}, /* ADI Required Write */
+
+ {ADV7482_I2C_HDMI, 0x83, 0x00}, /* Enable All Terminations */
+ {ADV7482_I2C_HDMI, 0xA3, 0x01}, /* ADI Required Write */
+ {ADV7482_I2C_HDMI, 0xBE, 0x00}, /* ADI Required Write */
+
+ {ADV7482_I2C_HDMI, 0x6C, 0x01}, /* HPA Manual Enable */
+ {ADV7482_I2C_HDMI, 0xF8, 0x01}, /* HPA Asserted */
+ {ADV7482_I2C_HDMI, 0x0F, 0x00}, /* Audio Mute Speed Set to Fastest */
+ /* (Smallest Step Size) */
+
+ /* EDID */
+ /* Header information(0-19th byte) */
+ {ADV7482_I2C_EDID, 0x00, 0x00}, /* Fixed header pattern */
+ {ADV7482_I2C_EDID, 0x01, 0xFF},
+ {ADV7482_I2C_EDID, 0x02, 0xFF},
+ {ADV7482_I2C_EDID, 0x03, 0xFF},
+ {ADV7482_I2C_EDID, 0x04, 0xFF},
+ {ADV7482_I2C_EDID, 0x05, 0xFF},
+ {ADV7482_I2C_EDID, 0x06, 0xFF},
+ {ADV7482_I2C_EDID, 0x07, 0x00},
+ {ADV7482_I2C_EDID, 0x08, 0x00}, /* Manufacturer ID */
+ {ADV7482_I2C_EDID, 0x09, 0x00},
+ {ADV7482_I2C_EDID, 0x0A, 0x00}, /* Manufacturer product code */
+ {ADV7482_I2C_EDID, 0x0B, 0x00},
+ {ADV7482_I2C_EDID, 0x0C, 0x00}, /* Serial number */
+ {ADV7482_I2C_EDID, 0x0D, 0x00},
+ {ADV7482_I2C_EDID, 0x0E, 0x00},
+ {ADV7482_I2C_EDID, 0x0F, 0x00},
+ {ADV7482_I2C_EDID, 0x10, 0x01}, /* Week of manufacture */
+ {ADV7482_I2C_EDID, 0x11, 0x0C}, /* Year of manufacture */
+ {ADV7482_I2C_EDID, 0x12, 0x01}, /* EDID version */
+ {ADV7482_I2C_EDID, 0x13, 0x03},
+ /* Basic display parameters(20-24th byte) */
+ {ADV7482_I2C_EDID, 0x14, 0x80}, /* Video input parameters bitmap */
+ {ADV7482_I2C_EDID, 0x15, 0x50}, /* Maximum horizontal image size */
+ {ADV7482_I2C_EDID, 0x16, 0x2D}, /* Maximum vertical image size */
+ {ADV7482_I2C_EDID, 0x17, 0x78}, /* Display gamma */
+ {ADV7482_I2C_EDID, 0x18, 0x0A}, /* Supported features bitmap */
+ /* Chromaticity coordinates(25-34th byte) */
+ {ADV7482_I2C_EDID, 0x19, 0x0D},
+ {ADV7482_I2C_EDID, 0x1A, 0xC9},
+ {ADV7482_I2C_EDID, 0x1B, 0xA0},
+ {ADV7482_I2C_EDID, 0x1C, 0x57},
+ {ADV7482_I2C_EDID, 0x1D, 0x47},
+ {ADV7482_I2C_EDID, 0x1E, 0x98},
+ {ADV7482_I2C_EDID, 0x1F, 0x27},
+ {ADV7482_I2C_EDID, 0x20, 0x12},
+ {ADV7482_I2C_EDID, 0x21, 0x48},
+ {ADV7482_I2C_EDID, 0x22, 0x4C},
+ /* Established timing bitmap(35-37th byte) */
+ {ADV7482_I2C_EDID, 0x23, 0x20},
+ {ADV7482_I2C_EDID, 0x24, 0x00},
+ {ADV7482_I2C_EDID, 0x25, 0x00},
+ /* Standard timing information(38-53th byte) */
+ /* Because they are unused, in this field, all values are 0101h. */
+ {ADV7482_I2C_EDID, 0x26, 0x01},
+ {ADV7482_I2C_EDID, 0x27, 0x01},
+ {ADV7482_I2C_EDID, 0x28, 0x01},
+ {ADV7482_I2C_EDID, 0x29, 0x01},
+ {ADV7482_I2C_EDID, 0x2A, 0x01},
+ {ADV7482_I2C_EDID, 0x2B, 0x01},
+ {ADV7482_I2C_EDID, 0x2C, 0x01},
+ {ADV7482_I2C_EDID, 0x2D, 0x01},
+ {ADV7482_I2C_EDID, 0x2E, 0x01},
+ {ADV7482_I2C_EDID, 0x2F, 0x01},
+ {ADV7482_I2C_EDID, 0x30, 0x01},
+ {ADV7482_I2C_EDID, 0x31, 0x01},
+ {ADV7482_I2C_EDID, 0x32, 0x01},
+ {ADV7482_I2C_EDID, 0x33, 0x01},
+ {ADV7482_I2C_EDID, 0x34, 0x01},
+ {ADV7482_I2C_EDID, 0x35, 0x01},
+ /* Descriptor blocks of Descriptor 1(54-71th byte) */
+ {ADV7482_I2C_EDID, 0x36, 0x02},
+ {ADV7482_I2C_EDID, 0x37, 0x3A},
+ {ADV7482_I2C_EDID, 0x38, 0x80},
+ {ADV7482_I2C_EDID, 0x39, 0x18},
+ {ADV7482_I2C_EDID, 0x3A, 0x71},
+ {ADV7482_I2C_EDID, 0x3B, 0x38},
+ {ADV7482_I2C_EDID, 0x3C, 0x2D},
+ {ADV7482_I2C_EDID, 0x3D, 0x40},
+ {ADV7482_I2C_EDID, 0x3E, 0x58},
+ {ADV7482_I2C_EDID, 0x3F, 0x2C},
+ {ADV7482_I2C_EDID, 0x40, 0x45},
+ {ADV7482_I2C_EDID, 0x41, 0x00},
+ {ADV7482_I2C_EDID, 0x42, 0xDC},
+ {ADV7482_I2C_EDID, 0x43, 0x0B},
+ {ADV7482_I2C_EDID, 0x44, 0x11},
+ {ADV7482_I2C_EDID, 0x45, 0x00},
+ {ADV7482_I2C_EDID, 0x46, 0x00},
+ {ADV7482_I2C_EDID, 0x47, 0x1E},
+ /* Descriptor blocks of Descriptor 2(72-89th byte) */
+ {ADV7482_I2C_EDID, 0x48, 0x8C},
+ {ADV7482_I2C_EDID, 0x49, 0x0A},
+ {ADV7482_I2C_EDID, 0x4A, 0xD0},
+ {ADV7482_I2C_EDID, 0x4B, 0x8A},
+ {ADV7482_I2C_EDID, 0x4C, 0x20},
+ {ADV7482_I2C_EDID, 0x4D, 0xE0},
+ {ADV7482_I2C_EDID, 0x4E, 0x2D},
+ {ADV7482_I2C_EDID, 0x4F, 0x10},
+ {ADV7482_I2C_EDID, 0x50, 0x10},
+ {ADV7482_I2C_EDID, 0x51, 0x3E},
+ {ADV7482_I2C_EDID, 0x52, 0x96},
+ {ADV7482_I2C_EDID, 0x53, 0x00},
+ {ADV7482_I2C_EDID, 0x54, 0x58},
+ {ADV7482_I2C_EDID, 0x55, 0xC2},
+ {ADV7482_I2C_EDID, 0x56, 0x21},
+ {ADV7482_I2C_EDID, 0x57, 0x00},
+ {ADV7482_I2C_EDID, 0x58, 0x00},
+ {ADV7482_I2C_EDID, 0x59, 0x18},
+ /* Descriptor blocks of Descriptor 3(90-107th byte) */
+ {ADV7482_I2C_EDID, 0x5A, 0x00},
+ {ADV7482_I2C_EDID, 0x5B, 0x00},
+ {ADV7482_I2C_EDID, 0x5C, 0x00},
+ {ADV7482_I2C_EDID, 0x5D, 0x7C},
+ {ADV7482_I2C_EDID, 0x5E, 0x00},
+ {ADV7482_I2C_EDID, 0x5F, 0x4D},
+ {ADV7482_I2C_EDID, 0x60, 0x59},
+ {ADV7482_I2C_EDID, 0x61, 0x20},
+ {ADV7482_I2C_EDID, 0x62, 0x48},
+ {ADV7482_I2C_EDID, 0x63, 0x44},
+ {ADV7482_I2C_EDID, 0x64, 0x54},
+ {ADV7482_I2C_EDID, 0x65, 0x56},
+ {ADV7482_I2C_EDID, 0x66, 0x0A},
+ {ADV7482_I2C_EDID, 0x67, 0x20},
+ {ADV7482_I2C_EDID, 0x68, 0x20},
+ {ADV7482_I2C_EDID, 0x69, 0x20},
+ {ADV7482_I2C_EDID, 0x6A, 0x20},
+ {ADV7482_I2C_EDID, 0x6B, 0x20},
+ /* Descriptor blocks of Descriptor 4(108-125th byte) */
+ {ADV7482_I2C_EDID, 0x6C, 0x00},
+ {ADV7482_I2C_EDID, 0x6D, 0x00},
+ {ADV7482_I2C_EDID, 0x6E, 0x00},
+ {ADV7482_I2C_EDID, 0x6F, 0x00},
+ {ADV7482_I2C_EDID, 0x70, 0x00},
+ {ADV7482_I2C_EDID, 0x71, 0x00},
+ {ADV7482_I2C_EDID, 0x72, 0x00},
+ {ADV7482_I2C_EDID, 0x73, 0x0A},
+ {ADV7482_I2C_EDID, 0x74, 0x2E},
+ {ADV7482_I2C_EDID, 0x75, 0x06},
+ {ADV7482_I2C_EDID, 0x76, 0x00},
+ {ADV7482_I2C_EDID, 0x77, 0x0A},
+ {ADV7482_I2C_EDID, 0x78, 0x20},
+ {ADV7482_I2C_EDID, 0x79, 0x20},
+ {ADV7482_I2C_EDID, 0x7A, 0x20},
+ {ADV7482_I2C_EDID, 0x7B, 0x20},
+ {ADV7482_I2C_EDID, 0x7C, 0x20},
+ {ADV7482_I2C_EDID, 0x7D, 0x20},
+ /* Number of extensions to follow(126th byte) */
+ {ADV7482_I2C_EDID, 0x7E, 0x01}, /* Extension enable */
+ /* Checksum(127th byte) */
+ {ADV7482_I2C_EDID, 0x7F, 0x75},
+
+ /* CEA EDID Timing Extension Version 3 */
+ {ADV7482_I2C_EDID, 0x80, 0x02}, /* CEA 861 Externsion Block */
+ {ADV7482_I2C_EDID, 0x81, 0x03},
+ {ADV7482_I2C_EDID, 0x82, 0x1E},
+ {ADV7482_I2C_EDID, 0x83, 0x40},
+ {ADV7482_I2C_EDID, 0x84, 0x83},
+ {ADV7482_I2C_EDID, 0x85, 0x7F},
+ {ADV7482_I2C_EDID, 0x86, 0x40},
+ {ADV7482_I2C_EDID, 0x87, 0x05},
+ {ADV7482_I2C_EDID, 0x88, 0x40},
+ {ADV7482_I2C_EDID, 0x89, 0x48},
+ {ADV7482_I2C_EDID, 0x8A, 0x10}, /* 1920x1080p @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x8B, 0x05}, /* 1920x1080i @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x8C, 0x04}, /* 1280x720p @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x8D, 0x01}, /* 640x480p @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x8E, 0x02}, /* 720x480p @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x8F, 0x06}, /* 720(1440)x480i @ 59.94/60 Hz */
+ {ADV7482_I2C_EDID, 0x90, 0x15}, /* 720(1440)x576i @ 50 Hz */
+ {ADV7482_I2C_EDID, 0x91, 0x11}, /* 720x576p @ 50 Hz */
+ {ADV7482_I2C_EDID, 0x92, 0x00},
+ {ADV7482_I2C_EDID, 0x93, 0x00},
+ {ADV7482_I2C_EDID, 0x94, 0x00},
+ {ADV7482_I2C_EDID, 0x95, 0x00},
+ {ADV7482_I2C_EDID, 0x96, 0x00},
+ {ADV7482_I2C_EDID, 0x97, 0x00},
+ {ADV7482_I2C_EDID, 0x98, 0x00},
+ {ADV7482_I2C_EDID, 0x99, 0x00},
+ {ADV7482_I2C_EDID, 0x9A, 0x00},
+ {ADV7482_I2C_EDID, 0x9B, 0x00},
+ {ADV7482_I2C_EDID, 0x9C, 0x00},
+ {ADV7482_I2C_EDID, 0x9D, 0x00},
+ {ADV7482_I2C_EDID, 0x9E, 0x00},
+ {ADV7482_I2C_EDID, 0x9F, 0x00},
+ {ADV7482_I2C_EDID, 0xA0, 0x00},
+ {ADV7482_I2C_EDID, 0xA1, 0x00},
+ {ADV7482_I2C_EDID, 0xA2, 0x00},
+ {ADV7482_I2C_EDID, 0xA3, 0x00},
+ {ADV7482_I2C_EDID, 0xA4, 0x00},
+ {ADV7482_I2C_EDID, 0xA5, 0x00},
+ {ADV7482_I2C_EDID, 0xA6, 0x00},
+ {ADV7482_I2C_EDID, 0xA7, 0x00},
+ {ADV7482_I2C_EDID, 0xA8, 0x00},
+ {ADV7482_I2C_EDID, 0xA9, 0x00},
+ {ADV7482_I2C_EDID, 0xAA, 0x00},
+ {ADV7482_I2C_EDID, 0xAB, 0x00},
+ {ADV7482_I2C_EDID, 0xAC, 0x00},
+ {ADV7482_I2C_EDID, 0xAD, 0x00},
+ {ADV7482_I2C_EDID, 0xAE, 0x00},
+ {ADV7482_I2C_EDID, 0xAF, 0x00},
+ {ADV7482_I2C_EDID, 0xB0, 0x00},
+ {ADV7482_I2C_EDID, 0xB1, 0x00},
+ {ADV7482_I2C_EDID, 0xB2, 0x00},
+ {ADV7482_I2C_EDID, 0xB3, 0x00},
+ {ADV7482_I2C_EDID, 0xB4, 0x00},
+ {ADV7482_I2C_EDID, 0xB5, 0x00},
+ {ADV7482_I2C_EDID, 0xB6, 0x00},
+ {ADV7482_I2C_EDID, 0xB7, 0x00},
+ {ADV7482_I2C_EDID, 0xB8, 0x00},
+ {ADV7482_I2C_EDID, 0xB9, 0x00},
+ {ADV7482_I2C_EDID, 0xBA, 0x20},
+ {ADV7482_I2C_EDID, 0xBB, 0x20},
+ {ADV7482_I2C_EDID, 0xBC, 0x20},
+ {ADV7482_I2C_EDID, 0xBD, 0x20},
+ {ADV7482_I2C_EDID, 0xBE, 0x20},
+ {ADV7482_I2C_EDID, 0xBF, 0x20},
+ {ADV7482_I2C_EDID, 0xC0, 0x20},
+ {ADV7482_I2C_EDID, 0xC1, 0x20},
+ {ADV7482_I2C_EDID, 0xC2, 0x20},
+ {ADV7482_I2C_EDID, 0xC3, 0x20},
+ {ADV7482_I2C_EDID, 0xC4, 0x20},
+ {ADV7482_I2C_EDID, 0xC5, 0x20},
+ {ADV7482_I2C_EDID, 0xC6, 0x00},
+ {ADV7482_I2C_EDID, 0xC7, 0x00},
+ {ADV7482_I2C_EDID, 0xC8, 0x00},
+ {ADV7482_I2C_EDID, 0xC9, 0xFF},
+ {ADV7482_I2C_EDID, 0xCA, 0x00},
+ {ADV7482_I2C_EDID, 0xCB, 0x0A},
+ {ADV7482_I2C_EDID, 0xCC, 0x20},
+ {ADV7482_I2C_EDID, 0xCD, 0x20},
+ {ADV7482_I2C_EDID, 0xCE, 0x20},
+ {ADV7482_I2C_EDID, 0xCF, 0x20},
+ {ADV7482_I2C_EDID, 0xD0, 0x20},
+ {ADV7482_I2C_EDID, 0xD1, 0x20},
+ {ADV7482_I2C_EDID, 0xD2, 0x20},
+ {ADV7482_I2C_EDID, 0xD3, 0x20},
+ {ADV7482_I2C_EDID, 0xD4, 0x20},
+ {ADV7482_I2C_EDID, 0xD5, 0x20},
+ {ADV7482_I2C_EDID, 0xD6, 0x20},
+ {ADV7482_I2C_EDID, 0xD7, 0x20},
+ {ADV7482_I2C_EDID, 0xD8, 0x00},
+ {ADV7482_I2C_EDID, 0xD9, 0x00},
+ {ADV7482_I2C_EDID, 0xDA, 0x00},
+ {ADV7482_I2C_EDID, 0xDB, 0x1B},
+ {ADV7482_I2C_EDID, 0xDC, 0x00},
+ {ADV7482_I2C_EDID, 0xDD, 0x0A},
+ {ADV7482_I2C_EDID, 0xDE, 0x20},
+ {ADV7482_I2C_EDID, 0xDF, 0x20},
+ {ADV7482_I2C_EDID, 0xE0, 0x20},
+ {ADV7482_I2C_EDID, 0xE1, 0x20},
+ {ADV7482_I2C_EDID, 0xE2, 0x20},
+ {ADV7482_I2C_EDID, 0xE3, 0x20},
+ {ADV7482_I2C_EDID, 0xE4, 0x20},
+ {ADV7482_I2C_EDID, 0xE5, 0x20},
+ {ADV7482_I2C_EDID, 0xE6, 0x20},
+ {ADV7482_I2C_EDID, 0xE7, 0x20},
+ {ADV7482_I2C_EDID, 0xE8, 0x20},
+ {ADV7482_I2C_EDID, 0xE9, 0x20},
+ {ADV7482_I2C_EDID, 0xEA, 0x00},
+ {ADV7482_I2C_EDID, 0xEB, 0x00},
+ {ADV7482_I2C_EDID, 0xEC, 0x00},
+ {ADV7482_I2C_EDID, 0xED, 0x00},
+ {ADV7482_I2C_EDID, 0xEE, 0x00},
+ {ADV7482_I2C_EDID, 0xEF, 0x00},
+ {ADV7482_I2C_EDID, 0xF0, 0x00},
+ {ADV7482_I2C_EDID, 0xF1, 0x00},
+ {ADV7482_I2C_EDID, 0xF2, 0x00},
+ {ADV7482_I2C_EDID, 0xF3, 0x00},
+ {ADV7482_I2C_EDID, 0xF4, 0x00},
+ {ADV7482_I2C_EDID, 0xF5, 0x00},
+ {ADV7482_I2C_EDID, 0xF6, 0x00},
+ {ADV7482_I2C_EDID, 0xF7, 0x00},
+ {ADV7482_I2C_EDID, 0xF8, 0x00},
+ {ADV7482_I2C_EDID, 0xF9, 0x00},
+ {ADV7482_I2C_EDID, 0xFA, 0x00},
+ {ADV7482_I2C_EDID, 0xFB, 0x00},
+ {ADV7482_I2C_EDID, 0xFC, 0x00},
+ {ADV7482_I2C_EDID, 0xFD, 0x00},
+ {ADV7482_I2C_EDID, 0xFE, 0x00},
+ {ADV7482_I2C_EDID, 0xFF, 0xD8}, /* Set the Most Significant Bit */
+
+ {ADV7482_I2C_REPEATER, 0x70, 0x10},
+ /* EDID image, in 128-byte blocks. */
+ {ADV7482_I2C_REPEATER, 0x74, 0x01},
+ /* Manual enable active for E-EDID on Port A */
+
+ {ADV7482_I2C_IO, 0x04, 0x02}, /* RGB Out of CP */
+ {ADV7482_I2C_IO, 0x12, 0xF0},
+ /* CSC Depends on ip Packets - SDR 444 */
+ {ADV7482_I2C_IO, 0x17, 0x80},
+ /* Luma & Chroma Values Can Reach 254d */
+ {ADV7482_I2C_IO, 0x03, 0x86}, /* CP-Insert_AV_Code */
+
+ {ADV7482_I2C_CP, 0x7C, 0x00}, /* ADI Required Write */
+
+ {ADV7482_I2C_IO, 0x0C, 0xE0}, /* Enable LLC_DLL & Double LLC Timing */
+ {ADV7482_I2C_IO, 0x0E, 0xDD}, /* LLC/PIX/SPI PINS TRISTATED AUD */
+ /* Outputs Enabled */
+ {ADV7482_I2C_IO, 0x10, 0xA0}, /* Enable 4-lane CSI Tx & Pixel Port */
+
+ {ADV7482_I2C_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV7482_I2C_TXA, 0x00, 0xA4}, /* Set Auto DPHY Timing */
+ {ADV7482_I2C_TXA, 0xDB, 0x10}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xD6, 0x07}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xC4, 0x0A}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x71, 0x33}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x72, 0x11}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xF0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
+
+ {ADV7482_I2C_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x1E, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV7482_I2C_WAIT, 0x00, 0x02}, /* delay 2 */
+ {ADV7482_I2C_TXA, 0x00, 0x24 }, /* Power-up CSI-TX */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXA, 0xC1, 0x2B}, /* ADI Required Write */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXA, 0x31, 0x80}, /* ADI Required Write */
+
+ /* for debug */
+#ifdef REL_DGB_FORCE_TO_SEND_COLORBAR
+ {ADV7482_I2C_CP, 0x37, 0x81}, /* Output Colorbars Pattern */
+#endif
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */
+/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */
+static const struct adv7482_reg_value adv7482_init_txb_1lane[] = {
+
+ {ADV7482_I2C_IO, 0x00, 0x30},
+ /* Disable chip powerdown - powerdown Rx */
+ {ADV7482_I2C_IO, 0xF2, 0x01}, /* Enable I2C Read Auto-Increment */
+
+ /* I2C Slave Address settings */
+ {ADV7482_I2C_IO, 0xF3, ADV7482_I2C_DPLL * 2}, /* DPLL Map */
+ {ADV7482_I2C_IO, 0xF4, ADV7482_I2C_CP * 2}, /* CP Map */
+ {ADV7482_I2C_IO, 0xF5, ADV7482_I2C_HDMI * 2}, /* HDMI Map */
+ {ADV7482_I2C_IO, 0xF6, ADV7482_I2C_EDID * 2}, /* EDID Map */
+ {ADV7482_I2C_IO, 0xF7, ADV7482_I2C_REPEATER * 2},
+ /* HDMI RX Repeater Map */
+ {ADV7482_I2C_IO, 0xF8, ADV7482_I2C_INFOFRAME * 2},
+ /* HDMI RX InfoFrame Map */
+ {ADV7482_I2C_IO, 0xFA, ADV7482_I2C_CEC * 2}, /* CEC Map */
+ {ADV7482_I2C_IO, 0xFB, ADV7482_I2C_SDP * 2}, /* SDP Map */
+ {ADV7482_I2C_IO, 0xFC, ADV7482_I2C_TXB * 2}, /* CSI-TXB Map */
+ {ADV7482_I2C_IO, 0xFD, ADV7482_I2C_TXA * 2}, /* CSI-TXA Map */
+
+ {ADV7482_I2C_IO, 0x0E, 0xFF}, /* LLC/PIX/AUD/SPI PINS TRISTATED */
+
+ {ADV7482_I2C_SDP, ADV7482_SDP_REG_PWR_MAN, ADV7482_SDP_PWR_MAN_ON},
+ /* Exit Power Down Mode */
+ {ADV7482_I2C_SDP, 0x52, 0xCD}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, ADV7482_SDP_REG_INPUT_CONTROL,
+ ADV7482_SDP_INPUT_CVBS_AIN8}, /* INSEL = CVBS in on Ain 8 */
+ {ADV7482_I2C_SDP, ADV7482_SDP_REG_CTRL, 0x80},
+ /* ADI Required Write */
+ {ADV7482_I2C_SDP, 0x9C, 0x00}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, 0x9C, 0xFF}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, ADV7482_SDP_REG_CTRL, ADV7482_SDP_MAIN_MAP_RW},
+ /* ADI Required Write */
+
+ /* ADI recommended writes for improved video quality */
+ {ADV7482_I2C_SDP, 0x80, 0x51}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, 0x81, 0x51}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, 0x82, 0x68}, /* ADI Required Write */
+
+ {ADV7482_I2C_SDP, 0x03, 0x42},
+ /* Tri-S Output Drivers, PwrDwn 656 pads */
+ {ADV7482_I2C_SDP, 0x04, 0xB5}, /* ITU-R BT.656-4 compatible */
+ {ADV7482_I2C_SDP, 0x13, 0x00}, /* ADI Required Write */
+
+ {ADV7482_I2C_SDP, 0x17, 0x41}, /* Select SH1 */
+ {ADV7482_I2C_SDP, 0x31, 0x12}, /* ADI Required Write */
+ {ADV7482_I2C_SDP, 0xE6, 0x4F},
+ /* Set V bit end position manually in NTSC mode */
+
+#ifdef REL_DGB_FORCE_TO_SEND_COLORBAR
+ {ADV7482_I2C_SDP, 0x0C, 0x01}, /* ColorBar */
+ {ADV7482_I2C_SDP, 0x14, 0x01}, /* ColorBar */
+#endif
+ /* Enable 1-Lane MIPI Tx, */
+ /* enable pixel output and route SD through Pixel port */
+ {ADV7482_I2C_IO, 0x10, 0x70},
+
+ {ADV7482_I2C_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
+ {ADV7482_I2C_TXB, 0x00, 0xA1}, /* Set Auto DPHY Timing */
+#if 0 /* If CVBS input only is used, please enable this code. */
+ {ADV7482_I2C_TXA, 0xF0, 0x00}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xD6, 0x07}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xC0, 0x3C}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xC3, 0x3C}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xC6, 0x3C}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xC9, 0x3C}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xCC, 0x3C}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xD5, 0x03}, /* ADI Required Write */
+#endif
+ {ADV7482_I2C_TXB, 0xD2, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0xC4, 0x0A}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x71, 0x33}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x72, 0x11}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0xF0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */
+ {ADV7482_I2C_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x1E, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+
+ {ADV7482_I2C_WAIT, 0x00, 0x02}, /* delay 2 */
+ {ADV7482_I2C_TXB, 0x00, 0x21 }, /* Power-up CSI-TX */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXB, 0xC1, 0x2B}, /* ADI Required Write */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXB, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_txa_4lane[] = {
+
+ {ADV7482_I2C_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV7482_I2C_TXA, 0x00, 0xA4}, /* Set Auto DPHY Timing */
+
+ {ADV7482_I2C_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x1E, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV7482_I2C_WAIT, 0x00, 0x02}, /* delay 2 */
+ {ADV7482_I2C_TXA, 0x00, 0x24 }, /* Power-up CSI-TX */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXA, 0xC1, 0x2B}, /* ADI Required Write */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXA, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_txa_4lane[] = {
+
+ {ADV7482_I2C_TXA, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x1E, 0x00}, /* ADI Required Write */
+ {ADV7482_I2C_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */
+ {ADV7482_I2C_TXA, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV7482_I2C_TXA, 0xC1, 0x3B}, /* ADI Required Write */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_txb_1lane[] = {
+
+ {ADV7482_I2C_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */
+ {ADV7482_I2C_TXB, 0x00, 0xA1}, /* Set Auto DPHY Timing */
+
+ {ADV7482_I2C_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x1E, 0x40}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV7482_I2C_WAIT, 0x00, 0x02}, /* delay 2 */
+ {ADV7482_I2C_TXB, 0x00, 0x21 }, /* Power-up CSI-TX */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXB, 0xC1, 0x2B}, /* ADI Required Write */
+ {ADV7482_I2C_WAIT, 0x00, 0x01}, /* delay 1 */
+ {ADV7482_I2C_TXB, 0x31, 0x80}, /* ADI Required Write */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_txb_1lane[] = {
+
+ {ADV7482_I2C_TXB, 0x31, 0x82}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x1E, 0x00}, /* ADI Required Write */
+ {ADV7482_I2C_TXB, 0x00, 0x81}, /* Enable 4-lane MIPI */
+ {ADV7482_I2C_TXB, 0xDA, 0x01}, /* i2c_mipi_pll_en - 1'b1 */
+ {ADV7482_I2C_TXB, 0xC1, 0x3B}, /* ADI Required Write */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_up_hdmi_rx[] = {
+
+ /* Disable chip powerdown & Enable HDMI Rx block */
+ {ADV7482_I2C_IO, 0x00, 0x40},
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_power_down_hdmi_rx[] = {
+
+ {ADV7482_I2C_IO, 0x00, 0x30}, /* Disable chip powerdown */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_enable_csi4_csi1[] = {
+
+ {ADV7482_I2C_IO, 0x10, 0xE0}, /* Enable 4-lane CSI Tx & Pixel Port */
+
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel0[] = {
+
+ {ADV7482_I2C_TXB, 0x0D, 0x00}, /* Set virtual channel 0 */
+ {ADV7482_I2C_TXA, 0x0D, 0x00}, /* Set virtual channel 0 */
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel1[] = {
+
+ {ADV7482_I2C_TXB, 0x0D, 0x40}, /* Set virtual channel 1 */
+ {ADV7482_I2C_TXA, 0x0D, 0x40}, /* Set virtual channel 1 */
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel2[] = {
+
+ {ADV7482_I2C_TXB, 0x0D, 0x80}, /* Set virtual channel 2 */
+ {ADV7482_I2C_TXA, 0x0D, 0x80}, /* Set virtual channel 2 */
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+static const struct adv7482_reg_value adv7482_set_virtual_channel3[] = {
+
+ {ADV7482_I2C_TXB, 0x0D, 0xC0}, /* Set virtual channel 3 */
+ {ADV7482_I2C_TXA, 0x0D, 0xC0}, /* Set virtual channel 3 */
+ {ADV7482_I2C_EOR, 0xFF, 0xFF} /* End of register table */
+};
+
+enum decoder_input_interface {
+ DECODER_INPUT_INTERFACE_RGB888,
+ DECODER_INPUT_INTERFACE_YCBCR422,
+};
+
+/**
+ * struct adv7482_link_config - Describes adv7482 hardware configuration
+ * @input_colorspace: The input colorspace (RGB, YUV444, YUV422)
+ */
+struct adv7482_link_config {
+ enum decoder_input_interface input_interface;
+ struct adv7482_reg_value *regs;
+
+ struct adv7482_reg_value *power_up;
+ struct adv7482_reg_value *power_down;
+
+ int (*init_device)(void *);
+ int (*init_controls)(void *);
+ int (*s_power)(void *);
+ int (*s_ctrl)(void *);
+ int (*enum_mbus_code)(void *);
+ int (*set_pad_format)(void *);
+ int (*get_pad_format)(void *);
+ int (*s_std)(void *);
+ int (*querystd)(void *);
+ int (*g_input_status)(void *);
+ int (*s_routing)(void *);
+ int (*g_mbus_config)(void *);
+
+ struct device *dev;
+ bool sw_reset;
+ bool hdmi_in;
+ bool sdp_in;
+ int vc_ch;
+};
+
+struct adv7482_state {
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ /* mutual excl. when accessing chip */
+ struct mutex mutex;
+ int irq;
+ v4l2_std_id curr_norm;
+ bool autodetect;
+ bool powered;
+ const struct adv7482_color_format *cfmt;
+ u32 width;
+ u32 height;
+
+ struct i2c_client *client;
+ unsigned int register_page;
+ struct i2c_client *csi_client;
+ enum v4l2_field field;
+
+ struct device *dev;
+ struct adv7482_link_config mipi_csi2_link[2];
+};
+
+/*****************************************************************************/
+/* Private functions */
+/*****************************************************************************/
+
+#define to_adv7482_sd(_ctrl) (&container_of(_ctrl->handler, \
+ struct adv7482_state, \
+ ctrl_hdl)->sd)
+
+static inline struct adv7482_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7482_state, sd);
+}
+
+/*
+ * adv7482_write_registers() - Write adv7482 device registers
+ * @client: pointer to i2c_client structure
+ * @regs: pointer to adv7482_reg_value structure
+ *
+ * Write the specified adv7482 register's values.
+ */
+static int adv7482_write_registers(struct i2c_client *client,
+ const struct adv7482_reg_value *regs)
+{
+ struct i2c_msg msg;
+ u8 data_buf[2];
+ int ret = -EINVAL;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &data_buf[0];
+
+ while (regs->addr != ADV7482_I2C_EOR) {
+
+ if (regs->addr == ADV7482_I2C_WAIT)
+ msleep(regs->value);
+ else {
+ msg.addr = regs->addr;
+ data_buf[0] = regs->reg;
+ data_buf[1] = regs->value;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ break;
+ }
+ regs++;
+ }
+
+ return (ret < 0) ? ret : 0;
+}
+
+/*
+ * adv7482_write_register() - Write adv7482 device register
+ * @client: pointer to i2c_client structure
+ * @addr: i2c slave address of adv7482 device
+ * @reg: adv7482 device register address
+ * @value: the value to be written
+ *
+ * Write the specified adv7482 register's value.
+ */
+static int adv7482_write_register(struct i2c_client *client,
+ u8 addr, u8 reg, u8 value)
+{
+ struct adv7482_reg_value regs[2];
+ int ret;
+
+ regs[0].addr = addr;
+ regs[0].reg = reg;
+ regs[0].value = value;
+ regs[1].addr = ADV7482_I2C_EOR;
+ regs[1].reg = 0xFF;
+ regs[1].value = 0xFF;
+
+ ret = adv7482_write_registers(client, regs);
+
+ return ret;
+}
+
+/*
+ * adv7482_read_register() - Read adv7482 device register
+ * @client: pointer to i2c_client structure
+ * @addr: i2c slave address of adv7482 device
+ * @reg: adv7482 device register address
+ * @value: pointer to the value
+ *
+ * Read the specified adv7482 register's value.
+ */
+static int adv7482_read_register(struct i2c_client *client,
+ u8 addr, u8 reg, u8 *value)
+{
+ struct i2c_msg msg[2];
+ u8 reg_buf, data_buf;
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = ®_buf;
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &data_buf;
+
+ reg_buf = reg;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ *value = data_buf;
+
+ return (ret < 0) ? ret : 0;
+}
+
+static int adv7482_read_sdp_main_info(struct i2c_client *client,
+ struct adv7482_sdp_main_info *info)
+{
+ int ret;
+ u8 value;
+
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_CTRL, ADV7482_SDP_RO_MAIN_MAP);
+ if (ret < 0)
+ return ret;
+
+ /* status_reg_10 */
+ ret = adv7482_read_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_R_REG_10, &value);
+ if (ret < 0)
+ return ret;
+
+ info->status_reg_10 = value;
+
+ return ret;
+}
+
+static v4l2_std_id adv7482_std_to_v4l2(u8 status_reg_10)
+{
+ /* in case V4L2_IN_ST_NO_SIGNAL */
+ if (!(status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK))
+ return V4L2_STD_UNKNOWN;
+
+ switch (status_reg_10 & ADV7482_SDP_R_REG_10_AUTOD_MASK) {
+ case ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J:
+ return V4L2_STD_NTSC;
+ case ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43:
+ return V4L2_STD_NTSC_443;
+ case ADV7482_SDP_R_REG_10_AUTOD_PAL_M:
+ return V4L2_STD_PAL_M;
+ case ADV7482_SDP_R_REG_10_AUTOD_PAL_60:
+ return V4L2_STD_PAL_60;
+ case ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G:
+ return V4L2_STD_PAL;
+ case ADV7482_SDP_R_REG_10_AUTOD_SECAM:
+ return V4L2_STD_SECAM;
+ case ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB:
+ return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
+ case ADV7482_SDP_R_REG_10_AUTOD_SECAM_525:
+ return V4L2_STD_SECAM;
+ default:
+ return V4L2_STD_UNKNOWN;
+ }
+}
+
+static u32 adv7482_status_to_v4l2(u8 status_reg_10)
+{
+ if (!(status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK))
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ return 0;
+}
+
+static int __adv7482_status(struct adv7482_state *state, u32 *status,
+ v4l2_std_id *std)
+{
+ int ret;
+ u8 status_reg_10;
+ struct adv7482_sdp_main_info sdp_info;
+
+ ret = adv7482_read_sdp_main_info(state->client, &sdp_info);
+ if (ret < 0)
+ return ret;
+
+ status_reg_10 = sdp_info.status_reg_10;
+
+ if (status_reg_10 < 0)
+ return (int)status_reg_10;
+
+ if (status)
+ *status = adv7482_status_to_v4l2(status_reg_10);
+ if (std)
+ *std = adv7482_std_to_v4l2(status_reg_10);
+
+ return 0;
+}
+
+/*
+ * adv7482_get_vid_info() - Get video information
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @progressive: progressive or interlace
+ * @width: line width
+ * @height: lines per frame
+ *
+ * Get video information.
+ */
+static int adv7482_get_vid_info(struct v4l2_subdev *sd, u8 *progressive,
+ u32 *width, u32 *height, u8 *signal)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 hdmi_int;
+ u8 msb;
+ u8 lsb;
+ int ret;
+
+ if (signal)
+ *signal = 0;
+
+ /* decide line width */
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_STATUS1_REG, &msb);
+ if (ret < 0)
+ return ret;
+
+ if (!(msb & ADV7482_HDMI_VF_LOCKED_FLG) ||
+ !(msb & ADV7482_HDMI_DERF_LOCKED_FLG))
+ return -EIO;
+
+ if (signal)
+ *signal = 1;
+
+ /* decide interlaced or progressive */
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_STATUS2_REG, &hdmi_int);
+ if (ret < 0)
+ return ret;
+
+ *progressive = 1;
+ if ((hdmi_int & ADV7482_HDMI_IP_FLAG) != 0)
+ *progressive = 0;
+
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_LWIDTH_REG, &lsb);
+ if (ret < 0)
+ return ret;
+
+ *width = (u32)(ADV7482_HDMI_LWIDTH_MSBS_MASK & msb);
+ *width = (lsb | (*width << 8));
+
+ /* decide lines per frame */
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_F0HEIGHT_MSBS_REG, &msb);
+ if (ret < 0)
+ return ret;
+
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_F0HEIGHT_LSBS_REG, &lsb);
+ if (ret < 0)
+ return ret;
+
+ *height = (u32)(ADV7482_HDMI_F0HEIGHT_MSBS_MASK & msb);
+ *height = (lsb | (*height << 8));
+ if (!(*progressive))
+ *height = *height * 2;
+
+ if (*width == 0 || *height == 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int adv7482_set_vid_info(struct v4l2_subdev *sd)
+{
+ struct adv7482_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ u8 progressive;
+ u8 signal;
+ u8 val = ADV7482_IO_CP_VID_STD_480P;
+ u32 width;
+ u32 height;
+ int ret;
+
+ /* Get video information */
+ ret = adv7482_get_vid_info(sd, &progressive, &width, &height, &signal);
+ if (ret < 0) {
+ width = ADV7482_MAX_WIDTH;
+ height = ADV7482_MAX_HEIGHT;
+ progressive = 1;
+ } else {
+ if ((width == 640) &&
+ (height == 480) && (progressive == 1)) {
+ val = ADV7482_IO_CP_VID_STD_VGA60;
+ dev_info(state->dev,
+ "Changed active resolution to 640x480p\n");
+ } else if ((width == 720) &&
+ (height == 480) && (progressive == 1)) {
+ val = ADV7482_IO_CP_VID_STD_480P;
+ dev_info(state->dev,
+ "Changed active resolution to 720x480p\n");
+ } else if ((width == 720) &&
+ (height == 576) && (progressive == 1)) {
+ val = ADV7482_IO_CP_VID_STD_576P;
+ dev_info(state->dev,
+ "Changed active resolution to 720x576p\n");
+ } else if ((width == 1280) &&
+ (height == 720) && (progressive == 1)) {
+ val = ADV7482_IO_CP_VID_STD_720P;
+ dev_info(state->dev,
+ "Changed active resolution to 1280x720p\n");
+ } else if ((width == 1920) &&
+ (height == 1080) && (progressive == 1)) {
+ val = ADV7482_IO_CP_VID_STD_1080P;
+ dev_info(state->dev,
+ "Changed active resolution to 1920x1080p\n");
+ } else if ((width == 1920) &&
+ (height == 1080) && (progressive == 0)) {
+ val = ADV7482_IO_CP_VID_STD_1080I;
+ dev_info(state->dev,
+ "Changed active resolution to 1920x1080i\n");
+ } else {
+ dev_err(state->dev,
+ "Not support resolution %dx%d%c\n",
+ width, height, (progressive) ? 'p' : 'i');
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * The resolution of 720p, 1080i and 1080p is Hsync width of 40 pixel
+ * clock cycles. These resolutions must be shifted horizontally to
+ * the left in active video mode.
+ */
+ if ((val == ADV7482_IO_CP_VID_STD_1080I) ||
+ (val == ADV7482_IO_CP_VID_STD_1080P)) {
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x43);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8C, 0xD4);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x4F);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8D, 0xD4);
+ } else if (val == ADV7482_IO_CP_VID_STD_720P) {
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x43);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8C, 0xD8);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x4F);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8D, 0xD8);
+ } else {
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x40);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8C, 0x00);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8B, 0x40);
+ ret = adv7482_write_register(client,
+ ADV7482_I2C_CP, 0x8D, 0x00);
+ }
+
+ ret = adv7482_write_register(client, ADV7482_I2C_IO,
+ ADV7482_IO_CP_VID_STD_REG, val);
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* V4L2 decoder i/f handler for v4l2_subdev_core_ops */
+/*****************************************************************************/
+
+/*
+ * adv7482_querystd() - V4L2 decoder i/f handler for querystd
+ * @sd: ptr to v4l2_subdev struct
+ * @std: standard input video id
+ *
+ * Obtains the video standard input id
+ */
+static int adv7482_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7482_state *state = to_state(sd);
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+ int err = mutex_lock_interruptible(&state->mutex);
+
+ if (err)
+ return err;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ /* when we are interrupt driven we know the state */
+ if (!state->autodetect)
+ *std = state->curr_norm;
+ else
+ err = __adv7482_status(state, NULL, std);
+ else
+ *std = V4L2_STD_ATSC;
+
+ mutex_unlock(&state->mutex);
+
+ return err;
+}
+
+/*
+ * adv7482_g_input_status() - V4L2 decoder i/f handler for g_input_status
+ * @sd: ptr to v4l2_subdev struct
+ * @status: video input status flag
+ *
+ * Obtains the video input status flags.
+ */
+static int adv7482_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7482_state *state = to_state(sd);
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+ u8 status1 = 0;
+ int ret = mutex_lock_interruptible(&state->mutex);
+
+ if (ret)
+ return ret;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ ret = __adv7482_status(state, status, NULL);
+ else {
+ ret = adv7482_read_register(client, ADV7482_I2C_HDMI,
+ ADV7482_HDMI_STATUS1_REG, &status1);
+ if (ret < 0)
+ goto out;
+
+ if (!(status1 & ADV7482_HDMI_VF_LOCKED_FLG))
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ else if (!(status1 & ADV7482_HDMI_DERF_LOCKED_FLG))
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ else
+ *status = 0;
+ }
+
+ ret = 0;
+out:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv7482_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct adv7482_state *state = to_state(sd);
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ if (code->index != 0)
+ return -EINVAL;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ else
+ code->code = MEDIA_BUS_FMT_RGB888_1X24;
+
+ return 0;
+}
+
+static int adv7482_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7482_state *state = to_state(sd);
+
+ u8 progressive;
+ u8 signal;
+ u32 width;
+ u32 height;
+ int ret;
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+ u8 status_reg_10;
+ struct adv7482_sdp_main_info sdp_info;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+ ret = __adv7482_status(state, NULL, &state->curr_norm);
+ if (ret < 0)
+ return ret;
+
+ fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->width = 720;
+ fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
+
+ /* Get video information */
+ ret = adv7482_read_sdp_main_info(client, &sdp_info);
+ if (ret < 0)
+ return ret;
+
+ status_reg_10 = sdp_info.status_reg_10;
+
+ if (status_reg_10 < 0)
+ dev_info(state->dev,
+ "Not detect any video input signal\n");
+ else {
+ if ((status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK) &&
+ (status_reg_10 & ADV7482_SDP_R_REG_10_FSC_LOCK)
+ && (((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_M) ==
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_M) ||
+ ((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_60) ==
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_60) ||
+ ((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G) ==
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_B_G) ||
+ ((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB) ==
+ ADV7482_SDP_R_REG_10_AUTOD_PAL_COMB)))
+ dev_info(state->dev,
+ "Detected the PAL video input signal\n");
+ else if ((status_reg_10 & ADV7482_SDP_R_REG_10_IN_LOCK)
+ && (status_reg_10 &
+ ADV7482_SDP_R_REG_10_FSC_LOCK) &&
+ (((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43) ==
+ ADV7482_SDP_R_REG_10_AUTOD_NTSC_4_43) ||
+ ((status_reg_10 &
+ ADV7482_SDP_R_REG_10_AUTOD_MASK) ==
+ ADV7482_SDP_R_REG_10_AUTOD_NTSM_M_J)))
+ dev_info(state->dev,
+ "Detected the NTSC video input signal\n");
+ else
+ dev_info(state->dev,
+ "Not detect any video input signal\n");
+ }
+
+ state->width = fmt->width;
+ state->height = fmt->height;
+ state->field = V4L2_FIELD_INTERLACED;
+
+ } else {
+ fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ /* Get video information */
+ ret = adv7482_get_vid_info(sd, &progressive,
+ &width, &height, &signal);
+ if (ret < 0) {
+ width = ADV7482_MAX_WIDTH;
+ height = ADV7482_MAX_HEIGHT;
+ progressive = 1;
+ }
+
+ if (signal)
+ dev_info(state->dev,
+ "Detected the HDMI video input signal (%dx%d%c)\n",
+ width, height, (progressive) ? 'p' : 'i');
+ else
+ dev_info(state->dev,
+ "Not detect any video input signal\n");
+
+ state->width = width;
+ state->height = height;
+ state->field =
+ (progressive) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
+
+ fmt->width = state->width;
+ fmt->height = state->height;
+ }
+
+ return 0;
+}
+
+/*
+ * adv7482_cropcap() - V4L2 decoder i/f handler for cropcap
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 cropcap structure
+ *
+ * Gets cropping limits, default cropping rectangle and pixel aspect.
+ */
+static int adv7482_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct adv7482_state *state = to_state(sd);
+ u8 progressive;
+ u32 width;
+ u32 height;
+ int ret;
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ /* cropping limits */
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+ ret = __adv7482_status(state, NULL, &state->curr_norm);
+ if (ret < 0)
+ return ret;
+
+ a->bounds.width = 720;
+ a->bounds.height = state->curr_norm & V4L2_STD_525_60
+ ? 480 : 576;
+ } else {
+ /* Get video information */
+ ret = adv7482_get_vid_info(sd, &progressive,
+ &width, &height, NULL);
+ if (ret < 0) {
+ a->bounds.width = ADV7482_MAX_WIDTH;
+ a->bounds.height = ADV7482_MAX_HEIGHT;
+ } else {
+ a->bounds.width = width;
+ a->bounds.height = height;
+ }
+ }
+
+ /* default cropping rectangle */
+ a->defrect = a->bounds;
+
+ /* does not support scaling */
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+/*
+ * adv7482_g_crop() - V4L2 decoder i/f handler for g_crop
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 cropcap structure
+ *
+ * Gets current cropping rectangle.
+ */
+static int adv7482_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct adv7482_state *state = to_state(sd);
+ u8 progressive;
+ u32 width;
+ u32 height;
+ int ret;
+
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ a->c.left = 0;
+ a->c.top = 0;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+ ret = __adv7482_status(state, NULL, &state->curr_norm);
+ if (ret < 0)
+ return ret;
+
+ a->c.width = 720;
+ a->c.height = state->curr_norm & V4L2_STD_525_60
+ ? 480 : 576;
+ } else {
+ /* Get video information */
+ ret = adv7482_get_vid_info(sd, &progressive,
+ &width, &height, NULL);
+ if (ret < 0) {
+ a->c.width = ADV7482_MAX_WIDTH;
+ a->c.height = ADV7482_MAX_HEIGHT;
+ } else {
+ a->c.width = width;
+ a->c.height = height;
+ }
+ }
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int adv7482_set_field_mode(struct adv7482_state *state)
+{
+ /* FIXME */
+
+ return 0;
+}
+
+static int adv7482_set_power(struct adv7482_state *state, bool on)
+{
+ u8 val;
+ int ret;
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422) {
+ ret = adv7482_read_register(state->client, ADV7482_I2C_TXB,
+ 0x1E, &val);
+ if (ret < 0)
+ return ret;
+
+ if (on && ((val & 0x40) == 0)) {
+ /* Power up */
+ ret = adv7482_write_registers(state->client,
+ adv7482_power_up_txb_1lane);
+ if (ret < 0)
+ goto fail;
+ } else {
+ /* Power down */
+ ret = adv7482_write_registers(state->client,
+ adv7482_power_down_txb_1lane);
+ if (ret < 0)
+ goto fail;
+ }
+ } else {
+ /* set active resolution */
+ ret = adv7482_set_vid_info(&state->sd);
+ ret = adv7482_read_register(state->client, ADV7482_I2C_TXA,
+ 0x1E, &val);
+ if (ret < 0)
+ return ret;
+
+ if (on && ((val & 0x40) == 0)) {
+ /* Power up */
+ ret = adv7482_write_registers(state->client,
+ adv7482_power_up_txa_4lane);
+ if (ret < 0)
+ goto fail;
+ } else {
+ /* Power down */
+ ret = adv7482_write_registers(state->client,
+ adv7482_power_down_txa_4lane);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+ return 0;
+fail:
+ pr_info("%s: Failed set power operation, ret = %d\n", __func__, ret);
+ return ret;
+}
+
+static int adv7482_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct adv7482_state *state = to_state(sd);
+ int ret;
+
+ ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = adv7482_set_power(state, on);
+ if (ret == 0)
+ state->powered = on;
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv7482_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7482_state *state = to_state(sd);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
+ else {
+ adv7482_mbus_fmt(sd, &format->format);
+ format->format.field = state->field;
+ }
+
+ return 0;
+}
+
+static int adv7482_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7482_state *state = to_state(sd);
+ struct v4l2_mbus_framefmt *framefmt;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ framefmt = &format->format;
+ if (state->field != format->format.field) {
+ state->field = format->format.field;
+ adv7482_set_power(state, false);
+ adv7482_set_field_mode(state);
+ adv7482_set_power(state, true);
+ }
+ } else {
+ adv7482_mbus_fmt(sd, &format->format);
+
+ if (format->format.field == V4L2_FIELD_ANY)
+ format->format.field = state->field;
+
+ return 0;
+ }
+
+ return adv7482_mbus_fmt(sd, framefmt);
+}
+
+/*
+ * adv7482_g_mbus_config() - V4L2 decoder i/f handler for g_mbus_config
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @cfg: pointer to V4L2 mbus_config structure
+ *
+ * Get mbus configuration.
+ */
+static int adv7482_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct adv7482_state *state = to_state(sd);
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ cfg->flags = V4L2_MBUS_CSI2_1_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+ else
+ cfg->flags = V4L2_MBUS_CSI2_LANES |
+ V4L2_MBUS_CSI2_CHANNELS |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ cfg->type = V4L2_MBUS_CSI2;
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+/* V4L2 decoder i/f handler for v4l2_ctrl_ops */
+/*****************************************************************************/
+static int adv7482_cp_s_ctrl(struct v4l2_ctrl *ctrl, struct i2c_client *client)
+{
+ u8 val;
+ int ret;
+
+ /* Enable video adjustment first */
+ ret = adv7482_read_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_VID_ADJ_REG, &val);
+ if (ret < 0)
+ return ret;
+ val |= ADV7482_CP_VID_ADJ_ENABLE;
+ ret = adv7482_write_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_VID_ADJ_REG, val);
+ if (ret < 0)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if ((ctrl->val < ADV7482_CP_BRI_MIN) ||
+ (ctrl->val > ADV7482_CP_BRI_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_BRI_REG, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ if ((ctrl->val < ADV7482_CP_HUE_MIN) ||
+ (ctrl->val > ADV7482_CP_HUE_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_HUE_REG, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ if ((ctrl->val < ADV7482_CP_CON_MIN) ||
+ (ctrl->val > ADV7482_CP_CON_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_CON_REG, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ if ((ctrl->val < ADV7482_CP_SAT_MIN) ||
+ (ctrl->val > ADV7482_CP_SAT_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_CP,
+ ADV7482_CP_SAT_REG, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int adv7482_sdp_s_ctrl(struct v4l2_ctrl *ctrl, struct i2c_client *client)
+{
+ int ret;
+
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_CTRL, ADV7482_SDP_MAIN_MAP_RW);
+ if (ret < 0)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if ((ctrl->val < ADV7482_SDP_BRI_MIN) ||
+ (ctrl->val > ADV7482_SDP_BRI_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_BRI, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ if ((ctrl->val < ADV7482_SDP_HUE_MIN) ||
+ (ctrl->val > ADV7482_SDP_HUE_MAX))
+ ret = -ERANGE;
+ else
+ /*Hue is inverted according to HSL chart */
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_HUE, -ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ if ((ctrl->val < ADV7482_SDP_CON_MIN) ||
+ (ctrl->val > ADV7482_SDP_CON_MAX))
+ ret = -ERANGE;
+ else
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_CON, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ /*
+ *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
+ *Let's not confuse the user, everybody understands saturation
+ */
+ if ((ctrl->val < ADV7482_SDP_SAT_MIN) ||
+ (ctrl->val > ADV7482_SDP_SAT_MAX))
+ ret = -ERANGE;
+ else {
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_SD_SAT_CB, ctrl->val);
+ if (ret < 0)
+ break;
+
+ ret = adv7482_write_register(client, ADV7482_I2C_SDP,
+ ADV7482_SDP_REG_SD_SAT_CR, ctrl->val);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * adv7482_s_ctrl() - V4L2 decoder i/f handler for s_ctrl
+ * @ctrl: pointer to standard V4L2 control structure
+ *
+ * Set a control in ADV7482 decoder device.
+ */
+static int adv7482_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_adv7482_sd(ctrl);
+ struct adv7482_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ struct adv7482_link_config *config = &state->mipi_csi2_link[0];
+
+ if (ret)
+ return ret;
+
+ if (config->input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ ret = adv7482_sdp_s_ctrl(ctrl, client);
+ else
+ ret = adv7482_cp_s_ctrl(ctrl, client);
+
+ mutex_unlock(&state->mutex);
+
+ return ret;
+}
+
+
+static const struct v4l2_subdev_core_ops adv7482_core_ops = {
+ .queryctrl = v4l2_subdev_queryctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_power = adv7482_s_power,
+};
+
+static const struct v4l2_subdev_video_ops adv7482_video_ops = {
+ .querystd = adv7482_querystd,
+ .g_input_status = adv7482_g_input_status,
+ .cropcap = adv7482_cropcap,
+ .g_crop = adv7482_g_crop,
+ .g_mbus_config = adv7482_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops adv7482_pad_ops = {
+ .enum_mbus_code = adv7482_enum_mbus_code,
+ .set_fmt = adv7482_set_pad_format,
+ .get_fmt = adv7482_get_pad_format,
+};
+
+static const struct v4l2_subdev_ops adv7482_ops = {
+ .core = &adv7482_core_ops,
+ .video = &adv7482_video_ops,
+ .pad = &adv7482_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops adv7482_ctrl_ops = {
+ .s_ctrl = adv7482_s_ctrl,
+};
+
+/*
+ * adv7482_init_controls() - Init controls
+ * @state: pointer to private state structure
+ *
+ * Init ADV7482 supported control handler.
+ */
+static int adv7482_cp_init_controls(struct adv7482_state *state)
+{
+ v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7482_CP_BRI_MIN,
+ ADV7482_CP_BRI_MAX, 1, ADV7482_CP_BRI_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_CONTRAST, ADV7482_CP_CON_MIN,
+ ADV7482_CP_CON_MAX, 1, ADV7482_CP_CON_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_SATURATION, ADV7482_CP_SAT_MIN,
+ ADV7482_CP_SAT_MAX, 1, ADV7482_CP_SAT_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_HUE, ADV7482_CP_HUE_MIN,
+ ADV7482_CP_HUE_MAX, 1, ADV7482_CP_HUE_DEF);
+ state->sd.ctrl_handler = &state->ctrl_hdl;
+ if (state->ctrl_hdl.error) {
+ int err = state->ctrl_hdl.error;
+
+ v4l2_ctrl_handler_free(&state->ctrl_hdl);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+ return 0;
+}
+
+static int adv7482_sdp_init_controls(struct adv7482_state *state)
+{
+ v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
+
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7482_SDP_BRI_MIN,
+ ADV7482_SDP_BRI_MAX, 1, ADV7482_SDP_BRI_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_CONTRAST, ADV7482_SDP_CON_MIN,
+ ADV7482_SDP_CON_MAX, 1, ADV7482_SDP_CON_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_SATURATION, ADV7482_SDP_SAT_MIN,
+ ADV7482_SDP_SAT_MAX, 1, ADV7482_SDP_SAT_DEF);
+ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7482_ctrl_ops,
+ V4L2_CID_HUE, ADV7482_SDP_HUE_MIN,
+ ADV7482_SDP_HUE_MAX, 1, ADV7482_SDP_HUE_DEF);
+
+ state->sd.ctrl_handler = &state->ctrl_hdl;
+ if (state->ctrl_hdl.error) {
+ int err = state->ctrl_hdl.error;
+
+ v4l2_ctrl_handler_free(&state->ctrl_hdl);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->ctrl_hdl);
+
+ return 0;
+}
+
+static void adv7482_exit_controls(struct adv7482_state *state)
+{
+ v4l2_ctrl_handler_free(&state->ctrl_hdl);
+}
+
+/*****************************************************************************/
+/* i2c driver interface handlers */
+/*****************************************************************************/
+
+static int adv7482_parse_dt(struct device_node *np,
+ struct adv7482_link_config *config)
+{
+ struct v4l2_of_endpoint bus_cfg;
+ struct device_node *endpoint;
+ const char *str;
+ int ret;
+ int ch;
+
+ /* Parse the endpoint. */
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint)
+ return -EINVAL;
+
+ v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ of_node_put(endpoint);
+
+ /* check input-interface */
+ ret = of_property_read_string(np, "adi,input-interface", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "rgb888")) {
+ config->input_interface = DECODER_INPUT_INTERFACE_RGB888;
+ config->regs =
+ (struct adv7482_reg_value *)adv7482_init_txa_4lane;
+ config->power_up =
+ (struct adv7482_reg_value *)adv7482_power_up_txa_4lane;
+ config->power_down =
+ (struct adv7482_reg_value *)adv7482_power_down_txa_4lane;
+ config->init_controls =
+ (int (*)(void *))adv7482_cp_init_controls;
+ } else {
+ config->input_interface = DECODER_INPUT_INTERFACE_YCBCR422;
+ config->regs =
+ (struct adv7482_reg_value *)adv7482_init_txb_1lane;
+ config->power_up =
+ (struct adv7482_reg_value *)adv7482_power_up_txb_1lane;
+ config->power_down =
+ (struct adv7482_reg_value *)adv7482_power_down_txb_1lane;
+ config->init_controls =
+ (int (*)(void *))adv7482_sdp_init_controls;
+ }
+
+ ret = of_property_read_string(np, "adi,input-hdmi", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "on"))
+ config->hdmi_in = 1;
+ else
+ config->hdmi_in = 0;
+
+ ret = of_property_read_string(np, "adi,input-sdp", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "on"))
+ config->sdp_in = 1;
+ else
+ config->sdp_in = 0;
+
+ ret = of_property_read_string(np, "adi,sw-reset", &str);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(str, "on"))
+ config->sw_reset = 1;
+ else
+ config->sw_reset = 0;
+
+ ret = of_property_read_u32(np, "adi,virtual-channel", &ch);
+ if (ret < 0)
+ return ret;
+
+ if ((ch < 0) || (ch > 3))
+ return -EINVAL;
+
+ config->vc_ch = ch;
+
+ config->init_device = NULL;
+ config->s_power = NULL;
+ config->s_ctrl = NULL;
+ config->enum_mbus_code = NULL;
+ config->set_pad_format = NULL;
+ config->get_pad_format = NULL;
+ config->s_std = NULL;
+ config->querystd = NULL;
+ config->g_input_status = NULL;
+ config->s_routing = NULL;
+ config->g_mbus_config = NULL;
+
+ return 0;
+}
+
+/*
+ * adv7482_probe - Probe a ADV7482 device
+ * @client: pointer to i2c_client structure
+ * @id: pointer to i2c_device_id structure
+ *
+ * Initialize the ADV7482 device
+ */
+static int adv7482_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7482_state *state;
+ struct adv7482_link_config link_config;
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct adv7482_state), GFP_KERNEL);
+ if (state == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ state->client = client;
+ state->irq = client->irq;
+
+ ret = adv7482_parse_dt(dev->of_node, &link_config);
+ if (ret) {
+ dev_err(&client->dev, "adv7482 parse error\n");
+ return ret;
+ }
+
+ state->mipi_csi2_link[0].input_interface = link_config.input_interface;
+
+ mutex_init(&state->mutex);
+ state->autodetect = true;
+ state->powered = true;
+ sd = &state->sd;
+ state->width = ADV7482_MAX_WIDTH;
+ state->height = ADV7482_MAX_HEIGHT;
+ state->field = V4L2_FIELD_NONE;
+
+ v4l2_i2c_subdev_init(sd, client, &adv7482_ops);
+
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ state->dev = dev;
+ state->mipi_csi2_link[0].dev = dev;
+
+ /* SW reset ADV7482 to its default values */
+ if (link_config.sw_reset) {
+ ret = adv7482_write_registers(client, adv7482_sw_reset);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* check rd_info */
+ {
+ u8 msb;
+ u8 lsb;
+
+ ret = adv7482_read_register(client, ADV7482_I2C_IO,
+ ADV7482_IO_RD_INFO1_REG, &lsb);
+ if (ret < 0)
+ return ret;
+
+ ret = adv7482_read_register(client, ADV7482_I2C_IO,
+ ADV7482_IO_RD_INFO2_REG, &msb);
+ if (ret < 0)
+ return ret;
+
+ v4l_info(client, "adv7482 revision is %02x%02x\n",
+ lsb, msb);
+ }
+ }
+
+ if (link_config.hdmi_in) {
+ ret = adv7482_write_registers(client,
+ adv7482_init_txa_4lane);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* Power down */
+ ret = adv7482_write_registers(client,
+ adv7482_power_down_txa_4lane);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ v4l_info(client, "adv7482 txa power down\n");
+ } else
+ v4l_info(client, "adv7482 hdmi_in is disabled.\n");
+
+ /* Initializes ADV7482 to its default values */
+ if (link_config.sdp_in) {
+ ret = adv7482_write_registers(client,
+ adv7482_init_txb_1lane);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* Power down */
+ ret = adv7482_write_registers(client,
+ adv7482_power_down_txb_1lane);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ v4l_info(client, "adv7482 txb power down\n");
+ } else
+ v4l_info(client, "adv7482 sdp_in is disabled.\n");
+
+ if (link_config.sdp_in && link_config.hdmi_in) {
+ /* Power up hdmi rx */
+ ret = adv7482_write_registers(client,
+ adv7482_power_up_hdmi_rx);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* Enable csi4 and sci1 */
+ ret = adv7482_write_registers(client,
+ adv7482_enable_csi4_csi1);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ v4l_info(client, "adv7482 enable csi1 and csi4\n");
+ }
+
+ if (link_config.input_interface == DECODER_INPUT_INTERFACE_YCBCR422)
+ ret = adv7482_sdp_init_controls(state);
+ else
+ ret = adv7482_cp_init_controls(state);
+ if (ret)
+ goto err_unreg_subdev;
+
+ /* Setting virtual channel for ADV7482 */
+ if (link_config.vc_ch == 0)
+ ret = adv7482_write_registers(client,
+ adv7482_set_virtual_channel0);
+ else if (link_config.vc_ch == 1)
+ ret = adv7482_write_registers(client,
+ adv7482_set_virtual_channel1);
+ else if (link_config.vc_ch == 2)
+ ret = adv7482_write_registers(client,
+ adv7482_set_virtual_channel2);
+ else if (link_config.vc_ch == 3)
+ ret = adv7482_write_registers(client,
+ adv7482_set_virtual_channel3);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ state->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
+ ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
+ if (ret)
+ goto err_free_ctrl;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto err_free_ctrl;
+
+ return 0;
+
+err_free_ctrl:
+ adv7482_exit_controls(state);
+
+err_unreg_subdev:
+ mutex_destroy(&state->mutex);
+ v4l2_device_unregister_subdev(sd);
+ kfree(state);
+err:
+ dev_err(&client->dev, ": Failed to probe: %d\n", ret);
+ return ret;
+}
+
+/*
+ * adv7482_remove - Remove ADV7482 device support
+ * @client: pointer to i2c_client structure
+ *
+ * Reset the ADV7482 device
+ */
+static int adv7482_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7482_state *state = to_state(sd);
+
+ v4l2_async_unregister_subdev(sd);
+
+ media_entity_cleanup(&sd->entity);
+ adv7482_exit_controls(state);
+
+ mutex_destroy(&state->mutex);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id adv7482_id[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * adv7482_suspend - Suspend ADV7482 device
+ * @client: pointer to i2c_client structure
+ * @state: power management state
+ *
+ * Power down the ADV7482 device
+ */
+static int adv7482_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = adv7482_write_register(client, ADV7482_I2C_IO,
+ ADV7482_IO_PWR_MAN_REG, ADV7482_IO_PWR_OFF);
+
+ return ret;
+}
+
+/*
+ * adv7482_resume - Resume ADV7482 device
+ * @client: pointer to i2c_client structure
+ *
+ * Power on and initialize the ADV7482 device
+ */
+static int adv7482_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adv7482_link_config link_config;
+ int ret;
+
+ ret = adv7482_write_register(client, ADV7482_I2C_IO,
+ ADV7482_IO_PWR_MAN_REG, ADV7482_IO_PWR_ON);
+ if (ret < 0)
+ return ret;
+
+ ret = adv7482_parse_dt(dev->of_node, &link_config);
+ if (ret)
+ return ret;
+
+ /* Initializes ADV7482 to its default values */
+ ret = adv7482_write_registers(client, link_config.regs);
+
+ /* Power down */
+ ret = adv7482_write_registers(client, link_config.power_down);
+
+ if (link_config.sdp_in && link_config.hdmi_in) {
+ /* Power up hdmi rx */
+ ret = adv7482_write_registers(client,
+ adv7482_power_up_hdmi_rx);
+ if (ret < 0)
+ return ret;
+
+ /* Enable csi4 and sci1 */
+ ret = adv7482_write_registers(client,
+ adv7482_enable_csi4_csi1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(adv7482_pm_ops, adv7482_suspend, adv7482_resume);
+#define ADV7482_PM_OPS (&adv7482_pm_ops)
+
+#else
+#define ADV7482_PM_OPS NULL
+#endif
+
+MODULE_DEVICE_TABLE(i2c, adv7482_id);
+
+static const struct of_device_id adv7482_of_ids[] = {
+ { .compatible = "adi,adv7482", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv7482_of_ids);
+
+static struct i2c_driver adv7482_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = ADV7482_PM_OPS,
+ .of_match_table = adv7482_of_ids,
+ },
+ .probe = adv7482_probe,
+ .remove = adv7482_remove,
+ .id_table = adv7482_id,
+};
+
+module_i2c_driver(adv7482_driver);
+
+MODULE_DESCRIPTION("HDMI Receiver ADV7482 video decoder driver");
+MODULE_ALIAS("platform:adv7482");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 0141af8..611b40c 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -273,6 +273,27 @@
To compile this driver as a module, choose M here: the module
will be called vsp1.
+config VIDEO_RENESAS_DEBUG
+ bool "Renesas VSP1 underrun debug messages"
+ depends on VIDEO_RENESAS_VSP1
+ ---help---
+ Enable debug underrun messages on Renesas VSP1 Video Processing
+ Engine driver.
+ If you set to enable, When an underrun occurred, a debug underrun
+ message is output.
+
+config VIDEO_RENESAS_VSP_ALPHA_BIT_ARGB1555
+ int "Renesas VSP alpha bit function of ARGB1555"
+ depends on VIDEO_RENESAS_VSP1
+ range 0 1
+ default 0
+ help
+ Choose this option if you select A bit function of ARGB1555.
+ If you set value to 0, pixel alpha blending is performed
+ when the value of A is 0.
+ If you set value to 1, pixel alpha blending is performed
+ when the value of A is 1.
+
config VIDEO_TI_VPE
tristate "TI VPE (Video Processing Engine) driver"
depends on VIDEO_DEV && VIDEO_V4L2
diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c
index 6a7bcc3..f674c68 100644
--- a/drivers/media/platform/rcar-fcp.c
+++ b/drivers/media/platform/rcar-fcp.c
@@ -107,6 +107,16 @@
EXPORT_SYMBOL_GPL(rcar_fcp_enable);
/**
+ * rcar_fcp_device - Get FCP device
+ * @fcp: The FCP instance
+ */
+struct device *rcar_fcp_device(struct rcar_fcp_device *fcp)
+{
+ return fcp ? fcp->dev : NULL;
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_device);
+
+/**
* rcar_fcp_disable - Disable an FCP
* @fcp: The FCP instance
*
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index 83029a4..826a958 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -35,6 +35,22 @@
---help---
This is a v4l2 driver for the R-Car VIN Interface
+config VIDEO_RCAR_VIN_DEBUG
+ bool "Renesas VIN overflow debug messages"
+ depends on VIDEO_RCAR_VIN
+ ---help---
+ Enable debug overflow messages on R-Car Video
+ Input driver.
+ If you set to enable, When an overflow occurred,
+ a debug overflow message is output.
+
+config VIDEO_RCAR_CSI2
+ tristate "R-Car MIPI CSI-2 Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
+ depends on ARCH_R8A7795 || ARCH_R8A7796 || COMPILE_TEST
+ ---help---
+ This is a v4l2 driver for the R-Car CSI-2 Interface
+
config VIDEO_SH_MOBILE_CSI2
tristate "SuperH Mobile MIPI CSI-2 Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
index 7ee71ae..0a193bd 100644
--- a/drivers/media/platform/soc_camera/Makefile
+++ b/drivers/media/platform/soc_camera/Makefile
@@ -10,4 +10,5 @@
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
+obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar_csi2.o
obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o
diff --git a/drivers/media/platform/soc_camera/rcar_csi2.c b/drivers/media/platform/soc_camera/rcar_csi2.c
new file mode 100644
index 0000000..851a4ca
--- /dev/null
+++ b/drivers/media/platform/soc_camera/rcar_csi2.c
@@ -0,0 +1,708 @@
+/*
+ * drivers/media/platform/soc_camera/rcar_csi2.c
+ * This file is the driver for the R-Car MIPI CSI-2 unit.
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
+ *
+ * This file is based on the drivers/media/platform/soc_camera/sh_mobile_csi2.c
+ *
+ * Driver for the SH-Mobile MIPI CSI-2 unit
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/rcar_csi2.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include <media/v4l2-of.h>
+
+#define DRV_NAME "rcar_csi2"
+#define CONNECT_SLAVE_NAME "adv7482"
+#define VC_MAX_CHANNEL 4
+
+#define RCAR_CSI2_TREF 0x00
+#define RCAR_CSI2_SRST 0x04
+#define RCAR_CSI2_PHYCNT 0x08
+#define RCAR_CSI2_CHKSUM 0x0C
+#define RCAR_CSI2_VCDT 0x10
+
+#define RCAR_CSI2_VCDT2 0x14 /* Channel Data Type Select */
+#define RCAR_CSI2_FRDT 0x18 /* Frame Data Type Select */
+#define RCAR_CSI2_FLD 0x1C /* Field Detection Control */
+#define RCAR_CSI2_ASTBY 0x20 /* Automatic standby control */
+#define RCAR_CSI2_LNGDT0 0x28
+#define RCAR_CSI2_LNGDT1 0x2C
+#define RCAR_CSI2_INTEN 0x30
+#define RCAR_CSI2_INTCLOSE 0x34
+#define RCAR_CSI2_INTSTATE 0x38
+#define RCAR_CSI2_INTERRSTATE 0x3C
+
+#define RCAR_CSI2_SHPDAT 0x40
+#define RCAR_CSI2_SHPCNT 0x44
+
+#define RCAR_CSI2_LINKCNT 0x48
+#define RCAR_CSI2_LSWAP 0x4C
+#define RCAR_CSI2_PHTC 0x58
+#define RCAR_CSI2_PHYPLL 0x68
+
+#define RCAR_CSI2_PHEERM 0x74
+#define RCAR_CSI2_PHCLM 0x78
+#define RCAR_CSI2_PHDLM 0x7C
+
+#define RCAR_CSI2_PHYCNT_SHUTDOWNZ (1 << 17)
+#define RCAR_CSI2_PHYCNT_RSTZ (1 << 16)
+#define RCAR_CSI2_PHYCNT_ENABLECLK (1 << 4)
+#define RCAR_CSI2_PHYCNT_ENABLE_3 (1 << 3)
+#define RCAR_CSI2_PHYCNT_ENABLE_2 (1 << 2)
+#define RCAR_CSI2_PHYCNT_ENABLE_1 (1 << 1)
+#define RCAR_CSI2_PHYCNT_ENABLE_0 (1 << 0)
+
+#define RCAR_CSI2_VCDT_VCDTN_EN (1 << 15)
+#define RCAR_CSI2_VCDT_SEL_VCN (1 << 8)
+#define RCAR_CSI2_VCDT_SEL_DTN_ON (1 << 6)
+#define RCAR_CSI2_VCDT_SEL_DTN (1 << 0)
+
+#define RCAR_CSI2_LINKCNT_MONITOR_EN (1 << 31)
+#define RCAR_CSI2_LINKCNT_REG_MONI_PACT_EN (1 << 25)
+
+#define RCAR_CSI2_LSWAP_L3SEL_PLANE0 (0 << 6)
+#define RCAR_CSI2_LSWAP_L3SEL_PLANE1 (1 << 6)
+#define RCAR_CSI2_LSWAP_L3SEL_PLANE2 (2 << 6)
+#define RCAR_CSI2_LSWAP_L3SEL_PLANE3 (3 << 6)
+
+#define RCAR_CSI2_LSWAP_L2SEL_PLANE0 (0 << 4)
+#define RCAR_CSI2_LSWAP_L2SEL_PLANE1 (1 << 4)
+#define RCAR_CSI2_LSWAP_L2SEL_PLANE2 (2 << 4)
+#define RCAR_CSI2_LSWAP_L2SEL_PLANE3 (3 << 4)
+
+#define RCAR_CSI2_LSWAP_L1SEL_PLANE0 (0 << 2)
+#define RCAR_CSI2_LSWAP_L1SEL_PLANE1 (1 << 2)
+#define RCAR_CSI2_LSWAP_L1SEL_PLANE2 (2 << 2)
+#define RCAR_CSI2_LSWAP_L1SEL_PLANE3 (3 << 2)
+
+#define RCAR_CSI2_LSWAP_L0SEL_PLANE0 (0 << 0)
+#define RCAR_CSI2_LSWAP_L0SEL_PLANE1 (1 << 0)
+#define RCAR_CSI2_LSWAP_L0SEL_PLANE2 (2 << 0)
+#define RCAR_CSI2_LSWAP_L0SEL_PLANE3 (3 << 0)
+
+#define RCAR_CSI2_PHTC_TESTCLR (1 << 0)
+
+/* interrupt status registers */
+#define RCAR_CSI2_INTSTATE_EBD_CH1 (1 << 29)
+#define RCAR_CSI2_INTSTATE_LESS_THAN_WC (1 << 28)
+#define RCAR_CSI2_INTSTATE_AFIFO_OF (1 << 27)
+#define RCAR_CSI2_INTSTATE_VD4_START (1 << 26)
+#define RCAR_CSI2_INTSTATE_VD4_END (1 << 25)
+#define RCAR_CSI2_INTSTATE_VD3_START (1 << 24)
+#define RCAR_CSI2_INTSTATE_VD3_END (1 << 23)
+#define RCAR_CSI2_INTSTATE_VD2_START (1 << 22)
+#define RCAR_CSI2_INTSTATE_VD2_END (1 << 21)
+#define RCAR_CSI2_INTSTATE_VD1_START (1 << 20)
+#define RCAR_CSI2_INTSTATE_VD1_END (1 << 19)
+#define RCAR_CSI2_INTSTATE_SHP (1 << 18)
+#define RCAR_CSI2_INTSTATE_FSFE (1 << 17)
+#define RCAR_CSI2_INTSTATE_LNP (1 << 16)
+#define RCAR_CSI2_INTSTATE_CRC_ERR (1 << 15)
+#define RCAR_CSI2_INTSTATE_HD_WC_ZERO (1 << 14)
+#define RCAR_CSI2_INTSTATE_FRM_SEQ_ERR1 (1 << 13)
+#define RCAR_CSI2_INTSTATE_FRM_SEQ_ERR2 (1 << 12)
+#define RCAR_CSI2_INTSTATE_ECC_ERR (1 << 11)
+#define RCAR_CSI2_INTSTATE_ECC_CRCT_ERR (1 << 10)
+#define RCAR_CSI2_INTSTATE_LPDT_START (1 << 9)
+#define RCAR_CSI2_INTSTATE_LPDT_END (1 << 8)
+#define RCAR_CSI2_INTSTATE_ULPS_START (1 << 7)
+#define RCAR_CSI2_INTSTATE_ULPS_END (1 << 6)
+#define RCAR_CSI2_INTSTATE_RESERVED (1 << 5)
+#define RCAR_CSI2_INTSTATE_ERRSOTHS (1 << 4)
+#define RCAR_CSI2_INTSTATE_ERRSOTSYNCCHS (1 << 3)
+#define RCAR_CSI2_INTSTATE_ERRESC (1 << 2)
+#define RCAR_CSI2_INTSTATE_ERRSYNCESC (1 << 1)
+#define RCAR_CSI2_INTSTATE_ERRCONTROL (1 << 0)
+
+/* monitoring registers of interrupt error status */
+#define RCAR_CSI2_INTSTATE_ECC_ERR (1 << 11)
+#define RCAR_CSI2_INTSTATE_ECC_CRCT_ERR (1 << 10)
+#define RCAR_CSI2_INTSTATE_LPDT_START (1 << 9)
+#define RCAR_CSI2_INTSTATE_LPDT_END (1 << 8)
+#define RCAR_CSI2_INTSTATE_ULPS_START (1 << 7)
+#define RCAR_CSI2_INTSTATE_ULPS_END (1 << 6)
+#define RCAR_CSI2_INTSTATE_RESERVED (1 << 5)
+#define RCAR_CSI2_INTSTATE_ERRSOTHS (1 << 4)
+#define RCAR_CSI2_INTSTATE_ERRSOTSYNCCHS (1 << 3)
+#define RCAR_CSI2_INTSTATE_ERRESC (1 << 2)
+#define RCAR_CSI2_INTSTATE_ERRSYNCESC (1 << 1)
+#define RCAR_CSI2_INTSTATE_ERRCONTROL (1 << 0)
+
+enum chip_id {
+ RCAR_GEN3,
+ RCAR_GEN2,
+};
+
+enum decoder_input_interface {
+ DECODER_INPUT_INTERFACE_RGB888,
+ DECODER_INPUT_INTERFACE_YCBCR422,
+ DECODER_INPUT_INTERFACE_NONE,
+};
+
+/**
+ * struct rcar_csi2_link_config - Describes rcar_csi2 hardware configuration
+ * @input_colorspace: The input colorspace (RGB, YUV444, YUV422)
+ */
+struct rcar_csi2_link_config {
+ enum decoder_input_interface input_interface;
+ unsigned char lanes;
+ unsigned long vcdt;
+ unsigned long vcdt2;
+};
+
+#define INIT_RCAR_CSI2_LINK_CONFIG(m) \
+{ \
+ m.input_interface = DECODER_INPUT_INTERFACE_NONE; \
+ m.lanes = 0; \
+}
+
+struct rcar_csi_irq_counter_log {
+ unsigned long crc_err;
+};
+
+struct rcar_csi2 {
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int irq;
+ unsigned long mipi_flags;
+ void __iomem *base;
+ struct platform_device *pdev;
+ struct rcar_csi2_client_config *client;
+ unsigned long vcdt;
+ unsigned long vcdt2;
+
+ unsigned int field;
+ unsigned int code;
+ unsigned int lanes;
+ spinlock_t lock;
+};
+
+#define RCAR_CSI_80MBPS 0
+#define RCAR_CSI_90MBPS 1
+#define RCAR_CSI_100MBPS 2
+#define RCAR_CSI_110MBPS 3
+#define RCAR_CSI_120MBPS 4
+#define RCAR_CSI_130MBPS 5
+#define RCAR_CSI_140MBPS 6
+#define RCAR_CSI_150MBPS 7
+#define RCAR_CSI_160MBPS 8
+#define RCAR_CSI_170MBPS 9
+#define RCAR_CSI_180MBPS 10
+#define RCAR_CSI_190MBPS 11
+#define RCAR_CSI_205MBPS 12
+#define RCAR_CSI_220MBPS 13
+#define RCAR_CSI_235MBPS 14
+#define RCAR_CSI_250MBPS 15
+#define RCAR_CSI_275MBPS 16
+#define RCAR_CSI_300MBPS 17
+#define RCAR_CSI_325MBPS 18
+#define RCAR_CSI_350MBPS 19
+#define RCAR_CSI_400MBPS 20
+#define RCAR_CSI_450MBPS 21
+#define RCAR_CSI_500MBPS 22
+#define RCAR_CSI_550MBPS 23
+#define RCAR_CSI_600MBPS 24
+#define RCAR_CSI_650MBPS 25
+#define RCAR_CSI_700MBPS 26
+#define RCAR_CSI_750MBPS 27
+#define RCAR_CSI_800MBPS 28
+#define RCAR_CSI_850MBPS 29
+#define RCAR_CSI_900MBPS 30
+#define RCAR_CSI_950MBPS 31
+#define RCAR_CSI_1000MBPS 32
+#define RCAR_CSI_1050MBPS 33
+#define RCAR_CSI_1100MBPS 34
+#define RCAR_CSI_1150MBPS 35
+#define RCAR_CSI_1200MBPS 36
+#define RCAR_CSI_1250MBPS 37
+#define RCAR_CSI_1300MBPS 38
+#define RCAR_CSI_1350MBPS 39
+#define RCAR_CSI_1400MBPS 40
+#define RCAR_CSI_1450MBPS 41
+#define RCAR_CSI_1500MBPS 42
+
+static int rcar_csi2_set_phy_freq(struct rcar_csi2 *priv)
+{
+ const uint32_t const hs_freq_range[43] = {
+ 0x00, 0x10, 0x20, 0x30, 0x01, /* 0-4 */
+ 0x11, 0x21, 0x31, 0x02, 0x12, /* 5-9 */
+ 0x22, 0x32, 0x03, 0x13, 0x23, /* 10-14 */
+ 0x33, 0x04, 0x14, 0x05, 0x15, /* 15-19 */
+ 0x25, 0x06, 0x16, 0x07, 0x17, /* 20-24 */
+ 0x08, 0x18, 0x09, 0x19, 0x29, /* 25-29 */
+ 0x39, 0x0A, 0x1A, 0x2A, 0x3A, /* 30-34 */
+ 0x0B, 0x1B, 0x2B, 0x3B, 0x0C, /* 35-39 */
+ 0x1C, 0x2C, 0x3C /* 40-42 */
+ };
+ uint32_t bps_per_lane = RCAR_CSI_190MBPS;
+
+ dev_dbg(&priv->pdev->dev, "Input size (%dx%d%c)\n",
+ priv->mf->width, priv->mf->height,
+ (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i');
+
+ switch (priv->lanes) {
+ case 1:
+ bps_per_lane = RCAR_CSI_400MBPS;
+ break;
+ case 4:
+ if (priv->mf->field == V4L2_FIELD_NONE) {
+ if ((priv->mf->width == 1920) &&
+ (priv->mf->height == 1080))
+ bps_per_lane = RCAR_CSI_900MBPS;
+ else if ((priv->mf->width == 1280) &&
+ (priv->mf->height == 720))
+ bps_per_lane = RCAR_CSI_450MBPS;
+ else if ((priv->mf->width == 720) &&
+ (priv->mf->height == 480))
+ bps_per_lane = RCAR_CSI_190MBPS;
+ else if ((priv->mf->width == 720) &&
+ (priv->mf->height == 576))
+ bps_per_lane = RCAR_CSI_190MBPS;
+ else if ((priv->mf->width == 640) &&
+ (priv->mf->height == 480))
+ bps_per_lane = RCAR_CSI_100MBPS;
+ else
+ goto error;
+ } else {
+ if ((priv->mf->width == 1920) &&
+ (priv->mf->height == 1080))
+ bps_per_lane = RCAR_CSI_450MBPS;
+ else
+ goto error;
+ }
+ break;
+ default:
+ dev_err(&priv->pdev->dev, "ERROR: lanes is invalid (%d)\n",
+ priv->lanes);
+ return -EINVAL;
+ }
+
+ dev_dbg(&priv->pdev->dev, "bps_per_lane (%d)\n", bps_per_lane);
+
+ iowrite32((hs_freq_range[bps_per_lane] << 16),
+ priv->base + RCAR_CSI2_PHYPLL);
+ return 0;
+
+error:
+ dev_err(&priv->pdev->dev, "Not support resolution (%dx%d%c)\n",
+ priv->mf->width, priv->mf->height,
+ (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i');
+ return -EINVAL;
+}
+
+static irqreturn_t rcar_csi2_irq(int irq, void *data)
+{
+ struct rcar_csi2 *priv = data;
+ u32 int_status;
+ unsigned int handled = 0;
+
+ spin_lock(&priv->lock);
+
+ int_status = ioread32(priv->base + RCAR_CSI2_INTSTATE);
+ if (!int_status)
+ goto done;
+
+ /* ack interrupts */
+ iowrite32(int_status, priv->base + RCAR_CSI2_INTSTATE);
+ handled = 1;
+
+done:
+ spin_unlock(&priv->lock);
+
+ return IRQ_RETVAL(handled);
+
+}
+
+static void rcar_csi2_hwdeinit(struct rcar_csi2 *priv)
+{
+ iowrite32(0, priv->base + RCAR_CSI2_PHYCNT);
+
+ /* reset CSI2 hardware */
+ iowrite32(0x00000001, priv->base + RCAR_CSI2_SRST);
+ udelay(5);
+ iowrite32(0x00000000, priv->base + RCAR_CSI2_SRST);
+}
+
+static int rcar_csi2_hwinit(struct rcar_csi2 *priv)
+{
+ int ret;
+ __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */
+
+ /* Reflect registers immediately */
+ iowrite32(0x00000001, priv->base + RCAR_CSI2_TREF);
+ /* reset CSI2 hardware */
+ iowrite32(0x00000001, priv->base + RCAR_CSI2_SRST);
+ udelay(5);
+ iowrite32(0x00000000, priv->base + RCAR_CSI2_SRST);
+
+ iowrite32(0x00000000, priv->base + RCAR_CSI2_PHTC);
+
+ /* setting HS reception frequency */
+ {
+ switch (priv->lanes) {
+ case 1:
+ /* First field number setting */
+ iowrite32(0x0001000f, priv->base + RCAR_CSI2_FLD);
+ tmp |= 0x1;
+ break;
+ case 4:
+ /* First field number setting */
+ iowrite32(0x0002000f, priv->base + RCAR_CSI2_FLD);
+ tmp |= 0xF;
+ break;
+ default:
+ dev_err(&priv->pdev->dev,
+ "ERROR: lanes is invalid (%d)\n",
+ priv->lanes);
+ return -EINVAL;
+ }
+
+ /* set PHY frequency */
+ ret = rcar_csi2_set_phy_freq(priv);
+ if (ret < 0)
+ return ret;
+
+ /* Enable lanes */
+ iowrite32(tmp, priv->base + RCAR_CSI2_PHYCNT);
+
+ iowrite32(tmp | RCAR_CSI2_PHYCNT_SHUTDOWNZ,
+ priv->base + RCAR_CSI2_PHYCNT);
+ iowrite32(tmp | (RCAR_CSI2_PHYCNT_SHUTDOWNZ |
+ RCAR_CSI2_PHYCNT_RSTZ),
+ priv->base + RCAR_CSI2_PHYCNT);
+ }
+
+ iowrite32(0x00000003, priv->base + RCAR_CSI2_CHKSUM);
+ iowrite32(priv->vcdt, priv->base + RCAR_CSI2_VCDT);
+ iowrite32(priv->vcdt2, priv->base + RCAR_CSI2_VCDT2);
+ iowrite32(0x00010000, priv->base + RCAR_CSI2_FRDT);
+ udelay(10);
+ iowrite32(0x83000000, priv->base + RCAR_CSI2_LINKCNT);
+ iowrite32(0x000000e4, priv->base + RCAR_CSI2_LSWAP);
+
+ dev_dbg(&priv->pdev->dev, "CSI2 VCDT: 0x%x\n",
+ ioread32(priv->base + RCAR_CSI2_VCDT));
+ dev_dbg(&priv->pdev->dev, "CSI2 VCDT2: 0x%x\n",
+ ioread32(priv->base + RCAR_CSI2_VCDT2));
+
+ /* wait until video decoder power off */
+ msleep(10);
+ {
+ int timeout = 100;
+
+ /* Read the PHY clock lane monitor register (PHCLM). */
+ while (!(ioread32(priv->base + RCAR_CSI2_PHCLM) & 0x01)
+ && timeout) {
+ timeout--;
+ }
+ if (timeout == 0)
+ dev_err(&priv->pdev->dev,
+ "Timeout of reading the PHY clock lane\n");
+ else
+ dev_dbg(&priv->pdev->dev,
+ "Detected the PHY clock lane\n");
+
+ timeout = 100;
+
+ /* Read the PHY data lane monitor register (PHDLM). */
+ while (!(ioread32(priv->base + RCAR_CSI2_PHDLM) & 0x01)
+ && timeout) {
+ timeout--;
+ }
+ if (timeout == 0)
+ dev_err(&priv->pdev->dev,
+ "Timeout of reading the PHY data lane\n");
+ else
+ dev_dbg(&priv->pdev->dev,
+ "Detected the PHY data lane\n");
+ }
+
+ return 0;
+}
+
+static int rcar_csi2_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev);
+ struct v4l2_subdev *tmp_sd;
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret = 0;
+
+ if (on) {
+ v4l2_device_for_each_subdev(tmp_sd, sd->v4l2_dev) {
+ if (strncmp(tmp_sd->name, CONNECT_SLAVE_NAME,
+ sizeof(CONNECT_SLAVE_NAME) - 1) == 0) {
+ v4l2_subdev_call(tmp_sd, pad, get_fmt,
+ NULL, &fmt);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ priv->mf = mf;
+ pm_runtime_get_sync(&priv->pdev->dev);
+ ret = rcar_csi2_hwinit(priv);
+ if (ret < 0)
+ return ret;
+ } else {
+ rcar_csi2_hwdeinit(priv);
+ pm_runtime_put_sync(&priv->pdev->dev);
+ }
+
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops rcar_csi2_subdev_core_ops = {
+ .s_power = rcar_csi2_s_power,
+};
+
+static struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
+ .core = &rcar_csi2_subdev_core_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rcar_csi2_of_table[] = {
+ { .compatible = "renesas,csi2-r8a7796", .data = (void *)RCAR_GEN3 },
+ { .compatible = "renesas,csi2-r8a7795", .data = (void *)RCAR_GEN3 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rcar_csi2_of_table);
+#endif
+
+static struct platform_device_id rcar_csi2_id_table[] = {
+ { "r8a7796-csi2", RCAR_GEN3 },
+ { "r8a7795-csi2", RCAR_GEN3 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, rcar_csi2_id_table);
+
+static int rcar_csi2_parse_dt(struct device_node *np,
+ struct rcar_csi2_link_config *config)
+{
+ struct v4l2_of_endpoint bus_cfg;
+ struct device_node *endpoint;
+ struct device_node *vc_np, *vc_ch;
+ const char *str;
+ char csi_name[9];
+ int ret;
+ int i, ch;
+
+ /* Parse the endpoint. */
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint)
+ return -EINVAL;
+
+ v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ of_node_put(endpoint);
+
+ config->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+ ret = of_property_read_string(np, "adi,input-interface", &str);
+ if (ret < 0)
+ return ret;
+
+ vc_np = of_get_child_by_name(np, "virtual,channel");
+
+ config->vcdt = 0;
+ config->vcdt2 = 0;
+ for (i = 0; i < VC_MAX_CHANNEL; i++) {
+ sprintf(csi_name, "csi2_vc%d", i);
+
+ vc_ch = of_get_child_by_name(vc_np, csi_name);
+ if (!vc_ch)
+ continue;
+ ret = of_property_read_string(vc_ch, "data,type", &str);
+ if (ret < 0)
+ return ret;
+ ret = of_property_read_u32(vc_ch, "receive,vc", &ch);
+ if (ret < 0)
+ return ret;
+
+ if (i < 2) {
+ if (!strcmp(str, "rgb888"))
+ config->vcdt |= (0x24 << (i * 16));
+ else if (!strcmp(str, "ycbcr422"))
+ config->vcdt |= (0x1e << (i * 16));
+ else
+ config->vcdt |= 0;
+
+ config->vcdt |= (ch << (8 + (i * 16)));
+ config->vcdt |= (RCAR_CSI2_VCDT_VCDTN_EN << (i * 16)) |
+ (RCAR_CSI2_VCDT_SEL_DTN_ON << (i * 16));
+ }
+ if (i >= 2) {
+ int j = (i - 2);
+
+ if (!strcmp(str, "rgb888"))
+ config->vcdt2 |= (0x24 << (j * 16));
+ else if (!strcmp(str, "ycbcr422"))
+ config->vcdt2 |= (0x1e << (j * 16));
+ else
+ config->vcdt2 |= 0;
+
+ config->vcdt2 |= (ch << (8 + (j * 16)));
+ config->vcdt2 |= (RCAR_CSI2_VCDT_VCDTN_EN << (j * 16)) |
+ (RCAR_CSI2_VCDT_SEL_DTN_ON << (j * 16));
+ }
+ }
+
+ return 0;
+}
+
+static int rcar_csi2_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+ struct rcar_csi2 *priv;
+ /* Platform data specify the PHY, lanes, ECC, CRC */
+ struct rcar_csi2_pdata *pdata;
+ struct rcar_csi2_link_config link_config;
+
+ dev_dbg(&pdev->dev, "CSI2 probed.\n");
+
+ INIT_RCAR_CSI2_LINK_CONFIG(link_config);
+
+ if (pdev->dev.of_node) {
+ ret = rcar_csi2_parse_dt(pdev->dev.of_node, &link_config);
+ if (ret)
+ return ret;
+
+ if (link_config.lanes == 4)
+ dev_info(&pdev->dev,
+ "Detected rgb888 in rcar_csi2_parse_dt\n");
+ else
+ dev_info(&pdev->dev,
+ "Detected YCbCr422 in rcar_csi2_parse_dt\n");
+ } else {
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_csi2), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ /* Interrupt unused so far */
+ irq = platform_get_irq(pdev, 0);
+
+ if (!res || (int)irq <= 0) {
+ dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
+ return -ENODEV;
+ }
+
+ priv->irq = irq;
+
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = devm_request_irq(&pdev->dev, irq, rcar_csi2_irq, IRQF_SHARED,
+ dev_name(&pdev->dev), priv);
+ if (ret)
+ return ret;
+
+ priv->pdev = pdev;
+ priv->subdev.owner = THIS_MODULE;
+ priv->subdev.dev = &pdev->dev;
+ priv->lanes = link_config.lanes;
+ priv->vcdt = link_config.vcdt;
+ priv->vcdt2 = link_config.vcdt2;
+
+ platform_set_drvdata(pdev, &priv->subdev);
+
+ v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+
+ snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "rcar_csi2.%s",
+ dev_name(&pdev->dev));
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret < 0)
+ return ret;
+
+ spin_lock_init(&priv->lock);
+
+ pm_runtime_enable(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "CSI2 probed.\n");
+
+ return 0;
+}
+
+static int rcar_csi2_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct rcar_csi2 *priv = container_of(subdev, struct rcar_csi2, subdev);
+
+ v4l2_async_unregister_subdev(&priv->subdev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rcar_csi2_suspend(struct device *dev)
+{
+ /* Empty function for now */
+ return 0;
+}
+
+static int rcar_csi2_resume(struct device *dev)
+{
+ /* Empty function for now */
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_csi2_pm_ops,
+ rcar_csi2_suspend, rcar_csi2_resume);
+#define DEV_PM_OPS (&rcar_csi2_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver __refdata rcar_csi2_pdrv = {
+ .remove = rcar_csi2_remove,
+ .probe = rcar_csi2_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(rcar_csi2_of_table),
+ },
+ .id_table = rcar_csi2_id_table,
+};
+
+module_platform_driver(rcar_csi2_pdrv);
+
+MODULE_DESCRIPTION("Renesas R-Car MIPI CSI-2 driver");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:rcar-csi2");
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 7653da3..0c0027e 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -1,6 +1,7 @@
/*
* SoC-camera host driver for Renesas R-Car VIN unit
*
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
*
@@ -14,6 +15,10 @@
* option) any later version.
*/
+#ifdef CONFIG_VIDEO_RCAR_VIN_DEBUG
+#define DEBUG
+#endif
+
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -25,6 +30,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
+#include <linux/list.h>
#include <media/soc_camera.h>
#include <media/drv-intf/soc_mediabus.h>
@@ -36,6 +42,8 @@
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
+#include <media/rcar_csi2.h>
+
#include "soc_scale_crop.h"
#define DRV_NAME "rcar_vin"
@@ -90,6 +98,8 @@
/* Register bit fields for R-Car VIN */
/* Video n Main Control Register bits */
+#define VNMC_DPINE (1 << 27)
+#define VNMC_SCLE (1 << 26)
#define VNMC_FOC (1 << 21)
#define VNMC_YCAL (1 << 19)
#define VNMC_INF_YUV8_BT656 (0 << 16)
@@ -98,6 +108,7 @@
#define VNMC_INF_YUV10_BT601 (3 << 16)
#define VNMC_INF_YUV16 (5 << 16)
#define VNMC_INF_RGB888 (6 << 16)
+#define VNMC_INF_MASK (7 << 16)
#define VNMC_VUP (1 << 10)
#define VNMC_IM_ODD (0 << 3)
#define VNMC_IM_ODD_EVEN (1 << 3)
@@ -119,12 +130,19 @@
/* Video n Interrupt Enable Register bits */
#define VNIE_FIE (1 << 4)
#define VNIE_EFE (1 << 1)
+#define VNIE_FOE (1 << 0)
+
+/* Video n Interrupt Status Register bits */
+#define VNINTS_FIS (1 << 4)
+#define VNINTS_EFS (1 << 1)
+#define VNINTS_FOS (1 << 0)
/* Video n Data Mode Register bits */
#define VNDMR_EXRGB (1 << 8)
#define VNDMR_BPSM (1 << 4)
#define VNDMR_DTMD_YCSEP (1 << 1)
#define VNDMR_DTMD_ARGB (1 << 0)
+#define VNDMR_DTMD_YCSEP_YCBCR420 (3 << 0)
/* Video n Data Mode Register 2 bits */
#define VNDMR2_VPS (1 << 30)
@@ -132,8 +150,25 @@
#define VNDMR2_FTEV (1 << 17)
#define VNDMR2_VLV(n) ((n & 0xf) << 12)
-#define VIN_MAX_WIDTH 2048
-#define VIN_MAX_HEIGHT 2048
+/* setting CSI2 on R-Car Gen3*/
+#define VNCSI_IFMD_REG 0x20 /* Video n CSI2 Interface Mode Register */
+
+#define VNCSI_IFMD_DES1 (1 << 26) /* CSI20 */
+#define VNCSI_IFMD_DES0 (1 << 25) /* H3:CSI40/41, M3:CSI40 */
+
+#define VNCSI_IFMD_CSI_CHSEL(n) (n << 0)
+#define VNCSI_IFMD_SEL_NUMBER 5
+
+/* UDS */
+#define VNUDS_CTRL_REG 0x80 /* Scaling Control Registers */
+#define VNUDS_CTRL_AMD (1 << 30)
+#define VNUDS_CTRL_BC (1 << 20)
+#define VNUDS_CTRL_TDIPC (1 << 1)
+
+#define VNUDS_SCALE_REG 0x84 /* Scaling Factor Register */
+#define VNUDS_PASS_BWIDTH_REG 0x90 /* Passband Registers */
+#define VNUDS_IPC_REG 0x98 /* 2D IPC Setting Register */
+#define VNUDS_CLIP_SIZE_REG 0xA4 /* UDS Output Size Clipping Register */
#define TIMEOUT_MS 100
@@ -141,15 +176,196 @@
#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1)
#define RCAR_VIN_BT601 (1 << 2)
#define RCAR_VIN_BT656 (1 << 3)
+#define RCAR_VIN_CSI2 (1 << 4)
+
+static int ifmd0_reg_match[VNCSI_IFMD_SEL_NUMBER];
+static int ifmd4_reg_match[VNCSI_IFMD_SEL_NUMBER];
+static int ifmd0_init = true;
+static int ifmd4_init = true;
enum chip_id {
RCAR_GEN3,
+ RCAR_M3,
+ RCAR_H3,
RCAR_GEN2,
RCAR_H1,
RCAR_M1,
RCAR_E1,
};
+enum csi2_ch {
+ RCAR_CSI_CH_NONE = -1,
+ RCAR_CSI40,
+ RCAR_CSI20,
+ RCAR_CSI41,
+ RCAR_CSI21,
+ RCAR_CSI_MAX,
+};
+
+enum gen3_vin_ch {
+ RCAR_VIN_CH_NONE = -1,
+ RCAR_VIDEO_0,
+ RCAR_VIDEO_1,
+ RCAR_VIDEO_2,
+ RCAR_VIDEO_3,
+ RCAR_VIDEO_4,
+ RCAR_VIDEO_5,
+ RCAR_VIDEO_6,
+ RCAR_VIDEO_7,
+ RCAR_VIDEO_MAX,
+};
+
+enum virtual_ch {
+ RCAR_VIRTUAL_NONE = -1,
+ RCAR_VIRTUAL_CH0,
+ RCAR_VIRTUAL_CH1,
+ RCAR_VIRTUAL_CH2,
+ RCAR_VIRTUAL_CH3,
+ RCAR_VIRTUAL_MAX,
+};
+
+struct vin_gen3_virtual_sel {
+ enum csi2_ch csi2_ch;
+ enum virtual_ch vc;
+};
+
+struct vin_gen3_ifmd {
+ unsigned int set_reg;
+ struct vin_gen3_virtual_sel v_sel[8];
+};
+
+static const struct vin_gen3_ifmd vin_h3_vc_ifmd[] = {
+ { 0x0000,
+ {
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH1},
+ }
+ },
+ { 0x0001,
+ {
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ }
+ },
+ { 0x0002,
+ {
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ }
+ },
+ { 0x0003,
+ {
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH3},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI41, RCAR_VIRTUAL_CH3},
+ }
+ },
+ { 0x0004,
+ {
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH3},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH3},
+ }
+ },
+};
+
+static const struct vin_gen3_ifmd vin_m3_vc_ifmd[] = {
+ { 0x0000,
+ {
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ }
+ },
+ { 0x0001,
+ {
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ }
+ },
+ { 0x0002,
+ {
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+ }
+ },
+ { 0x0003,
+ {
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH3},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI40, RCAR_VIRTUAL_CH3},
+ }
+ },
+ { 0x0004,
+ {
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH3},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH0},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH1},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH2},
+ {RCAR_CSI20, RCAR_VIRTUAL_CH3},
+ }
+ },
+};
+
+enum csi2_fmt {
+ RCAR_CSI_FMT_NONE = -1,
+ RCAR_CSI_RGB888,
+ RCAR_CSI_YCBCR422,
+};
+
struct vin_coeff {
unsigned short xs_value;
u32 coeff_set[24];
@@ -474,6 +690,19 @@
STOPPING,
};
+struct rcar_vin_async_client {
+ struct v4l2_async_subdev *sensor;
+ struct v4l2_async_notifier notifier;
+ struct platform_device *pdev;
+ struct list_head list; /* needed for clean up */
+};
+
+struct soc_of_info {
+ struct soc_camera_async_subdev sasd;
+ struct rcar_vin_async_client sasc;
+ struct v4l2_async_subdev *subdev;
+};
+
struct rcar_vin_priv {
void __iomem *base;
spinlock_t lock;
@@ -492,6 +721,23 @@
bool request_to_stop;
struct completion capture_stop;
enum chip_id chip;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int ratio_h;
+ unsigned int ratio_v;
+ bool error_flag;
+ enum csi2_ch csi_ch;
+ enum csi2_fmt csi_fmt;
+ enum virtual_ch vc;
+ bool csi_sync;
+
+ struct rcar_vin_async_client *async_client;
+ /* Asynchronous CSI2 linking */
+ struct v4l2_subdev *csi2_sd;
+ /* Synchronous probing compatibility */
+ struct platform_device *csi2_pdev;
+
+ unsigned int index;
};
#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM)
@@ -526,6 +772,69 @@
const struct soc_mbus_pixelfmt *extra_fmt;
};
+#define VIN_UT_IRQ 0x01
+
+static unsigned int vin_debug;
+module_param_named(debug, vin_debug, int, 0600);
+static int overflow_video[RCAR_VIDEO_MAX];
+module_param_array(overflow_video, int, NULL, 0600);
+
+#ifdef CONFIG_VIDEO_RCAR_VIN_DEBUG
+#define VIN_IRQ_DEBUG(fmt, args...) \
+ do { \
+ if (unlikely(vin_debug & VIN_UT_IRQ)) \
+ vin_ut_debug_printk(__func__, fmt, ##args); \
+ } while (0)
+#else
+#define VIN_IRQ_DEBUG(fmt, args...)
+#endif
+
+void vin_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ pr_debug("[" DRV_NAME ":%s] %pV", function_name, &vaf);
+
+ va_end(args);
+}
+
+static void rcar_vin_cpg_enable_for_ifmd(unsigned int ch, bool enable)
+{
+ void __iomem *smstpcr8;
+
+ smstpcr8 = ioremap(0xE6150990, 0x04);
+
+ if (enable) {
+ if (ch < RCAR_VIDEO_4)
+ iowrite32((ioread32(smstpcr8) & 0xFFFFF7FF), smstpcr8);
+ else
+ iowrite32((ioread32(smstpcr8) & 0xFFFFFF7F), smstpcr8);
+ } else {
+ if (ch < RCAR_VIDEO_4)
+ iowrite32((ioread32(smstpcr8) | 0x00000800), smstpcr8);
+ else
+ iowrite32((ioread32(smstpcr8) | 0x00000080), smstpcr8);
+ }
+
+ iounmap(smstpcr8);
+}
+
+static inline int is_scaling(struct rcar_vin_cam *cam)
+{
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+
+ if ((cam_subrect->width != cam->out_width) ||
+ (cam_subrect->height != cam->out_height))
+ return 1;
+
+ return 0;
+}
+
/*
* .queue_setup() is called to check whether the driver can accept the requested
* number of buffers and to fill in plane sizes for the current frame format if
@@ -539,6 +848,22 @@
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct rcar_vin_priv *priv = ici->priv;
+ struct rcar_vin_cam *cam = icd->host_priv;
+
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ if ((priv->ratio_h > 0x10000) || (priv->ratio_v > 0x10000)) {
+ dev_err(icd->parent, "Scaling rate parameter error\n");
+ return -EINVAL;
+ }
+ if (is_scaling(cam) && (cam->out_width % 32)) {
+ dev_err(icd->parent, "Scaling parameter error\n");
+ return -EINVAL;
+ }
+ if (!is_scaling(cam) && (cam->out_width % 16)) {
+ dev_err(icd->parent, "Image stride parameter error\n");
+ return -EINVAL;
+ }
+ }
alloc_ctxs[0] = priv->alloc_ctx;
@@ -628,8 +953,19 @@
/* output format */
switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ iowrite32(ALIGN((cam->out_width * cam->out_height),
+ 0x80), priv->base + VNUVAOF_REG);
+ dmr = VNDMR_DTMD_YCSEP_YCBCR420;
+ output_is_yuv = true;
+ } else {
+ dev_warn(icd->parent, "Not support format\n");
+ return -EINVAL;
+ }
+ break;
case V4L2_PIX_FMT_NV16:
- iowrite32(ALIGN(cam->width * cam->height, 0x80),
+ iowrite32(ALIGN((cam->out_width * cam->out_height), 0x80),
priv->base + VNUVAOF_REG);
dmr = VNDMR_DTMD_YCSEP;
output_is_yuv = true;
@@ -642,21 +978,22 @@
dmr = 0;
output_is_yuv = true;
break;
- case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_ARGB555:
dmr = VNDMR_DTMD_ARGB;
break;
case V4L2_PIX_FMT_RGB565:
dmr = 0;
break;
- case V4L2_PIX_FMT_RGB32:
- if (priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 &&
+ case V4L2_PIX_FMT_XBGR32:
+ if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3 &&
+ priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 &&
priv->chip != RCAR_E1)
goto e_format;
dmr = VNDMR_EXRGB;
break;
- case V4L2_PIX_FMT_ARGB32:
- if (priv->chip != RCAR_GEN3)
+ case V4L2_PIX_FMT_ABGR32:
+ if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3)
goto e_format;
dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB;
@@ -672,9 +1009,24 @@
if (input_is_yuv == output_is_yuv)
vnmc |= VNMC_BPS;
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ if (priv->pdata_flags & RCAR_VIN_CSI2)
+ vnmc &= ~VNMC_DPINE;
+ else
+ vnmc |= VNMC_DPINE;
+
+ if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12)
+ && is_scaling(cam))
+ vnmc |= VNMC_SCLE;
+ }
+
/* progressive or interlaced mode */
interrupts = progressive ? VNIE_FIE : VNIE_EFE;
+ /* Enable Overflow */
+ if (vin_debug)
+ interrupts |= VNIE_FOE;
+
/* ack interrupts */
iowrite32(interrupts, priv->base + VNINTS_REG);
/* enable interrupts */
@@ -874,16 +1226,25 @@
bool can_run = false, hw_stopped;
int slot;
unsigned int handled = 0;
+ int vin_ovr_cnt = 0;
spin_lock(&priv->lock);
int_status = ioread32(priv->base + VNINTS_REG);
if (!int_status)
goto done;
+
/* ack interrupts */
iowrite32(int_status, priv->base + VNINTS_REG);
handled = 1;
+ /* overflow occurs */
+ if (vin_debug && (int_status & VNINTS_FOS)) {
+ vin_ovr_cnt = ++overflow_video[priv->index];
+ VIN_IRQ_DEBUG("overflow occurrs num[%d] at VIN (%s)\n",
+ vin_ovr_cnt, dev_name(priv->ici.v4l2_dev.dev));
+ }
+
/* nothing to do if capture status is 'STOPPED' */
if (priv->state == STOPPED)
goto done;
@@ -897,31 +1258,36 @@
else
slot = 0;
- priv->queue_buf[slot]->field = priv->field;
- priv->queue_buf[slot]->sequence = priv->sequence++;
- priv->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf,
- VB2_BUF_STATE_DONE);
- priv->queue_buf[slot] = NULL;
+ if (!is_continuous_transfer(priv) || ((priv->state == RUNNING)
+ && !list_empty(&priv->capture))) {
+ priv->queue_buf[slot]->field = priv->field;
+ priv->queue_buf[slot]->sequence = priv->sequence++;
+ priv->queue_buf[slot]->vb2_buf.timestamp =
+ ktime_get_ns();
+ vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf,
+ VB2_BUF_STATE_DONE);
+ priv->queue_buf[slot] = NULL;
- if (priv->state != STOPPING)
can_run = rcar_vin_fill_hw_slot(priv);
-
- if (hw_stopped || !can_run) {
- priv->state = STOPPED;
- } else if (is_continuous_transfer(priv) &&
- list_empty(&priv->capture) &&
- priv->state == RUNNING) {
- /*
- * The continuous capturing requires an explicit stop
- * operation when there is no buffer to be set into
- * the VnMBm registers.
- */
- rcar_vin_request_capture_stop(priv);
- } else {
- rcar_vin_capture(priv);
}
+ if (is_continuous_transfer(priv)) {
+ if (hw_stopped)
+ priv->state = STOPPED;
+ else if (list_empty(&priv->capture) &&
+ priv->state == RUNNING)
+ /*
+ * The continuous capturing requires an
+ * explicit stop operation when there is no
+ * buffer to be set into the VnMBm registers.
+ */
+ rcar_vin_request_capture_stop(priv);
+ } else {
+ if (can_run)
+ rcar_vin_capture(priv);
+ else
+ priv->state = STOPPED;
+ }
} else if (hw_stopped) {
priv->state = STOPPED;
priv->request_to_stop = false;
@@ -934,6 +1300,21 @@
return IRQ_RETVAL(handled);
}
+static struct v4l2_subdev *find_csi2(struct rcar_vin_priv *pcdev)
+{
+ struct v4l2_subdev *sd;
+ char name[] = "rcar_csi2";
+
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) {
+ if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+ pcdev->csi2_sd = sd;
+ return sd;
+ }
+ }
+
+ return NULL;
+}
+
static int rcar_vin_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -945,8 +1326,40 @@
pm_runtime_get_sync(ici->v4l2_dev.dev);
- dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n",
- icd->devnum);
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ struct v4l2_subdev *csi2_sd = find_csi2(priv);
+ int ret;
+
+ if (csi2_sd) {
+ csi2_sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(csi2_sd, icd);
+
+ ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+ priv->csi_sync = true;
+
+ if (ret < 0 && ret != -EINVAL)
+ priv->csi_sync = false;
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+ }
+ /*
+ * -ENODEV is special:
+ * either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (csi2_sd && ret == -ENODEV)
+ csi2_sd->grp_id = 0;
+
+ dev_dbg(icd->parent,
+ "R-Car VIN/CSI-2 driver attached to camera %d\n",
+ icd->devnum);
+
+ } else
+ dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n",
+ icd->devnum);
+
+ priv->error_flag = false;
return 0;
}
@@ -956,6 +1369,7 @@
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct rcar_vin_priv *priv = ici->priv;
struct vb2_v4l2_buffer *vbuf;
+ struct v4l2_subdev *csi2_sd = find_csi2(priv);
int i;
/* disable capture, disable interrupts */
@@ -965,6 +1379,7 @@
priv->state = STOPPED;
priv->request_to_stop = false;
+ priv->error_flag = false;
/* make sure active buffer is cancelled */
spin_lock_irq(&priv->lock);
@@ -979,10 +1394,83 @@
pm_runtime_put(ici->v4l2_dev.dev);
+ if ((csi2_sd) && (priv->csi_sync))
+ v4l2_subdev_call(csi2_sd, core, s_power, 0);
+
dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n",
icd->devnum);
}
+struct rcar_vin_uds_regs {
+ unsigned long ctrl;
+ unsigned long scale;
+ unsigned long pass_bwidth;
+ unsigned long clip_size;
+};
+
+static unsigned long rcar_vin_get_bwidth(unsigned long ratio)
+{
+ unsigned long bwidth;
+ unsigned long mant, frac;
+
+ mant = (ratio & 0xF000) >> 12;
+ frac = ratio & 0x0FFF;
+ if (mant)
+ bwidth = 64 * 4096 * mant / (4096 * mant + frac);
+ else
+ bwidth = 64;
+
+ return bwidth;
+}
+
+static unsigned long rcar_vin_compute_ratio(unsigned int input,
+ unsigned int output)
+{
+ return ((input * 4096 / output) == 0x10000) ?
+ 0xFFFF : (input * 4096 / output);
+}
+
+int rcar_vin_uds_set(struct rcar_vin_priv *priv, struct rcar_vin_cam *cam)
+{
+ struct rcar_vin_uds_regs regs;
+ unsigned long ratio_h, ratio_v;
+ unsigned long bwidth_h, bwidth_v;
+ unsigned long ctrl;
+ unsigned long clip_size;
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+ u32 vnmc;
+
+ ratio_h = rcar_vin_compute_ratio(cam_subrect->width, cam->out_width);
+ ratio_v = rcar_vin_compute_ratio(cam_subrect->height, cam->out_height);
+
+ priv->ratio_h = ratio_h;
+ priv->ratio_v = ratio_v;
+
+ bwidth_h = rcar_vin_get_bwidth(ratio_h);
+ bwidth_v = rcar_vin_get_bwidth(ratio_v);
+
+ ctrl = VNUDS_CTRL_AMD;
+
+ if (priv->field == V4L2_FIELD_NONE)
+ clip_size = (cam->out_width << 16) | (cam->out_height);
+ else
+ clip_size = (cam->out_width << 16) | (cam->out_height / 2);
+
+ regs.ctrl = ctrl;
+ regs.scale = (ratio_h << 16) | ratio_v;
+ regs.pass_bwidth = (bwidth_h << 16) | bwidth_v;
+ regs.clip_size = clip_size;
+
+ vnmc = ioread32(priv->base + VNMC_REG);
+ iowrite32(vnmc | VNMC_SCLE, priv->base + VNMC_REG);
+ iowrite32(regs.ctrl, priv->base + VNUDS_CTRL_REG);
+ iowrite32(regs.scale, priv->base + VNUDS_SCALE_REG);
+ iowrite32(regs.pass_bwidth, priv->base + VNUDS_PASS_BWIDTH_REG);
+ iowrite32(regs.clip_size, priv->base + VNUDS_CLIP_SIZE_REG);
+
+ return 0;
+}
+
static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
{
int i;
@@ -1047,6 +1535,7 @@
unsigned char dsize = 0;
struct v4l2_rect *cam_subrect = &cam->subrect;
u32 value;
+ int ret = 0;
dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
@@ -1054,7 +1543,7 @@
left_offset = cam->vin_left;
top_offset = cam->vin_top;
- if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 &&
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_XBGR32 &&
priv->chip == RCAR_E1)
dsize = 1;
@@ -1083,49 +1572,69 @@
break;
}
- /* Set scaling coefficient */
- value = 0;
- if (cam_subrect->height != cam->out_height)
- value = (4096 * cam_subrect->height) / cam->out_height;
- dev_dbg(icd->parent, "YS Value: %x\n", value);
- iowrite32(value, priv->base + VNYS_REG);
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12)
+ && is_scaling(cam)) {
+ ret = rcar_vin_uds_set(priv, cam);
+ if (ret < 0)
+ return ret;
+ }
+ if (is_scaling(cam) ||
+ (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV16) ||
+ (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV12))
+ iowrite32(ALIGN(cam->out_width, 0x20),
+ priv->base + VNIS_REG);
+ else
+ iowrite32(ALIGN(cam->out_width, 0x10),
+ priv->base + VNIS_REG);
+ } else {
+ /* Set scaling coefficient */
+ value = 0;
+ if (cam_subrect->height != cam->out_height)
+ value = (4096 * cam_subrect->height) / cam->out_height;
+ dev_dbg(icd->parent, "YS Value: %x\n", value);
+ iowrite32(value, priv->base + VNYS_REG);
- value = 0;
- if (cam_subrect->width != cam->out_width)
- value = (4096 * cam_subrect->width) / cam->out_width;
+ value = 0;
+ if (cam_subrect->width != cam->out_width)
+ value = (4096 * cam_subrect->width) / cam->out_width;
- /* Horizontal upscaling is up to double size */
- if (0 < value && value < 2048)
- value = 2048;
+ /* Horizontal upscaling is up to double size */
+ if (value < 2048)
+ value = 2048;
- dev_dbg(icd->parent, "XS Value: %x\n", value);
- iowrite32(value, priv->base + VNXS_REG);
+ dev_dbg(icd->parent, "XS Value: %x\n", value);
+ iowrite32(value, priv->base + VNXS_REG);
- /* Horizontal upscaling is carried out by scaling down from double size */
- if (value < 4096)
- value *= 2;
+ /* Horizontal upscaling is carried out */
+ /* by scaling down from double size */
+ if (value < 4096)
+ value *= 2;
- set_coeff(priv, value);
+ set_coeff(priv, value);
- /* Set Start/End Pixel/Line Post-Clip */
- iowrite32(0, priv->base + VNSPPOC_REG);
- iowrite32(0, priv->base + VNSLPOC_REG);
- iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG);
- switch (priv->field) {
- case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- iowrite32(cam->out_height / 2 - 1,
- priv->base + VNELPOC_REG);
- break;
- default:
- iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG);
- break;
+ /* Set Start/End Pixel/Line Post-Clip */
+ iowrite32(0, priv->base + VNSPPOC_REG);
+ iowrite32(0, priv->base + VNSLPOC_REG);
+ iowrite32((cam->out_width - 1) << dsize,
+ priv->base + VNEPPOC_REG);
+ switch (priv->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ iowrite32(cam->out_height / 2 - 1,
+ priv->base + VNELPOC_REG);
+ break;
+ default:
+ iowrite32(cam->out_height - 1,
+ priv->base + VNELPOC_REG);
+ break;
+ }
+
+ iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
}
- iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
-
- return 0;
+ return ret;
}
static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc)
@@ -1214,7 +1723,17 @@
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- val = VNDMR2_FTEV | VNDMR2_VLV(1);
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ if (cfg.type == V4L2_MBUS_CSI2)
+ vnmc &= ~VNMC_DPINE;
+ else
+ vnmc |= VNMC_DPINE;
+ }
+
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3)
+ val = VNDMR2_FTEV;
+ else
+ val = VNDMR2_FTEV | VNDMR2_VLV(1);
if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
val |= VNDMR2_VPS;
if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
@@ -1267,6 +1786,14 @@
static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
{
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .name = "NV12",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
+ },
+ {
.fourcc = V4L2_PIX_FMT_NV16,
.name = "NV16",
.bits_per_sample = 8,
@@ -1299,7 +1826,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
{
- .fourcc = V4L2_PIX_FMT_RGB555X,
+ .fourcc = V4L2_PIX_FMT_ARGB555,
.name = "ARGB1555",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_NONE,
@@ -1307,7 +1834,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
{
- .fourcc = V4L2_PIX_FMT_RGB32,
+ .fourcc = V4L2_PIX_FMT_XBGR32,
.name = "RGB888",
.bits_per_sample = 32,
.packing = SOC_MBUS_PACKING_NONE,
@@ -1315,7 +1842,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
{
- .fourcc = V4L2_PIX_FMT_ARGB32,
+ .fourcc = V4L2_PIX_FMT_ABGR32,
.name = "ARGB8888",
.bits_per_sample = 32,
.packing = SOC_MBUS_PACKING_NONE,
@@ -1332,6 +1859,8 @@
int ret, k, n;
int formats = 0;
struct rcar_vin_cam *cam;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
struct v4l2_subdev_mbus_code_enum code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.index = idx,
@@ -1382,8 +1911,8 @@
* 1280x960, 640x480, 320x240
*/
for (shift = 0; shift < 3; shift++) {
- if (mf->width <= VIN_MAX_WIDTH &&
- mf->height <= VIN_MAX_HEIGHT)
+ if (mf->width <= priv->max_width &&
+ mf->height <= priv->max_height)
break;
mf->width = 1280 >> shift;
@@ -1474,6 +2003,294 @@
icd->host_priv = NULL;
}
+static bool rcar_vin_is_smaller(const struct v4l2_rect *r1,
+ const struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+static bool rcar_vin_is_inside(const struct v4l2_rect *r1,
+ const struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+static void rcar_vin_update_subrect(struct v4l2_rect *rect,
+ struct v4l2_rect *subrect)
+{
+ if (rect->width < subrect->width)
+ subrect->width = rect->width;
+
+ if (rect->height < subrect->height)
+ subrect->height = rect->height;
+
+ if (rect->left > subrect->left)
+ subrect->left = rect->left;
+ else if (rect->left + rect->width <
+ subrect->left + subrect->width)
+ subrect->left = rect->left + rect->width -
+ subrect->width;
+
+ if (rect->top > subrect->top)
+ subrect->top = rect->top;
+ else if (rect->top + rect->height <
+ subrect->top + subrect->height)
+ subrect->top = rect->top + rect->height -
+ subrect->height;
+}
+
+/* Iterative set_fmt, also updates cached client crop on success */
+static int rcar_vin_client_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ unsigned int max_width, unsigned int max_height,
+ struct v4l2_subdev_format *format, bool host_can_scale)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
+ struct v4l2_cropcap cap;
+ bool host_1to1;
+ int ret;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), pad,
+ set_fmt, NULL, format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
+
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ host_1to1 = true;
+ goto update_cache;
+ }
+
+ host_1to1 = false;
+ if (!host_can_scale)
+ goto update_cache;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ if (max_width > cap.bounds.width)
+ max_width = cap.bounds.width;
+ if (max_height > cap.bounds.height)
+ max_height = cap.bounds.height;
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = mf->width;
+ tmp_h = mf->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), pad,
+ set_fmt, NULL, format);
+ dev_dbg(dev, "Camera scaled to %ux%u\n",
+ mf->width, mf->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+update_cache:
+ /* Update cache */
+ ret = soc_camera_client_g_rect(sd, rect);
+ if (ret < 0)
+ return ret;
+
+ if (!host_1to1)
+ rcar_vin_update_subrect(rect, subrect);
+
+ return 0;
+}
+
+int rcar_vin_client_scale(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool host_can_scale, unsigned int shift)
+{
+ struct device *dev = icd->parent;
+ struct v4l2_subdev_format fmt_tmp = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format = *mf,
+ };
+ struct v4l2_mbus_framefmt *mf_tmp = &fmt_tmp.format;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /*
+ * 5. Apply iterative camera S_FMT for camera user window (also updates
+ * client crop cache and the imaginary sub-rectangle).
+ */
+ ret = rcar_vin_client_set_fmt(icd, rect, subrect, *width, *height,
+ &fmt_tmp, host_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "5: camera scaled to %ux%u\n",
+ mf_tmp->width, mf_tmp->height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "mf_tmp" */
+
+ /* 7. Calculate new client scales. */
+ scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp->width);
+ scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp->height);
+
+ mf->width = mf_tmp->width;
+ mf->height = mf_tmp->height;
+ mf->colorspace = mf_tmp->colorspace;
+
+ /*
+ * 8. Calculate new host crop - apply camera scales to previously
+ * updated "effective" crop.
+ */
+ *width = soc_camera_shift_scale(subrect->width, shift, scale_h);
+ *height = soc_camera_shift_scale(subrect->height, shift, scale_v);
+
+ dev_dbg(dev, "8: new client sub-window %ux%u\n", *width, *height);
+
+ return 0;
+}
+
+int rcar_vin_client_s_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *crop, struct v4l2_crop *cam_crop,
+ struct v4l2_rect *target_rect,
+ struct v4l2_rect *subrect)
+{
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
+ rect->width, rect->height, rect->left, rect->top);
+ *target_rect = *cam_rect;
+ *subrect = *rect;
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_dbg(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ /* Put user requested rectangle within sensor bounds */
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max_t(unsigned int, cam_rect->width, 2);
+ height = max_t(unsigned int, cam_rect->height, 2);
+
+ /*
+ * Loop as long as sensor is not covering the requested rectangle and
+ * is still within its bounds
+ */
+ while (!ret && (rcar_vin_is_smaller(cam_rect, rect) ||
+ rcar_vin_is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ * Instead we just drop to the left and top bounds.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_dbg(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (rcar_vin_is_smaller(cam_rect, rect) ||
+ rcar_vin_is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_dbg(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ if (!ret) {
+ *target_rect = *cam_rect;
+ *subrect = *rect;
+ rcar_vin_update_subrect(target_rect, subrect);
+ }
+
+ return ret;
+}
+
static int rcar_vin_set_crop(struct soc_camera_device *icd,
const struct v4l2_crop *a)
{
@@ -1501,7 +2318,7 @@
dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc);
/* Apply iterative camera S_CROP for new input window. */
- ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop,
+ ret = rcar_vin_client_s_crop(sd, &a_writable, &cam_crop,
&cam->rect, &cam->subrect);
if (ret < 0)
return ret;
@@ -1517,26 +2334,21 @@
if (ret < 0)
return ret;
- if (mf->width > VIN_MAX_WIDTH || mf->height > VIN_MAX_HEIGHT)
+ if (mf->width > priv->max_width || mf->height > priv->max_height)
return -EINVAL;
/* Cache camera output window */
cam->width = mf->width;
cam->height = mf->height;
- icd->user_width = cam->width;
- icd->user_height = cam->height;
-
- cam->vin_left = rect->left & ~1;
- cam->vin_top = rect->top & ~1;
+ cam->vin_left = rect->left;
+ cam->vin_top = rect->top;
/* Use VIN cropping to crop to the new window. */
ret = rcar_vin_set_rect(icd);
if (ret < 0)
return ret;
- cam->subrect = *rect;
-
dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n",
icd->user_width, icd->user_height,
cam->vin_left, cam->vin_top);
@@ -1587,6 +2399,20 @@
dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n",
pixfmt, pix->width, pix->height);
+ /* At the time of NV16 capture format, the user has to specify */
+ /* the width of the multiple of 32 for H/W specification. */
+ if (priv->error_flag == false)
+ priv->error_flag = true;
+ else {
+ if (((pixfmt == V4L2_PIX_FMT_NV16) ||
+ (pixfmt == V4L2_PIX_FMT_NV12)) &&
+ (pix->width & 0x1F)) {
+ dev_dbg(icd->parent,
+ "specify width of 32 multiple in separate format.\n");
+ return -EINVAL;
+ }
+ }
+
switch (pix->field) {
default:
pix->field = V4L2_FIELD_NONE;
@@ -1626,16 +2452,18 @@
mf.code = xlate->code;
switch (pixfmt) {
- case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_XBGR32:
can_scale = priv->chip != RCAR_E1;
break;
- case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_ARGB555:
+ case V4L2_PIX_FMT_NV16:
can_scale = true;
break;
+ case V4L2_PIX_FMT_NV12:
default:
can_scale = false;
break;
@@ -1643,7 +2471,7 @@
dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height);
- ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
+ ret = rcar_vin_client_scale(icd, &cam->rect, &cam->subrect,
&mf, &vin_sub_width, &vin_sub_height,
can_scale, 12);
@@ -1701,6 +2529,8 @@
struct v4l2_format *f)
{
const struct soc_camera_format_xlate *xlate;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct v4l2_subdev_pad_config pad_cfg;
@@ -1722,9 +2552,16 @@
pix->colorspace = icd->colorspace;
}
- /* FIXME: calculate using depth and bus width */
- v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1,
- &pix->height, 4, VIN_MAX_HEIGHT, 2, 0);
+ /* When performing a YCbCr-422 format output, even if it performs */
+ /* odd number clipping by pixel post clip processing, */
+ /* it is outputted to a memory per even pixels. */
+ if ((pixfmt == V4L2_PIX_FMT_NV16) || (pixfmt == V4L2_PIX_FMT_NV12) ||
+ (pixfmt == V4L2_PIX_FMT_YUYV) || (pixfmt == V4L2_PIX_FMT_UYVY))
+ v4l_bound_align_image(&pix->width, 5, priv->max_width, 1,
+ &pix->height, 2, priv->max_height, 0, 0);
+ else
+ v4l_bound_align_image(&pix->width, 5, priv->max_width, 0,
+ &pix->height, 2, priv->max_height, 0, 0);
width = pix->width;
height = pix->height;
@@ -1745,11 +2582,19 @@
if (ret < 0)
return ret;
- /* Adjust only if VIN cannot scale */
- if (pix->width > mf->width * 2)
- pix->width = mf->width * 2;
- if (pix->height > mf->height * 3)
- pix->height = mf->height * 3;
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ /* Adjust max scaling size for Gen3 */
+ if (pix->width > 4096)
+ pix->width = priv->max_width;
+ if (pix->height > 4096)
+ pix->height = priv->max_height;
+ } else {
+ /* Adjust only if VIN cannot scale */
+ if (pix->width > mf->width * 2)
+ pix->width = mf->width * 2;
+ if (pix->height > mf->height * 3)
+ pix->height = mf->height * 3;
+ }
pix->field = mf->field;
pix->colorspace = mf->colorspace;
@@ -1763,8 +2608,8 @@
* requested a bigger rectangle, it will not return a
* smaller one.
*/
- mf->width = VIN_MAX_WIDTH;
- mf->height = VIN_MAX_HEIGHT;
+ mf->width = priv->max_width;
+ mf->height = priv->max_height;
ret = v4l2_device_call_until_err(sd->v4l2_dev,
soc_camera_grp_id(icd),
pad, set_fmt, &pad_cfg,
@@ -1809,7 +2654,7 @@
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
vq->drv_priv = icd;
vq->ops = &rcar_vin_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
@@ -1820,6 +2665,63 @@
return vb2_queue_init(vq);
}
+static int rcar_vin_get_selection(struct soc_camera_device *icd,
+ struct v4l2_selection *sel)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r.left = sel->r.top = 0;
+ sel->r.width = mf->width;
+ sel->r.height = mf->height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rcar_vin_cropcap(struct soc_camera_device *icd,
+ struct v4l2_cropcap *crop)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret;
+
+ crop->bounds.left = 0;
+ crop->bounds.top = 0;
+ crop->bounds.width = mf->width;
+ crop->bounds.height = mf->height;
+
+ /* default cropping rectangle */
+ crop->defrect = crop->bounds;
+ crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
static struct soc_camera_host_ops rcar_vin_host_ops = {
.owner = THIS_MODULE,
.add = rcar_vin_add_device,
@@ -1834,11 +2736,14 @@
.querycap = rcar_vin_querycap,
.set_bus_param = rcar_vin_set_bus_param,
.init_videobuf2 = rcar_vin_init_videobuf2,
+ .get_selection = rcar_vin_get_selection,
+ .cropcap = rcar_vin_cropcap,
};
#ifdef CONFIG_OF
static const struct of_device_id rcar_vin_of_table[] = {
- { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_GEN3 },
+ { .compatible = "renesas,vin-r8a7796", .data = (void *)RCAR_M3 },
+ { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_H3 },
{ .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
@@ -1852,6 +2757,184 @@
MODULE_DEVICE_TABLE(of, rcar_vin_of_table);
#endif
+#define MAP_MAX_NUM 32
+static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
+static DEFINE_MUTEX(list_lock);
+
+static int rcar_vin_dyn_pdev(struct soc_camera_desc *sdesc,
+ struct rcar_vin_async_client *sasc)
+{
+ struct platform_device *pdev;
+ int ret, i;
+
+ mutex_lock(&list_lock);
+ i = find_first_zero_bit(device_map, MAP_MAX_NUM);
+ if (i < MAP_MAX_NUM)
+ set_bit(i, device_map);
+ mutex_unlock(&list_lock);
+ if (i >= MAP_MAX_NUM)
+ return -ENOMEM;
+
+ pdev = platform_device_alloc("soc-camera-pdrv", ((2 * i) + 1));
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc));
+ if (ret < 0) {
+ platform_device_put(pdev);
+ return ret;
+ }
+
+ sasc->pdev = pdev;
+
+ return 0;
+}
+
+static int rcar_vin_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ /* None. */
+ return 0;
+}
+
+static void rcar_vin_async_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ /* None. */
+}
+
+static int rcar_vin_async_probe(struct soc_camera_host *ici,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_host_desc *shd = &sdesc->host_desc;
+ struct device *control = NULL;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
+
+ if (shd->module_name)
+ ret = request_module(shd->module_name);
+
+ ret = shd->add_device(icd);
+
+ control = to_soc_camera_control(icd);
+ if (!control || !control->driver || !dev_get_drvdata(control) ||
+ !try_module_get(control->driver->owner)) {
+ shd->del_device(icd);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int rcar_vin_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rcar_vin_async_client *sasc = container_of(notifier,
+ struct rcar_vin_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (to_soc_camera_control(icd)) {
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ mutex_lock(&list_lock);
+ ret = rcar_vin_async_probe(ici, icd);
+ mutex_unlock(&list_lock);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct soc_camera_device *rcar_vin_add_pdev(
+ struct rcar_vin_async_client *sasc)
+{
+ struct platform_device *pdev = sasc->pdev;
+ int ret;
+
+ ret = platform_device_add(pdev);
+
+ if (ret < 0 || !pdev->dev.driver)
+ return NULL;
+
+ return platform_get_drvdata(pdev);
+}
+
+static int rcar_vin_soc_of_bind(struct rcar_vin_priv *priv,
+ struct soc_camera_host *ici,
+ struct device_node *ep,
+ struct device_node *remote)
+{
+ struct soc_camera_device *icd;
+ struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
+ struct rcar_vin_async_client *sasc;
+ struct soc_of_info *info;
+ struct i2c_client *client;
+ char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ int ret;
+
+ /* allocate a new subdev and add match info to it */
+ info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->sasd.asd.match.of.node = remote;
+ info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF;
+ info->subdev = &info->sasd.asd;
+
+ /* Or shall this be managed by the soc-camera device? */
+ sasc = &info->sasc;
+
+ ret = rcar_vin_dyn_pdev(&sdesc, sasc);
+ if (ret < 0)
+ goto eallocpdev;
+
+ sasc->sensor = &info->sasd.asd;
+
+ icd = rcar_vin_add_pdev(sasc);
+ if (!icd) {
+ ret = -ENOMEM;
+ goto eaddpdev;
+ }
+
+ sasc->notifier.subdevs = &info->subdev;
+ sasc->notifier.num_subdevs = 1;
+ sasc->notifier.bound = rcar_vin_async_bound;
+ sasc->notifier.unbind = rcar_vin_async_unbind;
+ sasc->notifier.complete = rcar_vin_async_complete;
+
+ priv->async_client = sasc;
+
+ client = of_find_i2c_device_by_node(remote);
+
+ if (client)
+ snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+ client->adapter->nr, client->addr);
+ else
+ snprintf(clk_name, sizeof(clk_name), "of-%s",
+ of_node_full_name(remote));
+
+ ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
+ if (!ret)
+ return 0;
+
+ platform_device_del(sasc->pdev);
+eaddpdev:
+ platform_device_put(sasc->pdev);
+eallocpdev:
+ devm_kfree(ici->v4l2_dev.dev, info);
+ dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
+
+ return ret;
+}
+
static int rcar_vin_probe(struct platform_device *pdev)
{
const struct of_device_id *match = NULL;
@@ -1861,6 +2944,10 @@
struct resource *mem;
unsigned int pdata_flags;
int irq, ret;
+ const char *str;
+ unsigned int i;
+ struct device_node *epn = NULL, *ren = NULL;
+ bool csi_use = false;
match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev);
@@ -1870,6 +2957,32 @@
return -EINVAL;
}
+ for (i = 0; ; i++) {
+ epn = of_graph_get_next_endpoint(pdev->dev.of_node,
+ epn);
+ if (!epn)
+ break;
+
+ ren = of_graph_get_remote_port(epn);
+ if (!ren) {
+ dev_notice(&pdev->dev, "no remote for %s\n",
+ of_node_full_name(epn));
+ continue;
+ }
+
+ /* so we now have a remote node to connect */
+ dev_dbg(&pdev->dev, "node name:%s\n",
+ of_node_full_name(ren->parent));
+
+ if (strcmp(ren->parent->name, "csi2") == 0)
+ csi_use = true;
+
+ of_node_put(ren);
+
+ if (i)
+ break;
+ }
+
ret = v4l2_of_parse_endpoint(np, &ep);
if (ret) {
dev_err(&pdev->dev, "could not parse endpoint\n");
@@ -1878,6 +2991,8 @@
if (ep.bus_type == V4L2_MBUS_BT656)
pdata_flags = RCAR_VIN_BT656;
+ else if (ep.bus_type == V4L2_MBUS_CSI2)
+ pdata_flags = RCAR_VIN_BT656 | RCAR_VIN_CSI2;
else {
pdata_flags = 0;
if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
@@ -1920,6 +3035,7 @@
priv->ici.v4l2_dev.dev = &pdev->dev;
priv->ici.drv_name = dev_name(&pdev->dev);
priv->ici.ops = &rcar_vin_host_ops;
+ priv->csi_sync = false;
priv->pdata_flags = pdata_flags;
if (!match) {
@@ -1930,6 +3046,163 @@
priv->chip = (enum chip_id)match->data;
}
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ priv->max_width = 4096;
+ priv->max_height = 4096;
+ } else {
+ priv->max_width = 2048;
+ priv->max_height = 2048;
+ }
+
+ if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+ u32 ifmd = 0;
+ bool match_flag = false;
+ const struct vin_gen3_ifmd *gen3_ifmd_table = NULL;
+ int vc, num;
+
+ num = VNCSI_IFMD_SEL_NUMBER;
+
+ if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef0000.video") == 0)
+ priv->index = RCAR_VIDEO_0;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef1000.video") == 0)
+ priv->index = RCAR_VIDEO_1;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef2000.video") == 0)
+ priv->index = RCAR_VIDEO_2;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef3000.video") == 0)
+ priv->index = RCAR_VIDEO_3;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef4000.video") == 0)
+ priv->index = RCAR_VIDEO_4;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef5000.video") == 0)
+ priv->index = RCAR_VIDEO_5;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef6000.video") == 0)
+ priv->index = RCAR_VIDEO_6;
+ else if (strcmp(dev_name(priv->ici.v4l2_dev.dev),
+ "e6ef7000.video") == 0)
+ priv->index = RCAR_VIDEO_7;
+ else
+ priv->index = RCAR_VIN_CH_NONE;
+
+ ret = of_property_read_string(np, "csi,select", &str);
+ if (ret) {
+ dev_err(&pdev->dev, "could not parse csi,select\n");
+ return ret;
+ }
+
+ if (strcmp(str, "csi40") == 0)
+ priv->csi_ch = RCAR_CSI40;
+ else if (strcmp(str, "csi20") == 0)
+ priv->csi_ch = RCAR_CSI20;
+ else if (strcmp(str, "csi41") == 0)
+ priv->csi_ch = RCAR_CSI41;
+ else if (strcmp(str, "csi21") == 0)
+ priv->csi_ch = RCAR_CSI21;
+ else
+ priv->csi_ch = RCAR_CSI_CH_NONE;
+
+ ret = of_property_read_u32(np, "virtual,channel", &vc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "could not parse virtual,channel\n");
+ return ret;
+ }
+
+ if (vc == 0)
+ priv->vc = RCAR_VIRTUAL_CH0;
+ else if (vc == 1)
+ priv->vc = RCAR_VIRTUAL_CH1;
+ else if (vc == 2)
+ priv->vc = RCAR_VIRTUAL_CH2;
+ else if (vc == 3)
+ priv->vc = RCAR_VIRTUAL_CH3;
+ else
+ priv->vc = RCAR_VIRTUAL_NONE;
+
+ dev_dbg(&pdev->dev, "csi_ch:%d, vc:%d\n",
+ priv->csi_ch, priv->vc);
+
+ ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0;
+
+ if (priv->chip == RCAR_H3)
+ gen3_ifmd_table = vin_h3_vc_ifmd;
+ else if (priv->chip == RCAR_M3)
+ gen3_ifmd_table = vin_m3_vc_ifmd;
+
+ for (i = 0; i < num; i++) {
+ if ((gen3_ifmd_table[i].v_sel[priv->index].csi2_ch
+ == priv->csi_ch) &&
+ (gen3_ifmd_table[i].v_sel[priv->index].vc
+ == priv->vc)) {
+ if (priv->index < RCAR_VIDEO_4) {
+ if (ifmd0_init) {
+ ifmd0_reg_match[i] = true;
+ match_flag = true;
+ } else if (ifmd0_reg_match[i])
+ match_flag = true;
+ } else {
+ if (ifmd4_init) {
+ ifmd4_reg_match[i] = true;
+ match_flag = true;
+ } else if (ifmd4_reg_match[i])
+ match_flag = true;
+ }
+ } else {
+ if (priv->index < RCAR_VIDEO_4)
+ ifmd0_reg_match[i] = false;
+ else
+ ifmd4_reg_match[i] = false;
+ }
+ }
+ if (priv->index < RCAR_VIDEO_4)
+ ifmd0_init = false;
+ else
+ ifmd4_init = false;
+
+ if (!match_flag) {
+ dev_err(&pdev->dev,
+ "Not match, virtual channel pattern error.\n");
+ return -EINVAL;
+ }
+
+ rcar_vin_cpg_enable_for_ifmd(priv->index, true);
+
+ if (priv->index < RCAR_VIDEO_4) {
+ void __iomem *ifmd0_mem;
+
+ for (i = 0; i < num; i++) {
+ if (ifmd0_reg_match[i]) {
+ ifmd |= gen3_ifmd_table[i].set_reg;
+ break;
+ }
+ }
+
+ ifmd0_mem = ioremap(0xe6ef0000 + VNCSI_IFMD_REG, 0x04);
+ iowrite32(ifmd, ifmd0_mem);
+ iounmap(ifmd0_mem);
+ } else {
+ void __iomem *ifmd4_mem;
+
+ for (i = 0; i < num; i++) {
+ if (ifmd4_reg_match[i]) {
+ ifmd |= gen3_ifmd_table[i].set_reg;
+ break;
+ }
+ }
+
+ ifmd4_mem = ioremap(0xe6ef4000 + VNCSI_IFMD_REG, 0x04);
+ iowrite32(ifmd, ifmd4_mem);
+ iounmap(ifmd4_mem);
+ }
+
+ rcar_vin_cpg_enable_for_ifmd(priv->index, false);
+ }
+
spin_lock_init(&priv->lock);
INIT_LIST_HEAD(&priv->capture);
@@ -1942,6 +3215,14 @@
if (ret)
goto cleanup;
+ if (csi_use) {
+ ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, ren->parent);
+ if (ret)
+ goto cleanup;
+ }
+
+ vin_debug = 0;
+
return 0;
cleanup:
@@ -1957,6 +3238,11 @@
struct rcar_vin_priv *priv = container_of(soc_host,
struct rcar_vin_priv, ici);
+ platform_device_del(priv->async_client->pdev);
+ platform_device_put(priv->async_client->pdev);
+
+ v4l2_async_notifier_unregister(&priv->async_client->notifier);
+
soc_camera_host_unregister(soc_host);
pm_runtime_disable(&pdev->dev);
vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
@@ -1986,11 +3272,10 @@
ifmd4_init = true;
if (priv->chip == RCAR_H3) {
- ifmd = VNCSI_IFMD_DES2 | VNCSI_IFMD_DES1 |
- VNCSI_IFMD_DES0;
+ ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0;
gen3_ifmd_table = vin_h3_vc_ifmd;
} else if (priv->chip == RCAR_M3) {
- ifmd = VNCSI_IFMD_DES2 | VNCSI_IFMD_DES1;
+ ifmd = VNCSI_IFMD_DES1;
gen3_ifmd_table = vin_m3_vc_ifmd;
}
@@ -2077,3 +3362,4 @@
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rcar_vin");
MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
+MODULE_AUTHOR("Koji Matsuoka <koji.matsuoka.xm@renesas.com>");
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
index bda29bc..673491a 100644
--- a/drivers/media/platform/soc_camera/soc_scale_crop.c
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.c
@@ -1,6 +1,7 @@
/*
* soc-camera generic scaling-cropping manipulation functions
*
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
* Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
@@ -405,3 +406,5 @@
mf->height = soc_camera_shift_scale(rect->height, shift, scale_v);
}
EXPORT_SYMBOL(soc_camera_calc_client_output);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 6bf6d54..a753662 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -1,7 +1,7 @@
/*
* vsp1.h -- R-Car VSP1 Driver
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -58,6 +58,7 @@
unsigned int wpf_count;
unsigned int num_bru_inputs;
bool uapi;
+ bool header_mode;
};
struct vsp1_device {
@@ -85,11 +86,18 @@
struct media_device media_dev;
struct media_entity_operations media_ops;
+ bool auto_fld_mode;
+
struct vsp1_drm *drm;
+ int index;
+
+ dma_addr_t dl_addr;
+ unsigned int dl_body;
};
int vsp1_device_get(struct vsp1_device *vsp1);
void vsp1_device_put(struct vsp1_device *vsp1);
+void vsp1_underrun_workaround(struct vsp1_device *vsp1, bool reset);
int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index);
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index 835593d..b14a6ae 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -1,7 +1,7 @@
/*
* vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -279,6 +279,26 @@
format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
bru->entity.source_pad);
+ if (pipe->vmute_flag) {
+ vsp1_bru_write(bru, dl, VI6_BRU_INCTRL, 0);
+ vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
+ (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
+ (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
+ vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
+ vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, (0xFF << 24));
+
+ for (i = 0; i < bru->entity.source_pad; ++i) {
+ vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
+ VI6_BRU_BLD_CCMDX_255_SRC_A |
+ VI6_BRU_BLD_CCMDY_SRC_A |
+ VI6_BRU_BLD_ACMDX_255_SRC_A |
+ VI6_BRU_BLD_ACMDY_COEFY |
+ VI6_BRU_BLD_COEFY_MASK);
+ }
+
+ return;
+ }
+
/* The hardware is extremely flexible but we have no userspace API to
* expose all the parameters, nor is it clear whether we would have use
* cases for all the supported modes. Let's just harcode the parameters
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index e238d9b..fc68669 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -1,7 +1,7 @@
/*
* vsp1_dl.h -- R-Car VSP1 Display List
*
- * Copyright (C) 2015 Renesas Corporation
+ * Copyright (C) 2015-2016 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -15,12 +15,18 @@
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/slab.h>
+#include <linux/soc/renesas/rcar_prr.h>
+
+#include <media/rcar-fcp.h>
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
#define VSP1_DL_NUM_ENTRIES 256
#define VSP1_DL_NUM_LISTS 3
+#define VSP1_DL_EXT_OFFSET 0x1000
#define VSP1_DLH_INT_ENABLE (1 << 1)
#define VSP1_DLH_AUTO_START (1 << 0)
@@ -35,6 +41,24 @@
struct vsp1_dl_header_list lists[8];
u32 next_header;
u32 flags;
+ /* if (VI6_DL_EXT_CTRL.EXT) */
+ u32 zero_bits;
+ /* zero_bits:6 + pre_ext_dl_exec:1 + */
+ /* post_ext_dl_exec:1 + zero_bits:8 + pre_ext_dl_num_cmd:16 */
+ u32 pre_post_num;
+ u32 pre_ext_dl_plist;
+ /* zero_bits:16 + post_ext_dl_num_cmd:16 */
+ u32 post_ext_dl_num_cmd;
+ u32 post_ext_dl_p_list;
+} __attribute__((__packed__));
+
+struct vsp1_ext_dl_body {
+ u32 ext_dl_cmd[2];
+ u32 ext_dl_data[2];
+} __attribute__((__packed__));
+
+struct vsp1_ext_addr {
+ u32 addr;
} __attribute__((__packed__));
struct vsp1_dl_entry {
@@ -68,6 +92,10 @@
* @dlm: the display list manager
* @header: display list header, NULL for headerless lists
* @dma: DMA address for the header
+ * @ext_body: display list extended body
+ * @ext_dma: DMA address for extended body
+ * @src_dst_addr: display list (Auto-FLD) source/destination address
+ * @ext_addr_dma: DMA address for display list (Auto-FLD)
* @body0: first display list body
* @fragments: list of extra display list bodies
*/
@@ -78,6 +106,12 @@
struct vsp1_dl_header *header;
dma_addr_t dma;
+ struct vsp1_ext_dl_body *ext_body;
+ dma_addr_t ext_dma;
+
+ struct vsp1_ext_addr *src_dst_addr;
+ dma_addr_t ext_addr_dma;
+
struct vsp1_dl_body body0;
struct list_head fragments;
};
@@ -124,12 +158,13 @@
size_t extra_size)
{
size_t size = num_entries * sizeof(*dlb->entries) + extra_size;
+ struct device *fcp = rcar_fcp_device(vsp1->fcp);
dlb->vsp1 = vsp1;
dlb->size = size;
- dlb->entries = dma_alloc_wc(vsp1->dev, dlb->size, &dlb->dma,
- GFP_KERNEL);
+ dlb->entries = dma_alloc_wc(fcp ? fcp : vsp1->dev, dlb->size +
+ (VSP1_DL_EXT_OFFSET * 2), &dlb->dma, GFP_KERNEL);
if (!dlb->entries)
return -ENOMEM;
@@ -141,7 +176,11 @@
*/
static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
{
- dma_free_wc(dlb->vsp1->dev, dlb->size, dlb->entries, dlb->dma);
+ struct device *fcp = rcar_fcp_device(dlb->vsp1->fcp);
+
+ dma_free_wc(fcp ? fcp : dlb->vsp1->dev,
+ dlb->size + (VSP1_DL_EXT_OFFSET * 2),
+ dlb->entries, dlb->dma);
}
/**
@@ -218,6 +257,102 @@
* Display List Transaction Management
*/
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_list *dl, struct vsp1_rwpf *rpf)
+{
+ const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+ const struct v4l2_rect *crop;
+ u32 y_top_index, y_bot_index;
+ u32 u_top_index, u_bot_index;
+ u32 v_top_index, v_bot_index;
+ dma_addr_t y_top_addr, y_bot_addr;
+ dma_addr_t u_top_addr, u_bot_addr;
+ dma_addr_t v_top_addr, v_bot_addr;
+ u32 width, stride;
+
+ crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+ width = ALIGN(crop->width, 16);
+ stride = width * fmtinfo->bpp[0] / 8;
+
+ y_top_index = rpf->entity.index * 8;
+ y_bot_index = rpf->entity.index * 8 + 1;
+ u_top_index = rpf->entity.index * 8 + 2;
+ u_bot_index = rpf->entity.index * 8 + 3;
+ v_top_index = rpf->entity.index * 8 + 4;
+ v_bot_index = rpf->entity.index * 8 + 5;
+
+ switch (rpf->fmtinfo->fourcc) {
+ case V4L2_PIX_FMT_YUV420M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+ u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride / 2;
+ v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride / 2;
+ break;
+
+ case V4L2_PIX_FMT_YUV422M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 2;
+ u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+ v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+ break;
+
+ case V4L2_PIX_FMT_YUV444M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 3;
+ u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride * 3;
+ v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride * 3;
+ break;
+
+ case V4L2_PIX_FMT_YVU420M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+ u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride / 2;
+ v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride / 2;
+ break;
+
+ case V4L2_PIX_FMT_YVU422M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 2;
+ u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+ v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+ break;
+
+ case V4L2_PIX_FMT_YVU444M:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride * 3;
+ u_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride * 3;
+ v_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride * 3;
+ break;
+
+ default:
+ y_top_addr = rpf->mem.addr[0] + rpf->offsets[0];
+ y_bot_addr = rpf->mem.addr[0] + rpf->offsets[0] + stride;
+ u_top_addr = rpf->mem.addr[1] + rpf->offsets[1];
+ u_bot_addr = rpf->mem.addr[1] + rpf->offsets[1] + stride;
+ v_top_addr = rpf->mem.addr[2] + rpf->offsets[1];
+ v_bot_addr = rpf->mem.addr[2] + rpf->offsets[1] + stride;
+ break;
+ }
+
+ dl->src_dst_addr[y_top_index].addr = y_top_addr;
+ dl->src_dst_addr[y_bot_index].addr = y_bot_addr;
+ dl->src_dst_addr[u_top_index].addr = u_top_addr;
+ dl->src_dst_addr[u_bot_index].addr = u_bot_addr;
+ dl->src_dst_addr[v_top_index].addr = v_top_addr;
+ dl->src_dst_addr[v_bot_index].addr = v_bot_addr;
+}
+
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl_list *dl;
@@ -254,9 +389,43 @@
dl->header = ((void *)dl->body0.entries) + header_offset;
dl->dma = dl->body0.dma + header_offset;
+ dl->ext_body = ((void *)dl->body0.entries) +
+ header_offset + VSP1_DL_EXT_OFFSET;
+ dl->ext_dma = dl->body0.dma + header_offset +
+ VSP1_DL_EXT_OFFSET;
+
+ dl->src_dst_addr = ((void *)dl->body0.entries) +
+ header_offset + (VSP1_DL_EXT_OFFSET * 2);
+ dl->ext_addr_dma = dl->body0.dma + header_offset +
+ (VSP1_DL_EXT_OFFSET * 2);
+
memset(dl->header, 0, sizeof(*dl->header));
dl->header->lists[0].addr = dl->body0.dma;
dl->header->flags = VSP1_DLH_INT_ENABLE;
+ if (dlm->vsp1->info->header_mode) {
+ dl->header->next_header = dl->dma;
+ dl->header->flags |= VSP1_DLH_AUTO_START;
+ }
+
+ if (dlm->vsp1->auto_fld_mode) {
+ /* Set extended display list header */
+ /* pre_ext_dl_exec = 1, pre_ext_dl_num_cmd = 1 */
+ dl->header->pre_post_num = (1 << 25) | (0x01);
+ dl->header->pre_ext_dl_plist = dl->ext_dma;
+ dl->header->post_ext_dl_num_cmd = 0;
+ dl->header->post_ext_dl_p_list = 0;
+
+ /* Set extended display list (Auto-FLD) */
+ /* Set opecode */
+ dl->ext_body->ext_dl_cmd[0] = 0x00000003;
+ /* RPF[0]-[4] address is updated */
+ dl->ext_body->ext_dl_cmd[1] = 0x001f0001;
+
+ /* Set pointer of source/destination address */
+ dl->ext_body->ext_dl_data[0] = dl->ext_addr_dma;
+ /* Should be set to 0 */
+ dl->ext_body->ext_dl_data[1] = 0;
+ }
}
return dl;
@@ -415,7 +584,13 @@
dl->header->num_lists = num_lists;
vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma);
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ vsp1->dl_addr = dl->dma;
+
dlm->active = dl;
+ __vsp1_dl_list_put(dl);
+
goto done;
}
@@ -439,6 +614,13 @@
vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
(dl->body0.num_entries * sizeof(*dl->header->lists)));
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) {
+ vsp1->dl_addr = dl->body0.dma;
+ vsp1->dl_body = VI6_DL_BODY_SIZE_UPD |
+ (dl->body0.num_entries * sizeof(*dl->header->lists));
+ }
+
__vsp1_dl_list_put(dlm->queued);
dlm->queued = dl;
@@ -508,6 +690,13 @@
(dl->body0.num_entries *
sizeof(*dl->header->lists)));
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) {
+ vsp1->dl_addr = dl->body0.dma;
+ vsp1->dl_body = VI6_DL_BODY_SIZE_UPD |
+ (dl->body0.num_entries *
+ sizeof(*dl->header->lists));
+ }
dlm->queued = dl;
dlm->pending = NULL;
}
@@ -523,10 +712,16 @@
| VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
| VI6_DL_CTRL_DLE;
+ if ((vsp1->info->header_mode) && (vsp1->auto_fld_mode)) {
+ vsp1_write(vsp1, VI6_DL_EXT_CTRL,
+ (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT)
+ | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT);
+ }
+
/* The DRM pipeline operates with display lists in Continuous Frame
* Mode, all other pipelines use manual start.
*/
- if (vsp1->drm)
+ if ((vsp1->drm) && (!vsp1->info->header_mode))
ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0;
vsp1_write(vsp1, VI6_DL_CTRL, ctrl);
@@ -556,13 +751,14 @@
{
struct vsp1_dl_manager *dlm;
unsigned int i;
+ int ret;
dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
if (!dlm)
return NULL;
dlm->index = index;
- dlm->mode = index == 0 && !vsp1->info->uapi
+ dlm->mode = index == 0 && !vsp1->info->uapi && !vsp1->info->header_mode
? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER;
dlm->vsp1 = vsp1;
@@ -579,6 +775,12 @@
list_add_tail(&dl->list, &dlm->free);
}
+ ret = RCAR_PRR_INIT();
+ if (ret) {
+ dev_dbg(dlm->vsp1->dev, "product register init fail.\n");
+ return NULL;
+ }
+
return dlm;
}
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index de387cd..7b4e522 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -31,6 +31,7 @@
void vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_list *dl, struct vsp1_rwpf *rpf);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data);
void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index c5ee06b..d74b0bd 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -1,7 +1,7 @@
/*
* vsp1_drm.c -- R-Car VSP1 DRM API
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/slab.h>
+#include <linux/soc/renesas/rcar_prr.h>
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
@@ -51,6 +52,20 @@
}
EXPORT_SYMBOL_GPL(vsp1_du_init);
+int vsp1_du_if_set_mute(struct device *dev, bool on)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+
+ if (on)
+ pipe->vmute_flag = true;
+ else
+ pipe->vmute_flag = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_if_set_mute);
+
/**
* vsp1_du_setup_lif - Setup the output part of the VSP pipeline
* @dev: the VSP device
@@ -296,12 +311,26 @@
rpf->fmtinfo = fmtinfo;
rpf->format.num_planes = fmtinfo->planes;
rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
- rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
+ if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV420M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU420M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV422M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU422M))
+ rpf->format.plane_fmt[1].bytesperline = cfg->pitch / 2;
+ else
+ rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
rpf->alpha = cfg->alpha;
+ rpf->interlaced = cfg->interlaced;
+
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0) && rpf->interlaced) {
+ dev_err(vsp1->dev,
+ "Interlaced mode is not supported.\n");
+ return -EINVAL;
+ }
rpf->mem.addr[0] = cfg->mem[0];
rpf->mem.addr[1] = cfg->mem[1];
- rpf->mem.addr[2] = 0;
+ rpf->mem.addr[2] = cfg->mem[2];
vsp1->drm->inputs[rpf_index].crop = cfg->src;
vsp1->drm->inputs[rpf_index].compose = cfg->dst;
@@ -383,12 +412,6 @@
/* BRU sink, propagate the format from the RPF source. */
format.pad = bru_input;
- format.format.code = rpf->fmtinfo->mbus;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL,
&format);
@@ -525,6 +548,58 @@
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
+int vsp1_du_setup_wb(struct device *dev, u32 pixelformat, unsigned int pitch,
+ dma_addr_t mem[2])
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+ struct vsp1_rwpf *wpf = pipe->output;
+ const struct vsp1_format_info *fmtinfo;
+ struct vsp1_rwpf *rpf = pipe->inputs[0];
+ unsigned long flags;
+ int i;
+
+ fmtinfo = vsp1_get_format_info(pixelformat);
+ if (!fmtinfo) {
+ dev_err(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
+ pixelformat);
+ return -EINVAL;
+ }
+
+ if (rpf->interlaced) {
+ dev_err(vsp1->dev, "Prohibited in interlaced mode\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ wpf->fmtinfo = fmtinfo;
+ wpf->format.num_planes = fmtinfo->planes;
+ wpf->format.plane_fmt[0].bytesperline = pitch;
+ wpf->format.plane_fmt[1].bytesperline = pitch;
+
+ for (i = 0; i < wpf->format.num_planes; ++i)
+ wpf->buf_addr[i] = mem[i];
+
+ pipe->output->write_back = 3;
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_setup_wb);
+
+void vsp1_du_wait_wb(struct device *dev, u32 count)
+{
+ int ret;
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_pipeline *pipe = &vsp1->drm->pipe;
+
+ ret = wait_event_interruptible(pipe->event_wait,
+ (pipe->output->write_back == count));
+}
+EXPORT_SYMBOL_GPL(vsp1_du_wait_wb);
+
/* -----------------------------------------------------------------------------
* Initialization
*/
@@ -579,6 +654,7 @@
{
struct vsp1_pipeline *pipe;
unsigned int i;
+ int ret;
vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL);
if (!vsp1->drm)
@@ -602,6 +678,14 @@
pipe->bru = &vsp1->bru->entity;
pipe->lif = &vsp1->lif->entity;
pipe->output = vsp1->wpf[0];
+ pipe->output->write_back = 0;
+ init_waitqueue_head(&pipe->event_wait);
+
+ ret = RCAR_PRR_INIT();
+ if (ret) {
+ dev_dbg(vsp1->dev, "product register init fail.\n");
+ return ret;
+ }
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index c626955..08a2ca4 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,7 +1,7 @@
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -11,6 +11,10 @@
* (at your option) any later version.
*/
+#ifdef CONFIG_VIDEO_RENESAS_DEBUG
+#define DEBUG
+#endif
+
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -21,6 +25,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/videodev2.h>
+#include <linux/soc/renesas/rcar_prr.h>
#include <media/rcar-fcp.h>
#include <media/v4l2-subdev.h>
@@ -39,31 +44,152 @@
#include "vsp1_uds.h"
#include "vsp1_video.h"
+#define VSP1_UT_IRQ 0x01
+
+static unsigned int vsp1_debug; /* 1 to enable debug output */
+module_param_named(debug, vsp1_debug, int, 0600);
+static int underrun_vspd[4];
+module_param_array(underrun_vspd, int, NULL, 0600);
+
+#ifdef CONFIG_VIDEO_RENESAS_DEBUG
+#define VSP1_IRQ_DEBUG(fmt, args...) \
+ do { \
+ if (unlikely(vsp1_debug & VSP1_UT_IRQ)) \
+ vsp1_ut_debug_printk(__func__, fmt, ##args); \
+ } while (0)
+#else
+#define VSP1_IRQ_DEBUG(fmt, args...)
+#endif
+
+void vsp1_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ pr_debug("[vsp1 :%s] %pV", function_name, &vaf);
+
+ va_end(args);
+}
+
+#define SRCR7_REG 0xe61501cc
+#define FCPVD0_REG 0xfea27000
+#define FCPVD1_REG 0xfea2f000
+#define FCPVD2_REG 0xfea37000
+#define FCPVD3_REG 0xfea3f000
+
+#define FCP_RST_REG 0x0010
+#define FCP_RST_SOFTRST 0x00000001
+#define FCP_RST_WORKAROUND 0x00000010
+
+#define FCP_STA_REG 0x0018
+#define FCP_STA_ACT 0x00000001
+
+static void __iomem *fcpv_reg[4];
+static const unsigned int fcpvd_offset[] = {
+ FCPVD0_REG, FCPVD1_REG, FCPVD2_REG, FCPVD3_REG
+};
+
+void vsp1_underrun_workaround(struct vsp1_device *vsp1, bool reset)
+{
+ unsigned int timeout = 0;
+
+ /* 1. Disable clock stop of VSP */
+ vsp1_write(vsp1, VI6_CLK_CTRL0, VI6_CLK_CTRL0_WORKAROUND);
+ vsp1_write(vsp1, VI6_CLK_CTRL1, VI6_CLK_CTRL1_WORKAROUND);
+ vsp1_write(vsp1, VI6_CLK_DCSWT, VI6_CLK_DCSWT_WORKAROUND1);
+ vsp1_write(vsp1, VI6_CLK_DCSM0, VI6_CLK_DCSM0_WORKAROUND);
+ vsp1_write(vsp1, VI6_CLK_DCSM1, VI6_CLK_DCSM1_WORKAROUND);
+
+ /* 2. Stop operation of VSP except bus access with module reset */
+ vsp1_write(vsp1, VI6_MRESET_ENB0, VI6_MRESET_ENB0_WORKAROUND1);
+ vsp1_write(vsp1, VI6_MRESET_ENB1, VI6_MRESET_ENB1_WORKAROUND);
+ vsp1_write(vsp1, VI6_MRESET, VI6_MRESET_WORKAROUND);
+
+ /* 3. Stop operation of FCPV with software reset */
+ iowrite32(FCP_RST_SOFTRST, fcpv_reg[vsp1->index] + FCP_RST_REG);
+
+ /* 4. Wait until FCP_STA.ACT become 0. */
+ while (1) {
+ if ((ioread32(fcpv_reg[vsp1->index] + FCP_STA_REG) &
+ FCP_STA_ACT) != FCP_STA_ACT)
+ break;
+
+ if (timeout == 100)
+ break;
+
+ timeout++;
+ udelay(1);
+ }
+
+ /* 5. Initialize the whole FCPV with module reset */
+ iowrite32(FCP_RST_WORKAROUND, fcpv_reg[vsp1->index] + FCP_RST_REG);
+
+ /* 6. Stop the whole operation of VSP with module reset */
+ /* (note that register setting is not cleared) */
+ vsp1_write(vsp1, VI6_MRESET_ENB0, VI6_MRESET_ENB0_WORKAROUND2);
+ vsp1_write(vsp1, VI6_MRESET_ENB1, VI6_MRESET_ENB1_WORKAROUND);
+ vsp1_write(vsp1, VI6_MRESET, VI6_MRESET_WORKAROUND);
+
+ /* 7. Enable clock stop of VSP */
+ vsp1_write(vsp1, VI6_CLK_CTRL0, 0);
+ vsp1_write(vsp1, VI6_CLK_CTRL1, 0);
+ vsp1_write(vsp1, VI6_CLK_DCSWT, VI6_CLK_DCSWT_WORKAROUND2);
+ vsp1_write(vsp1, VI6_CLK_DCSM0, 0);
+ vsp1_write(vsp1, VI6_CLK_DCSM1, 0);
+
+ /* 8. Restart VSPD */
+ if (!reset) {
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), vsp1->dl_addr);
+ /* Necessary when headerless display list */
+ if (!vsp1->info->header_mode)
+ vsp1_write(vsp1, VI6_DL_BODY_SIZE, vsp1->dl_body);
+ vsp1_write(vsp1, VI6_CMD(0), VI6_CMD_STRCMD);
+ }
+}
+
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
-
static irqreturn_t vsp1_irq_handler(int irq, void *data)
{
- u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
+ u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE
+ | VI6_WFP_IRQ_STA_UND;
struct vsp1_device *vsp1 = data;
irqreturn_t ret = IRQ_NONE;
unsigned int i;
u32 status;
+ unsigned int vsp_und_cnt = 0;
+ bool underrun = false;
for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
if (wpf == NULL)
continue;
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
+ if (vsp1_debug && (status & VI6_WFP_IRQ_STA_UND)) {
+ vsp_und_cnt = ++underrun_vspd[vsp1->index];
+
+ VSP1_IRQ_DEBUG(
+ "Underrun occurred num[%d] at VSPD (%s)\n",
+ vsp_und_cnt, dev_name(vsp1->dev));
+ }
+
if (status & VI6_WFP_IRQ_STA_FRE) {
- vsp1_pipeline_frame_end(wpf->pipe);
+ vsp1_pipeline_frame_end(pipe);
ret = IRQ_HANDLED;
}
+ if (status & VI6_WFP_IRQ_STA_UND)
+ underrun = true;
}
status = vsp1_read(vsp1, VI6_DISP_IRQ_STA);
@@ -74,6 +200,10 @@
ret = IRQ_HANDLED;
}
+ if ((RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) && underrun)
+ vsp1_underrun_workaround(vsp1, false);
+
return ret;
}
@@ -430,7 +560,12 @@
if (!(status & VI6_STATUS_SYS_ACT(index)))
return 0;
- vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ vsp1_underrun_workaround(vsp1, true);
+ else
+ vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
+
for (timeout = 10; timeout > 0; --timeout) {
status = vsp1_read(vsp1, VI6_STATUS);
if (!(status & VI6_STATUS_SYS_ACT(index)))
@@ -583,7 +718,7 @@
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_SRU,
.rpf_count = 5,
- .uds_count = 1,
+ .uds_count = 3,
.wpf_count = 4,
.num_bru_inputs = 4,
.uapi = true,
@@ -594,7 +729,7 @@
| VSP1_HAS_LUT,
.rpf_count = 4,
.uds_count = 1,
- .wpf_count = 4,
+ .wpf_count = 1,
.num_bru_inputs = 4,
.uapi = true,
}, {
@@ -603,7 +738,7 @@
.features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT
| VSP1_HAS_SRU,
.rpf_count = 5,
- .uds_count = 3,
+ .uds_count = 1,
.wpf_count = 4,
.num_bru_inputs = 4,
.uapi = true,
@@ -638,6 +773,7 @@
.rpf_count = 5,
.wpf_count = 2,
.num_bru_inputs = 5,
+ .header_mode = true,
},
};
@@ -718,6 +854,18 @@
dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
+ ret = RCAR_PRR_INIT();
+ if (ret) {
+ dev_dbg(vsp1->dev, "product register init fail.\n");
+ return ret;
+ }
+
+ if (vsp1->info->header_mode && !(RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)))
+ vsp1->auto_fld_mode = true;
+ else
+ vsp1->auto_fld_mode = false;
+
/* Instanciate entities */
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
@@ -725,6 +873,20 @@
goto done;
}
+ if (strcmp(dev_name(vsp1->dev), "fea20000.vsp") == 0)
+ vsp1->index = 0;
+ else if (strcmp(dev_name(vsp1->dev), "fea28000.vsp") == 0)
+ vsp1->index = 1;
+ else if (strcmp(dev_name(vsp1->dev), "fea30000.vsp") == 0)
+ vsp1->index = 2;
+ else if (strcmp(dev_name(vsp1->dev), "fea38000.vsp") == 0)
+ vsp1->index = 3;
+
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ fcpv_reg[vsp1->index] =
+ ioremap(fcpvd_offset[vsp1->index], 0x20);
+
done:
if (ret)
pm_runtime_disable(&pdev->dev);
@@ -736,11 +898,16 @@
{
struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
+ vsp1_device_put(vsp1);
vsp1_destroy_entities(vsp1);
rcar_fcp_put(vsp1->fcp);
pm_runtime_disable(&pdev->dev);
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ iounmap(fcpv_reg[vsp1->index]);
+
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index aadeb7d..a6414b9 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -118,7 +118,7 @@
{ V4L2_PIX_FMT_YVU420M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 3, { 8, 8, 8 }, false, true, 2, 2, false },
+ 3, { 8, 8, 8 }, false, false, 2, 2, false },
{ V4L2_PIX_FMT_YUV422M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -126,7 +126,7 @@
{ V4L2_PIX_FMT_YVU422M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 3, { 8, 8, 8 }, false, true, 2, 1, false },
+ 3, { 8, 8, 8 }, false, false, 2, 1, false },
{ V4L2_PIX_FMT_YUV444M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -134,7 +134,7 @@
{ V4L2_PIX_FMT_YVU444M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 3, { 8, 8, 8 }, false, true, 1, 1, false },
+ 3, { 8, 8, 8 }, false, false, 1, 1, false },
};
/*
@@ -173,13 +173,17 @@
bru->inputs[i].rpf = NULL;
}
- for (i = 0; i < pipe->num_inputs; ++i) {
- pipe->inputs[i]->pipe = NULL;
- pipe->inputs[i] = NULL;
+ for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
+ if (pipe->inputs[i]) {
+ pipe->inputs[i]->pipe = NULL;
+ pipe->inputs[i] = NULL;
+ }
}
- pipe->output->pipe = NULL;
- pipe->output = NULL;
+ if (pipe->output) {
+ pipe->output->pipe = NULL;
+ pipe->output = NULL;
+ }
if (pipe->hgo) {
struct vsp1_hgo *hgo = to_hgo(&pipe->hgo->subdev);
@@ -206,6 +210,7 @@
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
+ pipe->vmute_flag = false;
}
/* Must be called with the pipe irqlock held. */
@@ -292,9 +297,18 @@
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
+ unsigned long flags;
+
if (pipe == NULL)
return;
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->output->write_back != 0) {
+ pipe->output->write_back--;
+ wake_up_interruptible(&pipe->event_wait);
+ }
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
vsp1_dlm_irq_frame_end(pipe->output->dlm);
if (pipe->hgo)
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
index 3ecd3c1..d541b23 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -1,7 +1,7 @@
/*
* vsp1_pipe.h -- R-Car VSP1 Pipeline
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -104,10 +104,20 @@
struct vsp1_entity *uds_input;
struct list_head entities;
+ wait_queue_head_t event_wait;
struct vsp1_dl_list *dl;
+ bool vmute_flag;
};
+static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
+{
+ if (likely(e->pipe))
+ return container_of(e->pipe, struct vsp1_pipeline, pipe);
+ else
+ return NULL;
+}
+
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index 684a3ef..f9b1ed1 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -1,7 +1,7 @@
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
- * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -20,23 +20,49 @@
#define VI6_CMD(n) (0x0000 + (n) * 4)
#define VI6_CMD_STRCMD (1 << 0)
+#define VI6_CLK_CTRL0 0x0010
+#define VI6_CLK_CTRL0_WORKAROUND 0x10010F1F
+
+#define VI6_CLK_CTRL1 0x0014
+#define VI6_CLK_CTRL1_WORKAROUND 0xFF00FFFF
+
#define VI6_CLK_DCSWT 0x0018
#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8)
#define VI6_CLK_DCSWT_CSTPW_SHIFT 8
#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0)
#define VI6_CLK_DCSWT_CSTRW_SHIFT 0
+#define VI6_CLK_DCSWT_WORKAROUND1 0x00130808
+#define VI6_CLK_DCSWT_WORKAROUND2 0x00000808
+
+#define VI6_CLK_DCSM0 0x001C
+#define VI6_CLK_DCSM0_WORKAROUND 0x1FFF0F1F
+
+#define VI6_CLK_DCSM1 0x0020
+#define VI6_CLK_DCSM1_WORKAROUND 0xFF00FFFF
#define VI6_SRESET 0x0028
#define VI6_SRESET_SRTS(n) (1 << (n))
+#define VI6_MRESET_ENB0 0x002C
+#define VI6_MRESET_ENB0_WORKAROUND1 0x0000001F
+#define VI6_MRESET_ENB0_WORKAROUND2 0x30000F1F
+
+#define VI6_MRESET_ENB1 0x0030
+#define VI6_MRESET_ENB1_WORKAROUND 0xFF00FFFF
+
+#define VI6_MRESET 0x0034
+#define VI6_MRESET_WORKAROUND 0x00000001
+
#define VI6_STATUS 0x0038
#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
+#define VI6_WFP_IRQ_ENB_UNDE (1 << 16)
#define VI6_WFP_IRQ_ENB_DFEE (1 << 1)
#define VI6_WFP_IRQ_ENB_FREE (1 << 0)
#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12)
+#define VI6_WFP_IRQ_STA_UND (1 << 16)
#define VI6_WFP_IRQ_STA_DFE (1 << 1)
#define VI6_WFP_IRQ_STA_FRE (1 << 0)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index 4895038..a41e42c 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -49,19 +49,34 @@
static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
{
struct vsp1_rwpf *rpf = entity_to_rwpf(entity);
+ struct vsp1_device *vsp1 = entity->vsp1;
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
- rpf->mem.addr[0] + rpf->offsets[0]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
- rpf->mem.addr[1] + rpf->offsets[1]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
- rpf->mem.addr[2] + rpf->offsets[1]);
+ if (vsp1->auto_fld_mode)
+ vsp1_dl_set_addr_auto_fld(dl, rpf);
+ else {
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y,
+ rpf->mem.addr[0] + rpf->offsets[0]);
+ if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU420M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU422M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU444M)) {
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
+ rpf->mem.addr[2] + rpf->offsets[1]);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
+ rpf->mem.addr[1] + rpf->offsets[1]);
+ } else {
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0,
+ rpf->mem.addr[1] + rpf->offsets[1]);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1,
+ rpf->mem.addr[2] + rpf->offsets[1]);
+ }
+ }
}
static void rpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
{
+ struct vsp1_device *vsp1 = entity->vsp1;
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->format;
@@ -72,6 +87,16 @@
unsigned int top = 0;
u32 pstride;
u32 infmt;
+ u32 alph_sel = 0, laya = 0;
+ u32 i;
+ u32 crop_width, crop_height, crop_x, crop_y;
+
+ if (pipe->vmute_flag) {
+ for (i = 0; i < vsp1->info->rpf_count; ++i)
+ vsp1_rpf_write(rpf, dl, VI6_DPR_RPF_ROUTE(i),
+ VI6_DPR_NODE_UNUSED);
+ return;
+ }
/* Source size, stride and crop offsets.
*
@@ -81,28 +106,72 @@
*/
crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
- (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
- (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
- (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
- (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+ crop_width = crop->width;
+ crop_x = crop->left;
- rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
- + crop->left * fmtinfo->bpp[0] / 8;
+ if (rpf->interlaced) {
+ crop_height = crop->height / 2;
+ crop_y = crop->top / 2;
+
+ if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_UYVY) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_VYUY) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUYV) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVYU)) {
+ crop_width = round_down(crop_width, 2);
+ crop_x = round_down(crop_x, 2);
+ } else if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_NV12M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_NV21M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ crop_x = round_down(crop_x, 2);
+ crop_y = round_down(crop_y, 2);
+ } else if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_NV16M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_NV61M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_x = round_down(crop_x, 2);
+ } else if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV420M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV444M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU420M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU444M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ } else if ((rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YUV422M) ||
+ (rpf->fmtinfo->fourcc == V4L2_PIX_FMT_YVU422M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ crop_x = round_down(crop_x, 2);
+ crop_y = round_down(crop_y, 2);
+ }
+ } else {
+ crop_height = crop->height;
+ crop_y = crop->top;
+ }
+
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
+ (crop_width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (crop_height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
+ (crop_width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (crop_height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ rpf->offsets[0] = crop_y * format->plane_fmt[0].bytesperline
+ + crop_x * fmtinfo->bpp[0] / 8;
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
if (format->num_planes > 1) {
- rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
- + crop->left * fmtinfo->bpp[1] / 8;
+ rpf->offsets[1] = crop_y * format->plane_fmt[1].bytesperline
+ + crop_x * fmtinfo->bpp[1] / 8;
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
} else {
rpf->offsets[1] = 0;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
+ if (rpf->interlaced)
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride * 2);
+ else
+ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
@@ -138,7 +207,12 @@
top = compose->top;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+ if (rpf->interlaced)
+ vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+ (left << VI6_RPF_LOC_HCOORD_SHIFT) |
+ ((top / 2) << VI6_RPF_LOC_VCOORD_SHIFT));
+ else
+ vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
(left << VI6_RPF_LOC_HCOORD_SHIFT) |
(top << VI6_RPF_LOC_VCOORD_SHIFT));
@@ -164,23 +238,51 @@
*
* In all cases, disable color keying.
*/
- vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
- (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
- : VI6_RPF_ALPH_SEL_ASEL_FIXED));
+ switch (fmtinfo->fourcc) {
+ case V4L2_PIX_FMT_ARGB555:
+ if (CONFIG_VIDEO_RENESAS_VSP_ALPHA_BIT_ARGB1555 == 1)
+ alph_sel = VI6_RPF_ALPH_SEL_ASEL_SELECT |
+ VI6_RPF_ALPH_SEL_AEXT_EXT |
+ VI6_RPF_ALPH_SEL_ALPHA1_MASK |
+ (rpf->alpha &
+ VI6_RPF_ALPH_SEL_ALPHA0_MASK);
+ else
+ alph_sel = VI6_RPF_ALPH_SEL_ASEL_SELECT |
+ VI6_RPF_ALPH_SEL_AEXT_EXT |
+ ((rpf->alpha & 0xff) << 8) |
+ VI6_RPF_ALPH_SEL_ALPHA0_MASK;
+ laya = 0;
+ break;
+ case V4L2_PIX_FMT_ARGB444:
+ alph_sel = VI6_RPF_ALPH_SEL_AEXT_ONE;
+ laya = 0;
+ break;
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_ARGB32:
+ laya = 0;
+ break;
+ default:
+ alph_sel = VI6_RPF_ALPH_SEL_AEXT_EXT |
+ (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
+ : VI6_RPF_ALPH_SEL_ASEL_FIXED);
+ laya = rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT;
+ break;
+ }
- vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
- rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, alph_sel);
+ vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, laya);
if (entity->vsp1->info->gen == 3) {
u32 mult;
- if (fmtinfo->alpha) {
+ if ((fmtinfo->alpha) &&
+ (fmtinfo->fourcc != V4L2_PIX_FMT_ARGB555)) {
/* When the input contains an alpha channel enable the
* alpha multiplier. If the input is premultiplied we
* need to multiply both the alpha channel and the pixel
* components by the global alpha value to keep them
* premultiplied. Otherwise multiply the alpha channel
- * only.
+ * only. The alpha multiplier is disabled in ARGB1555.
*/
bool premultiplied = format->flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 9ff7c78..e95f23a 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -53,6 +53,11 @@
struct vsp1_rwpf_memory mem;
struct vsp1_dl_manager *dlm;
+
+ bool interlaced;
+
+ int write_back;
+ dma_addr_t buf_addr[3];
};
static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index bcf47e7..be0fab7 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -27,6 +27,8 @@
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
+#include <media/rcar-fcp.h>
+
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_dl.h"
@@ -937,6 +939,7 @@
{
struct vsp1_video *video;
const char *direction;
+ struct device *fcp;
int ret;
video = devm_kzalloc(vsp1->dev, sizeof(*video), GFP_KERNEL);
@@ -987,7 +990,8 @@
video_set_drvdata(&video->video, video);
/* ... and the buffers queue... */
- video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev);
+ fcp = rcar_fcp_device(vsp1->fcp);
+ video->alloc_ctx = vb2_dma_contig_init_ctx(fcp ? fcp : vsp1->dev);
if (IS_ERR(video->alloc_ctx)) {
ret = PTR_ERR(video->alloc_ctx);
goto error;
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 4e391cc..2ffb221 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -1,7 +1,7 @@
/*
* vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -104,6 +104,20 @@
u32 outfmt = 0;
u32 srcrpf = 0;
+ if (pipe->vmute_flag) {
+ vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF +
+ (0x100 * wpf->entity.index),
+ VI6_WPF_SRCRPF_VIRACT_MST);
+ vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP +
+ (0x100 * wpf->entity.index), 0);
+ vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP +
+ (0x100 * wpf->entity.index), 0);
+ vsp1_wpf_write(wpf, dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ VI6_DPR_WPF_FPORCH_FP_WPFN);
+
+ return;
+ }
+
/* Cropping */
crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config);
@@ -122,10 +136,21 @@
wpf->entity.config,
RWPF_PAD_SOURCE);
- if (!pipe->lif) {
+ if (!pipe->lif || (wpf->write_back == 2)) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
+ if (pipe->lif) {
+ vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y,
+ wpf->buf_addr[0]);
+ if (format->num_planes > 1)
+ vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0,
+ wpf->buf_addr[1]);
+ if (format->num_planes > 2)
+ vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1,
+ wpf->buf_addr[2]);
+ }
+
outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
if (fmtinfo->alpha)
@@ -154,7 +179,11 @@
vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
- vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
+ if (pipe->lif && (pipe->output->write_back == 2))
+ vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL,
+ VI6_WPF_WRBCK_CTRL_WBMD);
+ else
+ vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
/* Sources. If the pipeline has a single input and BRU is not used,
* configure it as the master layer. Otherwise configure all
@@ -180,7 +209,7 @@
/* Enable interrupts */
vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index),
- VI6_WFP_IRQ_ENB_FREE);
+ VI6_WFP_IRQ_ENB_FREE | VI6_WFP_IRQ_ENB_UNDE);
}
static const struct vsp1_entity_operations wpf_entity_ops = {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 99275e4..9dae222 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1130,6 +1130,7 @@
mmc_set_ios(host);
}
+EXPORT_SYMBOL(mmc_set_initial_state);
/**
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0aa484c..f0bb76d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -568,6 +568,16 @@
This provides support for the SDHI SD/SDIO controller found in
SuperH and ARM SH-Mobile SoCs
+config MMC_SDHI_PIO
+ bool "SH-Mobile SDHI PIO transfer mode setting"
+ depends on MMC_SDHI
+ help
+ This setting switches the transfer mode of the PIO and DMA.
+
+ Select Yes or No according to the following.
+ When switching the transfer mode from DMA to PIO, say Y here.
+ When switching the transfer mode from PIO to DMA, say N here.
+
config MMC_CB710
tristate "ENE CB710 MMC/SD Interface support"
depends on PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index af918d2..dc3e932 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -37,7 +37,13 @@
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o
tmio_mmc_core-y := tmio_mmc_pio.o
+ifeq ($(CONFIG_ARM64),y)
+# if CONFIG_ARM64, we assume it is gen3
+tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI)) += tmio_mmc_dma_gen3.o
+else
+# if no, We assume it is gen2 or older
tmio_mmc_core-$(subst m,y,$(CONFIG_MMC_SDHI)) += tmio_mmc_dma.o
+endif
obj-$(CONFIG_MMC_SDHI) += sh_mobile_sdhi.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 7f9c5e2..c707dbc 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -1,8 +1,8 @@
/*
* SuperH Mobile SDHI
*
+ * Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
- * Copyright (C) 2015-16 Renesas Electronics Corporation
* Copyright (C) 2009 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
@@ -37,10 +37,15 @@
#include "tmio_mmc.h"
-#define EXT_ACC 0xe4
+#define HOST_MODE 0xe4
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
+struct sh_mobile_sdhi_scc {
+ unsigned long clk; /* clock for SDR104 */
+ u32 tap; /* sampling clock position for SDR104 */
+};
+
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
unsigned long capabilities;
@@ -48,6 +53,12 @@
enum dma_slave_buswidth dma_buswidth;
dma_addr_t dma_rx_offset;
unsigned bus_shift;
+ unsigned int max_blk_count;
+ unsigned short max_segs;
+ bool sdbuf_64bit;
+ int scc_offset;
+ struct sh_mobile_sdhi_scc *taps;
+ int taps_num;
};
static const struct sh_mobile_sdhi_of_data of_default_cfg = {
@@ -60,19 +71,51 @@
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};
+/* Definitions for sampling clocks */
+static struct sh_mobile_sdhi_scc rcar_gen2_scc_taps[] = {
+ {
+ .clk = 156000000,
+ .tap = 0x00000703,
+ },
+ {
+ .clk = 0,
+ .tap = 0x00000300,
+ },
+};
+
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dma_rx_offset = 0x2000,
+ .scc_offset = 0x0300,
+ .taps = rcar_gen2_scc_taps,
+ .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps),
+};
+
+/* Definitions for sampling clocks */
+static struct sh_mobile_sdhi_scc rcar_gen3_scc_taps[] = {
+ {
+ .clk = 0,
+ .tap = 0x00000300,
+ },
};
static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
- TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
- .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+ TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_CLK_NO_SLEEP,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_CMD23,
.bus_shift = 2,
+ /* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */
+ .max_blk_count = 0xffffffff,
+ .max_segs = 1,
+ .sdbuf_64bit = true,
+ .scc_offset = 0x1000,
+ .taps = rcar_gen3_scc_taps,
+ .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
};
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
@@ -88,6 +131,9 @@
{ .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
+ { .compatible = "renesas,mmc-r8a7795", .data = &of_rcar_gen3_compatible, },
+ { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
+ { .compatible = "renesas,mmc-r8a7796", .data = &of_rcar_gen3_compatible, },
{},
};
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
@@ -129,7 +175,7 @@
return;
}
- sd_ctrl_write16(host, EXT_ACC, val);
+ sd_ctrl_write16(host, HOST_MODE, val);
}
static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
@@ -207,6 +253,51 @@
clk_disable_unprepare(priv->clk);
}
+static void sh_mobile_sdhi_set_clk_div(struct platform_device *pdev,
+ int state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ if (state) {
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x00ff);
+ }
+}
+
+#define SH_MOBILE_SDHI_SIGNAL_180V 0
+#define SH_MOBILE_SDHI_SIGNAL_330V 1
+
+static int sh_mobile_sdhi_set_ioctrl(struct tmio_mmc_host *host, int state)
+{
+ struct platform_device *pdev = host->pdev;
+ struct sh_mobile_sdhi *priv =
+ container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
+ struct pinctrl_state *pstate;
+ int ret;
+
+ if (state == SH_MOBILE_SDHI_SIGNAL_330V) {
+ pstate = priv->pins_default;
+ } else if (state == SH_MOBILE_SDHI_SIGNAL_180V) {
+ pstate = priv->pins_uhs;
+ } else {
+ dev_err(&pdev->dev, "update_ioctrl: unknown state\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!pstate) {
+ ret = -EIO;
+ goto err;
+ }
+
+ return pinctrl_select_state(priv->pinctrl, pstate);
+
+err:
+ return ret;
+}
+
static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
@@ -217,9 +308,53 @@
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
+ /* Enable 3.3V Signal */
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ /* ioctrl */
+ ret = sh_mobile_sdhi_set_ioctrl(host,
+ SH_MOBILE_SDHI_SIGNAL_330V);
+ if (ret) {
+ dev_err(&host->pdev->dev,
+ "3.3V pin function control failed\n");
+ return -EIO;
+ }
+
+ ret = regulator_set_voltage(mmc->supply.vqmmc,
+ 3300000, 3300000);
+ if (ret) {
+ dev_warn(&host->pdev->dev,
+ "3.3V signalling voltage failed\n");
+ return -EIO;
+ }
+ } else {
+ return -EIO;
+ }
+ usleep_range(5000, 10000);
pin_state = priv->pins_default;
break;
case MMC_SIGNAL_VOLTAGE_180:
+ /* Enable 1.8V Signal */
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ ret = regulator_set_voltage(mmc->supply.vqmmc,
+ 1800000, 1800000);
+ if (ret) {
+ dev_warn(&host->pdev->dev,
+ "1.8V signalling voltage failed\n");
+ return -EIO;
+ }
+ /* ioctrl */
+ ret = sh_mobile_sdhi_set_ioctrl(host,
+ SH_MOBILE_SDHI_SIGNAL_180V);
+ if (ret) {
+ dev_err(&host->pdev->dev,
+ "1.8V pin function control failed\n");
+ return -EIO;
+ }
+ } else {
+ return -EIO;
+ }
+ /* Wait for 5ms */
+ usleep_range(5000, 10000);
pin_state = priv->pins_uhs;
break;
default:
@@ -241,6 +376,237 @@
return pinctrl_select_state(priv->pinctrl, pin_state);
}
+static int sh_mobile_sdhi_card_busy(struct tmio_mmc_host *host)
+{
+ u32 dat0;
+
+ /* check to see DAT[3:0] */
+ dat0 = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
+ return !(dat0 & TMIO_STAT_DAT0);
+}
+
+/* SCC registers */
+#define SH_MOBILE_SDHI_SCC_DTCNTL 0x000
+#define SH_MOBILE_SDHI_SCC_TAPSET 0x002
+#define SH_MOBILE_SDHI_SCC_DT2FF 0x004
+#define SH_MOBILE_SDHI_SCC_CKSEL 0x006
+#define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008
+#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A
+#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E
+
+/* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN (1 << 0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_CKSEL register */
+#define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL (1 << 0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSCNTL register */
+#define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN (1 << 0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */
+#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR (1 << 2)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN (1 << 31)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */
+#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL (1 << 4)
+
+static inline u32 sd_scc_read32(struct tmio_mmc_host *host, int addr)
+{
+ struct platform_device *pdev = host->pdev;
+ const struct of_device_id *of_id =
+ of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
+ const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+
+ return readl(host->ctl + of_data->scc_offset +
+ (addr << host->bus_shift));
+}
+
+static inline void sd_scc_write32(struct tmio_mmc_host *host, int addr,
+ u32 val)
+{
+ struct platform_device *pdev = host->pdev;
+ const struct of_device_id *of_id =
+ of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
+ const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+
+ writel(val, host->ctl + of_data->scc_offset +
+ (addr << host->bus_shift));
+}
+
+static bool sh_mobile_sdhi_inquiry_tuning(struct tmio_mmc_host *host)
+{
+ /* SDHI should be tuning only SDR104 and HS200 */
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104 ||
+ host->mmc->ios.timing == MMC_TIMING_MMC_HS200)
+ return true;
+
+ return false;
+}
+
+static void sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host,
+ unsigned long *num)
+{
+ /* set sampling clock selection range */
+ if (host->scc_tapnum)
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
+ host->scc_tapnum << 16);
+
+ /* Initialize SCC */
+ sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x00000000);
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DTCNTL,
+ SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
+ SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_DT2FF, host->scc_tappos);
+
+ /* Read TAPNUM */
+ *num = (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_DTCNTL) >> 16) & 0xff;
+}
+
+static int sh_mobile_sdhi_prepare_tuning(struct tmio_mmc_host *host,
+ unsigned long tap)
+{
+ /* Set sampling clock position */
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TAPSET, tap);
+
+ return 0;
+}
+
+static void sh_mobile_sdhi_prepare_hs400_tuning(struct tmio_mmc_host *host)
+{
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ /* Set HS400 mode */
+ sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 |
+ sd_ctrl_read16(host, CTL_SDIF_MODE));
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TMPPORT2,
+ (SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
+ SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) |
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_TMPPORT2));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+}
+
+#define SH_MOBILE_SDHI_MAX_TAP 3
+static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host,
+ unsigned long *tap)
+{
+ unsigned long tap_num; /* total number of taps */
+ unsigned long tap_cnt; /* counter of tuning success */
+ unsigned long tap_set; /* tap position */
+ unsigned long tap_start; /* start position of tuning success */
+ unsigned long tap_end; /* end position of tuning success */
+ unsigned long ntap; /* temporary counter of tuning success */
+ unsigned long i;
+
+ /* Clear SCC_RVSREQ */
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
+
+ /* Select SCC */
+ tap_num = (sd_scc_read32(host,
+ SH_MOBILE_SDHI_SCC_DTCNTL) >> 16) & 0xff;
+
+ tap_cnt = 0;
+ ntap = 0;
+ tap_start = 0;
+ tap_end = 0;
+ for (i = 0; i < tap_num * 2; i++) {
+ if (tap[i] == 0)
+ ntap++;
+ else {
+ if (ntap > tap_cnt) {
+ tap_start = i - ntap;
+ tap_end = i - 1;
+ tap_cnt = ntap;
+ }
+ ntap = 0;
+ }
+ }
+
+ if (ntap > tap_cnt) {
+ tap_start = i - ntap;
+ tap_end = i - 1;
+ tap_cnt = ntap;
+ }
+
+ if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
+ tap_set = (tap_start + tap_end) / 2 % tap_num;
+ else
+ return -EIO;
+
+ /* Set SCC */
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TAPSET, tap_set);
+
+ /* Enable auto re-tuning */
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN |
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ return 0;
+}
+
+static bool sh_mobile_sdhi_retuning(struct tmio_mmc_host *host)
+{
+ /* Check SCC error */
+ if (sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL) &
+ SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &&
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSREQ) &
+ SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) {
+ /* Clear SCC error */
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
+ return true;
+ }
+ return false;
+}
+
+static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
+{
+ struct tmio_mmc_data *pdata = host->pdata;
+
+ if (pdata->flags & TMIO_MMC_HAS_UHS_SCC) {
+ /* Reset SCC */
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_CKSEL,
+ ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_CKSEL));
+
+ /* Reset HS400 mode */
+ sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 &
+ sd_ctrl_read16(host, CTL_SDIF_MODE));
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_TMPPORT2,
+ ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
+ SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_TMPPORT2));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ sd_scc_write32(host, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, SH_MOBILE_SDHI_SCC_RVSCNTL));
+ }
+}
+
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
{
int timeout = 1000;
@@ -269,7 +635,7 @@
case CTL_SD_MEM_CARD_OPT:
case CTL_TRANSACTION_CTL:
case CTL_DMA_ENABLE:
- case EXT_ACC:
+ case HOST_MODE:
return sh_mobile_sdhi_wait_idle(host);
}
@@ -296,10 +662,12 @@
static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
{
+ int dma_width = host->dma->sdbuf_64bit ? 64 : 32;
+
sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
/* enable 32bit access if DMA mode if possibile */
- sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16);
+ sh_mobile_sdhi_sdbuf_width(host, enable ? dma_width : 16);
}
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
@@ -312,7 +680,10 @@
struct tmio_mmc_host *host;
struct resource *res;
int irq, ret, i = 0;
+ struct device_node *np = pdev->dev.of_node;
struct tmio_mmc_dma *dma_priv;
+ int clk_rate;
+ u32 num, tapnum = 0, tappos;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -332,6 +703,21 @@
goto eprobe;
}
+ if (np && !of_property_read_u32(np, "renesas,clk-rate", &clk_rate)) {
+ if (clk_rate) {
+ clk_prepare_enable(priv->clk);
+ ret = clk_set_rate(priv->clk, clk_rate);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "cannot set clock rate: %d\n", ret);
+
+ clk_disable_unprepare(priv->clk);
+ }
+ }
+
+ if (np && !of_property_read_u32(np, "renesas,mmc-scc-tapnum", &num))
+ tapnum = num;
+
priv->pinctrl = devm_pinctrl_get(&pdev->dev);
if (!IS_ERR(priv->pinctrl)) {
priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
@@ -348,13 +734,49 @@
if (of_id && of_id->data) {
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+ const struct sh_mobile_sdhi_scc *taps = of_data->taps;
mmc_data->flags |= of_data->tmio_flags;
mmc_data->capabilities |= of_data->capabilities;
mmc_data->capabilities2 |= of_data->capabilities2;
mmc_data->dma_rx_offset = of_data->dma_rx_offset;
+ mmc_data->max_blk_count = of_data->max_blk_count;
+ mmc_data->max_segs = of_data->max_segs;
dma_priv->dma_buswidth = of_data->dma_buswidth;
+ dma_priv->sdbuf_64bit = of_data->sdbuf_64bit;
host->bus_shift = of_data->bus_shift;
+ if (np && !of_property_read_u32(np, "renesas,mmc-scc-tappos",
+ &tappos)) {
+ host->scc_tappos = tappos;
+ } else {
+ for (i = 0, taps = of_data->taps;
+ i < of_data->taps_num; i++, taps++) {
+ if (taps->clk == 0 || taps->clk == clk_rate) {
+ host->scc_tappos = taps->tap;
+ break;
+ }
+ }
+ if (taps->clk != 0 && taps->clk != clk_rate)
+ dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104 and HS200\n");
+ }
+ }
+
+ if (of_find_property(np, "sd-uhs-sdr50", NULL))
+ mmc_data->capabilities |= MMC_CAP_UHS_SDR50;
+ if (of_find_property(np, "sd-uhs-sdr104", NULL))
+ mmc_data->capabilities |= MMC_CAP_UHS_SDR104;
+ if (of_find_property(np, "mmc-hs200-1_8v", NULL))
+ mmc_data->capabilities2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_find_property(np, "mmc-hs400-1_8v", NULL))
+ mmc_data->capabilities2 |= MMC_CAP2_HS400_1_8V |
+ MMC_CAP2_HS200_1_8V_SDR;
+
+ if ((mmc_data->capabilities & MMC_CAP_UHS_SDR104) ||
+ (mmc_data->capabilities2 & MMC_CAP2_HS200_1_8V_SDR) ||
+ (mmc_data->capabilities2 & (MMC_CAP2_HS400_1_8V |
+ MMC_CAP2_HS200_1_8V_SDR))) {
+ mmc_data->capabilities |= MMC_CAP_HW_RESET;
+ mmc_data->flags |= TMIO_MMC_HAS_UHS_SCC;
}
host->dma = dma_priv;
@@ -362,11 +784,25 @@
host->clk_enable = sh_mobile_sdhi_clk_enable;
host->clk_update = sh_mobile_sdhi_clk_update;
host->clk_disable = sh_mobile_sdhi_clk_disable;
+ host->card_busy = sh_mobile_sdhi_card_busy;
host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
- host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch;
+ host->set_clk_div = sh_mobile_sdhi_set_clk_div;
+ host->start_signal_voltage_switch =
+ sh_mobile_sdhi_start_signal_voltage_switch;
+ host->inquiry_tuning = sh_mobile_sdhi_inquiry_tuning;
+ host->init_tuning = sh_mobile_sdhi_init_tuning;
+ host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
+ host->select_tuning = sh_mobile_sdhi_select_tuning;
+ host->retuning = sh_mobile_sdhi_retuning;
+ host->hw_reset = sh_mobile_sdhi_hw_reset;
+ host->scc_tapnum = tapnum;
+ host->prepare_hs400_tuning = sh_mobile_sdhi_prepare_hs400_tuning;
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
- if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */
+ if (resource_size(res) > 0x400)
+ host->bus_shift = 2;
+ else if (!host->bus_shift &&
+ resource_size(res) > 0x100) /* old way to determine the shift */
host->bus_shift = 1;
if (mmd)
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index c14fa22..5ec0f90 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -1,8 +1,8 @@
/*
* linux/drivers/mmc/host/tmio_mmc.h
*
+ * Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
- * Copyright (C) 2015-16 Renesas Electronics Corporation
* Copyright (C) 2007 Ian Molton
* Copyright (C) 2004 Ian Molton
*
@@ -19,6 +19,7 @@
#define TMIO_MMC_H
#include <linux/dmaengine.h>
+#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
@@ -45,6 +46,7 @@
#define CTL_DMA_ENABLE 0xd8
#define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2
+#define CTL_SDIF_MODE 0xe6
#define CTL_SDIO_REGS 0x100
#define CTL_CLK_AND_WAIT_CTL 0x138
#define CTL_RESET_SDIO 0x1e0
@@ -101,6 +103,7 @@
struct tmio_mmc_dma {
enum dma_slave_buswidth dma_buswidth;
+ bool sdbuf_64bit;
bool (*filter)(struct dma_chan *chan, void *arg);
void (*enable)(struct tmio_mmc_host *host, bool enable);
};
@@ -150,6 +153,10 @@
struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
bool sdio_irq_enabled;
+ u32 scc_tapnum;
+ u32 scc_tappos;
+ bool done_tuning;
+ struct completion completion;
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
int (*clk_enable)(struct tmio_mmc_host *host);
@@ -160,6 +167,14 @@
unsigned int direction, int blk_size);
int (*start_signal_voltage_switch)(struct mmc_host *mmc,
struct mmc_ios *ios);
+ int (*card_busy)(struct tmio_mmc_host *host);
+ bool (*inquiry_tuning)(struct tmio_mmc_host *host);
+ void (*init_tuning)(struct tmio_mmc_host *host, unsigned long *num);
+ int (*prepare_tuning)(struct tmio_mmc_host *host, unsigned long tap);
+ int (*select_tuning)(struct tmio_mmc_host *host, unsigned long *tap);
+ bool (*retuning)(struct tmio_mmc_host *host);
+ void (*hw_reset)(struct tmio_mmc_host *host);
+ void (*prepare_hs400_tuning)(struct tmio_mmc_host *host);
};
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
@@ -268,4 +283,6 @@
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
}
+extern void mmc_set_initial_state(struct mmc_host *host);
+
#endif
diff --git a/drivers/mmc/host/tmio_mmc_dma_gen3.c b/drivers/mmc/host/tmio_mmc_dma_gen3.c
new file mode 100644
index 0000000..b289dfa
--- /dev/null
+++ b/drivers/mmc/host/tmio_mmc_dma_gen3.c
@@ -0,0 +1,196 @@
+/*
+ * linux/drivers/mmc/tmio_mmc_dma_gen3.c
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * R-Car Gen3 DMA function for TMIO MMC implementations
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mmc/host.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+
+#include "tmio_mmc.h"
+
+#define DM_CM_DTRAN_MODE 0x820
+#define DM_CM_DTRAN_CTRL 0x828
+#define DM_CM_RST 0x830
+#define DM_CM_INFO1 0x840
+#define DM_CM_INFO1_MASK 0x848
+#define DM_CM_INFO2 0x850
+#define DM_CM_INFO2_MASK 0x858
+#define DM_DTRAN_ADDR 0x880
+
+/* DM_CM_DTRAN_MODE */
+#define DTRAN_MODE_CH_NUM_CH0 0 /* "downstream" = for write commands */
+#define DTRAN_MODE_CH_NUM_CH1 BIT(16) /* "uptream" = for read commands */
+#define DTRAN_MODE_BUS_WID_TH (BIT(5) | BIT(4))
+#define DTRAN_MODE_ADDR_MODE BIT(0) /* 1 = Increment address */
+
+/* DM_CM_DTRAN_CTRL */
+#define DTRAN_CTRL_DM_START BIT(0)
+
+/* DM_CM_RST */
+#define RST_DTRANRST1 BIT(9)
+#define RST_DTRANRST0 BIT(8)
+#define RST_RESERVED_BITS GENMASK_ULL(32, 0)
+
+/* DM_CM_INFO1 and DM_CM_INFO1_MASK */
+#define INFO1_CLEAR 0
+#define INFO1_DTRANEND1 BIT(17)
+#define INFO1_DTRANEND0 BIT(16)
+
+/* DM_CM_INFO2 and DM_CM_INFO2_MASK */
+#define INFO2_DTRANERR1 BIT(17)
+#define INFO2_DTRANERR0 BIT(16)
+
+/*
+ * Specification of this driver:
+ * - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
+ * - Since this SDHI DMAC register set has actual 32-bit and "bus_shift" is 2,
+ * this driver cannot use original sd_ctrl_{write,read}32 functions.
+ */
+
+static void tmio_dm_write(struct tmio_mmc_host *host, int addr, u64 val)
+{
+ writeq(val, host->ctl + addr);
+}
+
+void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
+{
+ if (!host->chan_tx || !host->chan_rx)
+ return;
+
+ if (!enable)
+ tmio_dm_write(host, DM_CM_INFO1, INFO1_CLEAR);
+
+ if (host->dma->enable)
+ host->dma->enable(host, enable);
+}
+
+void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
+{
+ u64 val = RST_DTRANRST1 | RST_DTRANRST0;
+
+ dev_dbg(&host->pdev->dev, "%s\n", __func__);
+
+ tmio_mmc_enable_dma(host, false);
+
+ tmio_dm_write(host, DM_CM_RST, RST_RESERVED_BITS & ~val);
+ tmio_dm_write(host, DM_CM_RST, RST_RESERVED_BITS | val);
+
+ tmio_mmc_enable_dma(host, true);
+}
+
+void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data)
+{
+ struct scatterlist *sg = host->sg_ptr;
+ u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
+ enum dma_data_direction dir;
+ int ret;
+ u32 irq_mask;
+
+ if (!host->chan_rx || !host->chan_tx)
+ return;
+
+ /* This DMAC cannot handle if sg_len is not 1 */
+ WARN_ON(host->sg_len > 1);
+
+ dev_dbg(&host->pdev->dev, "%s: %d, %x\n", __func__, host->sg_len,
+ data->flags);
+
+ /* This DMAC cannot handle if buffer is not 8-bytes alignment */
+ if (!IS_ALIGNED(sg->offset, 8)) {
+ host->force_pio = true;
+ tmio_mmc_enable_dma(host, false);
+ return;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
+ dir = DMA_FROM_DEVICE;
+ irq_mask = TMIO_STAT_RXRDY;
+ } else {
+ dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
+ dir = DMA_TO_DEVICE;
+ irq_mask = TMIO_STAT_TXRQ;
+ }
+
+ ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
+ if (ret < 0) {
+ dev_err(&host->pdev->dev, "%s: dma_map_sg failed\n", __func__);
+ return;
+ }
+
+ tmio_mmc_enable_dma(host, true);
+
+ /* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
+ tmio_mmc_disable_mmc_irqs(host, irq_mask);
+
+ /* set dma parameters */
+ tmio_dm_write(host, DM_CM_DTRAN_MODE, dtran_mode);
+ tmio_dm_write(host, DM_DTRAN_ADDR, sg->dma_address);
+}
+
+#ifndef CONFIG_MMC_SDHI_PIO
+static void tmio_mmc_issue_tasklet_fn(unsigned long arg)
+{
+ struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+
+ dev_dbg(&host->pdev->dev, "%s\n", __func__);
+
+ tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
+
+ /* start the DMAC */
+ tmio_dm_write(host, DM_CM_DTRAN_CTRL, DTRAN_CTRL_DM_START);
+}
+
+static void tmio_mmc_complete_tasklet_fn(unsigned long arg)
+{
+ struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+ enum dma_data_direction dir;
+
+ dev_dbg(&host->pdev->dev, "%s: %p\n", __func__, host->data);
+
+ if (!host->data)
+ return;
+
+ if (host->data->flags & MMC_DATA_READ)
+ dir = DMA_FROM_DEVICE;
+ else
+ dir = DMA_TO_DEVICE;
+
+ tmio_mmc_enable_dma(host, false);
+ dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
+ tmio_mmc_do_data_irq(host);
+}
+#endif
+
+void tmio_mmc_request_dma(struct tmio_mmc_host *host,
+ struct tmio_mmc_data *pdata)
+{
+#ifndef CONFIG_MMC_SDHI_PIO
+ /* Each value is set to non-zero to assume "enabling" each DMA */
+ host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
+
+ tasklet_init(&host->dma_complete, tmio_mmc_complete_tasklet_fn,
+ (unsigned long)host);
+ tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn,
+ (unsigned long)host);
+#endif
+}
+
+void tmio_mmc_release_dma(struct tmio_mmc_host *host)
+{
+ /* Each value is set to zero to assume "disabling" each DMA */
+ host->chan_rx = host->chan_tx = NULL;
+}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 438b823..add0f6e 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1,8 +1,8 @@
/*
* linux/drivers/mmc/host/tmio_mmc_pio.c
*
+ * Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (C) 2016 Sang Engineering, Wolfram Sang
- * Copyright (C) 2015-16 Renesas Electronics Corporation
* Copyright (C) 2011 Guennadi Liakhovetski
* Copyright (C) 2007 Ian Molton
* Copyright (C) 2004 Ian Molton
@@ -36,6 +36,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/tmio.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
@@ -52,6 +53,13 @@
#include "tmio_mmc.h"
+static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode);
+static int tmio_mmc_start_data(struct tmio_mmc_host *host,
+ struct mmc_data *data);
+static int tmio_mmc_start_command(struct tmio_mmc_host *host,
+ struct mmc_command *cmd);
+static void tmio_mmc_hw_reset(struct mmc_host *mmc);
+
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i)
{
host->sdcard_irq_mask &= ~(i & TMIO_MASK_IRQ);
@@ -160,8 +168,9 @@
msleep(host->pdata->flags & TMIO_MMC_MIN_RCAR2 ? 1 : 10);
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
- sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
- msleep(10);
+ sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, CLK_CTL_SCLKEN);
+ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+ msleep(10);
}
}
@@ -169,7 +178,8 @@
{
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
- msleep(10);
+ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+ msleep(10);
}
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
@@ -196,8 +206,13 @@
clock <<= 1;
/* 1/1 clock is option */
- if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1))
- clk |= 0xff;
+ if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
+ ((clk >> 22) & 0x1)) {
+ if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400))
+ clk |= 0xff;
+ else
+ clk &= ~0xff;
+ }
if (host->set_clk_div)
host->set_clk_div(host->pdev, (clk >> 22) & 1);
@@ -230,6 +245,7 @@
delayed_reset_work.work);
struct mmc_request *mrq;
unsigned long flags;
+ u16 clk;
spin_lock_irqsave(&host->lock, flags);
mrq = host->mrq;
@@ -263,7 +279,9 @@
spin_unlock_irqrestore(&host->lock, flags);
+ clk = sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL) & 0x0ff;
tmio_mmc_reset(host);
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk | 0x100);
/* Ready for new calls */
host->mrq = NULL;
@@ -277,6 +295,8 @@
{
struct mmc_request *mrq;
unsigned long flags;
+ bool result;
+ struct mmc_command *cmd = host->cmd;
spin_lock_irqsave(&host->lock, flags);
@@ -290,7 +310,9 @@
host->data = NULL;
host->force_pio = false;
- cancel_delayed_work(&host->delayed_reset_work);
+ if (!(host->inquiry_tuning && host->inquiry_tuning(host) &&
+ !host->done_tuning) || cmd != mrq->sbc)
+ cancel_delayed_work(&host->delayed_reset_work);
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
@@ -298,6 +320,29 @@
if (mrq->cmd->error || (mrq->data && mrq->data->error))
tmio_mmc_abort_dma(host);
+ if (host->inquiry_tuning && host->inquiry_tuning(host) &&
+ !host->done_tuning) {
+ /* call retuning() to clear SCC error bit */
+ if (host->retuning)
+ host->retuning(host);
+ /* finish processing tuning request */
+ complete(&host->completion);
+ return;
+ }
+
+ /* Check retuning */
+ if (host->retuning && host->done_tuning) {
+ result = host->retuning(host);
+ if (result || (mrq->cmd->error == -EILSEQ))
+ host->done_tuning = false;
+ }
+
+ if (cmd == mrq->sbc) {
+ /* finish SET_BLOCK_COUNT request */
+ complete(&host->completion);
+ return;
+ }
+
mmc_request_done(host->mmc, mrq);
}
@@ -308,6 +353,165 @@
tmio_mmc_finish_request(host);
}
+#define TMIO_MMC_MAX_TUNING_LOOP 40
+
+static int _tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct mmc_ios *ios = &mmc->ios;
+
+ unsigned long timeout, val, num;
+ unsigned long *tap;
+ int tuning_loop_counter = TMIO_MMC_MAX_TUNING_LOOP;
+ int ret, timeleft;
+
+ struct mmc_request mrq = {NULL};
+ struct mmc_command cmd = {0};
+ struct mmc_data data = {0};
+ struct scatterlist sg;
+ u8 *data_buf;
+ unsigned int tm = CMDREQ_TIMEOUT;
+ unsigned long flags;
+ u8 data_size = 64;
+
+ if (ios->timing != MMC_TIMING_UHS_SDR50 &&
+ ios->timing != MMC_TIMING_UHS_SDR104 &&
+ ios->timing != MMC_TIMING_MMC_HS200 &&
+ ios->timing != MMC_TIMING_MMC_HS400)
+ return 0;
+
+ if ((host->inquiry_tuning && !host->inquiry_tuning(host)) ||
+ host->done_tuning)
+ return 0;
+
+ host->init_tuning(host, &num);
+
+ tap = kmalloc(num * 2, GFP_KERNEL);
+ if (tap == NULL) {
+ ret = -ENOMEM;
+ goto err_tap;
+ }
+
+ if (ios->timing == MMC_TIMING_MMC_HS200)
+ data_size = 128;
+
+ data_buf = kmalloc(data_size, GFP_KERNEL);
+ if (data_buf == NULL) {
+ ret = -ENOMEM;
+ goto err_data;
+ }
+
+ val = 0;
+
+ /*
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches 40 times or a timeout of 150ms occurs.
+ */
+ timeout = 150;
+ do {
+ if (host->prepare_tuning)
+ host->prepare_tuning(host, val % num);
+
+ if (!tuning_loop_counter && !timeout)
+ break;
+
+ /*
+ * In response to CMD19, the card sends 64 bytes of tuning
+ * block to the Host Controller. So we set the block size
+ * to 64 here.
+ */
+
+ spin_lock_irqsave(&host->lock, flags);
+ init_completion(&host->completion);
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.retries = 0;
+ cmd.error = 0;
+
+ data.blksz = data_size;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+ data.error = 0;
+
+ sg_init_one(&sg, data_buf, data_size);
+
+ host->mrq = &mrq;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ ret = tmio_mmc_start_data(host, mrq.data);
+ if (ret)
+ goto out;
+
+ ret = tmio_mmc_start_command(host, mrq.cmd);
+ if (ret)
+ goto out;
+
+ timeleft = wait_for_completion_timeout(&host->completion,
+ msecs_to_jiffies(tm));
+ if (timeleft < 0) {
+ ret = timeleft;
+ goto out;
+ }
+
+ if (!timeleft) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* Check CRC error */
+ if (cmd.error && cmd.error != -EILSEQ) {
+ ret = cmd.error;
+ goto out;
+ }
+ if (data.error && data.error != -EILSEQ) {
+ ret = data.error;
+ goto out;
+ }
+
+ tap[val] = (cmd.error | data.error);
+
+ val++;
+ tuning_loop_counter--;
+ timeout--;
+ mdelay(1);
+ } while ((val < (num * 2)) && (tuning_loop_counter || timeout));
+
+ /*
+ * The Host Driver has exhausted the maximum number of loops allowed,
+ * so use fixed sampling frequency.
+ */
+ if (tuning_loop_counter || timeout) {
+ if (host->select_tuning) {
+ ret = host->select_tuning(host, tap);
+ if (ret < 0)
+ goto out;
+ }
+ host->done_tuning = true;
+ } else {
+ dev_warn(&host->pdev->dev, ": Tuning procedure failed\n");
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ kfree(data_buf);
+err_data:
+ kfree(tap);
+err_tap:
+ if (ret < 0)
+ tmio_mmc_hw_reset(mmc);
+
+ return ret;
+
+}
+
/* These are the bitmasks the tmio chip requires to implement the MMC response
* types. Note that R1 and R6 are the same in this scheme. */
#define APP_CMD 0x0040
@@ -337,6 +541,8 @@
switch (mmc_resp_type(cmd)) {
case MMC_RSP_NONE: c |= RESP_NONE; break;
case MMC_RSP_R1: c |= RESP_R1; break;
+ case MMC_RSP_R1 & ~MMC_RSP_CRC:
+ c |= RESP_R1; break;
case MMC_RSP_R1B: c |= RESP_R1B; break;
case MMC_RSP_R2: c |= RESP_R2; break;
case MMC_RSP_R3: c |= RESP_R3; break;
@@ -363,7 +569,8 @@
* multiple block transfer
*/
if ((host->pdata->flags & TMIO_MMC_HAVE_CMD12_CTRL) &&
- (cmd->opcode == SD_IO_RW_EXTENDED))
+ ((cmd->opcode == SD_IO_RW_EXTENDED) ||
+ host->mrq->sbc))
c |= NO_CMD12_ISSUE;
}
if (data->flags & MMC_DATA_READ)
@@ -520,7 +727,7 @@
schedule_work(&host->done);
}
-static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
+static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat)
{
struct mmc_data *data;
spin_lock(&host->lock);
@@ -529,6 +736,9 @@
if (!data)
goto out;
+ if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_TXUNDERRUN)
+ data->error = -EILSEQ;
if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) {
u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
bool done = false;
@@ -577,8 +787,6 @@
goto out;
}
- host->cmd = NULL;
-
/* This controller is sicker than the PXA one. Not only do we need to
* drop the top 8 bits of the first response word, we also need to
* modify the order of the response for short response command types.
@@ -598,14 +806,16 @@
if (stat & TMIO_STAT_CMDTIMEOUT)
cmd->error = -ETIMEDOUT;
- else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC)
+ else if ((stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC) ||
+ stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_CMD_IDX_ERR)
cmd->error = -EILSEQ;
/* If there is data to handle we enable data IRQs here, and
* we will ultimatley finish the request in the data_end handler.
* If theres no data or we encountered an error, finish now.
*/
- if (host->data && !cmd->error) {
+ if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
if (host->data->flags & MMC_DATA_READ) {
if (host->force_pio || !host->chan_rx)
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
@@ -666,7 +876,7 @@
/* Data transfer completion */
if (ireg & TMIO_STAT_DATAEND) {
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
- tmio_mmc_data_irq(host);
+ tmio_mmc_data_irq(host, status);
return true;
}
@@ -759,6 +969,7 @@
struct tmio_mmc_host *host = mmc_priv(mmc);
unsigned long flags;
int ret;
+ u32 opcode;
spin_lock_irqsave(&host->lock, flags);
@@ -778,6 +989,50 @@
spin_unlock_irqrestore(&host->lock, flags);
+ if (host->inquiry_tuning && host->inquiry_tuning(host) &&
+ !host->done_tuning && host->mmc->card) {
+ if (mmc_card_mmc(host->mmc->card))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ opcode = MMC_SEND_TUNING_BLOCK;
+ /* Start retuning */
+ ret = tmio_mmc_execute_tuning(mmc, opcode);
+ if (ret)
+ goto fail;
+ /* Restore request */
+ host->mrq = mrq;
+ }
+
+ if (mrq->sbc) {
+ init_completion(&host->completion);
+ ret = tmio_mmc_start_command(host, mrq->sbc);
+ if (ret)
+ goto fail;
+ ret = wait_for_completion_timeout(&host->completion,
+ msecs_to_jiffies(CMDREQ_TIMEOUT));
+ if (ret < 0)
+ goto fail;
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
+ host->last_req_ts = jiffies;
+ host->mrq = mrq;
+ if (host->inquiry_tuning && host->inquiry_tuning(host) &&
+ !host->done_tuning && host->mmc->card) {
+ if (mmc_card_mmc(host->mmc->card))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ opcode = MMC_SEND_TUNING_BLOCK;
+ /* Start retuning */
+ ret = tmio_mmc_execute_tuning(mmc, opcode);
+ if (ret)
+ goto fail;
+ /* Restore request */
+ host->mrq = mrq;
+ }
+ }
+
if (mrq->data) {
ret = tmio_mmc_start_data(host, mrq->data);
if (ret)
@@ -857,12 +1112,21 @@
static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
unsigned char bus_width)
{
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, ~0xa000 &
+ sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT));
+
switch (bus_width) {
case MMC_BUS_WIDTH_1:
- sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x8000 |
+ sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT));
break;
case MMC_BUS_WIDTH_4:
- sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x0000 |
+ sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT));
+ break;
+ case MMC_BUS_WIDTH_8:
+ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x2000 |
+ sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT));
break;
}
}
@@ -879,6 +1143,11 @@
struct device *dev = &host->pdev->dev;
unsigned long flags;
+ /* HS400 Register setting */
+ if (ios->timing == MMC_TIMING_MMC_HS400)
+ if (host->prepare_hs400_tuning)
+ host->prepare_hs400_tuning(host);
+
mutex_lock(&host->ios_lock);
spin_lock_irqsave(&host->lock, flags);
@@ -967,6 +1236,45 @@
return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_DAT0);
}
+static int tmio_mmc_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ int ret;
+
+ if (host->start_signal_voltage_switch) {
+ ret = host->start_signal_voltage_switch(mmc, ios);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ int ret;
+
+ ret = _tmio_mmc_execute_tuning(mmc, opcode);
+
+ if (ret)
+ mmc_set_initial_state(mmc);
+
+ return ret;
+}
+
+static void tmio_mmc_hw_reset(struct mmc_host *mmc)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ tmio_mmc_reset(host);
+
+ if (host->hw_reset)
+ host->hw_reset(host);
+
+ host->done_tuning = false;
+}
+
static struct mmc_host_ops tmio_mmc_ops = {
.request = tmio_mmc_request,
.set_ios = tmio_mmc_set_ios,
@@ -975,6 +1283,10 @@
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.card_busy = tmio_mmc_card_busy,
.multi_io_quirk = tmio_multi_io_quirk,
+ .start_signal_voltage_switch
+ = tmio_mmc_start_signal_voltage_switch,
+ .execute_tuning = tmio_mmc_execute_tuning,
+ .hw_reset = tmio_mmc_hw_reset,
};
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@@ -1077,13 +1389,14 @@
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 |= pdata->capabilities2;
- mmc->max_segs = 32;
+ mmc->max_segs = pdata->max_segs ? pdata->max_segs : 32;
mmc->max_blk_size = 512;
- mmc->max_blk_count = (PAGE_SIZE / mmc->max_blk_size) *
- mmc->max_segs;
+ mmc->max_blk_count = pdata->max_blk_count ? :
+ (PAGE_SIZE / mmc->max_blk_size) * mmc->max_segs;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
+ _host->done_tuning = false;
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
mmc->caps & MMC_CAP_NONREMOVABLE ||
@@ -1235,6 +1548,8 @@
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
+ tmio_mmc_hw_reset(mmc);
+
if (host->clk_disable)
host->clk_disable(host);
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 4e5d5e9..ed90a93 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -76,6 +76,7 @@
CDAR20 = 0x0060,
CDAR21 = 0x0064,
ESR = 0x0088,
+ APSR = 0x008C, /* R-Car Gen3 only */
RCR = 0x0090,
RQC0 = 0x0094,
RQC1 = 0x0098,
@@ -83,6 +84,7 @@
RQC3 = 0x00A0,
RQC4 = 0x00A4,
RPC = 0x00B0,
+ RTC = 0x00B4, /* R-Car Gen3 only */
UFCW = 0x00BC,
UFCS = 0x00C0,
UFCV0 = 0x00C4,
@@ -128,11 +130,52 @@
SFP29 = 0x0174,
SFP30 = 0x0178,
SFP31 = 0x017C,
+ SFV0 = 0x01B8, /* R-Car Gen3 only */
+ SFV1 = 0x01BC, /* R-Car Gen3 only */
SFM0 = 0x01C0,
SFM1 = 0x01C4,
+ SFL = 0x01C8, /* R-Car Gen3 only */
+ PCRC = 0x01CC, /* R-Car Gen3 only */
+ CIAR0 = 0x0200, /* R-Car Gen3 only */
+ CIAR1 = 0x0204, /* R-Car Gen3 only */
+ CIAR2 = 0x0208, /* R-Car Gen3 only */
+ CIAR3 = 0x020C, /* R-Car Gen3 only */
+ CIAR4 = 0x0210, /* R-Car Gen3 only */
+ CIAR5 = 0x0214, /* R-Car Gen3 only */
+ CIAR6 = 0x0218, /* R-Car Gen3 only */
+ CIAR7 = 0x021C, /* R-Car Gen3 only */
+ CIAR8 = 0x0220, /* R-Car Gen3 only */
+ CIAR9 = 0x0224, /* R-Car Gen3 only */
+ CIAR10 = 0x0228, /* R-Car Gen3 only */
+ CIAR11 = 0x022C, /* R-Car Gen3 only */
+ CIAR12 = 0x0230, /* R-Car Gen3 only */
+ CIAR13 = 0x0234, /* R-Car Gen3 only */
+ CIAR14 = 0x0238, /* R-Car Gen3 only */
+ CIAR15 = 0x023C, /* R-Car Gen3 only */
+ CIAR16 = 0x0240, /* R-Car Gen3 only */
+ CIAR17 = 0x0244, /* R-Car Gen3 only */
+ LIAR0 = 0x0280, /* R-Car Gen3 only */
+ LIAR1 = 0x0284, /* R-Car Gen3 only */
+ LIAR2 = 0x0288, /* R-Car Gen3 only */
+ LIAR3 = 0x028C, /* R-Car Gen3 only */
+ LIAR4 = 0x0290, /* R-Car Gen3 only */
+ LIAR5 = 0x0294, /* R-Car Gen3 only */
+ LIAR6 = 0x0298, /* R-Car Gen3 only */
+ LIAR7 = 0x029C, /* R-Car Gen3 only */
+ LIAR8 = 0x02A0, /* R-Car Gen3 only */
+ LIAR9 = 0x02A4, /* R-Car Gen3 only */
+ LIAR10 = 0x02A8, /* R-Car Gen3 only */
+ LIAR11 = 0x02AC, /* R-Car Gen3 only */
+ LIAR12 = 0x02B0, /* R-Car Gen3 only */
+ LIAR13 = 0x02B4, /* R-Car Gen3 only */
+ LIAR14 = 0x02B8, /* R-Car Gen3 only */
+ LIAR15 = 0x02BC, /* R-Car Gen3 only */
+ LIAR16 = 0x02C0, /* R-Car Gen3 only */
+ LIAR17 = 0x02C4, /* R-Car Gen3 only */
TGC = 0x0300,
TCCR = 0x0304,
TSR = 0x0308,
+ MFA = 0x030C,
TFA0 = 0x0310,
TFA1 = 0x0314,
TFA2 = 0x0318,
@@ -158,6 +201,8 @@
TIS = 0x037C,
ISS = 0x0380,
CIE = 0x0384, /* R-Car Gen3 only */
+ RIC3 = 0x0388, /* R-Car Gen3 only */
+ RIS3 = 0x038C, /* R-Car Gen3 only */
GCCR = 0x0390,
GMTT = 0x0394,
GPTC = 0x0398,
@@ -171,15 +216,46 @@
GCT0 = 0x03B8,
GCT1 = 0x03BC,
GCT2 = 0x03C0,
+ GSR = 0x03C4, /* R-Car Gen3 only */
GIE = 0x03CC, /* R-Car Gen3 only */
GID = 0x03D0, /* R-Car Gen3 only */
+ GIL = 0x03D4, /* R-Car Gen3 only */
+ GACP = 0x03DC, /* R-Car Gen3 only */
+ GPTF0 = 0x03E0, /* R-Car Gen3 only */
+ GPTF1 = 0x03E4, /* R-Car Gen3 only */
+ GPTF2 = 0x03E8, /* R-Car Gen3 only */
+ GPTF3 = 0x03EC, /* R-Car Gen3 only */
+ GCAT0 = 0x0400, /* R-Car Gen3 only */
+ GCAT1 = 0x0404, /* R-Car Gen3 only */
+ GCAT2 = 0x0408, /* R-Car Gen3 only */
+ GCAT3 = 0x040C, /* R-Car Gen3 only */
+ GCAT4 = 0x0410, /* R-Car Gen3 only */
+ GCAT5 = 0x0414, /* R-Car Gen3 only */
+ GCAT6 = 0x0418, /* R-Car Gen3 only */
+ GCAT7 = 0x041C, /* R-Car Gen3 only */
+ GCAT8 = 0x0420, /* R-Car Gen3 only */
+ GCAT9 = 0x0424, /* R-Car Gen3 only */
+ GCAT10 = 0x0428, /* R-Car Gen3 only */
+ GCAT11 = 0x042C, /* R-Car Gen3 only */
+ GCAT12 = 0x0430, /* R-Car Gen3 only */
+ GCAT13 = 0x0434, /* R-Car Gen3 only */
+ GCAT14 = 0x0438, /* R-Car Gen3 only */
+ GCAT15 = 0x043C, /* R-Car Gen3 only */
DIL = 0x0440, /* R-Car Gen3 only */
+ EIL = 0x0444, /* R-Car Gen3 only */
+ TIL = 0x0448, /* R-Car Gen3 only */
+ DIE = 0x0450, /* R-Car Gen3 only */
+ DID = 0x0454, /* R-Car Gen3 only */
+ EIE = 0x0458, /* R-Car Gen3 only */
+ EID = 0x045C, /* R-Car Gen3 only */
RIE0 = 0x0460, /* R-Car Gen3 only */
RID0 = 0x0464, /* R-Car Gen3 only */
RIE2 = 0x0470, /* R-Car Gen3 only */
RID2 = 0x0474, /* R-Car Gen3 only */
TIE = 0x0478, /* R-Car Gen3 only */
TID = 0x047c, /* R-Car Gen3 only */
+ RIE3 = 0x0488, /* R-Car Gen3 only */
+ RID3 = 0x048C, /* R-Car Gen3 only */
/* E-MAC registers */
ECMR = 0x0500,
@@ -189,9 +265,12 @@
PIR = 0x0520,
PSR = 0x0528,
PIPR = 0x052c,
+ APR = 0x0554, /* R-Car Gen3 only */
MPR = 0x0558,
PFTCR = 0x055c,
PFRCR = 0x0560,
+ PFTTLR = 0x0564, /* R-Car Gen3 only */
+ PFTTCR = 0x0568, /* R-Car Gen3 only */
GECMR = 0x05b0,
MAHR = 0x05c0,
MALR = 0x05c8,
@@ -248,6 +327,15 @@
ESR_EIL = 0x00001000,
};
+/* APSR */
+enum APSR_BIT {
+ APSR_MEMS = 0x00000002,
+ APSR_CMSW = 0x00000010,
+ APSR_DM = 0x0000A000,
+ APSR_DM_RDM = 0x00004000,
+ APSR_DM_TDM = 0x00006000,
+};
+
/* RCR */
enum RCR_BIT {
RCR_EFFS = 0x00000001,
@@ -319,6 +407,14 @@
RTC_MFL1 = 0x0FFF0000,
};
+/* SFL */
+enum SFL_BIT {
+ SFL_LC = 0x0000001F,
+ SFL_LC_SFM = 0x0000001D,
+ SFL_LC_SFO = 0x0000001E,
+ SFL_LC_LOADABLE = 0x0000001F,
+};
+
/* TGC */
enum TGC_BIT {
TGC_TSM0 = 0x00000001,
@@ -947,7 +1043,6 @@
#define RX_QUEUE_OFFSET 4
#define NUM_RX_QUEUE 2
#define NUM_TX_QUEUE 2
-#define NUM_TX_DESC 2 /* TX descriptors per packet */
struct ravb_tstamp_skb {
struct list_head list;
@@ -1024,6 +1119,7 @@
unsigned no_avb_link:1;
unsigned avb_link_active_low:1;
+ int num_tx_desc; /* TX descriptors per packet */
};
static inline u32 ravb_read(struct net_device *ndev, enum ravb_reg reg)
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 15fd342..d25e6b8 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -31,6 +31,9 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/soc/renesas/rcar_prr.h>
#include <asm/div64.h>
@@ -185,6 +188,7 @@
struct ravb_private *priv = netdev_priv(ndev);
int ring_size;
int i;
+ int num_tx_desc = priv->num_tx_desc;
/* Free RX skb ringbuffer */
if (priv->rx_skb[q]) {
@@ -216,7 +220,7 @@
if (priv->tx_ring[q]) {
ring_size = sizeof(struct ravb_tx_desc) *
- (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
+ (priv->num_tx_ring[q] * num_tx_desc + 1);
dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q],
priv->tx_desc_dma[q]);
priv->tx_ring[q] = NULL;
@@ -230,9 +234,10 @@
struct ravb_ex_rx_desc *rx_desc;
struct ravb_tx_desc *tx_desc;
struct ravb_desc *desc;
+ int num_tx_desc = priv->num_tx_desc;
int rx_ring_size = sizeof(*rx_desc) * priv->num_rx_ring[q];
int tx_ring_size = sizeof(*tx_desc) * priv->num_tx_ring[q] *
- NUM_TX_DESC;
+ num_tx_desc;
dma_addr_t dma_addr;
int i;
@@ -267,8 +272,10 @@
for (i = 0, tx_desc = priv->tx_ring[q]; i < priv->num_tx_ring[q];
i++, tx_desc++) {
tx_desc->die_dt = DT_EEMPTY;
- tx_desc++;
- tx_desc->die_dt = DT_EEMPTY;
+ if (num_tx_desc >= 2) {
+ tx_desc++;
+ tx_desc->die_dt = DT_EEMPTY;
+ }
}
tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);
tx_desc->die_dt = DT_LINKFIX; /* type */
@@ -291,6 +298,7 @@
struct sk_buff *skb;
int ring_size;
int i;
+ int num_tx_desc = priv->num_tx_desc;
/* Allocate RX and TX skb rings */
priv->rx_skb[q] = kcalloc(priv->num_rx_ring[q],
@@ -326,7 +334,7 @@
/* Allocate all TX descriptors. */
ring_size = sizeof(struct ravb_tx_desc) *
- (priv->num_tx_ring[q] * NUM_TX_DESC + 1);
+ (priv->num_tx_ring[q] * num_tx_desc + 1);
priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
&priv->tx_desc_dma[q],
GFP_KERNEL);
@@ -362,8 +370,6 @@
ravb_write(ndev,
(ndev->dev_addr[4] << 8) | (ndev->dev_addr[5]), MALR);
- ravb_write(ndev, 1, MPR);
-
/* E-MAC status register clear */
ravb_write(ndev, ECSR_ICD | ECSR_MPD, ECSR);
@@ -402,7 +408,8 @@
#endif
/* Set AVB RX */
- ravb_write(ndev, RCR_EFFS | RCR_ENCF | RCR_ETS0 | 0x18000000, RCR);
+ ravb_write(ndev,
+ RCR_EFFS | RCR_ENCF | RCR_ETS0 | RCR_ESF | 0x18000000, RCR);
/* Set FIFO size */
ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00222200, TGC);
@@ -441,10 +448,11 @@
int free_num = 0;
int entry;
u32 size;
+ int num_tx_desc = priv->num_tx_desc;
for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) {
entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] *
- NUM_TX_DESC);
+ num_tx_desc);
desc = &priv->tx_ring[q][entry];
if (desc->die_dt != DT_FEMPTY)
break;
@@ -452,12 +460,12 @@
dma_rmb();
size = le16_to_cpu(desc->ds_tagl) & TX_DS;
/* Free the original skb. */
- if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
+ if (priv->tx_skb[q][entry / num_tx_desc]) {
dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
size, DMA_TO_DEVICE);
/* Last packet descriptor? */
- if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
- entry /= NUM_TX_DESC;
+ if (entry % num_tx_desc == num_tx_desc - 1) {
+ entry /= num_tx_desc;
dev_kfree_skb_any(priv->tx_skb[q][entry]);
priv->tx_skb[q][entry] = NULL;
stats->tx_packets++;
@@ -1015,16 +1023,40 @@
* at this time.
*/
if (priv->chip_id == RCAR_GEN3) {
- int err;
+ struct platform_device *pdev = priv->pdev;
+ struct device *dev = &pdev->dev;
+ enum of_gpio_flags flags;
+ int err, gpio;
- err = phy_set_max_speed(phydev, SPEED_100);
- if (err) {
- netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
- phy_disconnect(phydev);
+ err = RCAR_PRR_INIT();
+ if (err)
return err;
+
+ if (RCAR_PRR_IS_PRODUCT(H3) &&
+ (RCAR_PRR_CHK_CUT(H3, WS10) == 0)) {
+ err = phy_set_max_speed(phydev, SPEED_100);
+ if (err) {
+ netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
+ phy_disconnect(phydev);
+ return err;
+ }
+ netdev_info(ndev, "limited PHY to 100Mbit/s\n");
}
- netdev_info(ndev, "limited PHY to 100Mbit/s\n");
+ gpio = of_get_named_gpio_flags(np, "phy-int-gpio", 0, &flags);
+ if (gpio_is_valid(gpio)) {
+ err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN,
+ "phy-int-gpio");
+ if (err == 0 && (phydev->irq == gpio_to_irq(gpio))) {
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ irq_set_irq_type(phydev->irq,
+ IRQ_TYPE_LEVEL_LOW);
+ else
+ irq_set_irq_type(phydev->irq,
+ IRQ_TYPE_LEVEL_HIGH);
+ }
+ }
+
}
/* 10BASE is not supported */
@@ -1484,41 +1516,56 @@
void *buffer;
u32 entry;
u32 len;
+ int num_tx_desc = priv->num_tx_desc;
spin_lock_irqsave(&priv->lock, flags);
if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) *
- NUM_TX_DESC) {
+ num_tx_desc) {
netif_err(priv, tx_queued, ndev,
"still transmitting with the full ring!\n");
netif_stop_subqueue(ndev, q);
spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_BUSY;
}
- entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * NUM_TX_DESC);
- priv->tx_skb[q][entry / NUM_TX_DESC] = skb;
+ entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * num_tx_desc);
+ priv->tx_skb[q][entry / num_tx_desc] = skb;
if (skb_put_padto(skb, ETH_ZLEN))
goto drop;
- buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
- entry / NUM_TX_DESC * DPTR_ALIGN;
- len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
- memcpy(buffer, skb->data, len);
- dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
- if (dma_mapping_error(ndev->dev.parent, dma_addr))
- goto drop;
+ if (num_tx_desc >= 2) {
+ buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
+ entry / num_tx_desc * DPTR_ALIGN;
+ len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
+ /* quick fix */
+ if (len == 0)
+ len = 4;
+ memcpy(buffer, skb->data, len);
+ dma_addr = dma_map_single(ndev->dev.parent, buffer, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, dma_addr))
+ goto drop;
- desc = &priv->tx_ring[q][entry];
- desc->ds_tagl = cpu_to_le16(len);
- desc->dptr = cpu_to_le32(dma_addr);
+ desc = &priv->tx_ring[q][entry];
+ desc->ds_tagl = cpu_to_le16(len);
+ desc->dptr = cpu_to_le32(dma_addr);
- buffer = skb->data + len;
- len = skb->len - len;
- dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
- if (dma_mapping_error(ndev->dev.parent, dma_addr))
- goto unmap;
+ buffer = skb->data + len;
+ len = skb->len - len;
+ dma_addr = dma_map_single(ndev->dev.parent, buffer, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, dma_addr))
+ goto unmap;
- desc++;
+ desc++;
+ } else {
+ desc = &priv->tx_ring[q][entry];
+ len = skb->len;
+ dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ndev->dev.parent, dma_addr))
+ goto drop;
+ }
desc->ds_tagl = cpu_to_le16(len);
desc->dptr = cpu_to_le32(dma_addr);
@@ -1526,9 +1573,11 @@
if (q == RAVB_NC) {
ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC);
if (!ts_skb) {
- desc--;
- dma_unmap_single(ndev->dev.parent, dma_addr, len,
- DMA_TO_DEVICE);
+ if (num_tx_desc >= 2) {
+ desc--;
+ dma_unmap_single(ndev->dev.parent, dma_addr,
+ len, DMA_TO_DEVICE);
+ }
goto unmap;
}
ts_skb->skb = skb;
@@ -1545,15 +1594,19 @@
skb_tx_timestamp(skb);
/* Descriptor type must be set after all the above writes */
dma_wmb();
- desc->die_dt = DT_FEND;
- desc--;
- desc->die_dt = DT_FSTART;
+ if (num_tx_desc >= 2) {
+ desc->die_dt = DT_FEND;
+ desc--;
+ desc->die_dt = DT_FSTART;
+ } else {
+ desc->die_dt = DT_FSINGLE;
+ }
ravb_modify(ndev, TCCR, TCCR_TSRQ0 << q, TCCR_TSRQ0 << q);
- priv->cur_tx[q] += NUM_TX_DESC;
+ priv->cur_tx[q] += num_tx_desc;
if (priv->cur_tx[q] - priv->dirty_tx[q] >
- (priv->num_tx_ring[q] - 1) * NUM_TX_DESC && !ravb_tx_free(ndev, q))
+ (priv->num_tx_ring[q] - 1) * num_tx_desc && !ravb_tx_free(ndev, q))
netif_stop_subqueue(ndev, q);
exit:
@@ -1566,7 +1619,7 @@
le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE);
drop:
dev_kfree_skb_any(skb);
- priv->tx_skb[q][entry / NUM_TX_DESC] = NULL;
+ priv->tx_skb[q][entry / num_tx_desc] = NULL;
goto exit;
}
@@ -1667,8 +1720,13 @@
priv->phydev = NULL;
}
- if (priv->chip_id == RCAR_GEN3)
+ if (priv->chip_id != RCAR_GEN2) {
+ free_irq(priv->tx_irqs[RAVB_NC], ndev);
+ free_irq(priv->rx_irqs[RAVB_NC], ndev);
+ free_irq(priv->tx_irqs[RAVB_BE], ndev);
+ free_irq(priv->rx_irqs[RAVB_BE], ndev);
free_irq(priv->emac_irq, ndev);
+ }
free_irq(ndev->irq, ndev);
napi_disable(&priv->napi[RAVB_NC]);
@@ -1831,6 +1889,7 @@
{ .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
+ { .compatible = "renesas,etheravb-r8a7796", .data = (void *)RCAR_GEN3 },
{ .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 },
{ }
};
@@ -1969,6 +2028,11 @@
priv->chip_id = chip_id;
+ if (chip_id == RCAR_GEN2)
+ priv->num_tx_desc = 2;
+ else
+ priv->num_tx_desc = 1;
+
/* Set function */
ndev->netdev_ops = &ravb_netdev_ops;
ndev->ethtool_ops = &ravb_ethtool_ops;
@@ -1991,6 +2055,10 @@
/* Request GTI loading */
ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI);
+ /* Set APSR */
+ if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID)
+ ravb_modify(ndev, APSR, APSR_DM, APSR_DM_TDM);
+
/* Allocate descriptor base address table */
priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM;
priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size,
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index 3929d93..8c0f882 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -28,6 +28,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/soc/renesas/s2ram_ddr_backup.h>
#define DRV_NAME "rcar-pcie"
@@ -43,6 +44,7 @@
/* Transfer control */
#define PCIETCTLR 0x02000
+#define DL_DOWN (1 << 3)
#define CFINIT 1
#define PCIETSTR 0x02004
#define DATA_LINK_ACTIVE 1
@@ -84,6 +86,14 @@
#define MACSR 0x011054
#define MACCTLR 0x011058
#define SCRAMBLE_DISABLE (1 << 27)
+#define PMSR 0x01105c
+#define L1FAEG (1 << 31)
+#define PM_ENTER_L1RX (1 << 23)
+#define PMSTATE (7 << 16)
+#define PMSTATE_L1 (3 << 16)
+#define PMCTLR 0x011060
+#define L1_INIT (1 << 31)
+
/* R-Car H1 PHY */
#define H1_PCIEPHYADRR 0x04000c
@@ -129,6 +139,7 @@
return container_of(chip, struct rcar_msi, chip);
}
+
/* Structure representing the PCIe interface */
struct rcar_pcie {
struct device *dev;
@@ -140,6 +151,147 @@
struct rcar_msi msi;
};
+static int rcar_pcie_wait_for_dl(struct rcar_pcie *pcie);
+
+#ifdef CONFIG_RCAR_DDR_BACKUP
+
+#define PCIE_BACKUP_REGS(pcie_ip_regs) \
+struct hw_register (pcie_ip_regs)[] = { \
+/* PCIEC transfer control registers */ \
+ {"PCIETCTLR", 0x2000, 32, 0}, \
+ {"PCIEINTER", 0x200C, 32, 0}, \
+ {"PCIEERRFER", 0x2024, 32, 0}, \
+ {"PCIETIER", 0x2030, 32, 0}, \
+ {"PCIEPMSCIER", 0x2038, 32, 0}, \
+ {"PCIEMSIALR", 0x2048, 32, 0}, \
+ {"PCIEMSIAUR", 0x204C, 32, 0}, \
+ {"PCIEMSIIER", 0x2050, 32, 0}, \
+ {"PCIEPRAR0", 0x2080, 32, 0}, \
+ {"PCIEPRAR1", 0x2084, 32, 0}, \
+ {"PCIEPRAR2", 0x2088, 32, 0}, \
+ {"PCIEPRAR3", 0x208C, 32, 0}, \
+ {"PCIEPRAR4", 0x2090, 32, 0}, \
+ {"PCIEPRAR5", 0x2094, 32, 0}, \
+ \
+ /* Local address registers */ \
+ {"PCIELAR0", 0x2200, 32, 0}, \
+ {"PCIELAMR0", 0x2208, 32, 0}, \
+ {"PCIELAR1", 0x2220, 32, 0}, \
+ {"PCIELAMR1", 0x2228, 32, 0}, \
+ {"PCIELAR2", 0x2240, 32, 0}, \
+ {"PCIELAMR2", 0x2248, 32, 0}, \
+ {"PCIELAR3", 0x2260, 32, 0}, \
+ {"PCIELAMR3", 0x2268, 32, 0}, \
+ {"PCIELAR4", 0x2280, 32, 0}, \
+ {"PCIELAMR4", 0x2288, 32, 0}, \
+ {"PCIELAR5", 0x22A0, 32, 0}, \
+ {"PCIELAMR5", 0x22A8, 32, 0}, \
+ \
+ /* PCIEC address registers */ \
+ {"PCIEPALR0", 0x3400, 32, 0}, \
+ {"PCIEPAUR0", 0x3404, 32, 0}, \
+ {"PCIEPAMR0", 0x3408, 32, 0}, \
+ {"PCIEPTCTLR0", 0x340C, 32, 0}, \
+ {"PCIEPALR1", 0x3420, 32, 0}, \
+ {"PCIEPAUR1", 0x3424, 32, 0}, \
+ {"PCIEPAMR1", 0x3428, 32, 0}, \
+ {"PCIEPTCTLR1", 0x342C, 32, 0}, \
+ {"PCIEPALR2", 0x3440, 32, 0}, \
+ {"PCIEPAUR2", 0x3444, 32, 0}, \
+ {"PCIEPAMR2", 0x3448, 32, 0}, \
+ {"PCIEPTCTLR2", 0x344C, 32, 0}, \
+ {"PCIEPALR3", 0x3460, 32, 0}, \
+ {"PCIEPAUR3", 0x3464, 32, 0}, \
+ {"PCIEPAMR3", 0x3468, 32, 0}, \
+ {"PCIEPTCTLR3", 0x346C, 32, 0}, \
+}
+
+static PCIE_BACKUP_REGS(pcie0_ip_regs);
+static PCIE_BACKUP_REGS(pcie1_ip_regs);
+
+static struct rcar_ip pcie0_ip = {
+ .ip_name = "pcie0",
+ .base_addr = 0xFE000000,
+ .size = 0x3470,
+ .reg_count = ARRAY_SIZE(pcie0_ip_regs),
+ .ip_reg = pcie0_ip_regs,
+};
+
+static struct rcar_ip pcie1_ip = {
+ .ip_name = "pcie1",
+ .base_addr = 0xEE800000,
+ .size = 0x3470,
+ .reg_count = ARRAY_SIZE(pcie1_ip_regs),
+ .ip_reg = pcie1_ip_regs,
+};
+
+struct ip_info {
+ const char *name;
+ struct rcar_ip *ip;
+};
+
+static struct ip_info ip_info_tbl[] = {
+ {"fe000000.pcie", &pcie0_ip },
+ {"ee800000.pcie", &pcie1_ip },
+ {NULL, NULL},
+};
+
+static struct rcar_ip *get_ip(const char *name)
+{
+ struct ip_info *ip_info = ip_info_tbl;
+ struct rcar_ip *ip = NULL;
+
+ while (ip_info->name) {
+ if (!strcmp(ip_info->name, name)) {
+ ip = ip_info->ip;
+ break;
+ }
+ ip_info++;
+ }
+
+ return ip;
+}
+
+static int rcar_pcie_save_regs(struct device *dev)
+{
+ struct rcar_ip *ip = get_ip(dev_name(dev));
+ int ret;
+
+ if (ip) {
+ if (!ip->virt_addr)
+ handle_registers(ip, DO_IOREMAP);
+
+ ret = handle_registers(ip, DO_BACKUP);
+ if (ret)
+ pr_err("%s: %s: BACKUP failed, ret=%d\n",
+ __func__, dev_name(dev), ret);
+ } else
+ pr_err("%s: Failed to find backup of dev: %s\n\n",
+ __func__, dev_name(dev));
+
+ return 0;
+}
+
+static int rcar_pcie_restore_regs(struct device *dev)
+{
+ struct rcar_ip *ip = get_ip(dev_name(dev));
+ int ret = -ENODEV;
+
+ if (ip) {
+ ret = handle_registers(ip, DO_RESTORE);
+ if (ret)
+ pr_err("%s: %s: RESTORE failed, ret=%d\n",
+ __func__, dev_name(dev), ret);
+
+ } else
+ pr_err("%s: Failed to find backup of dev: %s\n\n",
+ __func__, dev_name(dev));
+
+ return 0;
+}
+
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
static void rcar_pci_write_reg(struct rcar_pcie *pcie, unsigned long val,
unsigned long reg)
{
@@ -181,6 +333,7 @@
unsigned int devfn, int where, u32 *data)
{
int dev, func, reg, index;
+ u32 val;
dev = PCI_SLOT(devfn);
func = PCI_FUNC(devfn);
@@ -222,6 +375,29 @@
if (pcie->root_bus_nr < 0)
return PCIBIOS_DEVICE_NOT_FOUND;
+ /*
+ * If we are not in L1 link state and we have received PM_ENTER_L1 DLLP,
+ * transition to L1 link state. The HW will handle coming of of L1.
+ */
+ val = rcar_pci_read_reg(pcie, PMSR);
+
+ if ((val == 0) || (rcar_pci_read_reg(pcie, PCIETCTLR) & DL_DOWN)) {
+ /* Wait PCI Express link is re-initialized */
+ rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR);
+ rcar_pcie_wait_for_dl(pcie);
+ }
+
+ if ((val & PM_ENTER_L1RX) && ((val & PMSTATE) != PMSTATE_L1)) {
+ rcar_pci_write_reg(pcie, L1_INIT, PMCTLR);
+
+ /* Wait until we are in L1 */
+ while (!(val & L1FAEG))
+ val = rcar_pci_read_reg(pcie, PMSR);
+
+ /* Clear flags indicating link has transitioned to L1 */
+ rcar_pci_write_reg(pcie, L1FAEG | PM_ENTER_L1RX, PMSR);
+ }
+
/* Clear errors */
rcar_pci_write_reg(pcie, rcar_pci_read_reg(pcie, PCIEERRFR), PCIEERRFR);
@@ -404,7 +580,7 @@
return -ENODEV;
}
- pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+ pci_fixup_irqs_local(bus, pci_common_swizzle, of_irq_parse_and_map_pci);
pci_bus_size_bridges(bus);
pci_bus_assign_resources(bus);
@@ -467,7 +643,7 @@
if ((rcar_pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE))
return 0;
- msleep(5);
+ mdelay(5);
}
return -ETIMEDOUT;
@@ -773,15 +949,6 @@
if (err)
return err;
- pcie->clk = devm_clk_get(&pdev->dev, "pcie");
- if (IS_ERR(pcie->clk)) {
- dev_err(pcie->dev, "cannot get platform clock\n");
- return PTR_ERR(pcie->clk);
- }
- err = clk_prepare_enable(pcie->clk);
- if (err)
- goto fail_clk;
-
pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
if (IS_ERR(pcie->bus_clk)) {
dev_err(pcie->dev, "cannot get pcie bus clock\n");
@@ -819,7 +986,6 @@
err_map_reg:
clk_disable_unprepare(pcie->bus_clk);
fail_clk:
- clk_disable_unprepare(pcie->clk);
return err;
}
@@ -934,6 +1100,7 @@
{ .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
+ { .compatible = "renesas,pcie-r8a7796", .data = rcar_pcie_hw_init },
{},
};
MODULE_DEVICE_TABLE(of, rcar_pcie_of_match);
@@ -1008,6 +1175,13 @@
rcar_pcie_parse_request_of_pci_ranges(pcie);
+ pm_runtime_enable(pcie->dev);
+ err = pm_runtime_get_sync(pcie->dev);
+ if (err < 0) {
+ dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
+ goto err_pm_disable;
+ }
+
err = rcar_pcie_get_resources(pdev, pcie);
if (err < 0) {
dev_err(&pdev->dev, "failed to request resources: %d\n", err);
@@ -1023,18 +1197,11 @@
return -EINVAL;
hw_init_fn = of_id->data;
- pm_runtime_enable(pcie->dev);
- err = pm_runtime_get_sync(pcie->dev);
- if (err < 0) {
- dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
- goto err_pm_disable;
- }
-
/* Failure to get a link might just be that no cards are inserted */
err = hw_init_fn(pcie);
if (err) {
dev_info(&pdev->dev, "PCIe link down\n");
- err = 0;
+ err = -ENODEV;
goto err_pm_put;
}
@@ -1068,22 +1235,39 @@
#ifdef CONFIG_PM_SLEEP
static int rcar_pcie_suspend(struct device *dev)
{
- /* Empty functino for now */
- return 0;
+ int ret = 0;
+#ifdef CONFIG_RCAR_DDR_BACKUP
+ ret = rcar_pcie_save_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+
+ return ret;
}
static int rcar_pcie_resume(struct device *dev)
{
- /* Empty function for now */
+ struct rcar_pcie *pcie = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (pcie) {
+#ifdef CONFIG_RCAR_DDR_BACKUP
+ rcar_pcie_restore_regs(dev);
+#endif /* CONFIG_RCAR_DDR_BACKUP */
+ ret = rcar_pcie_hw_init(pcie);
+ if (ret)
+ pr_debug("%s: %s: re-init hw fail, ret=%d\n",
+ __func__, dev_name(dev), ret);
+ } else
+ pr_warn("%s: %s: pcie NULL\n", __func__, dev_name(dev));
+
return 0;
}
-static SIMPLE_DEV_PM_OPS(rcar_pcie_pm_ops,
- rcar_pcie_suspend,
- rcar_pcie_resume);
-
+static const struct dev_pm_ops rcar_pcie_pm_ops = {
+ .suspend = rcar_pcie_suspend,
+ .resume = rcar_pcie_resume,
+};
#define DEV_PM_OPS (&rcar_pcie_pm_ops)
-#else
+#else /* CONFIG_PM_SLEEP */
#define DEV_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
@@ -1098,6 +1282,34 @@
};
module_platform_driver(rcar_pcie_driver);
+static int rcar_pcie_pci_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_BOUND_DRIVER:
+ /* Force the DMA mask to lower 32-bits */
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block device_nb = {
+ .notifier_call = rcar_pcie_pci_notifier,
+};
+
+static int __init register_rcar_pcie_pci_notifier(void)
+{
+ return bus_register_notifier(&pci_bus_type, &device_nb);
+}
+
+arch_initcall(register_rcar_pcie_pci_notifier);
+
MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
MODULE_DESCRIPTION("Renesas R-Car PCIe driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 95c225b..90ea8fa 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -22,7 +22,8 @@
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
}
-static void pdev_fixup_irq(struct pci_dev *dev,
+static void pdev_fixup_irq(int domain_nr,
+ struct pci_dev *dev,
u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(const struct pci_dev *, u8, u8))
{
@@ -48,8 +49,15 @@
if (irq == -1)
irq = 0;
}
- dev->irq = irq;
+ /* Since pci_fixup_irqs() can be called more than once due to multiple
+ * host controllers, and we scan all PCI devices, not just those
+ * attached to this controller, make sure we don't clobber dev->irq
+ * that has nothing to do with this domain.
+ */
+ if (domain_nr >= 0 && dev->bus->domain_nr != domain_nr)
+ return;
+ dev->irq = irq;
dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq);
/* Always tell the device, so the driver knows what is
@@ -63,6 +71,17 @@
struct pci_dev *dev = NULL;
for_each_pci_dev(dev)
- pdev_fixup_irq(dev, swizzle, map_irq);
+ pdev_fixup_irq(-1, dev, swizzle, map_irq);
}
EXPORT_SYMBOL_GPL(pci_fixup_irqs);
+
+void pci_fixup_irqs_local(struct pci_bus *bus,
+ u8 (*swizzle)(struct pci_dev *, u8 *),
+ int (*map_irq)(const struct pci_dev *, u8, u8))
+{
+ struct pci_dev *dev = NULL;
+
+ for_each_pci_dev(dev)
+ pdev_fixup_irq(bus->domain_nr, dev, swizzle, map_irq);
+}
+EXPORT_SYMBOL_GPL(pci_fixup_irqs_local);
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
index 6fd0cc0..1925f37 100644
--- a/drivers/phy/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -22,6 +22,9 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy_companion.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/
#define USB2_INT_ENABLE 0x000
@@ -70,6 +73,7 @@
#define USB2_LINECTRL1_DP_RPD BIT(18)
#define USB2_LINECTRL1_DMRPD_EN BIT(17)
#define USB2_LINECTRL1_DM_RPD BIT(16)
+#define USB2_LINECTRL1_OPMODE_NODRV BIT(6)
/* ADPCTRL */
#define USB2_ADPCTRL_OTGSESSVLD BIT(20)
@@ -82,6 +86,7 @@
struct extcon_dev *extcon;
struct phy *phy;
struct regulator *vbus;
+ struct usb_phy usb_phy;
bool has_otg;
};
@@ -125,6 +130,12 @@
writel(val, usb2_base + USB2_ADPCTRL);
}
+static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
+{
+ return !!(readl(ch->base + USB2_ADPCTRL) &
+ USB2_ADPCTRL_OTGSESSVLD);
+}
+
static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
{
rcar_gen3_set_linectrl(ch, 1, 1);
@@ -133,6 +144,9 @@
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true);
extcon_set_cable_state_(ch->extcon, EXTCON_USB, false);
+
+ if (ch->has_otg && ch->usb_phy.otg)
+ ch->usb_phy.otg->state = OTG_STATE_A_HOST;
}
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
@@ -141,14 +155,65 @@
rcar_gen3_set_host_mode(ch, 0);
rcar_gen3_enable_vbus_ctrl(ch, 0);
+ if (ch->has_otg && ch->usb_phy.otg->gadget) {
+ if (rcar_gen3_check_vbus(ch))
+ usb_gadget_vbus_connect(ch->usb_phy.otg->gadget);
+ else
+ usb_gadget_vbus_disconnect(ch->usb_phy.otg->gadget);
+ ch->usb_phy.otg->state = OTG_STATE_B_PERIPHERAL;
+ }
+
extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false);
extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
}
-static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
+static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch)
{
- return !!(readl(ch->base + USB2_ADPCTRL) &
- USB2_ADPCTRL_OTGSESSVLD);
+ void __iomem *usb2_base = ch->base;
+ u32 val;
+
+ val = readl(usb2_base + USB2_LINECTRL1);
+ writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1);
+
+ if (ch->has_otg && ch->usb_phy.otg->gadget)
+ usb_gadget_vbus_disconnect(ch->usb_phy.otg->gadget);
+
+ rcar_gen3_set_linectrl(ch, 1, 1);
+ rcar_gen3_set_host_mode(ch, 1);
+ rcar_gen3_enable_vbus_ctrl(ch, 0);
+
+ val = readl(usb2_base + USB2_LINECTRL1);
+ writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1);
+
+ if (ch->has_otg && ch->usb_phy.otg)
+ ch->usb_phy.otg->state = OTG_STATE_B_HOST;
+}
+
+static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch)
+{
+ rcar_gen3_set_linectrl(ch, 0, 1);
+ rcar_gen3_set_host_mode(ch, 0);
+ rcar_gen3_enable_vbus_ctrl(ch, 1);
+
+ if (ch->has_otg && ch->usb_phy.otg->gadget) {
+ usb_gadget_vbus_connect(ch->usb_phy.otg->gadget);
+ ch->usb_phy.otg->state = OTG_STATE_A_PERIPHERAL;
+ }
+}
+
+static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch)
+{
+ void __iomem *usb2_base = ch->base;
+ u32 val;
+
+ val = readl(usb2_base + USB2_OBINTEN);
+ writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
+
+ rcar_gen3_enable_vbus_ctrl(ch, 0);
+ msleep(1000);
+ rcar_gen3_init_for_host(ch);
+
+ writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
}
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
@@ -156,20 +221,81 @@
return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
}
+static int rcar_gen3_phy_usb2_set_host(struct usb_otg *otg,
+ struct usb_bus *host)
+{
+ if (!otg)
+ return -ENODEV;
+
+ otg->host = host;
+ if (!host)
+ otg->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int rcar_gen3_phy_usb2_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ if (!otg)
+ return -ENODEV;
+
+ otg->gadget = gadget;
+ if (!gadget)
+ otg->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
{
- bool is_host = true;
-
- /* B-device? */
- if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch))
- is_host = false;
-
- if (is_host)
+ if (!rcar_gen3_check_id(ch))
rcar_gen3_init_for_host(ch);
else
rcar_gen3_init_for_peri(ch);
}
+static ssize_t otg_inputs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
+ const char *otg_str[] = {
+ "a_bus_req/", "a_bus_drop", "b_bus_req/", "b_bus_req",
+ };
+ bool is_host;
+
+ if (!ch->has_otg)
+ return -EIO;
+
+ is_host = !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI);
+
+ if (!strncmp(buf, otg_str[0], strlen(otg_str[0]))) {
+ /* fail if B-device */
+ if (rcar_gen3_check_id(ch))
+ return -EIO;
+ rcar_gen3_init_for_a_peri(ch);
+ } else if (!strncmp(buf, otg_str[1], strlen(otg_str[1]))) {
+ /* fail if B-device or A-host */
+ if (rcar_gen3_check_id(ch) || is_host)
+ return -EIO;
+ rcar_gen3_init_from_a_peri_to_a_host(ch);
+ } else if (!strncmp(buf, otg_str[2], strlen(otg_str[2]))) {
+ /* fail if A-device or B-peripheral */
+ if (!rcar_gen3_check_id(ch) || !is_host)
+ return -EIO;
+ rcar_gen3_init_for_peri(ch);
+ } else if (!strncmp(buf, otg_str[3], strlen(otg_str[3]))) {
+ /* fail if A-device */
+ if (!rcar_gen3_check_id(ch))
+ return -EIO;
+ rcar_gen3_init_for_b_host(ch);
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(otg_inputs);
+
static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
@@ -276,6 +402,7 @@
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{ .compatible = "renesas,usb2-phy-r8a7795" },
+ { .compatible = "renesas,usb2-phy-r8a7796" },
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
{ }
};
@@ -293,7 +420,7 @@
struct rcar_gen3_chan *channel;
struct phy_provider *provider;
struct resource *res;
- int irq;
+ int irq, ret;
if (!dev->of_node) {
dev_err(dev, "This driver needs device tree\n");
@@ -313,7 +440,7 @@
/* call request_irq for OTG */
irq = platform_get_irq(pdev, 0);
if (irq >= 0) {
- int ret;
+ struct usb_otg *otg;
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
IRQF_SHARED, dev_name(dev), channel);
@@ -330,6 +457,19 @@
dev_err(dev, "Failed to register extcon\n");
return ret;
}
+
+ otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg)
+ return -ENOMEM;
+
+ channel->usb_phy.dev = dev;
+ channel->usb_phy.label = "rcar_gen3_usb2_phy";
+ channel->usb_phy.otg = otg;
+ channel->usb_phy.type = USB_PHY_TYPE_USB2;
+ otg->set_host = rcar_gen3_phy_usb2_set_host;
+ otg->set_peripheral = rcar_gen3_phy_usb2_set_peripheral;
+ otg->usb_phy = &channel->usb_phy;
+
}
/* devm_phy_create() will call pm_runtime_enable(dev); */
@@ -350,15 +490,35 @@
phy_set_drvdata(channel->phy, channel);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (IS_ERR(provider))
+ if (IS_ERR(provider)) {
dev_err(dev, "Failed to register PHY provider\n");
+ } else if (channel->has_otg) {
+ ret = usb_add_phy_dev(&channel->usb_phy);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register usb PHY provider\n");
+ return ret;
+ }
+
+ ret = device_create_file(dev, &dev_attr_otg_inputs);
+ if (ret < 0) {
+ usb_remove_phy(&channel->usb_phy);
+ return ret;
+ }
+ }
return PTR_ERR_OR_ZERO(provider);
}
static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
{
+ struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
+
+ if (channel->has_otg) {
+ device_remove_file(&pdev->dev, &dev_attr_otg_inputs);
+ usb_remove_phy(&channel->usb_phy);
+ }
pm_runtime_disable(&pdev->dev);
+
return 0;
}
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index c182efc..c84dff4 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -305,7 +305,7 @@
config PWM_RCAR
tristate "Renesas R-Car PWM support"
- depends on ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || COMPILE_TEST
+ depends on ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || ARCH_RENESAS || COMPILE_TEST
depends on HAS_IOMEM
help
This driver exposes the PWM Timer controller found in Renesas
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 427142d..0f7b7db 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -457,7 +457,8 @@
{
int err;
- if (!pwm)
+ if (!pwm || !state || !state->period ||
+ state->duty_cycle > state->period)
return -EINVAL;
if (!memcmp(state, &pwm->state, sizeof(*state)))
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
index 7ac95b4..3d194df 100644
--- a/drivers/pwm/pwm-rcar.c
+++ b/drivers/pwm/pwm-rcar.c
@@ -20,6 +20,7 @@
#include <linux/soc/renesas/s2ram_ddr_backup.h>
#define RCAR_PWM_MAX_DIVISION 24
+#define RCAR_PWM_MIN_CYCLE 2
#define RCAR_PWM_MAX_CYCLE 1023
#define RCAR_PWMCR 0x00
@@ -204,12 +205,17 @@
static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
{
unsigned long clk_rate = clk_get_rate(rp->clk);
- unsigned long long max; /* max cycle / nanoseconds */
+ unsigned long long min, max; /* min, max cycle / nanoseconds */
unsigned int div;
if (clk_rate == 0)
return -EINVAL;
+ min = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MIN_CYCLE;
+ do_div(min, clk_rate);
+ if (period_ns < min)
+ return -ERANGE;
+
for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) {
max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE *
(1 << div);
@@ -286,7 +292,7 @@
return div;
/* Let the core driver set pwm->period if disabled and duty_ns == 0 */
- if (!pwm_is_enabled(pwm) && !duty_ns)
+ if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
return 0;
rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index d985992..01695d4 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -152,7 +152,7 @@
goto unlock;
}
- pwm_apply_state(pwm, &state);
+ ret = pwm_apply_state(pwm, &state);
unlock:
mutex_unlock(&export->lock);
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 5cdea92..f24b8e1 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -9,3 +9,10 @@
This enables DDR backup function for R-Car Gen3.
It supports to backup/restore module register during suspend/resume
sequence respectively when system enters S2RAM.
+
+config RCAR_THERMAL_EMS_ENABLED
+ tristate "Renesas R-Car Gen3 Enable Emergency Shutdown"
+ depends on RCAR_GEN3_THERMAL
+ help
+ Enable this option if you want to have support for Emergency Shutdown
+ in R-Car Gen3.
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 4b16dab..a5aa4c5 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -11,3 +11,6 @@
obj-$(CONFIG_ARCH_R8A7796) += rcar-avs.o
obj-$(CONFIG_RCAR_DDR_BACKUP) += s2ram_ddr_backup.o
+# EMS for R-Car Gen3
+obj-$(CONFIG_ARCH_R8A7795) += rcar_ems_ctrl.o
+obj-$(CONFIG_ARCH_R8A7796) += rcar_ems_ctrl.o
diff --git a/drivers/soc/renesas/rcar_ems_ctrl.c b/drivers/soc/renesas/rcar_ems_ctrl.c
new file mode 100644
index 0000000..8295365
--- /dev/null
+++ b/drivers/soc/renesas/rcar_ems_ctrl.c
@@ -0,0 +1,308 @@
+/*
+ * R-Car Gen3 Emergency shutdown for thermal management
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/thermal.h>
+
+#include <linux/soc/renesas/rcar_ems_ctrl.h>
+
+#define EMS_THERMAL_ZONE_MAX 10
+
+static void rcar_ems_monitor(struct work_struct *ws);
+static DECLARE_DELAYED_WORK(rcar_ems_monitor_work, rcar_ems_monitor);
+
+static RAW_NOTIFIER_HEAD(rcar_ems_chain);
+
+static int ems_mode = RCAR_EMS_MODE_OFF;
+static int ems_mode_on_temp;
+static int ems_mode_off_temp;
+static int ems_poll;
+
+static int thermal_zone_num;
+static struct thermal_zone_device *thermal_zone[EMS_THERMAL_ZONE_MAX];
+
+static int rcar_ems_notify(unsigned long state, void *p)
+{
+ return raw_notifier_call_chain(&rcar_ems_chain, state, p);
+}
+
+int register_rcar_ems_notifier(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&rcar_ems_chain, nb);
+}
+EXPORT_SYMBOL(register_rcar_ems_notifier);
+
+void unregister_rcar_ems_notifier(struct notifier_block *nb)
+{
+ raw_notifier_chain_unregister(&rcar_ems_chain, nb);
+}
+EXPORT_SYMBOL(unregister_rcar_ems_notifier);
+
+static void rcar_ems_monitor(struct work_struct *ws)
+{
+ int i, ret;
+ int temp, max_temp;
+
+ max_temp = INT_MIN;
+ for (i = 0; i < thermal_zone_num; i++) {
+ if (thermal_zone[i]) {
+ ret = thermal_zone_get_temp(
+ thermal_zone[i], &temp);
+ if (!ret) {
+ if (max_temp < temp)
+ max_temp = temp;
+ }
+ }
+ }
+
+ if (max_temp == INT_MIN)
+ goto skip;
+
+ if (ems_mode == RCAR_EMS_MODE_OFF) {
+ if (max_temp >= ems_mode_on_temp) {
+ ems_mode = RCAR_EMS_MODE_ON;
+ rcar_ems_notify(RCAR_EMS_MODE_ON,
+ (void *)(long)max_temp);
+ }
+ } else {
+ if (max_temp <= ems_mode_off_temp) {
+ ems_mode = RCAR_EMS_MODE_OFF;
+ rcar_ems_notify(RCAR_EMS_MODE_OFF,
+ (void *)(long)max_temp);
+ }
+ }
+
+skip:
+ schedule_delayed_work(&rcar_ems_monitor_work, ems_poll);
+
+}
+
+
+int rcar_ems_get_mode(void)
+{
+ return ems_mode;
+}
+EXPORT_SYMBOL(rcar_ems_get_mode);
+
+static int __init rcar_ems_ctrl_init(void)
+{
+ struct device_node *np, *c;
+ struct thermal_zone_device *zone;
+ u32 value;
+
+ if (!IS_ENABLED(CONFIG_RCAR_THERMAL_EMS_ENABLED)) {
+ pr_err("thermal emergency: disabled\n");
+ return 0;
+ }
+
+ np = of_find_node_by_name(NULL, "thermal-zones");
+ if (!np)
+ return 0;
+
+ for_each_child_of_node(np, c) {
+ if (!strcmp(c->name, "emergency")) {
+ if (!of_property_read_u32(c,
+ "polling-delay", &value))
+ ems_poll = msecs_to_jiffies(value);
+
+ if (!of_property_read_u32(c,
+ "on-temperature", &value))
+ ems_mode_on_temp = value;
+
+ if (!of_property_read_u32(c,
+ "off-temperature", &value))
+ ems_mode_off_temp = value;
+ } else {
+ zone = thermal_zone_get_zone_by_name(c->name);
+ if (IS_ERR(zone))
+ continue;
+
+ if (thermal_zone_num < EMS_THERMAL_ZONE_MAX) {
+ thermal_zone[thermal_zone_num] = zone;
+ thermal_zone_num++;
+ }
+ }
+ }
+ of_node_put(np);
+
+ if (thermal_zone_num == 0) {
+ pr_err("thermal emergency: not find thermal_zone\n");
+ return 0;
+ }
+
+ if (ems_poll == 0 ||
+ ems_mode_on_temp == 0 || ems_mode_off_temp == 0) {
+ pr_err("thermal emergency: not set value\n");
+ return 0;
+ }
+
+ schedule_delayed_work(&rcar_ems_monitor_work, ems_poll);
+
+ pr_info("thermal emergency: set temperature to %d celsius\n",
+ ems_mode_on_temp / 1000);
+
+ return 0;
+}
+
+static void __exit rcar_ems_ctrl_exit(void)
+{
+ cancel_delayed_work_sync(&rcar_ems_monitor_work);
+}
+
+late_initcall(rcar_ems_ctrl_init)
+module_exit(rcar_ems_ctrl_exit)
+
+
+/* emergency cpu shutdown function */
+static struct cpumask target_cpus;
+static struct cpumask freq_scaled_cpus;
+
+static int rcar_ems_cpufreq_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_policy *policy = data;
+ int mode;
+
+ if (!cpumask_test_cpu(policy->cpu, &freq_scaled_cpus))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case CPUFREQ_ADJUST:
+ mode = rcar_ems_get_mode();
+ if (mode == RCAR_EMS_MODE_ON) {
+ cpufreq_verify_within_limits(policy,
+ policy->cpuinfo.min_freq,
+ policy->cpuinfo.min_freq);
+ }
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rcar_ems_thermal_notifier_call(struct notifier_block *nb,
+ unsigned long state, void *val)
+{
+ long temp = (long)val;
+ int cpu;
+
+ pr_info("thermal emergency notifier: state=%ld (temp=%ld)\n",
+ state, temp);
+
+ switch (state) {
+ case RCAR_EMS_MODE_ON:
+ for_each_cpu(cpu, &target_cpus) {
+ if (cpu_online(cpu))
+ cpu_down(cpu);
+ }
+ break;
+
+ case RCAR_EMS_MODE_OFF:
+ for_each_cpu(cpu, &target_cpus) {
+ if (!cpu_online(cpu))
+ cpu_up(cpu);
+ }
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ cpufreq_update_policy(cpumask_any(&freq_scaled_cpus));
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ems_thermal_notifier_block = {
+ .notifier_call = rcar_ems_thermal_notifier_call,
+};
+static struct notifier_block ems_cpufreq_notifier_block = {
+ .notifier_call = rcar_ems_cpufreq_notifier_call,
+};
+
+static int __init rcar_ems_cpu_shutdown_init(void)
+{
+ int cpu;
+ struct device_node *cpu_node, *ems_node, *tmp_node;
+ int total_target_cpu, i;
+
+ if (!IS_ENABLED(CONFIG_RCAR_THERMAL_EMS_ENABLED))
+ return 0;
+
+ cpumask_clear(&target_cpus);
+ cpumask_clear(&freq_scaled_cpus);
+
+ ems_node = of_find_node_by_name(NULL, "emergency");
+
+ if (!ems_node)
+ return 0;
+
+ total_target_cpu = of_count_phandle_with_args(ems_node,
+ "target_cpus", 0);
+
+ for_each_online_cpu(cpu) {
+ tmp_node = of_get_cpu_node(cpu, NULL);
+ if (!of_device_is_compatible(tmp_node, "arm,cortex-a57"))
+ continue;
+ for (i = 0; i < total_target_cpu; i++) {
+ cpu_node = of_parse_phandle(ems_node, "target_cpus", i);
+ if (tmp_node == cpu_node) {
+ cpumask_set_cpu(cpu, &target_cpus);
+ break;
+ }
+ }
+
+ if (i == total_target_cpu)
+ cpumask_set_cpu(cpu, &freq_scaled_cpus);
+ }
+
+ if (cpumask_weight(&target_cpus) == 0) {
+ pr_err("thermal emergency: shutdown cpu none\n");
+ return 0;
+ }
+
+ register_rcar_ems_notifier(&ems_thermal_notifier_block);
+ cpufreq_register_notifier(&ems_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+
+ pr_info("thermal emergency: shutdown target cpus %*pbl\n",
+ cpumask_pr_args(&target_cpus));
+ pr_info("thermal emergency: freq scaled target cpus %*pbl\n",
+ cpumask_pr_args(&freq_scaled_cpus));
+
+ return 0;
+}
+
+static void __exit rcar_ems_cpu_shutdown_exit(void)
+{
+ unregister_rcar_ems_notifier(&ems_thermal_notifier_block);
+ cpufreq_unregister_notifier(&ems_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+
+late_initcall(rcar_ems_cpu_shutdown_init);
+module_exit(rcar_ems_cpu_shutdown_exit);
diff --git a/drivers/soc/renesas/s2ram_ddr_backup.c b/drivers/soc/renesas/s2ram_ddr_backup.c
index de12b3c..9e6e14f6 100644
--- a/drivers/soc/renesas/s2ram_ddr_backup.c
+++ b/drivers/soc/renesas/s2ram_ddr_backup.c
@@ -237,6 +237,7 @@
return 0;
}
+EXPORT_SYMBOL(handle_registers);
/*
* Handle backup/restore of IPs
@@ -260,6 +261,7 @@
return ret;
}
+EXPORT_SYMBOL(handle_ips);
#ifdef CONFIG_PM_SLEEP
static int ddr_bck_suspend(void)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b931ec..567b4a7 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -552,6 +552,26 @@
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
+config SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+ bool "Transfer Synchronization Debug support for MSIOF"
+ depends on SPI_SH_MSIOF
+ default n
+ help
+ In data transfer, the slave needs to have completed
+ a transfer preparation before the master.
+ As a test environment, it was to be able to put a sleep wait
+ before the data transfer of the master.
+
+config SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG_MSLEEP
+ int "Master of sleep latency (msec time)"
+ default 1
+ depends on SPI_SH_MSIOF && SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+ help
+ Select Sleep latency of the previous data transfer
+ at the time of master mode.
+ Examples:
+ N => N msec
+
config SPI_SH
tristate "SuperH SPI controller"
depends on SUPERH || COMPILE_TEST
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index aa71fe9..c1c632b 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1,6 +1,7 @@
/*
* SuperH MSIOF SPI Master Interface
*
+ * Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (c) 2009 Magnus Damm
* Copyright (C) 2014 Glider bvba
*
@@ -29,6 +30,7 @@
#include <linux/sh_dma.h>
#include <linux/soc/renesas/s2ram_ddr_backup.h>
+#include <linux/soc/renesas/rcar_prr.h>
#include <linux/spi/sh_msiof.h>
#include <linux/spi/spi.h>
@@ -40,6 +42,10 @@
u16 master_flags;
};
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+#define TRANSFAR_SYNC_DELAY (CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG_MSLEEP)
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
struct sh_msiof_spi_priv {
struct spi_master *master;
void __iomem *mapbase;
@@ -48,8 +54,10 @@
const struct sh_msiof_chipdata *chipdata;
struct sh_msiof_spi_info *info;
struct completion done;
+ struct completion done_dma_tx, done_dma_rx;
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
+ int mode;
void *tx_dma_page;
void *rx_dma_page;
dma_addr_t tx_dma_addr;
@@ -82,6 +90,7 @@
#define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */
#define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */
#define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */
+#define MDR1_DTDL_MASK 0x00700000 /* Data Pin Bit Delay Mask */
#define MDR1_DTDL_SHIFT 20 /* Data Pin Bit Delay for MSIOF_SYNC */
#define MDR1_SYNCDL_SHIFT 16 /* Frame Sync Signal Timing Delay */
#define MDR1_FLD_MASK 0x0000000c /* Frame Sync Signal Interval (0-3) */
@@ -328,6 +337,24 @@
}
#endif /* CONFIG_RCAR_DDR_BACKUP*/
+static int msiof_rcar_is_gen2(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+
+ return of_device_is_compatible(node, "renesas,msiof-r8a7790") ||
+ of_device_is_compatible(node, "renesas,msiof-r8a7791") ||
+ of_device_is_compatible(node, "renesas,msiof-r8a7793") ||
+ of_device_is_compatible(node, "renesas,msiof-r8a7794");
+}
+
+static int msiof_rcar_is_gen3(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+
+ return of_device_is_compatible(node, "renesas,msiof-r8a7795") ||
+ of_device_is_compatible(node, "renesas,msiof-r8a7796");
+}
+
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
{
switch (reg_offs) {
@@ -416,6 +443,18 @@
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_div_table) - 1);
+ /*
+ * In case of Gen2 / Gen3, BRDV[2:0]=B'111 is valid only
+ * when the BRPS[4:0] bits are set to B'00000 or B'00001.
+ */
+ if ((msiof_rcar_is_gen3(&p->pdev->dev) ||
+ msiof_rcar_is_gen2(&p->pdev->dev)) &&
+ sh_msiof_spi_div_table[k].brdv == SCR_BRDV_DIV_1 &&
+ !(brps == 1 || brps == 2)) {
+ k = 1; /* SCR_BRDV_DIV_1 -> SCR_BRDV_DIV_2 */
+ brps = DIV_ROUND_UP(brps, 2);
+ }
+
scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps);
sh_msiof_write(p, TSCR, scr);
if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX))
@@ -482,7 +521,34 @@
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
- sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+ if (RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS10) == 0)) {
+ if (p->mode == SPI_MSIOF_MASTER) {
+ tmp &= ~MDR1_DTDL_MASK;
+ tmp |= 0 << MDR1_DTDL_SHIFT;
+ }
+ }
+ if (RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) == 0)) {
+ if (p->mode == SPI_MSIOF_MASTER) {
+ tmp &= ~MDR1_DTDL_MASK;
+ tmp |= 1 << MDR1_DTDL_SHIFT;
+ }
+ }
+ if (p->mode == SPI_MSIOF_MASTER)
+ sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+ else
+ sh_msiof_write(p, TMDR1, tmp | TMDR1_PCON);
+ if (RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS10) == 0)) {
+ if (p->mode == SPI_MSIOF_MASTER) {
+ tmp &= ~MDR1_DTDL_MASK;
+ tmp |= 2 << MDR1_DTDL_SHIFT;
+ }
+ }
+ if (RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) == 0)) {
+ if (p->mode == SPI_MSIOF_MASTER) {
+ tmp &= ~MDR1_DTDL_MASK;
+ tmp |= 1 << MDR1_DTDL_SHIFT;
+ }
+ }
if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) {
/* These bits are reserved if RX needs TX */
tmp &= ~0x0000ffff;
@@ -490,8 +556,18 @@
sh_msiof_write(p, RMDR1, tmp);
tmp = 0;
- tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
- tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+ if (RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0)) {
+ if (p->mode == SPI_MSIOF_MASTER) {
+ tmp |= 0 << CTR_TSCKIZ_POL_SHIFT;
+ tmp |= 0 << CTR_RSCKIZ_POL_SHIFT;
+ } else {
+ tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
+ tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+ }
+ } else {
+ tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
+ tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
+ }
edge = cpol ^ !cpha;
@@ -709,17 +785,18 @@
static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
{
- int ret;
+ int ret = 0;
/* setup clock and rx/tx signals */
- ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+ if (p->mode == SPI_MSIOF_MASTER)
+ ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
if (rx_buf && !ret)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
if (!ret)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
/* start by setting frame bit */
- if (!ret)
+ if (!ret && p->mode == SPI_MSIOF_MASTER)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
return ret;
@@ -727,15 +804,16 @@
static int sh_msiof_spi_stop(struct sh_msiof_spi_priv *p, void *rx_buf)
{
- int ret;
+ int ret = 0;
/* shut down frame, rx/tx and clock signals */
- ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+ if (p->mode == SPI_MSIOF_MASTER)
+ ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
if (!ret)
ret = sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
if (rx_buf && !ret)
ret = sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
- if (!ret)
+ if (!ret && p->mode == SPI_MSIOF_MASTER)
ret = sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
return ret;
@@ -751,6 +829,9 @@
{
int fifo_shift;
int ret;
+ unsigned long timeout;
+
+ timeout = (p->mode == SPI_MSIOF_MASTER) ? HZ : MAX_SCHEDULE_TIMEOUT;
/* limit maximum word transfer to rx/tx fifo size */
if (tx_buf)
@@ -781,7 +862,7 @@
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
- if (!wait_for_completion_timeout(&p->done, HZ)) {
+ if (!wait_for_completion_timeout(&p->done, timeout)) {
dev_err(&p->pdev->dev, "PIO timeout\n");
ret = -ETIMEDOUT;
goto stop_reset;
@@ -810,12 +891,18 @@
return ret;
}
-static void sh_msiof_dma_complete(void *arg)
+static void sh_msiof_tx_dma_complete(void *arg)
{
struct sh_msiof_spi_priv *p = arg;
- sh_msiof_write(p, IER, 0);
- complete(&p->done);
+ complete(&p->done_dma_tx);
+}
+
+static void sh_msiof_rx_dma_complete(void *arg)
+{
+ struct sh_msiof_spi_priv *p = arg;
+
+ complete(&p->done_dma_rx);
}
static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
@@ -825,6 +912,9 @@
struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
dma_cookie_t cookie;
int ret;
+ unsigned long timeout;
+
+ timeout = (p->mode == SPI_MSIOF_MASTER) ? HZ : MAX_SCHEDULE_TIMEOUT;
/* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
@@ -835,7 +925,7 @@
if (!desc_rx)
return -EAGAIN;
- desc_rx->callback = sh_msiof_dma_complete;
+ desc_rx->callback = sh_msiof_rx_dma_complete;
desc_rx->callback_param = p;
cookie = dmaengine_submit(desc_rx);
if (dma_submit_error(cookie))
@@ -854,13 +944,8 @@
goto no_dma_tx;
}
- if (rx) {
- /* No callback */
- desc_tx->callback = NULL;
- } else {
- desc_tx->callback = sh_msiof_dma_complete;
- desc_tx->callback_param = p;
- }
+ desc_tx->callback = sh_msiof_tx_dma_complete;
+ desc_tx->callback_param = p;
cookie = dmaengine_submit(desc_tx);
if (dma_submit_error(cookie)) {
ret = cookie;
@@ -877,6 +962,8 @@
sh_msiof_write(p, IER, ier_bits);
reinit_completion(&p->done);
+ reinit_completion(&p->done_dma_tx);
+ reinit_completion(&p->done_dma_rx);
/* Now start DMA */
if (rx)
@@ -890,12 +977,37 @@
goto stop_dma;
}
- /* wait for tx fifo to be emptied / rx fifo to be filled */
- if (!wait_for_completion_timeout(&p->done, HZ)) {
- dev_err(&p->pdev->dev, "DMA timeout\n");
- ret = -ETIMEDOUT;
- goto stop_reset;
+ /* wait for Tx/Rx DMA completion */
+ if (tx) {
+ ret = wait_for_completion_timeout(&p->done_dma_tx, timeout);
+ if (!ret) {
+ dev_err(&p->pdev->dev, "Tx DMA timeout\n");
+ ret = -ETIMEDOUT;
+ goto stop_reset;
+ }
+ if (!rx) {
+ ier_bits = IER_TEOFE;
+ sh_msiof_write(p, IER, ier_bits);
+
+ /* wait for tx fifo to be emptied */
+ if (!wait_for_completion_timeout(&p->done, timeout)) {
+ dev_err(&p->pdev->dev,
+ "Tx fifo to be emptied timeout\n");
+ ret = -ETIMEDOUT;
+ goto stop_reset;
+ }
+ }
}
+ if (rx) {
+ ret = wait_for_completion_timeout(&p->done_dma_rx, timeout);
+ if (!ret) {
+ dev_err(&p->pdev->dev, "Rx DMA timeout\n");
+ ret = -ETIMEDOUT;
+ goto stop_reset;
+ }
+ }
+
+ sh_msiof_write(p, IER, 0);
/* clear status bits */
sh_msiof_reset_str(p);
@@ -988,7 +1100,8 @@
int ret;
/* setup clocks (clock already enabled in chipselect()) */
- sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
+ if (p->mode == SPI_MSIOF_MASTER)
+ sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
while (master->dma_tx && len > 15) {
/*
@@ -1007,7 +1120,7 @@
break;
copy32 = copy_bswap32;
} else if (bits <= 16) {
- if (l & 1)
+ if (l & 3)
break;
copy32 = copy_wswap32;
} else {
@@ -1017,6 +1130,11 @@
if (tx_buf)
copy32(p->tx_dma_page, tx_buf, l / 4);
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+ if (p->mode == SPI_MSIOF_MASTER)
+ msleep(TRANSFAR_SYNC_DELAY);
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l);
if (ret == -EAGAIN) {
pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
@@ -1090,6 +1208,12 @@
words = len / bytes_per_word;
while (words > 0) {
+
+#ifdef CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG
+ if (p->mode == SPI_MSIOF_MASTER)
+ msleep(TRANSFAR_SYNC_DELAY);
+#endif /* CONFIG_SPI_SH_MSIOF_TRANSFER_SYNC_DEBUG */
+
n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, tx_buf, rx_buf,
words, bits);
if (n < 0)
@@ -1125,6 +1249,8 @@
{ .compatible = "renesas,msiof-r8a7792", .data = &r8a779x_data },
{ .compatible = "renesas,msiof-r8a7793", .data = &r8a779x_data },
{ .compatible = "renesas,msiof-r8a7794", .data = &r8a779x_data },
+ { .compatible = "renesas,msiof-r8a7795", .data = &r8a779x_data },
+ { .compatible = "renesas,msiof-r8a7796", .data = &r8a779x_data },
{},
};
MODULE_DEVICE_TABLE(of, sh_msiof_match);
@@ -1149,6 +1275,11 @@
of_property_read_u32(np, "renesas,dtdl", &info->dtdl);
of_property_read_u32(np, "renesas,syncdl", &info->syncdl);
+ if (of_property_read_bool(np, "slave"))
+ info->mode = SPI_MSIOF_SLAVE;
+ else
+ info->mode = SPI_MSIOF_MASTER;
+
info->num_chipselect = num_cs;
return info;
@@ -1221,10 +1352,7 @@
return 0;
}
- /* The DMA engine uses the second register set, if present */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res)
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
master = p->master;
master->dma_tx = sh_msiof_request_dma_chan(dev, DMA_MEM_TO_DEV,
@@ -1301,6 +1429,8 @@
struct spi_master *master;
const struct of_device_id *of_id;
struct sh_msiof_spi_priv *p;
+ struct clk *ref_clk;
+ u32 clk_rate = 0;
int i;
int ret;
@@ -1331,6 +1461,8 @@
}
init_completion(&p->done);
+ init_completion(&p->done_dma_tx);
+ init_completion(&p->done_dma_rx);
p->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(p->clk)) {
@@ -1370,6 +1502,7 @@
p->tx_fifo_size = p->info->tx_fifo_override;
if (p->info->rx_fifo_override)
p->rx_fifo_size = p->info->rx_fifo_override;
+ p->mode = p->info->mode;
/* init master code */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
@@ -1394,6 +1527,23 @@
goto err2;
}
+ if (msiof_rcar_is_gen3(&master->dev)) {
+ ref_clk = devm_clk_get(&pdev->dev, "msiof_ref_clk");
+ if (!IS_ERR(ref_clk))
+ clk_rate = clk_get_rate(ref_clk);
+ if (clk_rate) {
+ clk_prepare_enable(p->clk);
+ clk_set_rate(p->clk, clk_rate);
+ clk_disable_unprepare(p->clk);
+ }
+ }
+
+ ret = RCAR_PRR_INIT();
+ if (ret) {
+ dev_err(&pdev->dev, "rcar workaround init error.\n");
+ goto err2;
+ }
+
return 0;
err2:
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index e3c19f3..b016a5a 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -4,6 +4,7 @@
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>
* Copyright (C) 2007 David Brownell (simplification, cleanup)
+ * Copyright (C) 2015 Renesas Electronics Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -695,6 +696,7 @@
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
+ { .compatible = "renesas,sh-msiof" },
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/drivers/staging/board/Makefile b/drivers/staging/board/Makefile
index 6842745..7a6fa98 100644
--- a/drivers/staging/board/Makefile
+++ b/drivers/staging/board/Makefile
@@ -1,3 +1,4 @@
obj-y := board.o
obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
obj-$(CONFIG_ARCH_R8A7740) += armadillo800eva.o
+obj-$(CONFIG_ARCH_R8A7795) += salvator-x.o
diff --git a/drivers/staging/board/salvator-x.c b/drivers/staging/board/salvator-x.c
new file mode 100644
index 0000000..313cc94
--- /dev/null
+++ b/drivers/staging/board/salvator-x.c
@@ -0,0 +1,67 @@
+/*
+ * Staging board support for Salvator-X.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cma.h>
+#include <linux/dma-contiguous.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include "board.h"
+#include "../../../mm/cma.h"
+
+struct cma *find_largest_nondefault_cma(void)
+{
+ unsigned long largest_size;
+ int k, largest_idx;
+
+ largest_size = 0;
+ largest_idx = -ENOENT;
+
+ for (k = 0; k < cma_area_count; k++) {
+ if (&cma_areas[k] == dma_contiguous_default_area)
+ continue;
+
+ if (cma_get_size(&cma_areas[k]) > largest_size) {
+ largest_size = cma_get_size(&cma_areas[k]);
+ largest_idx = k;
+ }
+ }
+
+ if (largest_idx != -ENOENT)
+ return &cma_areas[largest_idx];
+
+ return NULL;
+}
+
+struct cma *rcar_gen3_dma_contiguous;
+EXPORT_SYMBOL(rcar_gen3_dma_contiguous);
+
+static void __init salvator_x_board_staging_init(void)
+{
+ phys_addr_t cma_base;
+ unsigned long cma_size;
+
+ rcar_gen3_dma_contiguous = find_largest_nondefault_cma();
+
+ if (rcar_gen3_dma_contiguous) {
+ cma_base = cma_get_base(rcar_gen3_dma_contiguous);
+ cma_size = cma_get_size(rcar_gen3_dma_contiguous) / SZ_1M;
+
+ pr_info("%s: Located CMA at %pa, size %ld MiB\n",
+ __func__, &cma_base, cma_size);
+ }
+}
+
+board_staging("renesas,salvator-x", salvator_x_board_staging_init);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca..bb288d2 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -223,6 +223,14 @@
Enable this to plug the R-Car thermal sensor driver into the Linux
thermal framework.
+config RCAR_GEN3_THERMAL
+ tristate "Renesas R-Car Gen3 thermal driver"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Enable this to plug the R-Car Gen3 thermal sensor driver into the Linux
+ thermal framework.
+
config KIRKWOOD_THERMAL
tristate "Temperature sensor on Marvell Kirkwood SoCs"
depends on MACH_KIRKWOOD || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c1..d41d5dd 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -30,6 +30,7 @@
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
+obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
obj-y += samsung/
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c
new file mode 100644
index 0000000..7377ff4
--- /dev/null
+++ b/drivers/thermal/rcar_gen3_thermal.c
@@ -0,0 +1,618 @@
+/*
+ * R-Car Gen3 THS/CIVM thermal sensor driver
+ * Based on drivers/thermal/rcar_thermal.c
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/renesas/rcar_prr.h>
+#include <linux/spinlock.h>
+#include <linux/thermal.h>
+
+/* Register offset */
+#define REG_GEN3_CTSR 0x20
+#define REG_GEN3_THCTR 0x20
+#define REG_GEN3_IRQSTR 0x04
+#define REG_GEN3_IRQMSK 0x08
+#define REG_GEN3_IRQCTL 0x0C
+#define REG_GEN3_IRQEN 0x10
+#define REG_GEN3_IRQTEMP1 0x14
+#define REG_GEN3_IRQTEMP2 0x18
+#define REG_GEN3_IRQTEMP3 0x1C
+#define REG_GEN3_TEMP 0x28
+#define REG_GEN3_THCODE1 0x50
+#define REG_GEN3_THCODE2 0x54
+#define REG_GEN3_THCODE3 0x58
+
+#define PTAT_BASE 0xE6198000
+#define REG_GEN3_PTAT1 0x5C
+#define REG_GEN3_PTAT2 0x60
+#define REG_GEN3_PTAT3 0x64
+#define PTAT_SIZE REG_GEN3_PTAT3
+
+/* CTSR bit */
+#define PONM (0x1 << 8)
+#define AOUT (0x1 << 7)
+#define THBGR (0x1 << 5)
+#define VMEN (0x1 << 4)
+#define VMST (0x1 << 1)
+#define THSST (0x1 << 0)
+
+/* THCTR bit */
+#define CTCTL (0x1 << 24)
+#define THCNTSEN(x) (x << 16)
+
+#define BIT_LEN_12 0x1
+
+#define CTEMP_MASK 0xFFF
+
+#define IRQ_TEMP1_BIT (0x1 << 0)
+#define IRQ_TEMP2_BIT (0x1 << 1)
+#define IRQ_TEMP3_BIT (0x1 << 2)
+#define IRQ_TEMPD1_BIT (0x1 << 3)
+#define IRQ_TEMPD2_BIT (0x1 << 4)
+#define IRQ_TEMPD3_BIT (0x1 << 5)
+
+#define MCELSIUS(temp) ((temp) * 1000)
+#define TEMP_IRQ_SHIFT(tsc_id) (0x1 << tsc_id)
+#define TEMPD_IRQ_SHIFT(tsc_id) (0x1 << (tsc_id + 3))
+#define GEN3_FUSE_MASK 0xFFF
+
+/* Equation coefficients for thermal calculation formula.*/
+struct equation_coefs {
+ long a1;
+ long b1;
+ long a2;
+ long b2;
+};
+
+
+struct fuse_factors {
+ int thcode_1;
+ int thcode_2;
+ int thcode_3;
+ int ptat_1;
+ int ptat_2;
+ int ptat_3;
+};
+
+struct rcar_thermal_priv {
+ void __iomem *base;
+ struct device *dev;
+ struct thermal_zone_device *zone;
+ struct delayed_work work;
+ struct fuse_factors factor;
+ struct equation_coefs coef;
+ spinlock_t lock;
+ int id;
+ int irq;
+ const struct rcar_thermal_data *data;
+};
+
+struct rcar_thermal_data {
+ int (*thermal_init)(struct rcar_thermal_priv *priv);
+};
+
+#define rcar_priv_to_dev(priv) ((priv)->dev)
+#define rcar_has_irq_support(priv) ((priv)->irq)
+
+/* Temperature calculation */
+#define CODETSD(x) ((x) * 1000)
+#define TJ_1 96000L
+#define TJ_3 (-41000L)
+
+#define rcar_thermal_read(p, r) _rcar_thermal_read(p, r)
+static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
+{
+ return ioread32(priv->base + reg);
+}
+
+#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, r, d)
+static void _rcar_thermal_write(struct rcar_thermal_priv *priv,
+ u32 reg, u32 data)
+{
+ iowrite32(data, priv->base + reg);
+}
+
+static int round_temp(int temp)
+{
+ int tmp1, tmp2;
+ int result = 0;
+
+ tmp1 = abs(temp) % 1000;
+ tmp2 = abs(temp) / 1000;
+
+ if (tmp1 < 250)
+ result = CODETSD(tmp2);
+ else if (tmp1 < 750 && tmp1 >= 250)
+ result = CODETSD(tmp2) + 500;
+ else
+ result = CODETSD(tmp2) + 1000;
+
+ return ((temp < 0) ? (result * (-1)) : result);
+}
+
+static int thermal_read_fuse_factor(struct rcar_thermal_priv *priv)
+{
+ void __iomem *ptat_base;
+ int err;
+
+ err = RCAR_PRR_INIT();
+ if (err)
+ return err;
+
+ ptat_base = ioremap_nocache(PTAT_BASE, PTAT_SIZE);
+ if (!ptat_base) {
+ dev_err(rcar_priv_to_dev(priv), "Cannot map FUSE register\n");
+ return -ENOMEM;
+ }
+
+ /* For H3 WS1.0, H3 WS1.1 and M3 ES1.0
+ * these registers have not been programmed yet.
+ * We will use fixed value as temporary solution.
+ */
+ if ((RCAR_PRR_IS_PRODUCT(H3) && (RCAR_PRR_CHK_CUT(H3, WS11) <= 0))
+ || (RCAR_PRR_IS_PRODUCT(M3) &&
+ (RCAR_PRR_CHK_CUT(M3, ES10) == 0))) {
+ priv->factor.ptat_1 = 2351;
+ priv->factor.ptat_2 = 1509;
+ priv->factor.ptat_3 = 435;
+ switch (priv->id) {
+ case 0:
+ priv->factor.thcode_1 = 3248;
+ priv->factor.thcode_2 = 2800;
+ priv->factor.thcode_3 = 2221;
+ break;
+ case 1:
+ priv->factor.thcode_1 = 3245;
+ priv->factor.thcode_2 = 2795;
+ priv->factor.thcode_3 = 2216;
+ break;
+ case 2:
+ priv->factor.thcode_1 = 3250;
+ priv->factor.thcode_2 = 2805;
+ priv->factor.thcode_3 = 2237;
+ break;
+ }
+ } else {
+ priv->factor.thcode_1 = rcar_thermal_read(priv,
+ REG_GEN3_THCODE1)
+ & GEN3_FUSE_MASK;
+ priv->factor.thcode_2 = rcar_thermal_read(priv,
+ REG_GEN3_THCODE2)
+ & GEN3_FUSE_MASK;
+ priv->factor.thcode_3 = rcar_thermal_read(priv,
+ REG_GEN3_THCODE3)
+ & GEN3_FUSE_MASK;
+ priv->factor.ptat_1 = ioread32(ptat_base + REG_GEN3_PTAT1)
+ & GEN3_FUSE_MASK;
+ priv->factor.ptat_2 = ioread32(ptat_base + REG_GEN3_PTAT2)
+ & GEN3_FUSE_MASK;
+ priv->factor.ptat_3 = ioread32(ptat_base + REG_GEN3_PTAT3)
+ & GEN3_FUSE_MASK;
+ }
+
+ iounmap(ptat_base);
+
+ return 0;
+}
+
+static void thermal_coefficient_calculation(struct rcar_thermal_priv *priv)
+{
+ int tj_2 = 0;
+ long a1, b1;
+ long a2, b2;
+ long a1_num, a1_den;
+ long a2_num, a2_den;
+
+ tj_2 = (CODETSD((priv->factor.ptat_2 - priv->factor.ptat_3) * 137)
+ / (priv->factor.ptat_1 - priv->factor.ptat_3)) - CODETSD(41);
+
+ /*
+ * The following code is to calculate coefficients.
+ */
+ /* Coefficient a1 and b1 */
+ a1_num = CODETSD(priv->factor.thcode_2 - priv->factor.thcode_3);
+ a1_den = tj_2 - TJ_3;
+ a1 = (10000 * a1_num) / a1_den;
+ b1 = (10000 * priv->factor.thcode_3) - ((a1 * TJ_3) / 1000);
+
+ /* Coefficient a2 and b2 */
+ a2_num = CODETSD(priv->factor.thcode_2 - priv->factor.thcode_1);
+ a2_den = tj_2 - TJ_1;
+ a2 = (10000 * a2_num) / a2_den;
+ b2 = (10000 * priv->factor.thcode_1) - ((a2 * TJ_1) / 1000);
+
+ priv->coef.a1 = DIV_ROUND_CLOSEST(a1, 10);
+ priv->coef.b1 = DIV_ROUND_CLOSEST(b1, 10);
+ priv->coef.a2 = DIV_ROUND_CLOSEST(a2, 10);
+ priv->coef.b2 = DIV_ROUND_CLOSEST(b2, 10);
+}
+
+int thermal_temp_converter(struct equation_coefs coef,
+ int temp_code)
+{
+ int temp, temp1, temp2;
+
+ temp1 = MCELSIUS((CODETSD(temp_code) - coef.b1)) / coef.a1;
+ temp2 = MCELSIUS((CODETSD(temp_code) - coef.b2)) / coef.a2;
+ temp = (temp1 + temp2) / 2;
+
+ return round_temp(temp);
+}
+
+int thermal_celsius_to_temp(struct equation_coefs coef,
+ int ctemp)
+{
+ int temp_code, temp1, temp2;
+
+ temp1 = (ctemp * coef.a1 / 1000 + coef.b1) / 1000;
+ temp2 = (ctemp * coef.a2 / 1000 + coef.b2) / 1000;
+ temp_code = (temp1 + temp2) / 2;
+
+ return temp_code;
+}
+
+/*
+ * Zone device functions
+ */
+static int rcar_gen3_thermal_update_temp(struct rcar_thermal_priv *priv)
+{
+ u32 ctemp;
+ unsigned long flags;
+ int temp_cel, temp_code;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ctemp = rcar_thermal_read(priv, REG_GEN3_TEMP) & CTEMP_MASK;
+ if (rcar_has_irq_support(priv)) {
+ temp_cel = thermal_temp_converter(priv->coef, ctemp);
+
+ /* set the interrupts to exceed the temperature */
+ temp_code = thermal_celsius_to_temp(priv->coef,
+ temp_cel + MCELSIUS(1));
+ rcar_thermal_write(priv, REG_GEN3_IRQTEMP1, temp_code);
+
+ /* set the interrupts to fall below the temperature */
+ temp_code = thermal_celsius_to_temp(priv->coef,
+ temp_cel - MCELSIUS(1));
+ rcar_thermal_write(priv, REG_GEN3_IRQTEMP2, temp_code);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
+{
+ struct rcar_thermal_priv *priv = devdata;
+ int ctemp;
+ unsigned long flags;
+ u32 ctemp_code;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ctemp_code = rcar_thermal_read(priv, REG_GEN3_TEMP) & CTEMP_MASK;
+ ctemp = thermal_temp_converter(priv->coef, ctemp_code);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if ((ctemp < MCELSIUS(-40)) || (ctemp > MCELSIUS(125))) {
+ struct device *dev = rcar_priv_to_dev(priv);
+
+ dev_dbg(dev, "Temperature is not measured correctly!\n");
+
+ return -EIO;
+ }
+
+ *temp = ctemp;
+
+ return 0;
+}
+
+static int rcar_gen3_r8a7795_thermal_init(struct rcar_thermal_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rcar_thermal_write(priv, REG_GEN3_CTSR, THBGR);
+ rcar_thermal_write(priv, REG_GEN3_CTSR, 0x0);
+
+ udelay(1000);
+
+ rcar_thermal_write(priv, REG_GEN3_CTSR, PONM);
+ rcar_thermal_write(priv, REG_GEN3_IRQCTL, 0x3F);
+ rcar_thermal_write(priv, REG_GEN3_IRQEN,
+ IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT);
+ rcar_thermal_write(priv, REG_GEN3_CTSR,
+ PONM | AOUT | THBGR | VMEN);
+ udelay(100);
+
+ rcar_thermal_write(priv, REG_GEN3_CTSR,
+ PONM | AOUT | THBGR | VMEN | VMST | THSST);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rcar_gen3_r8a7796_thermal_init(struct rcar_thermal_priv *priv)
+{
+ unsigned long flags;
+ unsigned long reg_val;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ rcar_thermal_write(priv, REG_GEN3_THCTR, 0x0);
+ udelay(1000);
+ rcar_thermal_write(priv, REG_GEN3_IRQCTL, 0x3F);
+ rcar_thermal_write(priv, REG_GEN3_IRQEN,
+ IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT);
+ rcar_thermal_write(priv, REG_GEN3_THCTR, CTCTL | THCNTSEN(BIT_LEN_12));
+ reg_val = rcar_thermal_read(priv, REG_GEN3_THCTR);
+ reg_val &= ~CTCTL;
+ reg_val |= THSST;
+ rcar_thermal_write(priv, REG_GEN3_THCTR, reg_val);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+/*
+ * Interrupt
+ */
+#define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1)
+#define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0)
+static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
+{
+ unsigned long flags;
+
+ if (!rcar_has_irq_support(priv))
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ rcar_thermal_write(priv, REG_GEN3_IRQMSK,
+ enable ? (IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT) : 0);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void rcar_gen3_thermal_work(struct work_struct *work)
+{
+ struct rcar_thermal_priv *priv;
+
+ priv = container_of(work, struct rcar_thermal_priv, work.work);
+
+ rcar_gen3_thermal_update_temp(priv);
+ thermal_zone_device_update(priv->zone);
+
+ rcar_thermal_irq_enable(priv);
+}
+
+static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
+{
+ struct rcar_thermal_priv *priv = data;
+ unsigned long flags;
+ int status;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ status = rcar_thermal_read(priv, REG_GEN3_IRQSTR);
+ rcar_thermal_write(priv, REG_GEN3_IRQSTR, 0);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (status == 0)
+ return IRQ_NONE;
+
+ if (status & (IRQ_TEMP1_BIT | IRQ_TEMPD2_BIT)) {
+ rcar_thermal_irq_disable(priv);
+ schedule_delayed_work(&priv->work, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
+ .get_temp = rcar_gen3_thermal_get_temp,
+};
+
+/*
+ * Platform functions
+ */
+static int rcar_gen3_thermal_remove(struct platform_device *pdev)
+{
+ struct rcar_thermal_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ rcar_thermal_irq_disable(priv);
+ thermal_zone_of_sensor_unregister(dev, priv->zone);
+
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static const struct rcar_thermal_data r8a7795_data = {
+ .thermal_init = rcar_gen3_r8a7795_thermal_init,
+};
+
+static const struct rcar_thermal_data r8a7796_data = {
+ .thermal_init = rcar_gen3_r8a7796_thermal_init,
+};
+
+static const struct of_device_id rcar_thermal_dt_ids[] = {
+ { .compatible = "renesas,thermal-r8a7795", .data = &r8a7795_data},
+ { .compatible = "renesas,thermal-r8a7796", .data = &r8a7796_data},
+ {},
+};
+MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids);
+
+static int rcar_gen3_thermal_probe(struct platform_device *pdev)
+{
+ struct rcar_thermal_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct resource *res, *irq;
+ int ret = -ENODEV;
+ int idle;
+ struct device_node *tz_nd, *tmp_nd;
+ int i, irq_cnt;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->dev = dev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ priv->data = of_device_get_match_data(dev);
+ if (!priv->data)
+ goto error_unregister;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ priv->irq = 0;
+ if (irq) {
+ priv->irq = 1;
+ for_each_node_with_property(tz_nd, "polling-delay") {
+ tmp_nd = of_parse_phandle(tz_nd,
+ "thermal-sensors", 0);
+ if (tmp_nd && !strcmp(tmp_nd->full_name,
+ dev->of_node->full_name)) {
+ of_property_read_u32(tz_nd, "polling-delay",
+ &idle);
+ (idle > 0) ? (priv->irq = 0) :
+ (priv->irq = 1);
+ break;
+ }
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto error_unregister;
+
+ priv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto error_unregister;
+ }
+
+ spin_lock_init(&priv->lock);
+ INIT_DELAYED_WORK(&priv->work, rcar_gen3_thermal_work);
+
+ priv->id = of_alias_get_id(dev->of_node, "tsc");
+
+ priv->zone = devm_thermal_zone_of_sensor_register(dev, 0, priv,
+ &rcar_gen3_tz_of_ops);
+
+ if (IS_ERR(priv->zone)) {
+ dev_err(dev, "Can't register thermal zone\n");
+ ret = PTR_ERR(priv->zone);
+ priv->zone = NULL;
+ goto error_unregister;
+ }
+
+ priv->data->thermal_init(priv);
+ ret = thermal_read_fuse_factor(priv);
+ if (ret)
+ goto error_unregister;
+ thermal_coefficient_calculation(priv);
+ ret = rcar_gen3_thermal_update_temp(priv);
+
+ if (ret < 0)
+ goto error_unregister;
+
+ rcar_thermal_irq_enable(priv);
+
+ /* Interrupt */
+ if (rcar_has_irq_support(priv)) {
+ irq_cnt = platform_irq_count(pdev);
+ for (i = 0; i < irq_cnt; i++) {
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ ret = devm_request_irq(dev, irq->start,
+ rcar_gen3_thermal_irq,
+ IRQF_SHARED,
+ dev_name(dev), priv);
+ if (ret) {
+ dev_err(dev, "IRQ request failed\n ");
+ goto error_unregister;
+ }
+ }
+ }
+
+ dev_info(dev, "Thermal sensor probed\n");
+
+ return 0;
+
+error_unregister:
+ rcar_gen3_thermal_remove(pdev);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rcar_gen3_thermal_suspend(struct device *dev)
+{
+ struct rcar_thermal_priv *priv = dev_get_drvdata(dev);
+
+ pr_debug("%s\n", __func__);
+ rcar_thermal_irq_disable(priv);
+
+ return 0;
+}
+
+static int rcar_gen3_thermal_resume(struct device *dev)
+{
+ struct rcar_thermal_priv *priv = dev_get_drvdata(dev);
+
+ pr_debug("%s\n", __func__);
+ priv->data->thermal_init(priv);
+ rcar_thermal_irq_enable(priv);
+ rcar_gen3_thermal_update_temp(priv);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops,
+ rcar_gen3_thermal_suspend,
+ rcar_gen3_thermal_resume);
+
+#define DEV_PM_OPS (&rcar_gen3_thermal_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver rcar_gen3_thermal_driver = {
+ .driver = {
+ .name = "rcar_gen3_thermal",
+ .pm = DEV_PM_OPS,
+ .of_match_table = rcar_thermal_dt_ids,
+ },
+ .probe = rcar_gen3_thermal_probe,
+ .remove = rcar_gen3_thermal_remove,
+};
+module_platform_driver(rcar_gen3_thermal_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("R-Car Gen3 THS/CIVM driver");
+MODULE_AUTHOR("Renesas Electronics Corporation");
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 70b8b68..cf7528f 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1434,11 +1434,8 @@
int copied;
copied = tty_insert_flip_string(tport, buf, count);
- if (copied < count) {
- dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n",
- count - copied);
+ if (copied < count)
port->icount.buf_overrun++;
- }
port->icount.rx += copied;
@@ -1453,8 +1450,6 @@
if (s->active_rx == s->cookie_rx[i])
return i;
- dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__,
- s->active_rx);
return -1;
}
@@ -1515,9 +1510,9 @@
dma_async_issue_pending(chan);
+ spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",
__func__, s->cookie_rx[active], active, s->active_rx);
- spin_unlock_irqrestore(&port->lock, flags);
return;
fail:
@@ -1540,8 +1535,11 @@
dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
DMA_TO_DEVICE);
dma_release_channel(chan);
- if (enable_pio)
+ if (enable_pio) {
+ spin_lock_irqsave(&port->lock, flags);
sci_start_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
}
static void sci_submit_rx(struct sci_port *s)
@@ -1565,8 +1563,6 @@
if (dma_submit_error(s->cookie_rx[i]))
goto fail;
- dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__,
- s->cookie_rx[i], i);
}
s->active_rx = s->cookie_rx[0];
@@ -1580,7 +1576,6 @@
for (i = 0; i < 2; i++)
s->cookie_rx[i] = -EINVAL;
s->active_rx = -EINVAL;
- dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n");
sci_rx_dma_release(s, true);
}
@@ -1650,10 +1645,10 @@
int active, count;
u16 scr;
- spin_lock_irqsave(&port->lock, flags);
-
dev_dbg(port->dev, "DMA Rx timed out\n");
+ spin_lock_irqsave(&port->lock, flags);
+
active = sci_dma_rx_find_active(s);
if (active < 0) {
spin_unlock_irqrestore(&port->lock, flags);
@@ -1662,9 +1657,9 @@
status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
if (status == DMA_COMPLETE) {
+ spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(port->dev, "Cookie %d #%d has already completed\n",
s->active_rx, active);
- spin_unlock_irqrestore(&port->lock, flags);
/* Let packet complete handler take care of the packet */
return;
@@ -1688,8 +1683,6 @@
/* Handle incomplete DMA receive */
dmaengine_terminate_all(s->chan_rx);
read = sg_dma_len(&s->sg_rx[active]) - state.residue;
- dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read,
- s->active_rx);
if (read) {
count = sci_dma_rx_push(s, s->rx_buf[active], read);
@@ -1761,8 +1754,7 @@
dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
- if (!port->dev->of_node &&
- (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0))
+ if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)
return;
s->cookie_tx = -EINVAL;
@@ -2135,11 +2127,17 @@
static unsigned int sci_get_mctrl(struct uart_port *port)
{
+ struct sci_port *s = to_sci_port(port);
+ unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
+
/*
* CTS/RTS is handled in hardware when supported, while nothing
* else is wired up. Keep it simple and simply assert DSR/CAR.
*/
- return TIOCM_DSR | TIOCM_CAR;
+ if (s->cfg->capabilities & SCIx_HAVE_RTSCTS)
+ mctrl |= TIOCM_CTS;
+
+ return mctrl;
}
static void sci_break_ctl(struct uart_port *port, int break_state)
@@ -2147,6 +2145,7 @@
struct sci_port *s = to_sci_port(port);
const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
unsigned short scscr, scsptr;
+ unsigned long flags;
/* check wheter the port has SCSPTR */
if (!reg->size) {
@@ -2157,6 +2156,7 @@
return;
}
+ spin_lock_irqsave(&port->lock, flags);
scsptr = serial_port_in(port, SCSPTR);
scscr = serial_port_in(port, SCSCR);
@@ -2170,12 +2170,12 @@
serial_port_out(port, SCSPTR, scsptr);
serial_port_out(port, SCSCR, scscr);
+ spin_unlock_irqrestore(&port->lock, flags);
}
static int sci_startup(struct uart_port *port)
{
struct sci_port *s = to_sci_port(port);
- unsigned long flags;
int ret;
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
@@ -2186,11 +2186,6 @@
sci_request_dma(port);
- spin_lock_irqsave(&port->lock, flags);
- sci_start_tx(port);
- sci_start_rx(port);
- spin_unlock_irqrestore(&port->lock, flags);
-
return 0;
}
@@ -2198,12 +2193,17 @@
{
struct sci_port *s = to_sci_port(port);
unsigned long flags;
+ u16 scr;
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
spin_lock_irqsave(&port->lock, flags);
sci_stop_rx(port);
sci_stop_tx(port);
+ /* Stop RX and TX, disable related interrupts, keep clock source */
+ scr = serial_port_in(port, SCSCR);
+ serial_port_out(port, SCSCR, scr & (SCSCR_CKE1 | SCSCR_CKE0));
+
spin_unlock_irqrestore(&port->lock, flags);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
@@ -2358,6 +2358,15 @@
reg = sci_getreg(port, SCFCR);
if (reg->size)
serial_port_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
+
+ sci_clear_SCxSR(port,
+ SCxSR_RDxF_CLEAR(port) & SCxSR_ERROR_CLEAR(port) &
+ SCxSR_BREAK_CLEAR(port));
+ if (sci_getreg(port, SCLSR)->size) {
+ status = serial_port_in(port, SCLSR);
+ status &= ~(SCLSR_TO | SCLSR_ORER);
+ serial_port_out(port, SCLSR, status);
+ }
}
static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -2371,6 +2380,7 @@
int min_err = INT_MAX, err;
unsigned long max_freq = 0;
int best_clk = -1;
+ unsigned long flags;
if ((termios->c_cflag & CSIZE) == CS7)
smr_val |= SCSMR_CHR;
@@ -2480,6 +2490,8 @@
serial_port_out(port, SCCKS, sccks);
}
+ spin_lock_irqsave(&port->lock, flags);
+
sci_reset(port);
uart_update_timeout(port, termios->c_cflag, baud);
@@ -2497,9 +2509,6 @@
case 27: smr_val |= SCSMR_SRC_27; break;
}
smr_val |= cks;
- dev_dbg(port->dev,
- "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x DL %u SRR %u\n",
- scr_val, smr_val, brr, sccks, dl, srr);
serial_port_out(port, SCSCR, scr_val);
serial_port_out(port, SCSMR, smr_val);
serial_port_out(port, SCBRR, brr);
@@ -2513,7 +2522,6 @@
scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0);
smr_val |= serial_port_in(port, SCSMR) &
(SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS);
- dev_dbg(port->dev, "SCR 0x%x SMR 0x%x\n", scr_val, smr_val);
serial_port_out(port, SCSCR, scr_val);
serial_port_out(port, SCSMR, smr_val);
}
@@ -2542,7 +2550,6 @@
}
scr_val |= s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0);
- dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val);
serial_port_out(port, SCSCR, scr_val);
if ((srr + 1 == 5) &&
(port->type == PORT_SCIFA || port->type == PORT_SCIFB)) {
@@ -2591,8 +2598,6 @@
bits++;
s->rx_timeout = DIV_ROUND_UP((s->buf_len_rx * 2 * bits * HZ) /
(baud / 10), 10);
- dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n",
- s->rx_timeout * 1000 / HZ, port->timeout);
if (s->rx_timeout < msecs_to_jiffies(20))
s->rx_timeout = msecs_to_jiffies(20);
}
@@ -2601,6 +2606,8 @@
if ((termios->c_cflag & CREAD) != 0)
sci_start_rx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+
sci_port_disable(s);
}
@@ -3165,7 +3172,8 @@
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct plat_sci_port *p;
- int id;
+ int id, index;
+ struct of_phandle_args dma_spec;
if (!IS_ENABLED(CONFIG_OF) || !np)
return NULL;
@@ -3191,6 +3199,22 @@
p->type = SCI_OF_TYPE(match->data);
p->regtype = SCI_OF_REGTYPE(match->data);
p->scscr = SCSCR_RE | SCSCR_TE;
+ if (of_property_read_bool(np, "ctsrts"))
+ p->capabilities = SCIx_HAVE_RTSCTS;
+
+ index = of_property_match_string(np, "dma-names", "tx");
+ if (index >= 0)
+ index = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+ index, &dma_spec);
+ if (index >= 0)
+ p->dma_slave_tx = dma_spec.args[0];
+
+ index = of_property_match_string(np, "dma-names", "rx");
+ if (index >= 0)
+ index = of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+ index, &dma_spec);
+ if (index >= 0)
+ p->dma_slave_rx = dma_spec.args[0];
return p;
}
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index 7a4fa18..c590418 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -105,6 +105,7 @@
#define SCFCR_LOOP BIT(0) /* Loopback Test */
/* SCLSR (Line Status Register) on (H)SCIF */
+#define SCLSR_TO BIT(2) /* Timeout */
#define SCLSR_ORER BIT(0) /* Overrun Error */
/* SCSPTR (Serial Port Register), optional */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 37e327c..04d1740 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -125,6 +125,9 @@
.compatible = "renesas,xhci-r8a7795",
.data = &xhci_plat_renesas_rcar_gen3,
}, {
+ .compatible = "renesas,xhci-r8a7796",
+ .data = &xhci_plat_renesas_rcar_gen3,
+ }, {
.compatible = "renesas,rcar-gen2-xhci",
.data = &xhci_plat_renesas_rcar_gen2,
}, {
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 9d58963..d532b1c 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -92,6 +92,7 @@
struct device_node *node = dev->of_node;
return of_device_is_compatible(node, "renesas,xhci-r8a7795") ||
+ of_device_is_compatible(node, "renesas,xhci-r8a7796") ||
of_device_is_compatible(node, "renesas,rcar-gen3-xhci");
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index fa7e1ef..e70b5b8 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4886,7 +4886,7 @@
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
xhci_print_registers(xhci);
- xhci->quirks = quirks;
+ xhci->quirks |= quirks;
get_quirks(dev, xhci);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index baeb7d2..012a37a 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -482,6 +482,10 @@
.data = (void *)USBHS_TYPE_RCAR_GEN3,
},
{
+ .compatible = "renesas,usbhs-r8a7796",
+ .data = (void *)USBHS_TYPE_RCAR_GEN3,
+ },
+ {
.compatible = "renesas,rcar-gen2-usbhs",
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
@@ -514,7 +518,8 @@
if (gpio > 0)
dparam->enable_gpio = gpio;
- if (dparam->type == USBHS_TYPE_RCAR_GEN2)
+ if (dparam->type == USBHS_TYPE_RCAR_GEN2 ||
+ dparam->type == USBHS_TYPE_RCAR_GEN3)
dparam->has_usb_dmac = 1;
return info;
@@ -697,7 +702,7 @@
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
- dev_info(&pdev->dev, "probe failed\n");
+ dev_info(&pdev->dev, "probe failed (%d)\n", ret);
return ret;
}
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 30345c2..7828c5f 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -1064,9 +1064,14 @@
goto usbhs_mod_gadget_probe_err_gpriv;
}
- gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
+ gpriv->transceiver = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
+ if (PTR_ERR(gpriv->transceiver) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_add_udc;
+ }
+
dev_info(dev, "%stransceiver found\n",
- gpriv->transceiver ? "" : "no ");
+ !IS_ERR_OR_NULL(gpriv->transceiver) ? "" : "no ");
/*
* CAUTION
diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c
index 6ba5529..3f1d97c 100644
--- a/drivers/watchdog/renesas_wdt.c
+++ b/drivers/watchdog/renesas_wdt.c
@@ -76,6 +76,15 @@
return 0;
}
+static int rwdt_set_timeout(struct watchdog_device *wdev,
+ unsigned int new_timeout)
+{
+ wdev->timeout = new_timeout;
+ rwdt_init_timeout(wdev);
+
+ return 0;
+}
+
static int rwdt_start(struct watchdog_device *wdev)
{
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
@@ -121,6 +130,7 @@
.start = rwdt_start,
.stop = rwdt_stop,
.ping = rwdt_init_timeout,
+ .set_timeout = rwdt_set_timeout,
.get_timeleft = rwdt_get_timeleft,
};
diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h
index acd6af8..fd5ab0f 100644
--- a/include/drm/drm_gem_cma_helper.h
+++ b/include/drm/drm_gem_cma_helper.h
@@ -10,6 +10,7 @@
* @paddr: physical address of the backing memory
* @sgt: scatter/gather table for imported PRIME buffers
* @vaddr: kernel virtual address of the backing memory
+ * @dev: device used for backing memory alloc/free
*/
struct drm_gem_cma_object {
struct drm_gem_object base;
@@ -18,6 +19,7 @@
/* For objects with DMA memory allocated by GEM CMA */
void *vaddr;
+ struct device *dev;
};
static inline struct drm_gem_cma_object *
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 7a26286..4a88db8 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -99,11 +99,17 @@
*/
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
+/* The start or stop of SD clock don't wait 10msec. */
+#define TMIO_MMC_CLK_NO_SLEEP (1 << 9)
+
/*
* Some controllers allows to set SDx actual clock
*/
#define TMIO_MMC_CLK_ACTUAL (1 << 10)
+/* Some controllers have UHS-I sampling clock controller */
+#define TMIO_MMC_HAS_UHS_SCC (1 << 11)
+
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
@@ -125,6 +131,8 @@
unsigned int cd_gpio;
int alignment_shift;
dma_addr_t dma_rx_offset;
+ unsigned int max_blk_count;
+ unsigned short max_segs;
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
};
diff --git a/include/linux/of.h b/include/linux/of.h
index 77ddace..0ec3a01 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -585,7 +585,7 @@
return NULL;
}
-static inline int of_parse_phandle_with_args(struct device_node *np,
+static inline int of_parse_phandle_with_args(const struct device_node *np,
const char *list_name,
const char *cells_name,
int index,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 6a27454..361478f 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1128,6 +1128,9 @@
int pci_enable_resources(struct pci_dev *, int mask);
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
int (*)(const struct pci_dev *, u8, u8));
+void pci_fixup_irqs_local(struct pci_bus *,
+ u8 (*)(struct pci_dev *, u8 *),
+ int (*)(const struct pci_dev *, u8, u8));
#define HAVE_PCI_REQ_REGIONS 2
int __must_check pci_request_regions(struct pci_dev *, const char *);
int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 17018f3..908b67c 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -235,6 +235,9 @@
if (!pwm)
return -EINVAL;
+ if (duty_ns < 0 || period_ns < 0)
+ return -EINVAL;
+
pwm_get_state(pwm, &state);
if (state.duty_cycle == duty_ns && state.period == period_ns)
return 0;
diff --git a/include/linux/soc/renesas/rcar_ems_ctrl.h b/include/linux/soc/renesas/rcar_ems_ctrl.h
new file mode 100644
index 0000000..57ef8ad
--- /dev/null
+++ b/include/linux/soc/renesas/rcar_ems_ctrl.h
@@ -0,0 +1,11 @@
+#ifndef __RCAR_EMS_CTRL_H__
+#define __RCAR_EMS_CTRL_H__
+
+#define RCAR_EMS_MODE_OFF 0
+#define RCAR_EMS_MODE_ON 1
+
+extern int register_rcar_ems_notifier(struct notifier_block *nb);
+extern void unregister_rcar_ems_notifier(struct notifier_block *nb);
+extern int rcar_ems_get_mode(void);
+
+#endif /* __RCAR_EMS_CTRL_H__ */
diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h
index b087a85..f723aa4 100644
--- a/include/linux/spi/sh_msiof.h
+++ b/include/linux/spi/sh_msiof.h
@@ -1,10 +1,16 @@
#ifndef __SPI_SH_MSIOF_H__
#define __SPI_SH_MSIOF_H__
+enum {
+ SPI_MSIOF_MASTER,
+ SPI_MSIOF_SLAVE,
+};
+
struct sh_msiof_spi_info {
int tx_fifo_override;
int rx_fifo_override;
u16 num_chipselect;
+ int mode;
unsigned int dma_tx_id;
unsigned int dma_rx_id;
u32 dtdl;
diff --git a/include/media/rcar-fcp.h b/include/media/rcar-fcp.h
index 4c7fc77..ed29027 100644
--- a/include/media/rcar-fcp.h
+++ b/include/media/rcar-fcp.h
@@ -20,6 +20,7 @@
struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np);
void rcar_fcp_put(struct rcar_fcp_device *fcp);
int rcar_fcp_enable(struct rcar_fcp_device *fcp);
+struct device *rcar_fcp_device(struct rcar_fcp_device *fcp);
void rcar_fcp_disable(struct rcar_fcp_device *fcp);
#else
static inline struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
@@ -31,6 +32,10 @@
{
return -ENOSYS;
}
+static inline struct device *rcar_fcp_device(struct rcar_fcp_device *fcp)
+{
+ return NULL;
+}
static inline void rcar_fcp_disable(struct rcar_fcp_device *fcp) { }
#endif
diff --git a/include/media/rcar_csi2.h b/include/media/rcar_csi2.h
new file mode 100644
index 0000000..1a040fa
--- /dev/null
+++ b/include/media/rcar_csi2.h
@@ -0,0 +1,66 @@
+/*
+ * include/media/rcar_csi2.h
+ * This file is the driver header
+ * for the Renesas R-Car MIPI CSI-2 unit.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This file is based on the include/media/sh_mobile_csi2.h
+ *
+ * Driver header for the SH-Mobile MIPI CSI-2 unit
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef RCAR_MIPI_CSI
+#define RCAR_MIPI_CSI
+
+#include <linux/list.h>
+
+enum rcar_csi2_phy {
+ RCAR_CSI2_PHY_CSI40, /* CSI0 */
+ RCAR_CSI2_PHY_CSI20, /* CSI1 */
+ RCAR_CSI2_PHY_CSI41, /* CSI2 */
+ RCAR_CSI2_PHY_CSI21, /* CSI3 */
+};
+
+enum rcar_csi2_link {
+ RCAR_CSI2_LINK_CSI40,
+ RCAR_CSI2_LINK_CSI20,
+ RCAR_CSI2_LINK_CSI41,
+ RCAR_CSI2_LINK_CSI21,
+};
+
+enum rcar_csi2_type {
+ RCAR_CSI2_CSI4X,
+ RCAR_CSI2_CSI2X,
+};
+
+#define RCAR_CSI2_CRC (1 << 0)
+#define RCAR_CSI2_ECC (1 << 1)
+
+struct platform_device;
+
+struct rcar_csi2_client_config {
+ enum rcar_csi2_phy phy;
+ enum rcar_csi2_link link;
+ unsigned char lanes; /* bitmask[3:0] */
+ unsigned char channel; /* 0..3 */
+ struct platform_device *pdev; /* client platform device */
+ const char *name; /* async matching: client name */
+};
+
+struct v4l2_device;
+
+struct rcar_csi2_pdata {
+ enum rcar_csi2_type type;
+ unsigned int flags;
+ struct rcar_csi2_client_config *clients;
+ int num_clients;
+};
+
+#endif
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 9322d977..6d9fd98 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -1,7 +1,7 @@
/*
* vsp1.h -- R-Car VSP1 API
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2016 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -26,16 +26,22 @@
struct vsp1_du_atomic_config {
u32 pixelformat;
unsigned int pitch;
- dma_addr_t mem[2];
+ dma_addr_t mem[3];
struct v4l2_rect src;
struct v4l2_rect dst;
unsigned int alpha;
unsigned int zpos;
+ bool interlaced;
};
void vsp1_du_atomic_begin(struct device *dev);
int vsp1_du_atomic_update(struct device *dev, unsigned int rpf,
const struct vsp1_du_atomic_config *cfg);
void vsp1_du_atomic_flush(struct device *dev);
+int vsp1_du_if_set_mute(struct device *dev, bool on);
+int vsp1_du_setup_wb(struct device *dev, u32 pixelformat, unsigned int pitch,
+ dma_addr_t mem[2]);
+void vsp1_du_wait_wb(struct device *dev, u32 count);
+
#endif /* __MEDIA_VSP1_H__ */
diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild
index 9355dd8..061983a 100644
--- a/include/uapi/drm/Kbuild
+++ b/include/uapi/drm/Kbuild
@@ -12,6 +12,7 @@
header-y += qxl_drm.h
header-y += r128_drm.h
header-y += radeon_drm.h
+header-y += rcar_du_drm.h
header-y += savage_drm.h
header-y += sis_drm.h
header-y += tegra_drm.h
diff --git a/include/uapi/drm/rcar_du_drm.h b/include/uapi/drm/rcar_du_drm.h
new file mode 100644
index 0000000..43420db
--- /dev/null
+++ b/include/uapi/drm/rcar_du_drm.h
@@ -0,0 +1,42 @@
+/*
+ * rcar_du_drm.h -- R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_DRM_H__
+#define __RCAR_DU_DRM_H__
+
+struct rcar_du_vmute {
+ int crtc_id; /* CRTCs ID */
+ int on; /* Vmute function ON/OFF */
+};
+
+/* DRM_IOCTL_RCAR_DU_SET_SCRSHOT:VSPD screen shot */
+struct rcar_du_screen_shot {
+ unsigned long buff;
+ unsigned int buff_len;
+ unsigned int crtc_id;
+ unsigned int fmt;
+ unsigned int width;
+ unsigned int height;
+};
+
+/* rcar-du + vspd specific ioctls */
+#define DRM_RCAR_DU_SET_VMUTE 0
+#define DRM_RCAR_DU_SCRSHOT 4
+
+#define DRM_IOCTL_DRM_RCAR_DU_SET_VMUTE \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_RCAR_DU_SET_VMUTE, \
+ struct rcar_du_vmute)
+
+#define DRM_IOCTL_DRM_RCAR_DU_SCRSHOT \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_RCAR_DU_SCRSHOT, \
+ struct rcar_du_screen_shot)
+
+#endif /* __RCAR_DU_DRM_H__ */
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 4040eb9..64192ba 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1926,7 +1926,7 @@
goto out_unlock;
}
- sockc.tsflags = 0;
+ sockc.tsflags = sk->sk_tsflags;
if (msg->msg_controllen) {
err = sock_cmsg_send(sk, msg, &sockc);
if (unlikely(err)) {
@@ -2677,7 +2677,7 @@
dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
}
- sockc.tsflags = 0;
+ sockc.tsflags = po->sk.sk_tsflags;
if (msg->msg_controllen) {
err = sock_cmsg_send(&po->sk, msg, &sockc);
if (unlikely(err))
@@ -2880,7 +2880,7 @@
if (unlikely(!(dev->flags & IFF_UP)))
goto out_unlock;
- sockc.tsflags = 0;
+ sockc.tsflags = sk->sk_tsflags;
sockc.mark = sk->sk_mark;
if (msg->msg_controllen) {
err = sock_cmsg_send(sk, msg, &sockc);
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 49354d1..2145957 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -33,11 +33,15 @@
struct clk *clkout[CLKOUTMAX];
struct clk_onecell_data onecell;
struct rsnd_mod mod;
+ u32 flags;
int rbga_rate_for_441khz; /* RBGA */
int rbgb_rate_for_48khz; /* RBGB */
};
+#define LRCLK_ASYNC (1 << 0)
+#define adg_mode_flags(adg) (adg->flags)
+
#define for_each_rsnd_clk(pos, adg, i) \
for (i = 0; \
(i < CLKMAX) && \
@@ -355,6 +359,16 @@
rsnd_adg_set_ssi_clk(ssi_mod, data);
+ if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ u32 ckr = 0;
+
+ if (0 == (rate % 8000))
+ ckr = 0x80000000;
+
+ rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
+ }
+
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
data, rate);
@@ -518,7 +532,7 @@
}
}
- rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr);
rsnd_mod_write(adg_mod, BRRA, rbga);
rsnd_mod_write(adg_mod, BRRB, rbgb);
@@ -532,6 +546,7 @@
{
struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
if (!adg) {
@@ -545,6 +560,9 @@
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
+ if (of_get_property(np, "clkout-lr-asynchronous", NULL))
+ adg->flags = LRCLK_ASYNC;
+
priv->adg = adg;
return 0;
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index e39f916..969a516 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -226,8 +226,12 @@
ifscr = 0;
fsrate = 0;
if (fin != fout) {
+ u64 n;
+
ifscr = 1;
- fsrate = 0x0400000 / fout * fin;
+ n = (u64)0x0400000 * fin;
+ do_div(n, fout);
+ fsrate = n;
}
/*