diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2013-03-03 00:51:24 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2013-03-03 00:51:24 +0100 |
commit | d6fa4ec20db6bd0b403d2d99fd9423e6c0f3f845 (patch) | |
tree | e9ddb16309d2b546a400593afc07846ce9ed6746 | |
parent | dc1cfc34b2a07c5441d68ca21ae4296822ee4df6 (diff) | |
parent | 1ff6b9489fbc737e3e2bc7660b92cebbfaea5247 (diff) |
Conflicts:
arch/arm/mach-tegra/common.c
drivers/ata/ahci-tegra.c
42 files changed, 4564 insertions, 229 deletions
diff --git a/Documentation/video4linux/README.tegra b/Documentation/video4linux/README.tegra new file mode 100644 index 000000000000..610eeded2ae9 --- /dev/null +++ b/Documentation/video4linux/README.tegra @@ -0,0 +1,180 @@ +Theory of Operations +==================== + +There are three separate drivers within the V4L2 framework that are interesting +to Tegra-based platforms. They are as follows: + +Image Sensor driver +=================== +This driver communicates only with the image sensor hardware (typically via +I2C transactions), and is intentionally PLATFORM-AGNOSTIC. Existing image +sensor drivers can be found in drivers/media/video. For example, the ov9740 +driver communicates with the Omnivision OV9740 image sensor with built-in ISP. + +Some of the things that this driver is responsible for are: + +Setting up the proper output format of the image sensor, + +Setting up image output extents + +Setting up capture and crop regions + +Camera Host driver +================== +This driver communicates only with the camera controller on a given platform, +and is intentionally IMAGE-SENSOR-AGNOSTIC. Existing camera host drivers +can be found in drivers/media/video, of which tegra_v4l2_camera.c is the +example that is interesting to us. This camera host driver knows how to +program the CSI/VI block on Tegra2 and Tegra3 platforms. + +Some of the things that this driver is responsible for are: + +Setting up the proper input format (image frame data flowing from the image +sensor to the camera host), + +Setting up the proper output format (image frame data flowing from the +camera host to system memory), + +Programming the DMA destination to receive the image frame data, + +Starting and stopping the reception of image frame data. + +Videobuf driver +=============== +This driver is responsible for the allocation and deallocation of buffers that +are used to hold image frame data. Different camera hosts have different +DMA requirements, which makes it necessary to allow for different methods of +buffer allocation. For example, the Tegra2 and Tegra3 camera host cannot +DMA via a scatter-gather list, so the image frame buffers must be physically +contiguous. The videobuf-dma-contig.c videobuf driver can be found in +drivers/media/video, and contains a videobuf implementation that allocates +physically contiguous regions. One can also have a videobuf driver that +uses a different allocator like nvmap. + +The nvhost driver and Syncpts +============================= + +The camera host driver (tegra_v4l2_camera) has a dependency on the nvhost +driver/subsystem in order to make use of syncpts. In other words, the camera +host driver is a client of nvhost. + +A syncpt is essentially an incrementing hardware counter that triggers an +interrupt when a certain number (or threshold) is reached. The interrupt, +however, is hidden from clients of nvhost. Instead, asynchronous completion +notification is done via calling an nvhost routine that goes to sleep, and +wakes up upon completion. + +Tegra has a number of syncpts that serve various purposes. The two syncpts +that are used by the camera host driver are the VI and CSI syncpts. Other +syncpts are used in display, etc. + +A syncpt increments when a certain hardware condition is met. + +The public operations available for a syncpt are: + +nvhost_syncpt_read_ext(syncpt_id) - Read the current syncpt counter value. +nvhost_syncpt_wait_timeout_ext(syncpt_id, threshold, timeout) - Go to sleep + until the syncpt value reaches the threshold, or until the timeout + expires. +nvhost_syncpt_cpu_incr_ext(syncpt_id) - Manually increment a syncpt. + +Syncpts are used in the camera host driver in order to signify the completion +of an operation. The typical usage case can be illustrated by summarizing +the steps that the camera host driver takes in capturing a single frame +(this is called one-shot mode, where we program up each frame transfer +separately): + +0) At the very start, read the current syncpt values and remember them. See + tegra_camera_activate() -> tegra_camera_save_syncpts(), where we read + the current values and store them in pcdev->syncpt_csi and pcdev->syncpt_vi. + +1) Program the camera host registers to prepare to receive frames from the + image sensor using the proper input format. Note that we are at this + point NOT telling the camera host to DMA a frame. That comes later. See + tegra_camera_capture_setup(), where we do a whole bunch of magical + register writes depending on our input format, output format, image extents, + etc. + +2) Increment our remembered copies of the current syncpt values according to + how many syncpt increments we are expecting for the given operation we + want to perform. For capturing a single frame, we are expecting a single + increment on the CSI syncpt when the reception of the frame is complete, and + a single increment on the VI syncpt when the DMA of the frame is complete. + See tegra_camera_capture_start(), where we increment pcdev->syncpt_csi + and pcdev->syncpt_vi. + +3) Program the DMA destination registers, and toggle the bit in + TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND to do the DMA on the next available + frame. See tegra_camera_capture_start() for this. + +4) Call nvhost_syncpt_wait_timeout_ext() to wait on the CSI syncpt threshold. + Remember that we incremented our local syncpt values in step 2. Those + new values become the threshold to wait for. See + tegra_camera_capture_start(). + +5) When the frame finishes its transfer from the image sensor to the camera + host, the CSI syncpt hardware counter will be incremented by hardware. + Since the hardware syncpt value will now match the threshold, our call to + nvhost_syncpt_wait_timeout_ext() in step 4 wakes up. + +6) We now tell the camera host to get ready for the DMA to complete. We do + this by writing again to TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND. See + tegra_camera_capture_stop(). + +7) When the camera host finishes its DMA, we expect the hardware to increment + the VI syncpt. Therefore, we call nvhost_syncpt_wait_timeout_ext() on + the VI syncpt with our new threshold that we got by the incrementing in + step 2. See tegra_camera_capture_stop(). + +8) When the camera host finally finishes its DMA, the VI syncpt hardware + counter increments. Since our VI syncpt threshold is met, the call to + nvhost_syncpt_wait_timeout_ext() wakes up, and we are done. See + tegra_camera_capture_stop(). + +9) To capture the next frame, go back to step 2. The tegra_v4l2_camera driver + calls tegra_camera_capture_setup at the beginning, and then a worker thread + repeatedly calls tegra_camera_capture_start() and + tegra_camera_capture_stop(). See tegra_camera_work() -> + tegra_camera_capture_frame(). + +Note for VIP: Only a single syncpt is used for the VIP path. We use the +continuous VIP VSYNC syncpt to determine the completion of a frame transfer. +In addition, to start and finish the capture of a frame, the +VI_CAMERA_CONTROL register is used. See tegra_camera_capture_start() and +tegra_camera_capture_stop() to see how that register is used for the VIP path. +Essentially, steps 4, 5, and 6 are eliminated, and instead of writing to +TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND or TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, +we write to VI_CAMERA_CONTROL to achieve the same purpose for VIP. + +VIP versus CSI +============== +VI_VI_CORE_CONTROL bits 26:24 (INPUT_TO_CORE_EXT) should be set to 0 +(use INPUT_TO_CORE). + +VI_VI_INPUT_CONTROL bit 1 (VIP_INPUT_ENABLE) should be set to 1 (ENABLED), +bit 26:25 (SYNC_FORMAT) should be set to 1 (ITU656), and bit 27 (FIELD_DETECT) +should be set to 1 (ENABLED). + +VI_H_DOWNSCALE_CONTROL bit 0 (INPUT_H_SIZE_SEL) should be set to 0 (VIP), +and bits 3:2 (INPUT_H_SIZE_SEL_EXT) should be set to 0 (USE INPUT_H_SIZE_SEL). + +Rather than placing the image width and height into VI_CSI_PPA_H_ACTIVE and +VI_CSI_PPA_V_ACTIVE, respectively (or the CSI B counterparts), use +VI_VIP_H_ACTIVE and VI_VIP_V_ACTIVE bits 31:16. Bits 15:0 of VI_VIP_H_ACTIVE +and VI_VIP_V_ACTIVE are the number of clock cycles to wait after receiving +HSYNC or VSYNC before starting. This can be used to adjust the vertical and +horizontal back porches. + +VI_PIN_INPUT_ENABLE should be set to 0x00006fff, which enables input pins +VHS, VVS, and VD11..VD0. + +VI_PIN_INVERSION bits 1 and 2 can be used to invert input pins VHS and VVS, +respectively. + +VI_CONT_SYNCPT_VIP_VSYNC bit 8 (enable VIP_VSYNC) should be set to 1, and +bits 7:0 should hold the index of the syncpt to be used. When this syncpt +is enabled, the syncpt specified by the index will increment by 1 every +time a VSYNC occurs. We use this syncpt to signal frame completion. + +VI_CAMERA_CONTROL bit 0 should be set to 1 to start capturing. Writing a 0 +to this bit is ignored, so to stop capturing, write 1 to bit 2. diff --git a/arch/arm/configs/tegra3_defconfig b/arch/arm/configs/tegra3_defconfig index 41c39818ee70..d8ba44fc373e 100644 --- a/arch/arm/configs/tegra3_defconfig +++ b/arch/arm/configs/tegra3_defconfig @@ -175,11 +175,11 @@ CONFIG_NET_ACT_MIRRED=y CONFIG_BT=y CONFIG_BT_L2CAP=y CONFIG_BT_SCO=y -CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y -CONFIG_BT_BNEP=y -CONFIG_BT_HIDP=y -CONFIG_BT_HCIUART=y +CONFIG_BT_BNEP=m +CONFIG_BT_HIDP=m +CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_H4=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_BLUESLEEP=y @@ -187,10 +187,12 @@ CONFIG_CFG80211=y CONFIG_LIB80211=m CONFIG_MAC80211=m CONFIG_RFKILL=y +CONFIG_RFKILL_GPIO=y CONFIG_CAIF=y CONFIG_NFC=y CONFIG_PN544_NFC=y CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_BLK_DEV_LOOP=y CONFIG_MISC_DEVICES=y @@ -210,12 +212,13 @@ CONFIG_MPU_SENSORS_AK8975=m CONFIG_TEGRA_BB_SUPPORT=y CONFIG_TEGRA_BB_POWER=y CONFIG_TEGRA_BB_M7400=y -CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_CHR_DEV_SG=y CONFIG_SCSI_MULTI_LUN=y +CONFIG_ATA=y +CONFIG_SATA_AHCI_TEGRA=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y @@ -232,7 +235,6 @@ CONFIG_BCM4329_WIFI_CONTROL_FUNC=y CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_DIR="/lib/firmware" CONFIG_BCMDHD_NVRAM_DIR="/lib/firmware" -CONFIG_DHD_ENABLE_P2P=y CONFIG_BCMDHD_CFG80211=y CONFIG_BCMDHD_WIFI_CONTROL_FUNC=y CONFIG_BCMDHD_HW_OOB=y @@ -354,7 +356,28 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_TEGRA=y CONFIG_SND_SOC_TEGRA_WM8903=y CONFIG_SND_SOC_TEGRA_MAX98088=y +CONFIG_SND_SOC_TEGRA_RT5640=y +CONFIG_HID_A4TECH=m +CONFIG_HID_APPLE=m +CONFIG_HID_BELKIN=m +CONFIG_HID_ELECOM=m +CONFIG_HID_EZKEY=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_LOGITECH=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_SAMSUNG=m CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=m +CONFIG_HID_TIVO=m +CONFIG_HID_WACOM=m +CONFIG_HID_WACOM_POWER_SUPPLY=y +CONFIG_HID_WIIMOTE=m CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_DEVICEFS=y CONFIG_USB_SUSPEND=y @@ -413,6 +436,7 @@ CONFIG_VFAT_FS=y CONFIG_NTFS_FS=m CONFIG_NTFS_RW=y CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 915b1abf7b00..6ca839316e1a 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -189,6 +189,7 @@ config MACH_CARDHU select MACH_HAS_SND_SOC_TEGRA_WM8903 if SND_SOC select MACH_HAS_SND_SOC_TEGRA_TLV320AIC326X if SND_SOC select MACH_HAS_SND_SOC_TEGRA_MAX98095 if SND_SOC + select MACH_HAS_SND_SOC_TEGRA_RT5640 if SND_SOC help Support for NVIDIA Cardhu development platform diff --git a/arch/arm/mach-tegra/board-cardhu-kbc.c b/arch/arm/mach-tegra/board-cardhu-kbc.c index ce9b22cc1170..6239b1d2d77d 100644 --- a/arch/arm/mach-tegra/board-cardhu-kbc.c +++ b/arch/arm/mach-tegra/board-cardhu-kbc.c @@ -2,7 +2,7 @@ * arch/arm/mach-tegra/board-cardhu-kbc.c * Keys configuration for Nvidia tegra3 cardhu platform. * - * Copyright (C) 2011 NVIDIA, Inc. + * Copyright (C) 2011-2013 NVIDIA, Inc. * * 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 @@ -86,7 +86,8 @@ int __init cardhu_kbc_init(void) tegra_get_board_info(&board_info); if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) return 0; pr_info("Registering tegra-kbc\n"); @@ -240,6 +241,7 @@ int __init cardhu_keys_init(void) tegra_get_board_info(&board_info); if (!((board_info.board_id == BOARD_E1198) || (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315) || (board_info.board_id == BOARD_E1186) || (board_info.board_id == BOARD_E1257) || (board_info.board_id == BOARD_PM305) || @@ -300,6 +302,7 @@ int __init cardhu_keys_init(void) if ((board_info.board_id == BOARD_E1257) || (board_info.board_id == BOARD_E1186) || (board_info.board_id == BOARD_PM305) || + (board_info.board_id == BOARD_PM315) || (board_info.board_id == BOARD_PM311) || (board_info.board_id == BOARD_PM267) || (board_info.board_id == BOARD_PM269)) { diff --git a/arch/arm/mach-tegra/board-cardhu-memory.c b/arch/arm/mach-tegra/board-cardhu-memory.c index 4ec8839c44f6..1ea30fe74a6d 100644 --- a/arch/arm/mach-tegra/board-cardhu-memory.c +++ b/arch/arm/mach-tegra/board-cardhu-memory.c @@ -24,6 +24,608 @@ #include "tegra3_emc.h" #include "fuse.h" +static const struct tegra_emc_table cardhu_emc_tables_h5tc4g83mfr[] = { + { + 0x32, /* Rev 3.2 */ + 51000, /* SDRAM frequency */ + { + 0x00000002, /* EMC_RC */ + 0x0000000d, /* EMC_RFC */ + 0x00000001, /* EMC_RAS */ + 0x00000000, /* EMC_RP */ + 0x00000002, /* EMC_R2W */ + 0x0000000a, /* EMC_W2R */ + 0x00000003, /* EMC_R2P */ + 0x0000000b, /* EMC_W2P */ + 0x00000000, /* EMC_RD_RCD */ + 0x00000000, /* EMC_WR_RCD */ + 0x00000003, /* EMC_RRD */ + 0x00000001, /* EMC_REXT */ + 0x00000000, /* EMC_WEXT */ + 0x00000005, /* EMC_WDV */ + 0x00000005, /* EMC_QUSE */ + 0x00000004, /* EMC_QRST */ + 0x00000009, /* EMC_QSAFE */ + 0x0000000b, /* EMC_RDV */ + 0x00000181, /* EMC_REFRESH */ + 0x00000000, /* EMC_BURST_REFRESH_NUM */ + 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */ + 0x00000002, /* EMC_PDEX2WR */ + 0x00000002, /* EMC_PDEX2RD */ + 0x00000001, /* EMC_PCHG2PDEN */ + 0x00000000, /* EMC_ACT2PDEN */ + 0x00000007, /* EMC_AR2PDEN */ + 0x0000000f, /* EMC_RW2PDEN */ + 0x0000000e, /* EMC_TXSR */ + 0x0000000e, /* EMC_TXSRDLL */ + 0x00000004, /* EMC_TCKE */ + 0x00000002, /* EMC_TFAW */ + 0x00000000, /* EMC_TRPAB */ + 0x00000004, /* EMC_TCLKSTABLE */ + 0x00000005, /* EMC_TCLKSTOP */ + 0x0000018e, /* EMC_TREFBW */ + 0x00000006, /* EMC_QUSE_EXTRA */ + 0x00000004, /* EMC_FBIO_CFG6 */ + 0x00000000, /* EMC_ODT_WRITE */ + 0x00000000, /* EMC_ODT_READ */ + 0x00004288, /* EMC_FBIO_CFG5 */ + 0x007800a4, /* EMC_CFG_DIG_DLL */ + 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */ + 0x000fc000, /* EMC_DLL_XFORM_DQS0 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS1 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS2 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS3 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS4 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS5 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS6 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS7 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE0 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE1 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE2 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE3 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE4 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE5 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE6 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE7 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ0 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ1 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ2 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ3 */ + 0x000002a0, /* EMC_XM2CMDPADCTRL */ + 0x0800211c, /* EMC_XM2DQSPADCTRL2 */ + 0x00000000, /* EMC_XM2DQPADCTRL2 */ + 0x77fff884, /* EMC_XM2CLKPADCTRL */ + 0x01f1f108, /* EMC_XM2COMPPADCTRL */ + 0x05057404, /* EMC_XM2VTTGENPADCTRL */ + 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */ + 0x08000168, /* EMC_XM2QUSEPADCTRL */ + 0x08000000, /* EMC_XM2DQSPADCTRL3 */ + 0x00000802, /* EMC_CTT_TERM_CTRL */ + 0x00000000, /* EMC_ZCAL_INTERVAL */ + 0x00000040, /* EMC_ZCAL_WAIT_CNT */ + 0x000c000c, /* EMC_MRS_WAIT_CNT */ + 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */ + 0x00000000, /* EMC_CTT */ + 0x00000000, /* EMC_CTT_DURATION */ + 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */ + 0x00010003, /* MC_EMEM_ARB_CFG */ + 0xc000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RP */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RC */ + 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */ + 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */ + 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */ + 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */ + 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */ + 0x06020102, /* MC_EMEM_ARB_DA_TURNS */ + 0x000a0402, /* MC_EMEM_ARB_DA_COVERS */ + 0x74630303, /* MC_EMEM_ARB_MISC0 */ + 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */ + 0xe8000000, /* EMC_FBIO_SPARE */ + 0xff00ff00, /* EMC_CFG_RSV */ + }, + 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */ + 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */ + 0x00000001, /* EMC_CFG.PERIODIC_QRST */ + 0x80001221, /* Mode Register 0 */ + 0x80100003, /* Mode Register 1 */ + 0x80200008, /* Mode Register 2 */ + 0x00000001, /* EMC_CFG.DYN_SELF_REF */ + }, + { + 0x32, /* Rev 3.2 */ + 102000, /* SDRAM frequency */ + { + 0x00000004, /* EMC_RC */ + 0x0000001a, /* EMC_RFC */ + 0x00000003, /* EMC_RAS */ + 0x00000001, /* EMC_RP */ + 0x00000002, /* EMC_R2W */ + 0x0000000a, /* EMC_W2R */ + 0x00000003, /* EMC_R2P */ + 0x0000000b, /* EMC_W2P */ + 0x00000001, /* EMC_RD_RCD */ + 0x00000001, /* EMC_WR_RCD */ + 0x00000003, /* EMC_RRD */ + 0x00000001, /* EMC_REXT */ + 0x00000000, /* EMC_WEXT */ + 0x00000005, /* EMC_WDV */ + 0x00000005, /* EMC_QUSE */ + 0x00000004, /* EMC_QRST */ + 0x00000009, /* EMC_QSAFE */ + 0x0000000b, /* EMC_RDV */ + 0x00000303, /* EMC_REFRESH */ + 0x00000000, /* EMC_BURST_REFRESH_NUM */ + 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */ + 0x00000002, /* EMC_PDEX2WR */ + 0x00000002, /* EMC_PDEX2RD */ + 0x00000001, /* EMC_PCHG2PDEN */ + 0x00000000, /* EMC_ACT2PDEN */ + 0x00000007, /* EMC_AR2PDEN */ + 0x0000000f, /* EMC_RW2PDEN */ + 0x0000001c, /* EMC_TXSR */ + 0x0000001c, /* EMC_TXSRDLL */ + 0x00000004, /* EMC_TCKE */ + 0x00000004, /* EMC_TFAW */ + 0x00000000, /* EMC_TRPAB */ + 0x00000004, /* EMC_TCLKSTABLE */ + 0x00000005, /* EMC_TCLKSTOP */ + 0x0000031c, /* EMC_TREFBW */ + 0x00000006, /* EMC_QUSE_EXTRA */ + 0x00000004, /* EMC_FBIO_CFG6 */ + 0x00000000, /* EMC_ODT_WRITE */ + 0x00000000, /* EMC_ODT_READ */ + 0x00004288, /* EMC_FBIO_CFG5 */ + 0x007800a4, /* EMC_CFG_DIG_DLL */ + 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */ + 0x000fc000, /* EMC_DLL_XFORM_DQS0 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS1 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS2 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS3 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS4 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS5 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS6 */ + 0x000fc000, /* EMC_DLL_XFORM_DQS7 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE0 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE1 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE2 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE3 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE4 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE5 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE6 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE7 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ0 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ1 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ2 */ + 0x000fc000, /* EMC_DLL_XFORM_DQ3 */ + 0x000002a0, /* EMC_XM2CMDPADCTRL */ + 0x0800211c, /* EMC_XM2DQSPADCTRL2 */ + 0x00000000, /* EMC_XM2DQPADCTRL2 */ + 0x77fff884, /* EMC_XM2CLKPADCTRL */ + 0x01f1f108, /* EMC_XM2COMPPADCTRL */ + 0x05057404, /* EMC_XM2VTTGENPADCTRL */ + 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */ + 0x08000168, /* EMC_XM2QUSEPADCTRL */ + 0x08000000, /* EMC_XM2DQSPADCTRL3 */ + 0x00000802, /* EMC_CTT_TERM_CTRL */ + 0x00000000, /* EMC_ZCAL_INTERVAL */ + 0x00000040, /* EMC_ZCAL_WAIT_CNT */ + 0x000c000c, /* EMC_MRS_WAIT_CNT */ + 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */ + 0x00000000, /* EMC_CTT */ + 0x00000000, /* EMC_CTT_DURATION */ + 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */ + 0x00000001, /* MC_EMEM_ARB_CFG */ + 0xc0000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RP */ + 0x00000003, /* MC_EMEM_ARB_TIMING_RC */ + 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */ + 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */ + 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */ + 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */ + 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */ + 0x06020102, /* MC_EMEM_ARB_DA_TURNS */ + 0x000a0403, /* MC_EMEM_ARB_DA_COVERS */ + 0x73c30504, /* MC_EMEM_ARB_MISC0 */ + 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */ + 0xe8000000, /* EMC_FBIO_SPARE */ + 0xff00ff00, /* EMC_CFG_RSV */ + }, + 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */ + 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */ + 0x00000001, /* EMC_CFG.PERIODIC_QRST */ + 0x80001221, /* Mode Register 0 */ + 0x80100003, /* Mode Register 1 */ + 0x80200008, /* Mode Register 2 */ + 0x00000001, /* EMC_CFG.DYN_SELF_REF */ + }, + { + 0x32, /* Rev 3.2 */ + 204000, /* SDRAM frequency */ + { + 0x00000009, /* EMC_RC */ + 0x00000035, /* EMC_RFC */ + 0x00000007, /* EMC_RAS */ + 0x00000002, /* EMC_RP */ + 0x00000002, /* EMC_R2W */ + 0x0000000a, /* EMC_W2R */ + 0x00000003, /* EMC_R2P */ + 0x0000000b, /* EMC_W2P */ + 0x00000002, /* EMC_RD_RCD */ + 0x00000002, /* EMC_WR_RCD */ + 0x00000003, /* EMC_RRD */ + 0x00000001, /* EMC_REXT */ + 0x00000000, /* EMC_WEXT */ + 0x00000005, /* EMC_WDV */ + 0x00000005, /* EMC_QUSE */ + 0x00000004, /* EMC_QRST */ + 0x00000009, /* EMC_QSAFE */ + 0x0000000b, /* EMC_RDV */ + 0x00000607, /* EMC_REFRESH */ + 0x00000000, /* EMC_BURST_REFRESH_NUM */ + 0x00000181, /* EMC_PRE_REFRESH_REQ_CNT */ + 0x00000002, /* EMC_PDEX2WR */ + 0x00000002, /* EMC_PDEX2RD */ + 0x00000001, /* EMC_PCHG2PDEN */ + 0x00000000, /* EMC_ACT2PDEN */ + 0x00000007, /* EMC_AR2PDEN */ + 0x0000000f, /* EMC_RW2PDEN */ + 0x00000038, /* EMC_TXSR */ + 0x00000038, /* EMC_TXSRDLL */ + 0x00000004, /* EMC_TCKE */ + 0x00000007, /* EMC_TFAW */ + 0x00000000, /* EMC_TRPAB */ + 0x00000004, /* EMC_TCLKSTABLE */ + 0x00000005, /* EMC_TCLKSTOP */ + 0x00000638, /* EMC_TREFBW */ + 0x00000006, /* EMC_QUSE_EXTRA */ + 0x00000004, /* EMC_FBIO_CFG6 */ + 0x00000000, /* EMC_ODT_WRITE */ + 0x00000000, /* EMC_ODT_READ */ + 0x00004288, /* EMC_FBIO_CFG5 */ + 0x004400a4, /* EMC_CFG_DIG_DLL */ + 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */ + 0x00080000, /* EMC_DLL_XFORM_DQS0 */ + 0x00080000, /* EMC_DLL_XFORM_DQS1 */ + 0x00080000, /* EMC_DLL_XFORM_DQS2 */ + 0x00080000, /* EMC_DLL_XFORM_DQS3 */ + 0x00080000, /* EMC_DLL_XFORM_DQS4 */ + 0x00080000, /* EMC_DLL_XFORM_DQS5 */ + 0x00080000, /* EMC_DLL_XFORM_DQS6 */ + 0x00080000, /* EMC_DLL_XFORM_DQS7 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE0 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE1 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE2 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE3 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE4 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE5 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE6 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE7 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */ + 0x00080000, /* EMC_DLL_XFORM_DQ0 */ + 0x00080000, /* EMC_DLL_XFORM_DQ1 */ + 0x00080000, /* EMC_DLL_XFORM_DQ2 */ + 0x00080000, /* EMC_DLL_XFORM_DQ3 */ + 0x000002a0, /* EMC_XM2CMDPADCTRL */ + 0x0800211c, /* EMC_XM2DQSPADCTRL2 */ + 0x00000000, /* EMC_XM2DQPADCTRL2 */ + 0x77fff884, /* EMC_XM2CLKPADCTRL */ + 0x01f1f108, /* EMC_XM2COMPPADCTRL */ + 0x05057404, /* EMC_XM2VTTGENPADCTRL */ + 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */ + 0x08000168, /* EMC_XM2QUSEPADCTRL */ + 0x08000000, /* EMC_XM2DQSPADCTRL3 */ + 0x00000802, /* EMC_CTT_TERM_CTRL */ + 0x00020000, /* EMC_ZCAL_INTERVAL */ + 0x00000100, /* EMC_ZCAL_WAIT_CNT */ + 0x000c000c, /* EMC_MRS_WAIT_CNT */ + 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */ + 0x00000000, /* EMC_CTT */ + 0x00000000, /* EMC_CTT_DURATION */ + 0x80000d22, /* EMC_DYN_SELF_REF_CONTROL */ + 0x00000003, /* MC_EMEM_ARB_CFG */ + 0xc0000025, /* MC_EMEM_ARB_OUTSTANDING_REQ */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RP */ + 0x00000005, /* MC_EMEM_ARB_TIMING_RC */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RAS */ + 0x00000003, /* MC_EMEM_ARB_TIMING_FAW */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */ + 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */ + 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */ + 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */ + 0x06020102, /* MC_EMEM_ARB_DA_TURNS */ + 0x000a0405, /* MC_EMEM_ARB_DA_COVERS */ + 0x73840a06, /* MC_EMEM_ARB_MISC0 */ + 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */ + 0xe8000000, /* EMC_FBIO_SPARE */ + 0xff00ff00, /* EMC_CFG_RSV */ + }, + 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */ + 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */ + 0x00000001, /* EMC_CFG.PERIODIC_QRST */ + 0x80001221, /* Mode Register 0 */ + 0x80100003, /* Mode Register 1 */ + 0x80200008, /* Mode Register 2 */ + 0x00000001, /* EMC_CFG.DYN_SELF_REF */ + }, + { + 0x32, /* Rev 3.2 */ + 400000, /* SDRAM frequency */ + { + 0x00000012, /* EMC_RC */ + 0x00000066, /* EMC_RFC */ + 0x0000000c, /* EMC_RAS */ + 0x00000004, /* EMC_RP */ + 0x00000003, /* EMC_R2W */ + 0x00000008, /* EMC_W2R */ + 0x00000002, /* EMC_R2P */ + 0x0000000a, /* EMC_W2P */ + 0x00000004, /* EMC_RD_RCD */ + 0x00000004, /* EMC_WR_RCD */ + 0x00000002, /* EMC_RRD */ + 0x00000001, /* EMC_REXT */ + 0x00000000, /* EMC_WEXT */ + 0x00000004, /* EMC_WDV */ + 0x00000006, /* EMC_QUSE */ + 0x00000004, /* EMC_QRST */ + 0x0000000a, /* EMC_QSAFE */ + 0x0000000d, /* EMC_RDV */ + 0x00000bf0, /* EMC_REFRESH */ + 0x00000000, /* EMC_BURST_REFRESH_NUM */ + 0x000002fc, /* EMC_PRE_REFRESH_REQ_CNT */ + 0x00000001, /* EMC_PDEX2WR */ + 0x00000008, /* EMC_PDEX2RD */ + 0x00000001, /* EMC_PCHG2PDEN */ + 0x00000000, /* EMC_ACT2PDEN */ + 0x00000008, /* EMC_AR2PDEN */ + 0x0000000f, /* EMC_RW2PDEN */ + 0x0000006c, /* EMC_TXSR */ + 0x00000200, /* EMC_TXSRDLL */ + 0x00000004, /* EMC_TCKE */ + 0x0000000c, /* EMC_TFAW */ + 0x00000000, /* EMC_TRPAB */ + 0x00000004, /* EMC_TCLKSTABLE */ + 0x00000005, /* EMC_TCLKSTOP */ + 0x00000c30, /* EMC_TREFBW */ + 0x00000000, /* EMC_QUSE_EXTRA */ + 0x00000006, /* EMC_FBIO_CFG6 */ + 0x00000000, /* EMC_ODT_WRITE */ + 0x00000000, /* EMC_ODT_READ */ + 0x00007088, /* EMC_FBIO_CFG5 */ + 0x001d0084, /* EMC_CFG_DIG_DLL */ + 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */ + 0x00038000, /* EMC_DLL_XFORM_DQS0 */ + 0x00038000, /* EMC_DLL_XFORM_DQS1 */ + 0x00038000, /* EMC_DLL_XFORM_DQS2 */ + 0x00038000, /* EMC_DLL_XFORM_DQS3 */ + 0x00038000, /* EMC_DLL_XFORM_DQS4 */ + 0x00038000, /* EMC_DLL_XFORM_DQS5 */ + 0x00038000, /* EMC_DLL_XFORM_DQS6 */ + 0x00038000, /* EMC_DLL_XFORM_DQS7 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE0 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE1 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE2 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE3 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE4 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE5 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE6 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE7 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */ + 0x00030000, /* EMC_DLL_XFORM_DQ0 */ + 0x00030000, /* EMC_DLL_XFORM_DQ1 */ + 0x00030000, /* EMC_DLL_XFORM_DQ2 */ + 0x00030000, /* EMC_DLL_XFORM_DQ3 */ + 0x000002a0, /* EMC_XM2CMDPADCTRL */ + 0x0800013d, /* EMC_XM2DQSPADCTRL2 */ + 0x00000000, /* EMC_XM2DQPADCTRL2 */ + 0x77fff884, /* EMC_XM2CLKPADCTRL */ + 0x01f1f508, /* EMC_XM2COMPPADCTRL */ + 0x05057404, /* EMC_XM2VTTGENPADCTRL */ + 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */ + 0x080001e8, /* EMC_XM2QUSEPADCTRL */ + 0x08000021, /* EMC_XM2DQSPADCTRL3 */ + 0x00000802, /* EMC_CTT_TERM_CTRL */ + 0x00020000, /* EMC_ZCAL_INTERVAL */ + 0x00000100, /* EMC_ZCAL_WAIT_CNT */ + 0x0158000c, /* EMC_MRS_WAIT_CNT */ + 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */ + 0x00000000, /* EMC_CTT */ + 0x00000000, /* EMC_CTT_DURATION */ + 0x800018c8, /* EMC_DYN_SELF_REF_CONTROL */ + 0x00000006, /* MC_EMEM_ARB_CFG */ + 0x80000048, /* MC_EMEM_ARB_OUTSTANDING_REQ */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RP */ + 0x00000009, /* MC_EMEM_ARB_TIMING_RC */ + 0x00000005, /* MC_EMEM_ARB_TIMING_RAS */ + 0x00000005, /* MC_EMEM_ARB_TIMING_FAW */ + 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */ + 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */ + 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */ + 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */ + 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */ + 0x06030202, /* MC_EMEM_ARB_DA_TURNS */ + 0x000d0709, /* MC_EMEM_ARB_DA_COVERS */ + 0x7086120a, /* MC_EMEM_ARB_MISC0 */ + 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */ + 0xe8000000, /* EMC_FBIO_SPARE */ + 0xff00ff89, /* EMC_CFG_RSV */ + }, + 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */ + 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */ + 0x00000000, /* EMC_CFG.PERIODIC_QRST */ + 0x80000521, /* Mode Register 0 */ + 0x80100002, /* Mode Register 1 */ + 0x80200000, /* Mode Register 2 */ + 0x00000000, /* EMC_CFG.DYN_SELF_REF */ + }, + { + 0x32, /* Rev 3.2 */ + 800000, /* SDRAM frequency */ + { + 0x00000025, /* EMC_RC */ + 0x000000ee, /* EMC_RFC */ + 0x0000001a, /* EMC_RAS */ + 0x00000009, /* EMC_RP */ + 0x00000005, /* EMC_R2W */ + 0x0000000d, /* EMC_W2R */ + 0x00000004, /* EMC_R2P */ + 0x00000013, /* EMC_W2P */ + 0x00000009, /* EMC_RD_RCD */ + 0x00000009, /* EMC_WR_RCD */ + 0x00000003, /* EMC_RRD */ + 0x00000001, /* EMC_REXT */ + 0x00000000, /* EMC_WEXT */ + 0x00000007, /* EMC_WDV */ + 0x0000000b, /* EMC_QUSE */ + 0x00000009, /* EMC_QRST */ + 0x0000000b, /* EMC_QSAFE */ + 0x00000012, /* EMC_RDV */ + 0x00001820, /* EMC_REFRESH */ + 0x00000000, /* EMC_BURST_REFRESH_NUM */ + 0x00000608, /* EMC_PRE_REFRESH_REQ_CNT */ + 0x00000003, /* EMC_PDEX2WR */ + 0x00000012, /* EMC_PDEX2RD */ + 0x00000001, /* EMC_PCHG2PDEN */ + 0x00000000, /* EMC_ACT2PDEN */ + 0x0000000f, /* EMC_AR2PDEN */ + 0x00000018, /* EMC_RW2PDEN */ + 0x000000f8, /* EMC_TXSR */ + 0x00000200, /* EMC_TXSRDLL */ + 0x00000005, /* EMC_TCKE */ + 0x00000018, /* EMC_TFAW */ + 0x00000000, /* EMC_TRPAB */ + 0x00000007, /* EMC_TCLKSTABLE */ + 0x00000008, /* EMC_TCLKSTOP */ + 0x00001860, /* EMC_TREFBW */ + 0x0000000c, /* EMC_QUSE_EXTRA */ + 0x00000004, /* EMC_FBIO_CFG6 */ + 0x00000000, /* EMC_ODT_WRITE */ + 0x00000000, /* EMC_ODT_READ */ + 0x00005088, /* EMC_FBIO_CFG5 */ + 0xf0070191, /* EMC_CFG_DIG_DLL */ + 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */ + 0x0000c00a, /* EMC_DLL_XFORM_DQS0 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS1 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS2 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS3 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS4 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS5 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS6 */ + 0x0000000a, /* EMC_DLL_XFORM_DQS7 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE0 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE1 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE2 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE3 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE4 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE5 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE6 */ + 0x00000000, /* EMC_DLL_XFORM_QUSE7 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */ + 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */ + 0x0000000a, /* EMC_DLL_XFORM_DQ0 */ + 0x0000000a, /* EMC_DLL_XFORM_DQ1 */ + 0x0000000a, /* EMC_DLL_XFORM_DQ2 */ + 0x0000000a, /* EMC_DLL_XFORM_DQ3 */ + 0x000002a0, /* EMC_XM2CMDPADCTRL */ + 0x0800013d, /* EMC_XM2DQSPADCTRL2 */ + 0x22220000, /* EMC_XM2DQPADCTRL2 */ + 0x77fff884, /* EMC_XM2CLKPADCTRL */ + 0x01f1f501, /* EMC_XM2COMPPADCTRL */ + 0x07077404, /* EMC_XM2VTTGENPADCTRL */ + 0x54000000, /* EMC_XM2VTTGENPADCTRL2 */ + 0x080001e8, /* EMC_XM2QUSEPADCTRL */ + 0x06000021, /* EMC_XM2DQSPADCTRL3 */ + 0x00000802, /* EMC_CTT_TERM_CTRL */ + 0x00020000, /* EMC_ZCAL_INTERVAL */ + 0x00000100, /* EMC_ZCAL_WAIT_CNT */ + 0x00d0000c, /* EMC_MRS_WAIT_CNT */ + 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */ + 0x00000000, /* EMC_CTT */ + 0x00000000, /* EMC_CTT_DURATION */ + 0x8000308c, /* EMC_DYN_SELF_REF_CONTROL */ + 0x0000000c, /* MC_EMEM_ARB_CFG */ + 0x80000090, /* MC_EMEM_ARB_OUTSTANDING_REQ */ + 0x00000004, /* MC_EMEM_ARB_TIMING_RCD */ + 0x00000005, /* MC_EMEM_ARB_TIMING_RP */ + 0x00000013, /* MC_EMEM_ARB_TIMING_RC */ + 0x0000000c, /* MC_EMEM_ARB_TIMING_RAS */ + 0x0000000b, /* MC_EMEM_ARB_TIMING_FAW */ + 0x00000002, /* MC_EMEM_ARB_TIMING_RRD */ + 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */ + 0x0000000c, /* MC_EMEM_ARB_TIMING_WAP2PRE */ + 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */ + 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */ + 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */ + 0x00000008, /* MC_EMEM_ARB_TIMING_W2R */ + 0x08040202, /* MC_EMEM_ARB_DA_TURNS */ + 0x00160d13, /* MC_EMEM_ARB_DA_COVERS */ + 0x712c2414, /* MC_EMEM_ARB_MISC0 */ + 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */ + 0xe8000000, /* EMC_FBIO_SPARE */ + 0xff00ff49, /* EMC_CFG_RSV */ + }, + 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */ + 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */ + 0x00000001, /* EMC_CFG.PERIODIC_QRST */ + 0x80000d71, /* Mode Register 0 */ + 0x80100002, /* Mode Register 1 */ + 0x80200018, /* Mode Register 2 */ + 0x00000000, /* EMC_CFG.DYN_SELF_REF */ + } +}; static const struct tegra_emc_table cardhu_emc_tables_h5tc2g[] = { { @@ -6798,7 +7400,6 @@ int cardhu_emc_init(void) struct board_info board; tegra_get_board_info(&board); - switch (board.board_id) { case BOARD_PM269: tegra_init_dram_bit_map(pm269_bit_swap_map, @@ -6819,6 +7420,10 @@ int cardhu_emc_init(void) tegra_init_emc(cardhu_emc_tables_h5tc2g_pm311, ARRAY_SIZE(cardhu_emc_tables_h5tc2g_pm311)); break; + case BOARD_PM315: + tegra_init_emc(cardhu_emc_tables_h5tc4g83mfr, + ARRAY_SIZE(cardhu_emc_tables_h5tc4g83mfr)); + break; default: if (tegra_get_revision() == TEGRA_REVISION_A01) tegra_init_emc(cardhu_emc_tables_h5tc2g, diff --git a/arch/arm/mach-tegra/board-cardhu-panel.c b/arch/arm/mach-tegra/board-cardhu-panel.c index d7101af55712..bbd6d4aaac37 100644 --- a/arch/arm/mach-tegra/board-cardhu-panel.c +++ b/arch/arm/mach-tegra/board-cardhu-panel.c @@ -1444,9 +1444,16 @@ skip_lvds: res->start = tegra_fb2_start; res->end = tegra_fb2_start + tegra_fb2_size - 1; - /* Copy the bootloader fb to the fb2. */ - tegra_move_framebuffer(tegra_fb2_start, tegra_bootloader_fb_start, - min(tegra_fb2_size, tegra_bootloader_fb_size)); + /* + * If the bootloader fb2 is valid, copy it to the fb2, or else + * clear fb2 to avoid garbage on dispaly2. + */ + if (tegra_bootloader_fb2_size) + tegra_move_framebuffer(tegra_fb2_start, + tegra_bootloader_fb2_start, + min(tegra_fb2_size, tegra_bootloader_fb2_size)); + else + tegra_clear_framebuffer(tegra_fb2_start, tegra_fb2_size); if (!err) err = nvhost_device_register(&cardhu_disp2_device); diff --git a/arch/arm/mach-tegra/board-cardhu-pinmux.c b/arch/arm/mach-tegra/board-cardhu-pinmux.c index 346dccfd715b..c416d453e960 100644 --- a/arch/arm/mach-tegra/board-cardhu-pinmux.c +++ b/arch/arm/mach-tegra/board-cardhu-pinmux.c @@ -497,6 +497,20 @@ static __initdata struct tegra_pingroup_config cardhu_pinmux_e1291_a04[] = { DEFAULT_PINMUX(GPIO_PU4, RSVD1, PULL_UP, NORMAL, INPUT), }; +static __initdata struct tegra_pingroup_config cardhu_pinmux_pm315[] = { + DEFAULT_PINMUX(GMI_AD15, NAND, PULL_DOWN, NORMAL, OUTPUT), + DEFAULT_PINMUX(ULPI_DATA6, UARTA, NORMAL, NORMAL, OUTPUT), + DEFAULT_PINMUX(SPI2_MOSI, SPI6, NORMAL, NORMAL, INPUT), + DEFAULT_PINMUX(DAP3_SCLK, RSVD1, NORMAL, NORMAL, OUTPUT), + /* PCIE dock detect */ + DEFAULT_PINMUX(GPIO_PU4, RSVD1, PULL_UP, NORMAL, INPUT), + /* CDC enable for realtek RTL5640 */ + DEFAULT_PINMUX(SPI2_SCK, SPI2, NORMAL, NORMAL, OUTPUT), + DEFAULT_PINMUX(SPI2_CS1_N, SPI2, NORMAL, NORMAL, INPUT), + /* Power up for USB1, USB3 */ + DEFAULT_PINMUX(GMI_AD13, NAND, PULL_UP, NORMAL, INPUT), +}; + static __initdata struct tegra_pingroup_config cardhu_pinmux_e1198[] = { DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT), @@ -606,10 +620,33 @@ static __initdata struct tegra_pingroup_config gmi_pins_269[] = { DEFAULT_PINMUX(GMI_WP_N, NAND, NORMAL, NORMAL, INPUT), }; -static void __init cardhu_pinmux_audio_init(void) +static void __init cardhu_wm8903_audio_init(void) +{ + int ret = gpio_request(TEGRA_GPIO_CDC_IRQ, "wm8903"); + if (ret < 0) + pr_err("%s() Error in gpio_request() for gpio %d\n", + __func__, ret); + ret = gpio_direction_input(TEGRA_GPIO_CDC_IRQ); + if (ret < 0) { + pr_err("%s() Error in setting gpio %d to in/out\n", + __func__, ret); + gpio_free(TEGRA_GPIO_CDC_IRQ); + } +} + +static void __init beaver_rt5640_audio_init(void) { - gpio_request(TEGRA_GPIO_CDC_IRQ, "wm8903"); - gpio_direction_input(TEGRA_GPIO_CDC_IRQ); + int ret = gpio_request(TEGRA_GPIO_RTL_CDC_IRQ, "rt5640"); + if (ret < 0) + pr_err("%s() Error in gpio_request() for gpio %d\n", + __func__, ret); + ret = gpio_direction_input(TEGRA_GPIO_RTL_CDC_IRQ); + if (ret < 0) { + pr_err("%s() Error in setting gpio %d to in/out\n", + __func__, ret); + gpio_free(TEGRA_GPIO_RTL_CDC_IRQ); + } + } #define GPIO_INIT_PIN_MODE(_gpio, _is_input, _value) \ @@ -639,6 +676,13 @@ static struct gpio_init_pin_info init_gpio_mode_e1291_a04[] = { GPIO_INIT_PIN_MODE(TEGRA_GPIO_PR2, false, 0), }; +static struct gpio_init_pin_info init_gpio_mode_pm315[] = { + GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD6, false, 0), + GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD4, false, 0), + GPIO_INIT_PIN_MODE(TEGRA_GPIO_PH5, false, 1), + GPIO_INIT_PIN_MODE(TEGRA_GPIO_PR2, false, 0), +}; + static void __init cardhu_gpio_init_configure(void) { struct board_info board_info; @@ -670,6 +714,10 @@ static void __init cardhu_gpio_init_configure(void) pins_info = init_gpio_mode_e1291_a04; } break; + case BOARD_PM315: + len = ARRAY_SIZE(init_gpio_mode_pm315); + pins_info = init_gpio_mode_pm315; + break; default: return; } @@ -718,7 +766,12 @@ int __init cardhu_pinmux_init(void) tegra_pinmux_config_table(cardhu_pinmux_e1291_a04, ARRAY_SIZE(cardhu_pinmux_e1291_a04)); break; - + case BOARD_PM315: + tegra_pinmux_config_table(cardhu_pinmux_cardhu_a03, + ARRAY_SIZE(cardhu_pinmux_cardhu_a03)); + tegra_pinmux_config_table(cardhu_pinmux_pm315, + ARRAY_SIZE(cardhu_pinmux_pm315)); + break; case BOARD_PM269: case BOARD_PM305: case BOARD_PM311: @@ -749,7 +802,11 @@ int __init cardhu_pinmux_init(void) break; } - cardhu_pinmux_audio_init(); + if (board_info.board_id == BOARD_PM315) + beaver_rt5640_audio_init(); + else + cardhu_wm8903_audio_init(); + return 0; } diff --git a/arch/arm/mach-tegra/board-cardhu-power.c b/arch/arm/mach-tegra/board-cardhu-power.c index 677b38347514..2ca143010165 100644 --- a/arch/arm/mach-tegra/board-cardhu-power.c +++ b/arch/arm/mach-tegra/board-cardhu-power.c @@ -115,6 +115,18 @@ static struct regulator_consumer_supply tps6591x_ldo1_supply_0[] = { REGULATOR_SUPPLY("vdd_pexa", NULL), }; +static struct regulator_consumer_supply tps6591x_ldo1_supply_pm315[] = { + REGULATOR_SUPPLY("avdd_pexb", NULL), + REGULATOR_SUPPLY("vdd_pexb", NULL), + REGULATOR_SUPPLY("avdd_pex_pll", NULL), + REGULATOR_SUPPLY("avdd_pexa", NULL), + REGULATOR_SUPPLY("vdd_pexa", NULL), + REGULATOR_SUPPLY("avdd_sata", NULL), + REGULATOR_SUPPLY("vdd_sata", NULL), + REGULATOR_SUPPLY("avdd_sata_pll", NULL), + REGULATOR_SUPPLY("avdd_plle", NULL), +}; + static struct regulator_consumer_supply tps6591x_ldo2_supply_0[] = { REGULATOR_SUPPLY("avdd_sata", NULL), REGULATOR_SUPPLY("vdd_sata", NULL), @@ -453,14 +465,24 @@ int __init cardhu_regulator_init(void) pr_info("VSEL 1:0 %d%d\n", tps62361_pdata.vsel1_def_state, tps62361_pdata.vsel0_def_state); + } else if (board_info.board_id == BOARD_PM315) { + /* On PM315, SATA rails are on LDO1 */ + pdata_ldo1_0.regulator.num_consumer_supplies = + ARRAY_SIZE(tps6591x_ldo1_supply_pm315); + pdata_ldo1_0.regulator.consumer_supplies = + tps6591x_ldo1_supply_pm315; + pdata_ldo2_0.regulator.num_consumer_supplies = 0; + pdata_ldo2_0.regulator.consumer_supplies = NULL; } - if ((board_info.board_id == BOARD_E1291) && + if (((board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) && (board_info.sku & SKU_DCDC_TPS62361_SUPPORT)) ext_core_regulator = true; if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { if (ext_core_regulator) { tps_platform.num_subdevs = ARRAY_SIZE(tps_devs_e1198_skubit0_1); @@ -485,7 +507,8 @@ int __init cardhu_regulator_init(void) } /* E1291-A04/A05: Enable DEV_SLP and enable sleep on GPIO2 */ - if ((board_info.board_id == BOARD_E1291) && + if (((board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) && ((board_info.fab == BOARD_FAB_A04) || (board_info.fab == BOARD_FAB_A05) || (board_info.fab == BOARD_FAB_A07))) { @@ -660,6 +683,13 @@ static struct regulator_consumer_supply fixed_reg_en_1v8_cam_supply[] = { REGULATOR_SUPPLY("vdd_i2c", "2-0033"), }; +/* Enable realtek Codec for PM315 */ +static struct regulator_consumer_supply fixed_reg_cdc_en_supply[] = { + REGULATOR_SUPPLY("cdc_en", NULL), +}; + + + static struct regulator_consumer_supply fixed_reg_en_vbrtr_supply[] = { REGULATOR_SUPPLY("vdd_vbrtr", NULL), }; @@ -679,6 +709,11 @@ static struct regulator_consumer_supply fixed_reg_en_vddio_vid_oc_supply[] = { REGULATOR_SUPPLY("vdd_hdmi_con", NULL), }; +/* Battery powered rail*/ +static struct regulator_consumer_supply fixed_reg_en_battery_supply[] = { + REGULATOR_SUPPLY("usb_vbus", "tegra-ehci.1"), +}; + /* Macro for defining fixed regulator sub device data */ #define FIXED_SUPPLY(_name) "fixed_reg_"#_name #define FIXED_REG_OD(_id, _var, _name, _in_supply, _always_on, \ @@ -748,6 +783,10 @@ FIXED_REG(1, en_5v0_a04, en_5v0, NULL, 0, 0, TPS6591X_GPIO_8, true FIXED_REG(2, en_ddr_a04, en_ddr, NULL, 1, 0, TPS6591X_GPIO_7, true, 1, 1500); FIXED_REG(3, en_3v3_sys_a04, en_3v3_sys, NULL, 0, 0, TPS6591X_GPIO_6, true, 1, 3300); +/* PM315 Rev C realtek alc5640 codec */ +FIXED_REG(23, en_cdc, cdc_en, FIXED_SUPPLY(en_3v3_sys), 0, 1, TEGRA_GPIO_PX2, true, 0, 1200); + + /* Specific to pm269 */ FIXED_REG(4, en_vdd_bl_pm269, en_vdd_bl, NULL, 0, 0, TEGRA_GPIO_PH3, true, 1, 5000); FIXED_REG(6, en_vdd_pnl1_pm269, en_vdd_pnl1, FIXED_SUPPLY(en_3v3_sys), 0, 0, TEGRA_GPIO_PW1, true, 1, 3300); @@ -794,6 +833,8 @@ FIXED_REG_OD(16, en_usb3_vbus_oc_a03, en_usb3_vbus_oc, FIXED_SUPPLY(en_5v0), 0 /* E1198/E1291 specific */ FIXED_REG_OD(17, en_vddio_vid_oc, en_vddio_vid_oc, FIXED_SUPPLY(en_5v0), 0, 0, TEGRA_GPIO_PT0, true, 0, 5000, true); +/* Always ON */ +FIXED_REG(22, en_battery, en_battery, NULL, 1, 1, -1, true, 1, 5000); /* * Creating the fixed/gpio-switch regulator device tables for different boards */ @@ -812,7 +853,8 @@ FIXED_REG_OD(17, en_vddio_vid_oc, en_vddio_vid_oc, FIXED_SUPPLY(en_5v0), 0, ADD_FIXED_REG(en_3v3_emmc), \ ADD_FIXED_REG(en_vdd_sdmmc1), \ ADD_FIXED_REG(en_3v3_pex_hvdd), \ - ADD_FIXED_REG(en_1v8_cam), + ADD_FIXED_REG(en_1v8_cam), \ + ADD_FIXED_REG(en_battery), #define COMMON_FIXED_REG_E1291_A04 \ ADD_FIXED_REG(en_5v_cp), \ @@ -827,7 +869,8 @@ FIXED_REG_OD(17, en_vddio_vid_oc, en_vddio_vid_oc, FIXED_SUPPLY(en_5v0), 0, ADD_FIXED_REG(en_3v3_emmc), \ ADD_FIXED_REG(en_vdd_sdmmc1), \ ADD_FIXED_REG(en_3v3_pex_hvdd), \ - ADD_FIXED_REG(en_1v8_cam), + ADD_FIXED_REG(en_1v8_cam), \ + ADD_FIXED_REG(en_battery), #define PM269_FIXED_REG \ ADD_FIXED_REG(en_5v_cp), \ @@ -962,6 +1005,11 @@ static struct platform_device *fixed_reg_devs_e1198_a02[] = { ADD_FIXED_REG(en_vddio_vid_oc), }; +#define PM315_FIXED_REG \ + ADD_FIXED_REG(en_cdc), + + + /* Fixed regulator devices for PM269 */ static struct platform_device *fixed_reg_devs_pm269[] = { PM269_FIXED_REG @@ -1010,6 +1058,15 @@ static struct platform_device *fixed_reg_devs_e1291_a04[] = { E1198_FIXED_REG }; +/* Fixed regulator devices for PM315 */ +static struct platform_device *fixed_reg_devs_pm315[] = { + COMMON_FIXED_REG_E1291_A04 + E1291_A03_FIXED_REG + E1198_FIXED_REG + PM315_FIXED_REG +}; + + static bool is_display_board_dsi(u16 display_board_id) { return ((display_board_id == BOARD_DISPLAY_E1213) || @@ -1063,7 +1120,10 @@ int __init cardhu_fixed_regulator_init(void) fixed_reg_devs = fixed_reg_devs_e1198_base; } break; - + case BOARD_PM315: + nfixreg_devs = ARRAY_SIZE(fixed_reg_devs_pm315); + fixed_reg_devs = fixed_reg_devs_pm315; + break; case BOARD_PM311: case BOARD_PM305: nfixreg_devs = ARRAY_SIZE(fixed_reg_devs_pm311); diff --git a/arch/arm/mach-tegra/board-cardhu-sensors.c b/arch/arm/mach-tegra/board-cardhu-sensors.c index 7e9774f290eb..08ad0d5fb0fc 100644 --- a/arch/arm/mach-tegra/board-cardhu-sensors.c +++ b/arch/arm/mach-tegra/board-cardhu-sensors.c @@ -88,7 +88,8 @@ static int cardhu_camera_init(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { ret = gpio_request(CAM1_POWER_DWN_GPIO, "camera_power_en"); if (ret < 0) pr_err("%s: gpio_request failed for gpio %s\n", @@ -137,7 +138,8 @@ static int cardhu_left_ov5650_power_on(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { if (cardhu_vdd_2v8_cam1 == NULL) { cardhu_vdd_2v8_cam1 = regulator_get(NULL, "vdd_2v8_cam1"); @@ -164,7 +166,8 @@ static int cardhu_left_ov5650_power_on(void) mdelay(5); if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 0); mdelay(20); gpio_direction_output(OV5650_RESETN_GPIO, 0); @@ -199,7 +202,8 @@ static int cardhu_left_ov5650_power_off(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 1); gpio_direction_output(CAM2_POWER_DWN_GPIO, 1); gpio_direction_output(CAM3_POWER_DWN_GPIO, 1); @@ -281,7 +285,8 @@ static int cardhu_right_ov5650_power_on(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 0); gpio_direction_output(CAM2_POWER_DWN_GPIO, 0); @@ -342,7 +347,8 @@ static int cardhu_right_ov5650_power_off(void) /* Boards E1198 and E1291 are of Cardhu personality * and do not have TCA6416 for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 1); gpio_direction_output(CAM2_POWER_DWN_GPIO, 1); gpio_direction_output(CAM3_POWER_DWN_GPIO, 1); @@ -363,8 +369,8 @@ static void cardhu_ov5650_synchronize_sensors(void) mdelay(50); gpio_direction_output(CAM1_POWER_DWN_GPIO, 0); mdelay(50); - } - else if (board_info.board_id == BOARD_E1291) { + } else if ((board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 1); gpio_direction_output(CAM2_POWER_DWN_GPIO, 1); mdelay(50); @@ -401,7 +407,8 @@ static int cardhu_ov2710_power_on(void) /* Boards E1198 and E1291 are of Cardhu personality * and do not have TCA6416 for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { if (cardhu_vdd_cam3 == NULL) { cardhu_vdd_cam3 = regulator_get(NULL, "vdd_cam3"); if (WARN_ON(IS_ERR(cardhu_vdd_cam3))) { @@ -446,7 +453,8 @@ static int cardhu_ov2710_power_off(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 1); gpio_direction_output(CAM2_POWER_DWN_GPIO, 1); gpio_direction_output(CAM3_POWER_DWN_GPIO, 1); @@ -473,7 +481,8 @@ static int cardhu_ov5640_power_on(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 0); gpio_direction_output(CAM2_POWER_DWN_GPIO, 0); @@ -526,7 +535,8 @@ static int cardhu_ov5640_power_off(void) /* Boards E1198 and E1291 are of Cardhu personality * and donot have TCA6416 exp for camera */ if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { gpio_direction_output(CAM1_POWER_DWN_GPIO, 1); gpio_direction_output(CAM2_POWER_DWN_GPIO, 1); gpio_direction_output(CAM3_POWER_DWN_GPIO, 1); @@ -858,7 +868,8 @@ static int cardhu_nct1008_init(void) (board_info.board_id == BOARD_E1257) || (board_info.board_id == BOARD_PM269) || (board_info.board_id == BOARD_PM305) || - (board_info.board_id == BOARD_PM311)) { + (board_info.board_id == BOARD_PM311) || + (board_info.board_id == BOARD_PM315)) { nct1008_port = TEGRA_GPIO_PCC2; } else if ((board_info.board_id == BOARD_E1186) || (board_info.board_id == BOARD_E1187) || @@ -1109,7 +1120,7 @@ int __init cardhu_sensors_init(void) if (board_info.sku == BOARD_SKU_B11) i2c_register_board_info(2, cardhu_i2c2_ltr_board_info, ARRAY_SIZE(cardhu_i2c2_ltr_board_info)); - else + else if (board_info.board_id != BOARD_PM315) i2c_register_board_info(2, cardhu_i2c2_isl_board_info, ARRAY_SIZE(cardhu_i2c2_isl_board_info)); @@ -1120,7 +1131,8 @@ int __init cardhu_sensors_init(void) i2c_register_board_info(4, cardhu_i2c4_nct1008_board_info, ARRAY_SIZE(cardhu_i2c4_nct1008_board_info)); - mpuirq_init(); + if (board_info.board_id != BOARD_PM315) + mpuirq_init(); return 0; } @@ -1165,19 +1177,20 @@ int __init cardhu_ov5650_late_init(void) return 0; if ((board_info.board_id == BOARD_E1198) || - (board_info.board_id == BOARD_E1291)) + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) return 0; - printk("%s: \n", __func__); + printk(KERN_INFO "%s:\n", __func__); for (i = 0; i < ARRAY_SIZE(ov5650_gpio_keys); i++) { ret = gpio_request(ov5650_gpio_keys[i].gpio, ov5650_gpio_keys[i].name); if (ret < 0) { - printk("%s: gpio_request failed for gpio #%d\n", + printk(KERN_INFO "%s: gpio_request failed for gpio #%d\n", __func__, i); goto fail; } - printk("%s: enable - %d\n", __func__, i); + printk(KERN_INFO "%s: enable - %d\n", __func__, i); gpio_direction_output(ov5650_gpio_keys[i].gpio, ov5650_gpio_keys[i].enabled); gpio_export(ov5650_gpio_keys[i].gpio, false); diff --git a/arch/arm/mach-tegra/board-cardhu.c b/arch/arm/mach-tegra/board-cardhu.c index e14e6b25d644..4b03ddc03b7b 100644 --- a/arch/arm/mach-tegra/board-cardhu.c +++ b/arch/arm/mach-tegra/board-cardhu.c @@ -56,6 +56,7 @@ #include <mach/io.h> #include <mach/i2s.h> #include <mach/tegra_asoc_pdata.h> +#include <mach/tegra_rt5640_pdata.h> #include <mach/tegra_wm8903_pdata.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -432,8 +433,16 @@ static struct i2c_board_info __initdata cardhu_codec_max98095_info = { .platform_data = &cardhu_max98095_pdata, }; +static struct i2c_board_info __initdata rt5640_board_info = { + I2C_BOARD_INFO("rt5640", 0x1c), +}; + + static void cardhu_i2c_init(void) { + struct board_info board_info; + + tegra_get_board_info(&board_info); tegra_i2c_device1.dev.platform_data = &cardhu_i2c1_platform_data; tegra_i2c_device2.dev.platform_data = &cardhu_i2c2_platform_data; tegra_i2c_device3.dev.platform_data = &cardhu_i2c3_platform_data; @@ -446,7 +455,10 @@ static void cardhu_i2c_init(void) platform_device_register(&tegra_i2c_device2); platform_device_register(&tegra_i2c_device1); - i2c_register_board_info(4, &cardhu_codec_wm8903_info, 1); + if (board_info.board_id == BOARD_PM315) + i2c_register_board_info(4, &rt5640_board_info, 1); + else + i2c_register_board_info(4, &cardhu_codec_wm8903_info, 1); i2c_register_board_info(4, &cardhu_codec_max98095_info, 1); i2c_register_board_info(4, &cardhu_codec_aic326x_info, 1); @@ -830,6 +842,38 @@ static struct platform_device cardhu_audio_aic326x_device = { }, }; +static struct tegra_asoc_platform_data beaver_audio_rt5640_pdata = { + .codec_name = "rt5640.4-001c", + .codec_dai_name = "rt5640-aif1", + .gpio_spkr_en = TEGRA_GPIO_RTL_SPKR_EN, + .gpio_hp_det = TEGRA_GPIO_RTL_HP_DET, + .gpio_hp_mute = -1, + .gpio_int_mic_en = TEGRA_GPIO_RTL_INT_MIC_EN, + .gpio_ext_mic_en = -1, /* TEGRA_GPIO_EXT_MIC_EN,*/ + .i2s_param[HIFI_CODEC] = { + .audio_port_id = 0, + .is_i2s_master = 1, + .i2s_mode = TEGRA_DAIFMT_I2S, + }, + .i2s_param[BASEBAND] = { + .audio_port_id = -1, + }, + .i2s_param[BT_SCO] = { + .audio_port_id = 3, + .is_i2s_master = 1, + .i2s_mode = TEGRA_DAIFMT_DSP_A, + }, +}; + +static struct platform_device beaver_audio_rt5640_device = { + .name = "tegra-snd-rt5640", + .id = 0, + .dev = { + .platform_data = &beaver_audio_rt5640_pdata, + }, +}; + + static struct platform_device *cardhu_devices[] __initdata = { &tegra_pmu_device, &tegra_rtc_device, @@ -860,7 +904,6 @@ static struct platform_device *cardhu_devices[] __initdata = { &baseband_dit_device, &cardhu_bt_rfkill_device, &tegra_pcm_device, - &cardhu_audio_wm8903_device, &cardhu_audio_max98095_device, &cardhu_audio_aic326x_device, &tegra_hda_device, @@ -943,6 +986,15 @@ static const u8 config_sku2000[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static struct platform_device *cardhu_audio_devices[] __initdata = { + &cardhu_audio_wm8903_device, + +}; + +static struct platform_device *beaver_audio_devices[] __initdata = { + &beaver_audio_rt5640_device, + +}; static struct mxt_platform_data atmel_mxt_info = { .x_line = 27, @@ -1359,10 +1411,17 @@ static void cardhu_pci_init(void) cardhu_pci_platform_data.port_status[2] = 1; cardhu_pci_platform_data.use_dock_detect = 1; cardhu_pci_platform_data.gpio = DOCK_DETECT_GPIO; + } else if (board_info.board_id == BOARD_PM315) { + cardhu_pci_platform_data.port_status[0] = 1; + cardhu_pci_platform_data.port_status[1] = 0; + cardhu_pci_platform_data.port_status[2] = 1; + cardhu_pci_platform_data.use_dock_detect = 0; + cardhu_pci_platform_data.gpio = 0; } if ((board_info.board_id == BOARD_E1186) || - (board_info.board_id == BOARD_E1187) || - (board_info.board_id == BOARD_E1291)) { + (board_info.board_id == BOARD_E1187) || + (board_info.board_id == BOARD_E1291) || + (board_info.board_id == BOARD_PM315)) { tegra_pci_device.dev.platform_data = &cardhu_pci_platform_data; platform_device_register(&tegra_pci_device); } @@ -1428,6 +1487,9 @@ static void cardhu_sata_init(void) { } static void __init tegra_cardhu_init(void) { + struct board_info board_info; + + tegra_get_board_info(&board_info); tegra_thermal_init(&thermal_data, throttle_list, ARRAY_SIZE(throttle_list)); @@ -1442,6 +1504,17 @@ static void __init tegra_cardhu_init(void) cardhu_uart_init(); tegra_camera_init(); platform_add_devices(cardhu_devices, ARRAY_SIZE(cardhu_devices)); + switch (board_info.board_id) { + case BOARD_PM315: + platform_add_devices(beaver_audio_devices, + ARRAY_SIZE(beaver_audio_devices)); + break; + default: + platform_add_devices(cardhu_audio_devices, + ARRAY_SIZE(cardhu_audio_devices)); + + break; + } tegra_ram_console_debug_init(); tegra_io_dpd_init(); cardhu_sdhci_init(); @@ -1457,7 +1530,13 @@ static void __init tegra_cardhu_init(void) cardhu_pmon_init(); cardhu_sensors_init(); cardhu_setup_bluesleep(); - cardhu_sata_init(); + /* + * if you want to add support for SATA in your board + * then add your board check here like + * board_info.board_id == BOARD_E1186 + */ + if (board_info.board_id == BOARD_PM315) + cardhu_sata_init(); //audio_wired_jack_init(); cardhu_pins_state_init(); cardhu_emc_init(); diff --git a/arch/arm/mach-tegra/board-cardhu.h b/arch/arm/mach-tegra/board-cardhu.h index 3dd3d23d9414..2f8e21a6492d 100644 --- a/arch/arm/mach-tegra/board-cardhu.h +++ b/arch/arm/mach-tegra/board-cardhu.h @@ -38,6 +38,7 @@ #define BOARD_E1208 0x0C08 #define BOARD_PM305 0x0305 #define BOARD_PM311 0x030B +#define BOARD_PM315 0x030F #define BOARD_PMU_PM298 0x0262 #define BOARD_PMU_PM299 0x0263 @@ -162,6 +163,12 @@ #define TEGRA_GPIO_SPKR_EN CARDHU_GPIO_WM8903(2) #define TEGRA_GPIO_HP_DET TEGRA_GPIO_PW2 +/* PM315 Realtek audio related GPIOs */ +#define TEGRA_GPIO_RTL_CDC_IRQ TEGRA_GPIO_PX3 +#define TEGRA_GPIO_RTL_SPKR_EN -1 +#define TEGRA_GPIO_RTL_HP_DET TEGRA_GPIO_PW2 +#define TEGRA_GPIO_RTL_INT_MIC_EN TEGRA_GPIO_PK3 + /* CAMERA RELATED GPIOs on CARDHU */ #define OV5650_RESETN_GPIO TEGRA_GPIO_PBB0 #define CAM1_POWER_DWN_GPIO TEGRA_GPIO_PBB5 diff --git a/arch/arm/mach-tegra/board-ventana-panel.c b/arch/arm/mach-tegra/board-ventana-panel.c index 56a222432fe0..d2d89392e1c5 100644 --- a/arch/arm/mach-tegra/board-ventana-panel.c +++ b/arch/arm/mach-tegra/board-ventana-panel.c @@ -433,9 +433,16 @@ int __init ventana_panel_init(void) tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start, min(tegra_fb_size, tegra_bootloader_fb_size)); - /* Copy the bootloader fb to the fb2. */ - tegra_move_framebuffer(tegra_fb2_start, tegra_bootloader_fb_start, - min(tegra_fb2_size, tegra_bootloader_fb_size)); + /* + * If the bootloader fb2 is valid, copy it to the fb2, or else + * clear fb2 to avoid garbage on dispaly2. + */ + if (tegra_bootloader_fb2_size) + tegra_move_framebuffer(tegra_fb2_start, + tegra_bootloader_fb2_start, + min(tegra_fb2_size, tegra_bootloader_fb2_size)); + else + tegra_clear_framebuffer(tegra_fb2_start, tegra_fb2_size); #if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC) diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 85d02d501b63..6aaba219c916 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -102,12 +102,15 @@ void __init tegra_protected_aperture_init(unsigned long aperture); int __init tegra_init_board_info(void); void tegra_move_framebuffer(unsigned long to, unsigned long from, unsigned long size); +void tegra_clear_framebuffer(unsigned long to, unsigned long size); bool is_tegra_debug_uartport_hs(void); int get_tegra_uart_debug_port_id(void); int arb_lost_recovery(int scl_gpio, int sda_gpio); extern unsigned long tegra_bootloader_fb_start; extern unsigned long tegra_bootloader_fb_size; +extern unsigned long tegra_bootloader_fb2_start; +extern unsigned long tegra_bootloader_fb2_size; extern unsigned long tegra_fb_start; extern unsigned long tegra_fb_size; extern unsigned long tegra_fb2_start; diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index a34d37f2f250..d5acd8bdea76 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -85,6 +85,8 @@ unsigned long tegra_bootloader_fb_start; unsigned long tegra_bootloader_fb_size; +unsigned long tegra_bootloader_fb2_start; +unsigned long tegra_bootloader_fb2_size; unsigned long tegra_fb_start; unsigned long tegra_fb_size; unsigned long tegra_fb2_start; @@ -497,6 +499,21 @@ static int __init tegra_bootloader_fb_arg(char *options) } early_param("tegra_fbmem", tegra_bootloader_fb_arg); +static int __init tegra_bootloader_fb2_arg(char *options) +{ + char *p = options; + + tegra_bootloader_fb2_size = memparse(p, &p); + if (*p == '@') + tegra_bootloader_fb2_start = memparse(p+1, &p); + + pr_info("Found tegra_fbmem2: %08lx@%08lx\n", + tegra_bootloader_fb2_size, tegra_bootloader_fb2_start); + + return 0; +} +early_param("tegra_fbmem2", tegra_bootloader_fb2_arg); + /* To specify NVIDIA carveout memory */ static int __init parse_nvmem(char *p) { @@ -877,6 +894,30 @@ out: iounmap(to_io); } +void tegra_clear_framebuffer(unsigned long to, unsigned long size) +{ + void __iomem *to_io; + unsigned long i; + + BUG_ON(PAGE_ALIGN((unsigned long)to) != (unsigned long)to); + BUG_ON(PAGE_ALIGN(size) != size); + + to_io = ioremap(to, size); + if (!to_io) { + pr_err("%s: Failed to map target framebuffer\n", __func__); + return; + } + + if (pfn_valid(page_to_pfn(phys_to_page(to)))) { + for (i = 0 ; i < size; i += PAGE_SIZE) + memset(to_io + i, 0, PAGE_SIZE); + } else { + for (i = 0; i < size; i += 4) + writel(0, to_io + i); + } + iounmap(to_io); +} + void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size, unsigned long fb2_size) { @@ -961,19 +1002,36 @@ void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size, } } + if (tegra_bootloader_fb2_size) { + tegra_bootloader_fb2_size = + PAGE_ALIGN(tegra_bootloader_fb2_size); + if (memblock_reserve(tegra_bootloader_fb2_start, + tegra_bootloader_fb2_size)) { + pr_err("Failed to reserve bootloader frame buffer2 " + "%08lx@%08lx\n", tegra_bootloader_fb2_size, + tegra_bootloader_fb2_start); + tegra_bootloader_fb2_start = 0; + tegra_bootloader_fb2_size = 0; + } + } + pr_info("Tegra reserved memory:\n" - "LP0: %08lx - %08lx\n" - "Bootloader framebuffer: %08lx - %08lx\n" - "Framebuffer: %08lx - %08lx\n" - "2nd Framebuffer: %08lx - %08lx\n" - "Carveout: %08lx - %08lx\n" - "Vpr: %08lx - %08lx\n", + "LP0: %08lx - %08lx\n" + "Bootloader framebuffer: %08lx - %08lx\n" + "Bootloader framebuffer2: %08lx - %08lx\n" + "Framebuffer: %08lx - %08lx\n" + "2nd Framebuffer: %08lx - %08lx\n" + "Carveout: %08lx - %08lx\n" + "Vpr: %08lx - %08lx\n", tegra_lp0_vec_start, tegra_lp0_vec_size ? tegra_lp0_vec_start + tegra_lp0_vec_size - 1 : 0, tegra_bootloader_fb_start, tegra_bootloader_fb_size ? - tegra_bootloader_fb_start + tegra_bootloader_fb_size - 1 : 0, + tegra_bootloader_fb_start + tegra_bootloader_fb_size - 1 : 0, + tegra_bootloader_fb2_start, + tegra_bootloader_fb2_size ? + tegra_bootloader_fb2_start + tegra_bootloader_fb2_size - 1 : 0, tegra_fb_start, tegra_fb_size ? tegra_fb_start + tegra_fb_size - 1 : 0, @@ -1040,6 +1098,10 @@ void __init tegra_release_bootloader_fb(void) if (memblock_free(tegra_bootloader_fb_start, tegra_bootloader_fb_size)) pr_err("Failed to free bootloader fb.\n"); + if (tegra_bootloader_fb2_size) + if (memblock_free(tegra_bootloader_fb2_start, + tegra_bootloader_fb2_size)) + pr_err("Failed to free bootloader fb2.\n"); } #ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND diff --git a/arch/arm/mach-tegra/p852/board-p852-power.c b/arch/arm/mach-tegra/p852/board-p852-power.c index 949537ed5d94..e69f98c8cba9 100644 --- a/arch/arm/mach-tegra/p852/board-p852-power.c +++ b/arch/arm/mach-tegra/p852/board-p852-power.c @@ -173,11 +173,12 @@ static void __init tps6586x_rtc_preinit(void) system_rev == P852_SKU5_C01) { for (i = 0; i < tps_platform.num_subdevs; ++i) if (!strcmp(tps_platform.subdevs[i].name, - "tps6586x-rtc")) + "tps6586x-rtc")) { rtc_pdata = (struct tps6586x_rtc_platform_data *) (tps_platform.subdevs[i].platform_data); rtc_pdata->cl_sel = TPS6586X_RTC_CL_SEL_1_5PF; + } } } diff --git a/drivers/ata/ahci-tegra.c b/drivers/ata/ahci-tegra.c index df524ff72cc8..c90472827b9b 100644 --- a/drivers/ata/ahci-tegra.c +++ b/drivers/ata/ahci-tegra.c @@ -258,10 +258,10 @@ static int tegra_ahci_controller_resume(struct platform_device *pdev); static int tegra_ahci_suspend(struct platform_device *pdev, pm_message_t mesg); static int tegra_ahci_resume(struct platform_device *pdev); static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap); -//static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap); static bool tegra_ahci_are_all_ports_idle(struct ata_host *host); -//static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host); #ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE +static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap); +static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host); static unsigned int tegra_ahci_qc_issue(struct ata_queued_cmd *qc); static int tegra_ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); @@ -926,7 +926,7 @@ static int tegra_ahci_controller_resume(struct platform_device *pdev) } spin_lock_irqsave(&host->lock, flags); - if (!tegra_hpriv->pg_state == SATA_ON) { + if (tegra_hpriv->pg_state == SATA_ON) { dev_dbg(host->dev, "resume: SATA already powered on\n"); } else { dev_dbg(host->dev, "resume: powering on SATA...\n"); @@ -1623,7 +1623,20 @@ static enum port_idle_status tegra_ahci_is_port_idle(struct ata_port *ap) return PORT_IS_IDLE; } -#if 0 +/* check if all supported ports are idle (no outstanding commands) */ +static bool tegra_ahci_are_all_ports_idle(struct ata_host *host) +{ int i; + struct ata_port *ap; + + for (i = 0; i < host->n_ports; i++) { + ap = host->ports[i]; + if (ap && (tegra_ahci_is_port_idle(ap) == PORT_IS_NOT_IDLE)) + return false; + } + return true; +} + +#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); @@ -1638,22 +1651,7 @@ static enum port_idle_status tegra_ahci_is_port_slumber(struct ata_port *ap) return PORT_IS_SLUMBER; return PORT_IS_IDLE_NOT_SLUMBER; } -#endif -/* check if all supported ports are idle (no outstanding commands) */ -static bool tegra_ahci_are_all_ports_idle(struct ata_host *host) -{ int i; - struct ata_port *ap; - - for (i = 0; i < host->n_ports; i++) { - ap = host->ports[i]; - if (ap && (tegra_ahci_is_port_idle(ap) == PORT_IS_NOT_IDLE)) - return false; - } - return true; -} - -#if 0 /* check if all supported ports are in slumber */ static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host) { int i; @@ -1666,9 +1664,7 @@ static bool tegra_ahci_are_all_ports_slumber(struct ata_host *host) } return true; } -#endif -#ifdef CONFIG_TEGRA_SATA_IDLE_POWERGATE static void tegra_ahci_to_add_idle_timer(struct ata_host *host) { struct tegra_ahci_host_priv *tegra_hpriv; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 38719599a476..651a17e04fd0 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -66,6 +66,13 @@ config VIDEOBUF2_DMA_SG select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS tristate + +config VIDEOBUF2_DMA_NVMAP + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + select NVMAP_ALLOW_SYSMEM + tristate + # # Multimedia Video device configuration # @@ -848,6 +855,12 @@ config SOC_CAMERA_OV2640 help This is a ov2640 camera driver +config SOC_CAMERA_OV5640 + tristate "ov5640 camera support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV5640 sensor + config SOC_CAMERA_OV5642 tristate "ov5642 camera support" depends on SOC_CAMERA && I2C @@ -921,6 +934,13 @@ config VIDEO_SH_MOBILE_CEU ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface +config VIDEO_TEGRA + tristate "Tegra soc_camera host driver" + depends on VIDEO_DEV && ARCH_TEGRA && SOC_CAMERA && HAS_DMA && HAVE_CLK + select VIDEOBUF2_DMA_NVMAP + ---help--- + This is a v4l2 driver for the Tegra camera interface + config VIDEO_OMAP1 tristate "OMAP1 Camera Interface driver" depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 225f8823de2b..f63ad7e88fda 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o +obj-$(CONFIG_SOC_CAMERA_OV5640) += ov5640.o obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o @@ -122,6 +123,7 @@ obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o +obj-$(CONFIG_VIDEOBUF2_DMA_NVMAP) += videobuf2-dma-nvmap.o obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o @@ -168,6 +170,7 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_VIDEO_TEGRA) += tegra_v4l2_camera.o obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o @@ -194,3 +197,4 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/video/tegra/host diff --git a/drivers/media/video/ov5640.c b/drivers/media/video/ov5640.c new file mode 100644 index 000000000000..8e45fa3de5d2 --- /dev/null +++ b/drivers/media/video/ov5640.c @@ -0,0 +1,1151 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#define to_ov5640(sd) container_of(sd, struct ov5640_priv, subdev) + +#define OV5640_SYSTEM_CTRL 0x3008 +#define OV5640_CHIP_ID_HI 0x300a +#define OV5640_CHIP_ID_LO 0x300b +#define OV5640_PAD_OUTPUT_ENABLE00 0x3016 +#define OV5640_PAD_OUTPUT_ENABLE01 0x3017 +#define OV5640_PAD_OUTPUT_ENABLE02 0x3018 +#define OV5640_SC_PLL_CTRL0 0x3034 +#define OV5640_SC_PLL_CTRL1 0x3035 +#define OV5640_SC_PLL_CTRL2 0x3036 +#define OV5640_SC_PLL_CTRL3 0x3037 + +/* SCCB Control */ +#define OV5640_SCCB_SYSTEM_CTRL1 0x3103 +#define OV5640_SYSTEM_ROOT_DIVIDER 0x3108 + +/* Timing Control */ +#define OV5640_TIMING_HS_HI 0x3800 +#define OV5640_TIMING_HS_LO 0x3801 +#define OV5640_TIMING_VS_HI 0x3802 +#define OV5640_TIMING_VS_LO 0x3803 +#define OV5640_TIMING_HW_HI 0x3804 +#define OV5640_TIMING_HW_LO 0x3805 +#define OV5640_TIMING_VH_HI 0x3806 +#define OV5640_TIMING_VH_LO 0x3807 +#define OV5640_TIMING_DVPHO_HI 0x3808 +#define OV5640_TIMING_DVPHO_LO 0x3809 +#define OV5640_TIMING_DVPVO_HI 0x380a +#define OV5640_TIMING_DVPVO_LO 0x380b +#define OV5640_TIMING_HTS_HI 0x380c +#define OV5640_TIMING_HTS_LO 0x380d +#define OV5640_TIMING_VTS_HI 0x380e +#define OV5640_TIMING_VTS_LO 0x380f +#define OV5640_TIMING_HOFFSET_HI 0x3810 +#define OV5640_TIMING_HOFFSET_LO 0x3811 +#define OV5640_TIMING_VOFFSET_HI 0x3812 +#define OV5640_TIMING_VOFFSET_LO 0x3813 +#define OV5640_TIMING_X_INC 0x3814 +#define OV5640_TIMING_Y_INC 0x3815 +#define OV5640_TIMING_TC_REG20 0x3820 +#define OV5640_TIMING_TC_REG21 0x3821 + +/* AEC/AGC Power Down Domain Control */ +#define OV5640_AEC_MAX_EXPO_60HZ_HI 0x3a02 +#define OV5640_AEC_MAX_EXPO_60HZ_LO 0x3a03 +#define OV5640_AEC_B50_STEP_HI 0x3a08 +#define OV5640_AEC_B50_STEP_LO 0x3a09 +#define OV5640_AEC_B60_STEP_HI 0x3a0a +#define OV5640_AEC_B60_STEP_LO 0x3a0b +#define OV5640_AEC_CTRL0C 0x3a0c +#define OV5640_AEC_CTRL0D 0x3a0d +#define OV5640_AEC_CTRL0E 0x3a0e +#define OV5640_AEC_CTRL0F 0x3a0f +#define OV5640_AEC_CTRL10 0x3a10 +#define OV5640_AEC_CTRL11 0x3a11 +#define OV5640_AEC_CTRL12 0x3a12 +#define OV5640_AEC_CTRL13 0x3a13 +#define OV5640_AEC_MAX_EXPO_50HZ_HI 0x3a14 +#define OV5640_AEC_MAX_EXPO_50HZ_LO 0x3a15 +#define OV5640_AEC_GAIN_CEILING_HI 0x3a18 +#define OV5640_AEC_GAIN_CEILING_LO 0x3a19 +#define OV5640_AEC_CTRL1B 0x3a1b +#define OV5640_AEC_CTRL1E 0x3a1e +#define OV5640_AEC_CTRL1F 0x3a1f + +/* 50/60Hz Detector Control */ +#define OV5640_5060HZ_CTRL00 0x3c00 +#define OV5640_5060HZ_CTRL01 0x3c01 +#define OV5640_5060HZ_CTRL02 0x3c02 +#define OV5640_5060HZ_CTRL03 0x3c03 +#define OV5640_5060HZ_CTRL04 0x3c04 +#define OV5640_5060HZ_CTRL05 0x3c05 +#define OV5640_LIGHT_METER1_THRESHOLD_HI 0x3c06 +#define OV5640_LIGHT_METER1_THRESHOLD_LO 0x3c07 +#define OV5640_LIGHT_METER2_THRESHOLD_HI 0x3c08 +#define OV5640_LIGHT_METER2_THRESHOLD_LO 0x3c09 +#define OV5640_SAMPLE_NUMBER_HI 0x3c0a +#define OV5640_SAMPLE_NUMBER_LO 0x3c0b + +/* ISP General Controls */ +#define OV5640_ISP_CTRL00 0x5000 +#define OV5640_ISP_CTRL01 0x5001 +#define OV5640_ISP_CTRL37 0x5025 + +/* AWB Control */ +#define OV5640_AWB_CTRL00 0x5180 +#define OV5640_AWB_CTRL01 0x5181 +#define OV5640_AWB_CTRL02 0x5182 +#define OV5640_AWB_CTRL03 0x5183 +#define OV5640_AWB_CTRL04 0x5184 +#define OV5640_AWB_CTRL05 0x5185 +#define OV5640_AWB_CTRL06 0x5186 +#define OV5640_AWB_CTRL07 0x5187 +#define OV5640_AWB_CTRL08 0x5188 +#define OV5640_AWB_CTRL09 0x5189 +#define OV5640_AWB_CTRL10 0x518a +#define OV5640_AWB_CTRL11 0x518b +#define OV5640_AWB_CTRL12 0x518c +#define OV5640_AWB_CTRL13 0x518d +#define OV5640_AWB_CTRL14 0x518e +#define OV5640_AWB_CTRL15 0x518f +#define OV5640_AWB_CTRL16 0x5190 +#define OV5640_AWB_CTRL17 0x5191 +#define OV5640_AWB_CTRL18 0x5192 +#define OV5640_AWB_CTRL19 0x5193 +#define OV5640_AWB_CTRL20 0x5194 +#define OV5640_AWB_CTRL21 0x5195 +#define OV5640_AWB_CTRL22 0x5196 +#define OV5640_AWB_CTRL23 0x5197 +#define OV5640_AWB_CTRL24 0x5198 +#define OV5640_AWB_CTRL25 0x5199 +#define OV5640_AWB_CTRL26 0x519a +#define OV5640_AWB_CTRL27 0x519b +#define OV5640_AWB_CTRL28 0x519c +#define OV5640_AWB_CTRL29 0x519d +#define OV5640_AWB_CTRL30 0x519e + +/* CIP Control */ +#define OV5640_CIP_SHARPENMT_THRESHOLD_1 0x5300 +#define OV5640_CIP_SHARPENMT_THRESHOLD_2 0x5301 +#define OV5640_CIP_SHARPENMT_OFFSET_1 0x5302 +#define OV5640_CIP_SHARPENMT_OFFSET_2 0x5303 +#define OV5640_CIP_DNS_THRESHOLD_1 0x5304 +#define OV5640_CIP_DNS_THRESHOLD_2 0x5305 +#define OV5640_CIP_DNS_OFFSET_1 0x5306 +#define OV5640_CIP_DNS_OFFSET_2 0x5307 +#define OV5640_CIP_CTRL 0x5308 +#define OV5640_CIP_SHARPENTH_THRESHOLD_1 0x5309 +#define OV5640_CIP_SHARPENTH_THRESHOLD_2 0x530a +#define OV5640_CIP_SHARPENTH_OFFSET_1 0x530b +#define OV5640_CIP_SHARPENTH_OFFSET_2 0x530c +#define OV5640_CIP_EDGE_MT_AUTO 0x530d +#define OV5640_CIP_DNS_THRESHOLD_AUTO 0x530e +#define OF5640_CIP_SHARPEN_THRESHOLD_AUTO 0x530f + +/* CMX Control */ +#define OV5640_CMX_CTRL 0x5380 +#define OV5640_CMX1 0x5381 +#define OV5640_CMX2 0x5382 +#define OV5640_CMX3 0x5383 +#define OV5640_CMX4 0x5384 +#define OV5640_CMX5 0x5385 +#define OV5640_CMX6 0x5386 +#define OV5640_CMX7 0x5387 +#define OV5640_CMX8 0x5388 +#define OV5640_CMX9 0x5389 +#define OV5640_CMXSIGN_HI 0x538a +#define OV5640_CMXSIGN_LO 0x538b + +/* Gamma Control */ +#define OV5640_GAMMA_CTRL00 0x5480 +#define OV5640_YST00 0x5481 +#define OV5640_YST01 0x5482 +#define OV5640_YST02 0x5483 +#define OV5640_YST03 0x5484 +#define OV5640_YST04 0x5485 +#define OV5640_YST05 0x5486 +#define OV5640_YST06 0x5487 +#define OV5640_YST07 0x5488 +#define OV5640_YST08 0x5489 +#define OV5640_YST09 0x548a +#define OV5640_YST0A 0x548b +#define OV5640_YST0B 0x548c +#define OV5640_YST0C 0x548d +#define OV5640_YST0D 0x548e +#define OV5640_YST0E 0x548f +#define OV5640_YST0F 0x5490 + +/* SDE Control */ +#define OV5640_SDE_CTRL_0 0x5580 +#define OV5640_SDE_CTRL_1 0x5581 +#define OV5640_SDE_CTRL_2 0x5582 +#define OV5640_SDE_CTRL_3 0x5583 +#define OV5640_SDE_CTRL_4 0x5584 +#define OV5640_SDE_CTRL_5 0x5585 +#define OV5640_SDE_CTRL_6 0x5586 +#define OV5640_SDE_CTRL_7 0x5587 +#define OV5640_SDE_CTRL_8 0x5588 +#define OV5640_SDE_CTRL_9 0x5589 +#define OV5640_SDE_CTRL_10 0x558a +#define OV5640_SDE_CTRL_11 0x558b +#define OV5640_SDE_CTRL_12 0x558c + +/* LENC Control */ +#define OV5640_GMTRX00 0x5800 +#define OV5640_GMTRX01 0x5801 +#define OV5640_GMTRX02 0x5802 +#define OV5640_GMTRX03 0x5803 +#define OV5640_GMTRX04 0x5804 +#define OV5640_GMTRX05 0x5805 +#define OV5640_GMTRX10 0x5806 +#define OV5640_GMTRX11 0x5807 +#define OV5640_GMTRX12 0x5808 +#define OV5640_GMTRX13 0x5809 +#define OV5640_GMTRX14 0x580a +#define OV5640_GMTRX15 0x580b +#define OV5640_GMTRX20 0x580c +#define OV5640_GMTRX21 0x580d +#define OV5640_GMTRX22 0x580e +#define OV5640_GMTRX23 0x580f +#define OV5640_GMTRX24 0x5810 +#define OV5640_GMTRX25 0x5811 +#define OV5640_GMTRX30 0x5812 +#define OV5640_GMTRX31 0x5813 +#define OV5640_GMTRX32 0x5814 +#define OV5640_GMTRX33 0x5815 +#define OV5640_GMTRX34 0x5816 +#define OV5640_GMTRX35 0x5817 +#define OV5640_GMTRX40 0x5818 +#define OV5640_GMTRX41 0x5819 +#define OV5640_GMTRX42 0x581a +#define OV5640_GMTRX43 0x581b +#define OV5640_GMTRX44 0x581c +#define OV5640_GMTRX45 0x581d +#define OV5640_GMTRX50 0x581e +#define OV5640_GMTRX51 0x581f +#define OV5640_GMTRX52 0x5820 +#define OV5640_GMTRX53 0x5821 +#define OV5640_GMTRX54 0x5822 +#define OV5640_GMTRX55 0x5823 +#define OV5640_BRMATRX00 0x5824 +#define OV5640_BRMATRX01 0x5825 +#define OV5640_BRMATRX02 0x5826 +#define OV5640_BRMATRX03 0x5827 +#define OV5640_BRMATRX04 0x5828 +#define OV5640_BRMATRX05 0x5829 +#define OV5640_BRMATRX06 0x582a +#define OV5640_BRMATRX07 0x582b +#define OV5640_BRMATRX08 0x582c +#define OV5640_BRMATRX09 0x582d +#define OV5640_BRMATRX20 0x582e +#define OV5640_BRMATRX21 0x582f +#define OV5640_BRMATRX22 0x5830 +#define OV5640_BRMATRX23 0x5831 +#define OV5640_BRMATRX24 0x5832 +#define OV5640_BRMATRX30 0x5833 +#define OV5640_BRMATRX31 0x5834 +#define OV5640_BRMATRX32 0x5835 +#define OV5640_BRMATRX33 0x5836 +#define OV5640_BRMATRX34 0x5837 +#define OV5640_BRMATRX40 0x5838 +#define OV5640_BRMATRX41 0x5839 +#define OV5640_BRMATRX42 0x583a +#define OV5640_BRMATRX43 0x583b +#define OV5640_BRMATRX44 0x583c +#define OV5640_LENC_BR_OFFSET 0x583d + +#define OV5640_MAX_WIDTH 640 +#define OV5640_MAX_HEIGHT 480 + +/* Misc. structures */ +struct ov5640_reg { + u16 reg; + u8 val; +}; + +struct ov5640_priv { + struct v4l2_subdev subdev; + + int ident; + u16 chip_id; + u8 revision; + u8 manid; + u8 smiaver; + + bool flag_vflip; + bool flag_hflip; + + /* For suspend/resume. */ + struct v4l2_mbus_framefmt current_mf; + bool current_enable; +}; + +static const struct ov5640_reg ov5640_defaults[] = { + { OV5640_SCCB_SYSTEM_CTRL1, 0x11}, + { OV5640_SYSTEM_CTRL, 0x82}, + { OV5640_SYSTEM_CTRL, 0x42}, + { OV5640_SCCB_SYSTEM_CTRL1, 0x03}, + { OV5640_PAD_OUTPUT_ENABLE01, 0x00}, + { OV5640_PAD_OUTPUT_ENABLE02, 0x00}, + { OV5640_SC_PLL_CTRL0, 0x18}, + { OV5640_SC_PLL_CTRL1, 0x14}, + { OV5640_SC_PLL_CTRL2, 0x38}, + { OV5640_SC_PLL_CTRL3, 0x13}, + { 0x4800, 0x24}, /* noncontinuous clock */ + { OV5640_SYSTEM_ROOT_DIVIDER, 0x01}, + { 0x3630, 0x36}, + { 0x3631, 0x0e}, + { 0x3632, 0xe2}, + { 0x3633, 0x12}, + { 0x3621, 0xe0}, + { 0x3704, 0xa0}, + { 0x3703, 0x5a}, + { 0x3715, 0x78}, + { 0x3717, 0x01}, + { 0x370b, 0x60}, + { 0x3705, 0x1a}, + { 0x3905, 0x02}, + { 0x3906, 0x10}, + { 0x3901, 0x0a}, + { 0x3731, 0x12}, + { 0x3600, 0x08}, + { 0x3601, 0x33}, + { 0x302d, 0x60}, + { 0x3620, 0x52}, + { 0x371b, 0x20}, + { 0x471c, 0x50}, + { 0x3a13, 0x43}, + { 0x3a18, 0x00}, + { 0x3a19, 0xf8}, + { 0x3635, 0x13}, + { 0x3636, 0x03}, + { 0x3634, 0x40}, + { 0x3622, 0x01}, + { 0x3c01, 0x34}, + { 0x3c04, 0x28}, + { 0x3c05, 0x98}, + { 0x3c06, 0x00}, + { 0x3c07, 0x08}, + { 0x3c08, 0x00}, + { 0x3c09, 0x1c}, + { 0x3c0a, 0x9c}, + { 0x3c0b, 0x40}, + { OV5640_TIMING_TC_REG20, 0x41}, + { OV5640_TIMING_TC_REG21, 0x01}, + { 0x3814, 0x31}, + { 0x3815, 0x31}, + { 0x3800, 0x00}, + { 0x3801, 0x00}, + { 0x3802, 0x00}, + { 0x3803, 0x04}, + { 0x3804, 0x0a}, + { 0x3805, 0x3f}, + { 0x3806, 0x07}, + { 0x3807, 0x9b}, + { 0x3808, 0x02}, + { 0x3809, 0x80}, + { 0x380a, 0x01}, + { 0x380b, 0xe0}, + { 0x380c, 0x07}, + { 0x380d, 0x68}, + { 0x380e, 0x03}, + { 0x380f, 0xd8}, + { 0x3810, 0x00}, + { 0x3811, 0x10}, + { 0x3812, 0x00}, + { 0x3813, 0x06}, + { 0x3618, 0x00}, + { 0x3612, 0x29}, + { 0x3708, 0x64}, + { 0x3709, 0x52}, + { 0x370c, 0x03}, + + /* AEC/AGC Power Down Domain Control */ + { OV5640_AEC_MAX_EXPO_60HZ_HI, 0x03}, + { OV5640_AEC_MAX_EXPO_60HZ_LO, 0xd8}, + { OV5640_AEC_B50_STEP_HI, 0x01}, + { OV5640_AEC_B50_STEP_LO, 0x27}, + { OV5640_AEC_B60_STEP_HI, 0x00}, + { OV5640_AEC_B60_STEP_LO, 0xf6}, + { OV5640_AEC_CTRL0E, 0x03}, + { OV5640_AEC_CTRL0D, 0x04}, + { OV5640_AEC_MAX_EXPO_50HZ_HI, 0x03}, + { OV5640_AEC_MAX_EXPO_50HZ_LO, 0xd8}, + + { 0x4001, 0x02}, + { 0x4004, 0x02}, + { 0x3000, 0x00}, + { 0x3002, 0x1c}, + { 0x3004, 0xff}, + { 0x3006, 0xc3}, + { 0x300e, 0x45}, + { 0x302e, 0x08}, + /* org:30 bit[3:0] + 0x0:YUYV 0x1:YVYU 0x2:UYVY + 0x3:VYUY 0xF:UYVY 0x4~0xE:Not-allowed + */ + { 0x4300, 0x32}, + { 0x501f, 0x00}, + { 0x4713, 0x03}, + { 0x4407, 0x04}, + { 0x440e, 0x00}, + { 0x460b, 0x35}, + { 0x460c, 0x22}, + { 0x4837, 0x44}, + { 0x3824, 0x02}, + { 0x5000, 0xa7}, + { 0x5001, 0xa3}, + + /* AWB Control */ + { OV5640_AWB_CTRL00, 0xff}, { OV5640_AWB_CTRL01, 0xf2}, + { OV5640_AWB_CTRL02, 0x00}, { OV5640_AWB_CTRL03, 0x14}, + { OV5640_AWB_CTRL04, 0x25}, { OV5640_AWB_CTRL05, 0x24}, + { OV5640_AWB_CTRL06, 0x09}, { OV5640_AWB_CTRL07, 0x09}, + { OV5640_AWB_CTRL08, 0x09}, { OV5640_AWB_CTRL09, 0x75}, + { OV5640_AWB_CTRL10, 0x54}, { OV5640_AWB_CTRL11, 0xe0}, + { OV5640_AWB_CTRL12, 0xb2}, { OV5640_AWB_CTRL13, 0x42}, + { OV5640_AWB_CTRL14, 0x3d}, { OV5640_AWB_CTRL15, 0x56}, + { OV5640_AWB_CTRL16, 0x46}, { OV5640_AWB_CTRL17, 0xf8}, + { OV5640_AWB_CTRL18, 0x04}, { OV5640_AWB_CTRL19, 0x70}, + { OV5640_AWB_CTRL20, 0xf0}, { OV5640_AWB_CTRL21, 0xf0}, + { OV5640_AWB_CTRL22, 0x03}, { OV5640_AWB_CTRL23, 0x01}, + { OV5640_AWB_CTRL24, 0x04}, { OV5640_AWB_CTRL25, 0x12}, + { OV5640_AWB_CTRL26, 0x04}, { OV5640_AWB_CTRL27, 0x00}, + { OV5640_AWB_CTRL28, 0x06}, { OV5640_AWB_CTRL29, 0x82}, + { OV5640_AWB_CTRL30, 0x38}, + + /* CMX Control */ + { OV5640_CMX1, 0x1e}, + { OV5640_CMX2, 0x5b}, + { OV5640_CMX3, 0x08}, + { OV5640_CMX4, 0x0a}, + { OV5640_CMX5, 0x7e}, + { OV5640_CMX6, 0x88}, + { OV5640_CMX7, 0x7c}, + { OV5640_CMX8, 0x6c}, + { OV5640_CMX9, 0x10}, + { OV5640_CMXSIGN_HI, 0x01}, + { OV5640_CMXSIGN_LO, 0x98}, + + /* CIP Control */ + { OV5640_CIP_SHARPENMT_THRESHOLD_1, 0x08}, + { OV5640_CIP_SHARPENMT_THRESHOLD_2, 0x30}, + { OV5640_CIP_SHARPENMT_OFFSET_1, 0x10}, + { OV5640_CIP_SHARPENMT_OFFSET_2, 0x00}, + { OV5640_CIP_DNS_THRESHOLD_1, 0x08}, + { OV5640_CIP_DNS_THRESHOLD_2, 0x30}, + { OV5640_CIP_DNS_OFFSET_1, 0x08}, + { OV5640_CIP_DNS_OFFSET_2, 0x16}, + { OV5640_CIP_SHARPENTH_THRESHOLD_1, 0x08}, + { OV5640_CIP_SHARPENTH_THRESHOLD_2, 0x30}, + { OV5640_CIP_SHARPENTH_OFFSET_1, 0x04}, + { OV5640_CIP_SHARPENTH_OFFSET_2, 0x06}, + + /* Gamma Control */ + { OV5640_GAMMA_CTRL00, 0x01}, + { OV5640_YST00, 0x08}, { OV5640_YST01, 0x14}, + { OV5640_YST02, 0x28}, { OV5640_YST03, 0x51}, + { OV5640_YST04, 0x65}, { OV5640_YST05, 0x71}, + { OV5640_YST06, 0x7d}, { OV5640_YST07, 0x87}, + { OV5640_YST08, 0x91}, { OV5640_YST09, 0x9a}, + { OV5640_YST0A, 0xaa}, { OV5640_YST0B, 0xb8}, + { OV5640_YST0C, 0xcd}, { OV5640_YST0D, 0xdd}, + { OV5640_YST0E, 0xea}, { OV5640_YST0F, 0x1d}, + + /* SDE Control */ + { OV5640_SDE_CTRL_0, 0x02}, + { OV5640_SDE_CTRL_3, 0x40}, + { OV5640_SDE_CTRL_4, 0x10}, + { OV5640_SDE_CTRL_9, 0x10}, + { OV5640_SDE_CTRL_10, 0x00}, + { OV5640_SDE_CTRL_11, 0xf8}, + + /* LENC Control */ + { OV5640_GMTRX00, 0x23}, { OV5640_GMTRX01, 0x14}, + { OV5640_GMTRX02, 0x0f}, { OV5640_GMTRX03, 0x0f}, + { OV5640_GMTRX04, 0x12}, { OV5640_GMTRX05, 0x26}, + { OV5640_GMTRX10, 0x0c}, { OV5640_GMTRX11, 0x08}, + { OV5640_GMTRX12, 0x05}, { OV5640_GMTRX13, 0x05}, + { OV5640_GMTRX14, 0x08}, { OV5640_GMTRX15, 0x0d}, + { OV5640_GMTRX20, 0x08}, { OV5640_GMTRX21, 0x03}, + { OV5640_GMTRX22, 0x00}, { OV5640_GMTRX23, 0x00}, + { OV5640_GMTRX24, 0x03}, { OV5640_GMTRX25, 0x09}, + { OV5640_GMTRX30, 0x07}, { OV5640_GMTRX31, 0x03}, + { OV5640_GMTRX32, 0x00}, { OV5640_GMTRX33, 0x01}, + { OV5640_GMTRX34, 0x03}, { OV5640_GMTRX35, 0x08}, + { OV5640_GMTRX40, 0x0d}, { OV5640_GMTRX41, 0x08}, + { OV5640_GMTRX42, 0x05}, { OV5640_GMTRX43, 0x06}, + { OV5640_GMTRX44, 0x08}, { OV5640_GMTRX45, 0x0e}, + { OV5640_GMTRX50, 0x29}, { OV5640_GMTRX51, 0x17}, + { OV5640_GMTRX52, 0x11}, { OV5640_GMTRX53, 0x11}, + { OV5640_GMTRX54, 0x15}, { OV5640_GMTRX55, 0x28}, + { OV5640_BRMATRX00, 0x46}, { OV5640_BRMATRX01, 0x26}, + { OV5640_BRMATRX02, 0x08}, { OV5640_BRMATRX03, 0x26}, + { OV5640_BRMATRX04, 0x64}, { OV5640_BRMATRX05, 0x26}, + { OV5640_BRMATRX06, 0x24}, { OV5640_BRMATRX07, 0x22}, + { OV5640_BRMATRX08, 0x24}, { OV5640_BRMATRX09, 0x24}, + { OV5640_BRMATRX20, 0x06}, { OV5640_BRMATRX21, 0x22}, + { OV5640_BRMATRX22, 0x40}, { OV5640_BRMATRX23, 0x42}, + { OV5640_BRMATRX24, 0x24}, { OV5640_BRMATRX30, 0x26}, + { OV5640_BRMATRX31, 0x24}, { OV5640_BRMATRX32, 0x22}, + { OV5640_BRMATRX33, 0x22}, { OV5640_BRMATRX34, 0x26}, + { OV5640_BRMATRX40, 0x44}, { OV5640_BRMATRX41, 0x24}, + { OV5640_BRMATRX42, 0x26}, { OV5640_BRMATRX43, 0x28}, + { OV5640_BRMATRX44, 0x42}, { OV5640_LENC_BR_OFFSET, 0xce}, + + { OV5640_ISP_CTRL37, 0x00}, + { OV5640_AEC_CTRL0F, 0x30}, + { OV5640_AEC_CTRL10, 0x28}, + { OV5640_AEC_CTRL1B, 0x30}, + { OV5640_AEC_CTRL1E, 0x26}, + { OV5640_AEC_CTRL11, 0x60}, + { OV5640_AEC_CTRL1F, 0x14}, + { OV5640_SYSTEM_CTRL, 0x02}, +}; + +static enum v4l2_mbus_pixelcode ov5640_codes[] = { + V4L2_MBUS_FMT_YUYV8_2X8, +}; + +static const struct v4l2_queryctrl ov5640_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* read a register */ +static int ov5640_reg_read(struct i2c_client *client, u16 reg, u8 *val) +{ + int ret; + unsigned char data[2] = { reg >> 8, reg & 0xff }; + + ret = i2c_master_send(client, data, 2); + if (ret < 2) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 1) { + dev_err(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/* write a register */ +static int ov5640_reg_write(struct i2c_client *client, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val }; + + ret = i2c_master_send(client, data, 3); + if (ret < 3) { + dev_err(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov5640_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov5640_reg_read(client, reg, &val); + if (ret < 0) { + dev_err(&client->dev, + "[Read]-Modify-Write of register 0x%04x failed!\n", + reg); + return ret; + } + + val |= set; + val &= ~unset; + + ret = ov5640_reg_write(client, reg, val); + if (ret < 0) { + dev_err(&client->dev, + "Read-Modify-[Write] of register 0x%04x failed!\n", + reg); + return ret; + } + + return 0; +} + +static int ov5640_reg_write_array(struct i2c_client *client, + const struct ov5640_reg *regarray, + int regarraylen) +{ + int i; + int ret; + + for (i = 0; i < regarraylen; i++) { + ret = ov5640_reg_write(client, + regarray[i].reg, regarray[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Start/Stop streaming from the device */ +static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5640_priv *priv = to_ov5640(sd); + int ret; + + /* Program orientation register. */ + if (priv->flag_vflip) + ret = ov5640_reg_rmw(client, OV5640_TIMING_TC_REG20, 0x2, 0); + else + ret = ov5640_reg_rmw(client, OV5640_TIMING_TC_REG20, 0, 0x2); + if (ret < 0) + return ret; + + if (priv->flag_hflip) + ret = ov5640_reg_rmw(client, OV5640_TIMING_TC_REG21, 0x2, 0); + else + ret = ov5640_reg_rmw(client, OV5640_TIMING_TC_REG21, 0, 0x2); + if (ret < 0) + return ret; + + if (!enable) { + /* Software Reset */ + ret = ov5640_reg_write(client, OV5640_SYSTEM_CTRL, 0x82); + if (!ret) + /* Setting Streaming to Standby */ + ret = ov5640_reg_write(client, OV5640_SYSTEM_CTRL, + 0x42); + } + + priv->current_enable = enable; + + return ret; +} + +/* Alter bus settings on camera side */ +static int ov5640_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov5640_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +/* select nearest higher resolution for capture */ +static void ov5640_res_roundup(u32 *width, u32 *height) +{ + /* Width must be a multiple of 4 pixels. */ + *width = ALIGN(*width, 4); + + /* Max resolution is 1280x720 (720p). */ + if (*width > OV5640_MAX_WIDTH) + *width = OV5640_MAX_WIDTH; + + if (*height > OV5640_MAX_HEIGHT) + *height = OV5640_MAX_HEIGHT; +} + +/* Setup registers according to resolution and color encoding */ +static int ov5640_set_res(struct i2c_client *client, u32 width, u32 height) +{ + /* Note, this stuff is bogus. It's just copied from ov9740.c. */ +#if 0 + u32 x_start; + u32 y_start; + u32 x_end; + u32 y_end; + bool scaling = 0; + u32 scale_input_x; + u32 scale_input_y; + int ret; + + if ((width != OV5640_MAX_WIDTH) || (height != OV5640_MAX_HEIGHT)) + scaling = 1; + + /* + * Try to use as much of the sensor area as possible when supporting + * smaller resolutions. Depending on the aspect ratio of the + * chosen resolution, we can either use the full width of the sensor, + * or the full height of the sensor (or both if the aspect ratio is + * the same as 1280x720. + */ + if ((OV5640_MAX_WIDTH * height) > (OV5640_MAX_HEIGHT * width)) { + scale_input_x = (OV5640_MAX_HEIGHT * width) / height; + scale_input_y = OV5640_MAX_HEIGHT; + } else { + scale_input_x = OV5640_MAX_WIDTH; + scale_input_y = (OV5640_MAX_WIDTH * height) / width; + } + + /* These describe the area of the sensor to use. */ + x_start = (OV5640_MAX_WIDTH - scale_input_x) / 2; + y_start = (OV5640_MAX_HEIGHT - scale_input_y) / 2; + x_end = x_start + scale_input_x - 1; + y_end = y_start + scale_input_y - 1; + + ret = ov5640_reg_write(client, OV5640_X_ADDR_START_HI, x_start >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_X_ADDR_START_LO, x_start & 0xff); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_ADDR_START_HI, y_start >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_ADDR_START_LO, y_start & 0xff); + if (ret) + goto done; + + ret = ov5640_reg_write(client, OV5640_X_ADDR_END_HI, x_end >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_X_ADDR_END_LO, x_end & 0xff); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_ADDR_END_HI, y_end >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_ADDR_END_LO, y_end & 0xff); + if (ret) + goto done; + + ret = ov5640_reg_write(client, OV5640_X_OUTPUT_SIZE_HI, width >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_X_OUTPUT_SIZE_LO, width & 0xff); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_OUTPUT_SIZE_HI, height >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_Y_OUTPUT_SIZE_LO, height & 0xff); + if (ret) + goto done; + + ret = ov5640_reg_write(client, OV5640_ISP_CTRL1E, scale_input_x >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_ISP_CTRL1F, scale_input_x & 0xff); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_ISP_CTRL20, scale_input_y >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_ISP_CTRL21, scale_input_y & 0xff); + if (ret) + goto done; + + ret = ov5640_reg_write(client, OV5640_VFIFO_READ_START_HI, + (scale_input_x - width) >> 8); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_VFIFO_READ_START_LO, + (scale_input_x - width) & 0xff); + if (ret) + goto done; + + ret = ov5640_reg_write(client, OV5640_ISP_CTRL00, 0xff); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_ISP_CTRL01, 0xef | + (scaling << 4)); + if (ret) + goto done; + ret = ov5640_reg_write(client, OV5640_ISP_CTRL03, 0xff); + +done: + return ret; +#endif + return 0; +} + +/* set the format we will capture in */ +static int ov5640_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5640_priv *priv = to_ov5640(sd); + enum v4l2_colorspace cspace; + enum v4l2_mbus_pixelcode code = mf->code; + int ret; + + ov5640_res_roundup(&mf->width, &mf->height); + + switch (code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + cspace = V4L2_COLORSPACE_SRGB; + break; + default: + return -EINVAL; + } + + ret = ov5640_reg_write_array(client, ov5640_defaults, + ARRAY_SIZE(ov5640_defaults)); + if (ret < 0) + return ret; + + ret = ov5640_set_res(client, mf->width, mf->height); + if (ret < 0) + return ret; + + mf->code = code; + mf->colorspace = cspace; + + memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt)); + + return ret; +} + +static int ov5640_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + ov5640_res_roundup(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int ov5640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(ov5640_codes)) + return -EINVAL; + + *code = ov5640_codes[index]; + + return 0; +} + +static int ov5640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = OV5640_MAX_WIDTH; + a->bounds.height = OV5640_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ov5640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = OV5640_MAX_WIDTH; + a->c.height = OV5640_MAX_HEIGHT; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +/* Get status of additional camera capabilities */ +static int ov5640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov5640_priv *priv = to_ov5640(sd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov5640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov5640_priv *priv = to_ov5640(sd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->value; + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->value; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Get chip identification */ +static int ov5640_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct ov5640_priv *priv = to_ov5640(sd); + + id->ident = priv->ident; + id->revision = priv->revision; + + return 0; +} + +static int ov5640_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5640_priv *priv = to_ov5640(sd); + + if (!priv->current_enable) + return 0; + + if (on) { + ov5640_s_fmt(sd, &priv->current_mf); + ov5640_s_stream(sd, priv->current_enable); + } else { + ov5640_s_stream(sd, 0); + priv->current_enable = true; + } + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov5640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val; + + if (reg->reg & ~0xffff) + return -EINVAL; + + reg->size = 2; + + ret = ov5640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return ret; +} + +static int ov5640_set_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg & ~0xffff || reg->val & ~0xff) + return -EINVAL; + + return ov5640_reg_write(client, reg->reg, reg->val); +} +#endif + +static int ov5640_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5640_priv *priv = to_ov5640(sd); + u8 chip_id_hi, chip_id_lo; + int ret; + + /* We must have a parent by now. And it cannot be a wrong one. */ + BUG_ON(!icd->parent || + to_soc_camera_host(icd->parent)->nr != icd->iface); + + /* + * check and show product ID and manufacturer ID + */ + ret = ov5640_reg_read(client, OV5640_CHIP_ID_HI, &chip_id_hi); + if (ret < 0) + goto err; + + ret = ov5640_reg_read(client, OV5640_CHIP_ID_LO, &chip_id_lo); + if (ret < 0) + goto err; + + priv->chip_id = (chip_id_hi << 8) | chip_id_lo; + + if (priv->chip_id != 0x5640) { + ret = -ENODEV; + goto err; + } + + priv->ident = V4L2_IDENT_OV5640; + + dev_info(&client->dev, "Chip ID 0x%04x\n", priv->chip_id); + +err: + return ret; +} + +static struct soc_camera_ops ov5640_ops = { + .set_bus_param = ov5640_set_bus_param, + .query_bus_param = ov5640_query_bus_param, + .controls = ov5640_controls, + .num_controls = ARRAY_SIZE(ov5640_controls), +}; + +static struct v4l2_subdev_video_ops ov5640_video_ops = { + .s_stream = ov5640_s_stream, + .s_mbus_fmt = ov5640_s_fmt, + .try_mbus_fmt = ov5640_try_fmt, + .enum_mbus_fmt = ov5640_enum_fmt, + .cropcap = ov5640_cropcap, + .g_crop = ov5640_g_crop, +}; + +static struct v4l2_subdev_core_ops ov5640_core_ops = { + .g_ctrl = ov5640_g_ctrl, + .s_ctrl = ov5640_s_ctrl, + .g_chip_ident = ov5640_g_chip_ident, + .s_power = ov5640_s_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov5640_get_register, + .s_register = ov5640_set_register, +#endif +}; + +static struct v4l2_subdev_ops ov5640_subdev_ops = { + .core = &ov5640_core_ops, + .video = &ov5640_video_ops, +}; + +/* + * i2c_driver function + */ +static int ov5640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov5640_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov5640_priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "Failed to allocate private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov5640_subdev_ops); + + icd->ops = &ov5640_ops; + + ret = ov5640_video_probe(icd, client); + if (ret < 0) { + icd->ops = NULL; + kfree(priv); + } + + return ret; +} + +static int ov5640_remove(struct i2c_client *client) +{ + struct ov5640_priv *priv = i2c_get_clientdata(client); + + kfree(priv); + + return 0; +} + +static const struct i2c_device_id ov5640_id[] = { + { "ov5640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5640_id); + +static struct i2c_driver ov5640_i2c_driver = { + .driver = { + .name = "ov5640", + }, + .probe = ov5640_probe, + .remove = ov5640_remove, + .id_table = ov5640_id, +}; + +static int __init ov5640_module_init(void) +{ + return i2c_add_driver(&ov5640_i2c_driver); +} + +static void __exit ov5640_module_exit(void) +{ + i2c_del_driver(&ov5640_i2c_driver); +} + +module_init(ov5640_module_init); +module_exit(ov5640_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV5640"); +MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c index 4501abb9a735..d5edcbe685bf 100644 --- a/drivers/media/video/tegra/nvavp/nvavp_dev.c +++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c @@ -1355,7 +1355,6 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp, int channel_ if (!ret) nvavp->refcount++; - clientctx->nvmap = nvavp->nvmap; clientctx->nvavp = nvavp; filp->private_data = clientctx; diff --git a/drivers/media/video/tegra_v4l2_camera.c b/drivers/media/video/tegra_v4l2_camera.c new file mode 100644 index 000000000000..644d0be53803 --- /dev/null +++ b/drivers/media/video/tegra_v4l2_camera.c @@ -0,0 +1,1765 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/nvhost.h> + +#include <mach/iomap.h> + +#include <media/soc_camera.h> +#include <media/soc_mediabus.h> +#include <media/videobuf2-dma-nvmap.h> +#include <media/tegra_v4l2_camera.h> + +#include "dev.h" +#include "bus_client.h" +#include "host1x/host1x_syncpt.h" + +#define TEGRA_CAM_DRV_NAME "vi" +#define TEGRA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) + +#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 200 +#define TEGRA_SYNCPT_CSI_WAIT_TIMEOUT 200 + +#define TEGRA_SYNCPT_RETRY_COUNT 10 + +#define TEGRA_VIP_H_ACTIVE_START 0x98 +#define TEGRA_VIP_V_ACTIVE_START 0x10 + +/* SYNCPTs 12-17 are reserved for VI. */ +#define TEGRA_VI_SYNCPT_VI NVSYNCPT_VI_ISP_2 +#define TEGRA_VI_SYNCPT_CSI NVSYNCPT_VI_ISP_3 + +/* Tegra CSI-MIPI registers. */ +#define TEGRA_VI_OUT_1_INCR_SYNCPT 0x0000 +#define TEGRA_VI_OUT_1_INCR_SYNCPT_CNTRL 0x0004 +#define TEGRA_VI_OUT_1_INCR_SYNCPT_ERROR 0x0008 +#define TEGRA_VI_OUT_2_INCR_SYNCPT 0x0020 +#define TEGRA_VI_OUT_2_INCR_SYNCPT_CNTRL 0x0024 +#define TEGRA_VI_OUT_2_INCR_SYNCPT_ERROR 0x0028 +#define TEGRA_VI_MISC_INCR_SYNCPT 0x0040 +#define TEGRA_VI_MISC_INCR_SYNCPT_CNTRL 0x0044 +#define TEGRA_VI_MISC_INCR_SYNCPT_ERROR 0x0048 +#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060 +#define TEGRA_VI_CONT_SYNCPT_OUT_2 0x0064 +#define TEGRA_VI_CONT_SYNCPT_VIP_VSYNC 0x0068 +#define TEGRA_VI_CONT_SYNCPT_VI2EPP 0x006c +#define TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_START 0x0070 +#define TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_END 0x0074 +#define TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_START 0x0078 +#define TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_END 0x007c +#define TEGRA_VI_CTXSW 0x0080 +#define TEGRA_VI_INTSTATUS 0x0084 +#define TEGRA_VI_VI_INPUT_CONTROL 0x0088 +#define TEGRA_VI_VI_CORE_CONTROL 0x008c +#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090 +#define TEGRA_VI_VI_SECOND_OUTPUT_CONTROL 0x0094 +#define TEGRA_VI_HOST_INPUT_FRAME_SIZE 0x0098 +#define TEGRA_VI_HOST_H_ACTIVE 0x009c +#define TEGRA_VI_HOST_V_ACTIVE 0x00a0 +#define TEGRA_VI_VIP_H_ACTIVE 0x00a4 +#define TEGRA_VI_VIP_V_ACTIVE 0x00a8 +#define TEGRA_VI_VI_PEER_CONTROL 0x00ac +#define TEGRA_VI_VI_DMA_SELECT 0x00b0 +#define TEGRA_VI_HOST_DMA_WRITE_BUFFER 0x00b4 +#define TEGRA_VI_HOST_DMA_BASE_ADDRESS 0x00b8 +#define TEGRA_VI_HOST_DMA_WRITE_BUFFER_STATUS 0x00bc +#define TEGRA_VI_HOST_DMA_WRITE_PEND_BUFCOUNT 0x00c0 +#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4 +#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8 +#define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc +#define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0 +#define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4 +#define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8 +#define TEGRA_VI_VB_SCRATCH_ADDRESS_UV 0x00dc +#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0 +#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4 +#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8 +#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec +#define TEGRA_VI_VB0_START_ADDRESS_SECOND 0x00f0 +#define TEGRA_VI_VB0_BASE_ADDRESS_SECOND 0x00f4 +#define TEGRA_VI_SECOND_OUTPUT_FRAME_SIZE 0x00f8 +#define TEGRA_VI_VB0_COUNT_SECOND 0x00fc +#define TEGRA_VI_VB0_SIZE_SECOND 0x0100 +#define TEGRA_VI_VB0_BUFFER_STRIDE_SECOND 0x0104 +#define TEGRA_VI_H_LPF_CONTROL 0x0108 +#define TEGRA_VI_H_DOWNSCALE_CONTROL 0x010c +#define TEGRA_VI_V_DOWNSCALE_CONTROL 0x0110 +#define TEGRA_VI_CSC_Y 0x0114 +#define TEGRA_VI_CSC_UV_R 0x0118 +#define TEGRA_VI_CSC_UV_G 0x011c +#define TEGRA_VI_CSC_UV_B 0x0120 +#define TEGRA_VI_CSC_ALPHA 0x0124 +#define TEGRA_VI_HOST_VSYNC 0x0128 +#define TEGRA_VI_COMMAND 0x012c +#define TEGRA_VI_HOST_FIFO_STATUS 0x0130 +#define TEGRA_VI_INTERRUPT_MASK 0x0134 +#define TEGRA_VI_INTERRUPT_TYPE_SELECT 0x0138 +#define TEGRA_VI_INTERRUPT_POLARITY_SELECT 0x013c +#define TEGRA_VI_INTERRUPT_STATUS 0x0140 +#define TEGRA_VI_VIP_INPUT_STATUS 0x0144 +#define TEGRA_VI_VIDEO_BUFFER_STATUS 0x0148 +#define TEGRA_VI_SYNC_OUTPUT 0x014c +#define TEGRA_VI_VVS_OUTPUT_DELAY 0x0150 +#define TEGRA_VI_PWM_CONTROL 0x0154 +#define TEGRA_VI_PWM_SELECT_PULSE_A 0x0158 +#define TEGRA_VI_PWM_SELECT_PULSE_B 0x015c +#define TEGRA_VI_PWM_SELECT_PULSE_C 0x0160 +#define TEGRA_VI_PWM_SELECT_PULSE_D 0x0164 +#define TEGRA_VI_VI_DATA_INPUT_CONTROL 0x0168 +#define TEGRA_VI_PIN_INPUT_ENABLE 0x016c +#define TEGRA_VI_PIN_OUTPUT_ENABLE 0x0170 +#define TEGRA_VI_PIN_INVERSION 0x0174 +#define TEGRA_VI_PIN_INPUT_DATA 0x0178 +#define TEGRA_VI_PIN_OUTPUT_DATA 0x017c +#define TEGRA_VI_PIN_OUTPUT_SELECT 0x0180 +#define TEGRA_VI_RAISE_VIP_BUFFER_FIRST_OUTPUT 0x0184 +#define TEGRA_VI_RAISE_VIP_FRAME_FIRST_OUTPUT 0x0188 +#define TEGRA_VI_RAISE_VIP_BUFFER_SECOND_OUTPUT 0x018c +#define TEGRA_VI_RAISE_VIP_FRAME_SECOND_OUTPUT 0x0190 +#define TEGRA_VI_RAISE_HOST_FIRST_OUTPUT 0x0194 +#define TEGRA_VI_RAISE_HOST_SECOND_OUTPUT 0x0198 +#define TEGRA_VI_RAISE_EPP 0x019c +#define TEGRA_VI_CAMERA_CONTROL 0x01a0 +#define TEGRA_VI_VI_ENABLE 0x01a4 +#define TEGRA_VI_VI_ENABLE_2 0x01a8 +#define TEGRA_VI_VI_RAISE 0x01ac +#define TEGRA_VI_Y_FIFO_WRITE 0x01b0 +#define TEGRA_VI_U_FIFO_WRITE 0x01b4 +#define TEGRA_VI_V_FIFO_WRITE 0x01b8 +#define TEGRA_VI_VI_MCCIF_FIFOCTRL 0x01bc +#define TEGRA_VI_TIMEOUT_WCOAL_VI 0x01c0 +#define TEGRA_VI_MCCIF_VIRUV_HP 0x01c4 +#define TEGRA_VI_MCCIF_VIWSB_HP 0x01c8 +#define TEGRA_VI_MCCIF_VIWU_HP 0x01cc +#define TEGRA_VI_MCCIF_VIWV_HP 0x01d0 +#define TEGRA_VI_MCCIF_VIWY_HP 0x01d4 +#define TEGRA_VI_CSI_PPA_RAISE_FRAME_START 0x01d8 +#define TEGRA_VI_CSI_PPA_RAISE_FRAME_END 0x01dc +#define TEGRA_VI_CSI_PPB_RAISE_FRAME_START 0x01e0 +#define TEGRA_VI_CSI_PBB_RAISE_FRAME_END 0x01e4 +#define TEGRA_VI_CSI_PPA_H_ACTIVE 0x01e8 +#define TEGRA_VI_CSI_PPA_V_ACTIVE 0x01ec +#define TEGRA_VI_CSI_PPB_H_ACTIVE 0x01f0 +#define TEGRA_VI_CSI_PPB_V_ACTIVE 0x01f4 +#define TEGRA_VI_ISP_H_ACTIVE 0x01f8 +#define TEGRA_VI_ISP_V_ACTIVE 0x01fc +#define TEGRA_VI_STREAM_1_RESOURCE_DEFINE 0x0200 +#define TEGRA_VI_STREAM_2_RESOURCE_DEFINE 0x0204 +#define TEGRA_VI_RAISE_STREAM_1_DONE 0x0208 +#define TEGRA_VI_RAISE_STREAM_2_DONE 0x020c +#define TEGRA_VI_TS_MODE 0x0210 +#define TEGRA_VI_TS_CONTROL 0x0214 +#define TEGRA_VI_TS_PACKET_COUNT 0x0218 +#define TEGRA_VI_TS_ERROR_COUNT 0x021c +#define TEGRA_VI_TS_CPU_FLOW_CTL 0x0220 +#define TEGRA_VI_VB0_CHROMA_BUFFER_STRIDE_FIRST 0x0224 +#define TEGRA_VI_VB0_CHROMA_LINE_STRIDE_FIRST 0x0228 +#define TEGRA_VI_EPP_LINES_PER_BUFFER 0x022c +#define TEGRA_VI_BUFFER_RELEASE_OUTPUT1 0x0230 +#define TEGRA_VI_BUFFER_RELEASE_OUTPUT2 0x0234 +#define TEGRA_VI_DEBUG_FLOW_CONTROL_COUNTER_OUTPUT1 0x0238 +#define TEGRA_VI_DEBUG_FLOW_CONTROL_COUNTER_OUTPUT2 0x023c +#define TEGRA_VI_TERMINATE_BW_FIRST 0x0240 +#define TEGRA_VI_TERMINATE_BW_SECOND 0x0244 +#define TEGRA_VI_VB0_FIRST_BUFFER_ADDR_MODE 0x0248 +#define TEGRA_VI_VB0_SECOND_BUFFER_ADDR_MODE 0x024c +#define TEGRA_VI_RESERVE_0 0x0250 +#define TEGRA_VI_RESERVE_1 0x0254 +#define TEGRA_VI_RESERVE_2 0x0258 +#define TEGRA_VI_RESERVE_3 0x025c +#define TEGRA_VI_RESERVE_4 0x0260 +#define TEGRA_VI_MCCIF_VIRUV_HYST 0x0264 +#define TEGRA_VI_MCCIF_VIWSB_HYST 0x0268 +#define TEGRA_VI_MCCIF_VIWU_HYST 0x026c +#define TEGRA_VI_MCCIF_VIWV_HYST 0x0270 +#define TEGRA_VI_MCCIF_VIWY_HYST 0x0274 + +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0800 +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0808 +#define TEGRA_CSI_INPUT_STREAM_A_CONTROL 0x0810 +#define TEGRA_CSI_PIXEL_STREAM_A_CONTROL0 0x0818 +#define TEGRA_CSI_PIXEL_STREAM_A_CONTROL1 0x081c +#define TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT 0x0820 +#define TEGRA_CSI_PIXEL_STREAM_A_GAP 0x0824 +#define TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND 0x0828 +#define TEGRA_CSI_INPUT_STREAM_B_CONTROL 0x083c +#define TEGRA_CSI_PIXEL_STREAM_B_CONTROL0 0x0844 +#define TEGRA_CSI_PIXEL_STREAM_B_CONTROL1 0x0848 +#define TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT 0x084c +#define TEGRA_CSI_PIXEL_STREAM_B_GAP 0x0850 +#define TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND 0x0854 +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0868 +#define TEGRA_CSI_PHY_CILA_CONTROL0 0x086c +#define TEGRA_CSI_PHY_CILB_CONTROL0 0x0870 +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0878 +#define TEGRA_CSI_CSI_CIL_STATUS 0x087c +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0880 +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0884 +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0888 +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x088c +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0890 +#define TEGRA_CSI_CILA_PAD_CONFIG0 0x0894 +#define TEGRA_CSI_CILA_PAD_CONFIG1 0x0898 +#define TEGRA_CSI_CILB_PAD_CONFIG0 0x089c +#define TEGRA_CSI_CILB_PAD_CONFIG1 0x08a0 +#define TEGRA_CSI_CIL_PAD_CONFIG 0x08a4 +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x08a8 +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x08ac +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x08b0 +#define TEGRA_CSI_CLKEN_OVERRIDE 0x08b4 +#define TEGRA_CSI_DEBUG_CONTROL 0x08b8 +#define TEGRA_CSI_DEBUG_COUNTER_0 0x08bc +#define TEGRA_CSI_DEBUG_COUNTER_1 0x08c0 +#define TEGRA_CSI_DEBUG_COUNTER_2 0x08c4 +#define TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME 0x08c8 +#define TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME 0x08cc +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x08d0 + +#define TC_VI_REG_RD(DEV, REG) readl(DEV->vi_base + REG) +#define TC_VI_REG_WT(DEV, REG, VAL) writel(VAL, DEV->vi_base + REG) + +#define tegra_camera_port_is_valid(port) \ + (((port) >= TEGRA_CAMERA_PORT_CSI_A) && \ + ((port) <= TEGRA_CAMERA_PORT_VIP)) + +#define tegra_camera_port_is_csi(port) \ + (((port) == TEGRA_CAMERA_PORT_CSI_A) || \ + ((port) == TEGRA_CAMERA_PORT_CSI_B)) + +/* + * Structures + */ + +/* buffer for one video frame */ +struct tegra_buffer { + struct vb2_buffer vb; /* v4l buffer must be first */ + struct list_head queue; + + /* + * Various buffer addresses shadowed so we don't have to recalculate + * per frame. These are calculated during videobuf_prepare. + */ + dma_addr_t buffer_addr; + dma_addr_t buffer_addr_u; + dma_addr_t buffer_addr_v; + dma_addr_t start_addr; + dma_addr_t start_addr_u; + dma_addr_t start_addr_v; +}; + +struct tegra_camera_dev { + struct soc_camera_host ici; + struct soc_camera_device *icd; + struct nvhost_device *ndev; + struct tegra_camera_platform_data *pdata; + + struct clk *clk_vi; + struct clk *clk_vi_sensor; + struct clk *clk_csi; + struct clk *clk_isp; + struct clk *clk_csus; + + void __iomem *vi_base; + spinlock_t videobuf_queue_lock; + struct list_head capture; + struct vb2_buffer *active; + struct vb2_alloc_ctx *alloc_ctx; + enum v4l2_field field; + int sequence; + + struct work_struct work; + struct mutex work_mutex; + + u32 syncpt_vi; + u32 syncpt_csi; + + /* Debug */ + int num_frames; +}; + +static const struct soc_mbus_pixelfmt tegra_camera_formats[] = { + { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "YUV422 (UYVY) packed", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "YUV422 (VYUY) packed", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUV422 (YUYV) packed", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YUV422 (YVYU) packed", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .name = "YUV420 (YU12) planar", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + .name = "YVU420 (YV12) planar", + .bits_per_sample = 12, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + }, +}; + +static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb) +{ + return container_of(vb, struct tegra_buffer, vb); +} + +static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev) +{ + pcdev->syncpt_csi = + nvhost_syncpt_read_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_CSI); + + pcdev->syncpt_vi = + nvhost_syncpt_read_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_VI); +} + +static void tegra_camera_incr_syncpts(struct tegra_camera_dev *pcdev) +{ + nvhost_syncpt_cpu_incr_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_CSI); + + nvhost_syncpt_cpu_incr_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_VI); +} + +static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev, + int input_format, + int yuv_input_format) +{ + struct soc_camera_device *icd = pcdev->icd; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x02000000); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, + (yuv_input_format << 8) | + (input_format << 2)); + + TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000004); + TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000004); + + /* CSI-A H_ACTIVE and V_ACTIVE */ + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_H_ACTIVE, + (icd->user_width << 16)); + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_V_ACTIVE, + (icd->user_height << 16)); + + /* CSI A */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_GAP, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, 0x00000000); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000); + + /* pad1s enabled, virtual channel ID 00 */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, + (0x1 << 16) | /* Output 1 pixel per clock */ + (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */ + (0x1 << 7) | /* Check header CRC */ + (0x1 << 6) | /* Use word count field in the header */ + (0x1 << 5) | /* Look at data identifier byte in hdr */ + (0x1 << 4)); /* Expect packet header */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1, + 0x1); /* Frame # for top field detect for interlaced */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT, + bytes_per_line); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_GAP, 0x00140000); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME, + (icd->user_height << 16) | + (0x100 << 4) | /* Wait 0x100 vi clks for timeout */ + 0x1); /* Enable line timeout */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL, + (0x3f << 16) | /* Skip packet threshold */ + (pcdev->pdata->lanes - 1)); + + /* Use 0x00000022 for continuous clock mode. */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0, + (pcdev->pdata->continuous_clk << 5) | + 0x5); /* Clock settle time */ + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_END, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_CSI); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_VI); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00020001); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, 0x0000f002); +} + +static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev, + int input_format, + int yuv_input_format) +{ + struct soc_camera_device *icd = pcdev->icd; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x04000000); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, + (yuv_input_format << 8) | + (input_format << 2)); + + TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000008); + TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000008); + + /* CSI-B H_ACTIVE and V_ACTIVE */ + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_H_ACTIVE, + (icd->user_width << 16)); + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_V_ACTIVE, + (icd->user_height << 16)); + + /* CSI B */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_GAP, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x00000000); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000); + + /* pad1s enabled, virtual channel ID 00 */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0, + (0x1 << 16) | /* Output 1 pixel per clock */ + (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */ + (0x1 << 7) | /* Check header CRC */ + (0x1 << 6) | /* Use word count field in the header */ + (0x1 << 5) | /* Look at data identifier byte in hdr */ + (0x1 << 4) | /* Expect packet header */ + 0x1); /* Set PPB stream source to CSI B */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL1, + 0x1); /* Frame # for top field detect for interlaced */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT, + bytes_per_line); + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_GAP, 0x00140000); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME, + (icd->user_height << 16) | + (0x100 << 4) | /* Wait 0x100 vi clks for timeout */ + 0x1); /* Enable line timeout */ + + TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL, + (0x3f << 16) | /* Skip packet threshold */ + (pcdev->pdata->lanes - 1)); + + /* Use 0x00000022 for continuous clock mode. */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0, + (pcdev->pdata->continuous_clk << 5) | + 0x5); /* Clock settle time */ + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_END, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_CSI); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_VI); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00010002); + + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x0000f002); +} + +static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev, + int input_format, + int yuv_input_format) +{ + struct soc_camera_device *icd = pcdev->icd; + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x00000000); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, + (1 << 27) | /* field detect */ + (1 << 25) | /* hsync/vsync decoded from data (BT.656) */ + (yuv_input_format << 8) | + (1 << 1) | /* VIP_INPUT_ENABLE */ + (input_format << 2)); + + TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000000); + + /* VIP H_ACTIVE and V_ACTIVE */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_H_ACTIVE, + (icd->user_width << 16) | + TEGRA_VIP_H_ACTIVE_START); + TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_V_ACTIVE, + (icd->user_height << 16) | + TEGRA_VIP_V_ACTIVE_START); + + /* + * For VIP, D9..D2 is mapped to the video decoder's P7..P0. + * Disable/mask out the other Dn wires. + */ + TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INPUT_ENABLE, 0x000003fc); + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_DATA_INPUT_CONTROL, 0x000003fc); + TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INVERSION, 0x00000000); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_VIP_VSYNC, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_VI); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004); +} + +static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev) +{ + struct soc_camera_device *icd = pcdev->icd; + const struct soc_camera_format_xlate *current_fmt = icd->current_fmt; + enum v4l2_mbus_pixelcode input_code = current_fmt->code; + u32 output_fourcc = current_fmt->host_fmt->fourcc; + int yuv_input_format = 0x0; + int input_format = 0x0; /* Default to YUV422 */ + int yuv_output_format = 0x0; + int output_format = 0x3; /* Default to YUV422 */ + int port = pcdev->pdata->port; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + switch (input_code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + yuv_input_format = 0x2; + break; + case V4L2_MBUS_FMT_VYUY8_2X8: + yuv_input_format = 0x3; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + yuv_input_format = 0x0; + break; + case V4L2_MBUS_FMT_YVYU8_2X8: + yuv_input_format = 0x1; + break; + default: + BUG_ON(1); + } + + switch (output_fourcc) { + case V4L2_PIX_FMT_UYVY: + yuv_output_format = 0x0; + break; + case V4L2_PIX_FMT_VYUY: + yuv_output_format = 0x1; + break; + case V4L2_PIX_FMT_YUYV: + yuv_output_format = 0x2; + break; + case V4L2_PIX_FMT_YVYU: + yuv_output_format = 0x3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + output_format = 0x6; /* YUV420 planar */ + break; + default: + BUG_ON(1); + } + + BUG_ON(!tegra_camera_port_is_valid(port)); + + /* + * Set up low pass filter. Use 0x240 for chromaticity and 0x240 + * for luminance, which is the default and means not to touch + * anything. + */ + TC_VI_REG_WT(pcdev, TEGRA_VI_H_LPF_CONTROL, 0x02400240); + + /* Set up raise-on-edge, so we get an interrupt on end of frame. */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_RAISE, 0x00000001); + + if (port == TEGRA_CAMERA_PORT_CSI_A) + tegra_camera_capture_setup_csi_a(pcdev, input_format, + yuv_input_format); + else if (port == TEGRA_CAMERA_PORT_CSI_B) + tegra_camera_capture_setup_csi_b(pcdev, input_format, + yuv_input_format); + else + tegra_camera_capture_setup_vip(pcdev, input_format, + yuv_input_format); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL, + (pcdev->pdata->flip_v ? (0x1 << 20) : 0) | + (pcdev->pdata->flip_h ? (0x1 << 19) : 0) | + (yuv_output_format << 17) | + output_format); + + /* + * Set up frame size. Bits 31:16 are the number of lines, and + * bits 15:0 are the number of pixels per line. + */ + TC_VI_REG_WT(pcdev, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE, + (icd->user_height << 16) | icd->user_width); + + /* First output memory enabled */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000); + + /* Set the number of frames in the buffer. */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_FIRST, 0x00000001); + + /* Set up buffer frame size. */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_FIRST, + (icd->user_height << 16) | icd->user_width); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, + (icd->user_height * bytes_per_line)); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000); +} + +static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, + struct tegra_buffer *buf) +{ + struct soc_camera_device *icd = pcdev->icd; + int port = pcdev->pdata->port; + int err; + + pcdev->syncpt_csi++; + pcdev->syncpt_vi++; + + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_U, + buf->buffer_addr_u); + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_U, + buf->start_addr_u); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_V, + buf->buffer_addr_v); + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_V, + buf->start_addr_v); + + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, + buf->buffer_addr); + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_FIRST, + buf->start_addr); + + break; + + default: + BUG_ON(1); + } + + BUG_ON(!tegra_camera_port_is_valid(port)); + + if (port == TEGRA_CAMERA_PORT_CSI_A) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, + 0x0000f005); + else if (port == TEGRA_CAMERA_PORT_CSI_B) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, + 0x0000f005); + else + TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, + 0x00000001); + + /* + * Only wait on CSI frame end syncpt if we're using CSI. Otherwise, + * wait on VIP VSYNC syncpt. + */ + if (tegra_camera_port_is_csi(port)) + err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_CSI, + pcdev->syncpt_csi, + TEGRA_SYNCPT_CSI_WAIT_TIMEOUT, + NULL); + else + err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_VI, + pcdev->syncpt_csi, + TEGRA_SYNCPT_VI_WAIT_TIMEOUT, + NULL); + + if (!err) + return 0; + + if (tegra_camera_port_is_csi(port)) { + u32 ppstatus; + u32 cilstatus; + u32 rostatus; + + dev_err(&pcdev->ndev->dev, "Timeout on CSI syncpt\n"); + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", + buf->buffer_addr); + + ppstatus = TC_VI_REG_RD(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); + cilstatus = TC_VI_REG_RD(pcdev, + TEGRA_CSI_CSI_CIL_STATUS); + rostatus = TC_VI_REG_RD(pcdev, + TEGRA_CSI_CSI_READONLY_STATUS); + + dev_err(&pcdev->ndev->dev, + "PPSTATUS = 0x%08x, " + "CILSTATUS = 0x%08x, " + "ROSTATUS = 0x%08x\n", + ppstatus, cilstatus, rostatus); + } else { + u32 vip_input_status; + + dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", + buf->buffer_addr); + + vip_input_status = TC_VI_REG_RD(pcdev, + TEGRA_VI_VIP_INPUT_STATUS); + + dev_err(&pcdev->ndev->dev, + "VIP_INPUT_STATUS = 0x%08x\n", + vip_input_status); + } + + return err; +} + +static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev) +{ + int port = pcdev->pdata->port; + int err; + + BUG_ON(!tegra_camera_port_is_valid(port)); + + if (port == TEGRA_CAMERA_PORT_CSI_A) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, + 0x0000f002); + else if (port == TEGRA_CAMERA_PORT_CSI_B) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, + 0x0000f002); + else + TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, + 0x00000005); + + if (tegra_camera_port_is_csi(port)) + err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_VI, + pcdev->syncpt_vi, + TEGRA_SYNCPT_VI_WAIT_TIMEOUT, + NULL); + else + err = 0; + + if (err) { + u32 buffer_addr; + u32 ppstatus; + u32 cilstatus; + + dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); + buffer_addr = TC_VI_REG_RD(pcdev, + TEGRA_VI_VB0_BASE_ADDRESS_FIRST); + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", + buffer_addr); + + ppstatus = TC_VI_REG_RD(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); + cilstatus = TC_VI_REG_RD(pcdev, + TEGRA_CSI_CSI_CIL_STATUS); + dev_err(&pcdev->ndev->dev, + "PPSTATUS = 0x%08x, CILSTATUS = 0x%08x\n", + ppstatus, cilstatus); + } + + return err; +} + +static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev) +{ + struct vb2_buffer *vb; + struct tegra_buffer *buf; + int retry = TEGRA_SYNCPT_RETRY_COUNT; + int port = pcdev->pdata->port; + int err; + + if (!pcdev->active) + return 0; + + vb = pcdev->active; + buf = to_tegra_vb(vb); + + while (retry) { + err = tegra_camera_capture_start(pcdev, buf); + if (!err) + err = tegra_camera_capture_stop(pcdev); + + if (err != 0) { + retry--; + + /* Stop streaming. */ + if (port == TEGRA_CAMERA_PORT_CSI_A) { + TC_VI_REG_WT(pcdev, + TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, + 0x0000f002); + /* Clear status registers. */ + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, + 0xffffffff); + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_CIL_STATUS, + 0xffffffff); + } else if (port == TEGRA_CAMERA_PORT_CSI_B) { + TC_VI_REG_WT(pcdev, + TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, + 0x0000f002); + /* Clear status registers. */ + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, + 0xffffffff); + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_CIL_STATUS, + 0xffffffff); + } else { + TC_VI_REG_WT(pcdev, + TEGRA_VI_CAMERA_CONTROL, + 0x00000005); + } + + + tegra_camera_incr_syncpts(pcdev); + tegra_camera_save_syncpts(pcdev); + + continue; + } + + break; + } + + spin_lock_irq(&pcdev->videobuf_queue_lock); + + /* + * If vb->state is VB2_BUF_STATE_ERROR, then the vb has already been + * removed, so we shouldn't remove it again. + */ + if (vb->state != VB2_BUF_STATE_ERROR) + list_del_init(&buf->queue); + + if (!list_empty(&pcdev->capture)) + pcdev->active = &list_entry(pcdev->capture.next, + struct tegra_buffer, queue)->vb; + else + pcdev->active = NULL; + + do_gettimeofday(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.field = pcdev->field; + vb->v4l2_buf.sequence = pcdev->sequence++; + + vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + pcdev->num_frames++; + + spin_unlock_irq(&pcdev->videobuf_queue_lock); + + return err; +} + +static void tegra_camera_work(struct work_struct *work) +{ + struct tegra_camera_dev *pcdev = + container_of(work, struct tegra_camera_dev, work); + + mutex_lock(&pcdev->work_mutex); + + while (pcdev->active) + tegra_camera_capture_frame(pcdev); + + mutex_unlock(&pcdev->work_mutex); +} + +static void tegra_camera_activate(struct tegra_camera_dev *pcdev) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + u32 val; + void __iomem *apb_misc; +#endif + + nvhost_module_busy_ext(pcdev->ndev); + + /* Turn on relevant clocks. */ + clk_enable(pcdev->clk_vi); + clk_enable(pcdev->clk_vi_sensor); + clk_enable(pcdev->clk_csi); + clk_enable(pcdev->clk_isp); + clk_enable(pcdev->clk_csus); + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE); + val = readl(apb_misc + 0x42c); + writel(val | 0x1, apb_misc + 0x42c); +#endif + + /* Save current syncpt values. */ + tegra_camera_save_syncpts(pcdev); +} + +static void tegra_camera_deactivate(struct tegra_camera_dev *pcdev) +{ + mutex_lock(&pcdev->work_mutex); + + /* Cancel active buffer. */ + if (pcdev->active) { + list_del_init(&to_tegra_vb(pcdev->active)->queue); + vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); + pcdev->active = NULL; + } + + mutex_unlock(&pcdev->work_mutex); + + /* Turn off relevant clocks. */ + clk_disable(pcdev->clk_vi); + clk_disable(pcdev->clk_vi_sensor); + clk_disable(pcdev->clk_csi); + clk_disable(pcdev->clk_isp); + clk_disable(pcdev->clk_csus); + + nvhost_module_idle_ext(pcdev->ndev); +} + +static void tegra_camera_init_buffer(struct tegra_camera_dev *pcdev, + struct tegra_buffer *buf) +{ + struct soc_camera_device *icd = pcdev->icd; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); + buf->start_addr = buf->buffer_addr; + + if (pcdev->pdata->flip_v) + buf->start_addr += bytes_per_line * + (icd->user_height-1); + + if (pcdev->pdata->flip_h) + buf->start_addr += bytes_per_line - 1; + + break; + + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); + buf->buffer_addr_u = buf->buffer_addr + + icd->user_width * icd->user_height; + buf->buffer_addr_v = buf->buffer_addr_u + + (icd->user_width * icd->user_height) / 4; + + /* For YVU420, we swap the locations of the U and V planes. */ + if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420) { + dma_addr_t temp = buf->buffer_addr_u; + buf->buffer_addr_u = buf->buffer_addr_v; + buf->buffer_addr_v = temp; + } + + buf->start_addr = buf->buffer_addr; + buf->start_addr_u = buf->buffer_addr_u; + buf->start_addr_v = buf->buffer_addr_v; + + if (pcdev->pdata->flip_v) { + buf->start_addr += icd->user_width * + (icd->user_height - 1); + + buf->start_addr_u += ((icd->user_width/2) * + ((icd->user_height/2) - 1)); + + buf->start_addr_v += ((icd->user_width/2) * + ((icd->user_height/2) - 1)); + } + + if (pcdev->pdata->flip_h) { + buf->start_addr += icd->user_width - 1; + + buf->start_addr_u += (icd->user_width/2) - 1; + + buf->start_addr_v += (icd->user_width/2) - 1; + } + + break; + + default: + BUG_ON(1); + } +} + +/* + * Videobuf operations + */ +static int tegra_camera_videobuf_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned long sizes[], + void *alloc_ctxs[]) +{ + struct soc_camera_device *icd = container_of(vq, + struct soc_camera_device, + vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + + dev_dbg(icd->parent, "In tegra_camera_videobuf_setup()\n"); + + if (bytes_per_line < 0) + return bytes_per_line; + + *num_planes = 1; + + pcdev->sequence = 0; + sizes[0] = bytes_per_line * icd->user_height; + alloc_ctxs[0] = pcdev->alloc_ctx; + + if (!*num_buffers) + *num_buffers = 2; + + dev_dbg(icd->parent, "num_buffers=%u, size=%u\n", + *num_buffers, sizes[0]); + + tegra_camera_capture_setup(pcdev); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_setup()\n"); + + return 0; +} + +static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, + vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + struct tegra_buffer *buf; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + unsigned long size; + + dev_dbg(icd->parent, "In tegra_camera_videobuf_prepare()\n"); + + if (bytes_per_line < 0) + return bytes_per_line; + + buf = to_tegra_vb(vb); + + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_plane_size(vb, 0)); + +#ifdef PREFILL_BUFFER + /* + * This can be useful if you want to see if we actually fill + * the buffer with something + */ + if (vb2_plane_vaddr(vb, 0)) + memset(vb2_plane_vaddr(vb, 0), 0xbd, vb2_plane_size(vb, 0)); +#endif + + BUG_ON(NULL == icd->current_fmt); + + size = icd->user_height * bytes_per_line; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->parent, "Buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -ENOBUFS; + } + + vb2_set_plane_payload(vb, 0, size); + + tegra_camera_init_buffer(pcdev, buf); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_prepare()\n"); + + return 0; +} + +static void tegra_camera_videobuf_queue(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, + vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + struct tegra_buffer *buf = to_tegra_vb(vb); + + dev_dbg(icd->parent, "In tegra_camera_videobuf_queue()\n"); + + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); + + spin_lock_irq(&pcdev->videobuf_queue_lock); + list_add_tail(&buf->queue, &pcdev->capture); + + if (!pcdev->active) { + pcdev->active = vb; + schedule_work(&pcdev->work); + } + spin_unlock_irq(&pcdev->videobuf_queue_lock); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_queue()\n"); +} + +static void tegra_camera_videobuf_release(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = container_of(vb->vb2_queue, + struct soc_camera_device, + vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_buffer *buf = to_tegra_vb(vb); + struct tegra_camera_dev *pcdev = ici->priv; + + dev_dbg(icd->parent, "In tegra_camera_videobuf_release()\n"); + + mutex_lock(&pcdev->work_mutex); + + spin_lock_irq(&pcdev->videobuf_queue_lock); + + if (pcdev->active == vb) + pcdev->active = NULL; + + /* + * Doesn't hurt also if the list is empty, but it hurts, if queuing the + * buffer failed, and .buf_init() hasn't been called + */ + if (buf->queue.next) + list_del_init(&buf->queue); + + spin_unlock_irq(&pcdev->videobuf_queue_lock); + + mutex_unlock(&pcdev->work_mutex); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_release()\n"); +} + +static int tegra_camera_videobuf_init(struct vb2_buffer *vb) +{ + /* This is for locking debugging only */ + INIT_LIST_HEAD(&to_tegra_vb(vb)->queue); + + return 0; +} + +static int tegra_camera_stop_streaming(struct vb2_queue *q) +{ + struct soc_camera_device *icd = container_of(q, + struct soc_camera_device, + vb2_vidq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + struct list_head *buf_head, *tmp; + + mutex_lock(&pcdev->work_mutex); + + spin_lock_irq(&pcdev->videobuf_queue_lock); + + pcdev->active = NULL; + + list_for_each_safe(buf_head, tmp, &pcdev->capture) + list_del_init(buf_head); + + spin_unlock_irq(&pcdev->videobuf_queue_lock); + + mutex_unlock(&pcdev->work_mutex); + + return 0; +} + +static struct vb2_ops tegra_camera_videobuf_ops = { + .queue_setup = tegra_camera_videobuf_setup, + .buf_prepare = tegra_camera_videobuf_prepare, + .buf_queue = tegra_camera_videobuf_queue, + .buf_cleanup = tegra_camera_videobuf_release, + .buf_init = tegra_camera_videobuf_init, + .wait_prepare = soc_camera_unlock, + .wait_finish = soc_camera_lock, + .stop_streaming = tegra_camera_stop_streaming, +}; + +/* + * SOC camera host operations + */ +static int tegra_camera_init_videobuf(struct vb2_queue *q, + struct soc_camera_device *icd) +{ + dev_dbg(icd->parent, "In tegra_camera_init_videobuf()\n"); + + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = icd; + q->ops = &tegra_camera_videobuf_ops; + q->mem_ops = &vb2_dma_nvmap_memops; + q->buf_struct_size = sizeof(struct tegra_buffer); + + dev_dbg(icd->parent, "Finished tegra_camera_init_videobuf()\n"); + + return vb2_queue_init(q); +} + +/* + * Called with .video_lock held + */ +static int tegra_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + int err; + + if (pcdev->icd) + return -EBUSY; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + if (pcdev->pdata->enable_camera) { + err = pcdev->pdata->enable_camera(pcdev->ndev); + if (IS_ERR_VALUE(err)) + return err; + } + + tegra_camera_activate(pcdev); + + pcdev->icd = icd; + + pcdev->num_frames = 0; + + dev_dbg(icd->parent, "TEGRA Camera host attached to camera %d\n", + icd->devnum); + + return 0; +} + +/* Called with .video_lock held */ +static void tegra_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + + tegra_camera_deactivate(pcdev); + + pcdev->icd = NULL; + + if (pcdev->pdata->disable_camera) + pcdev->pdata->disable_camera(pcdev->ndev); + + pm_runtime_put_sync(ici->v4l2_dev.dev); + + dev_dbg(icd->parent, "Frames captured: %d\n", pcdev->num_frames); + + dev_dbg(icd->parent, "TEGRA camera host detached from camera %d\n", + icd->devnum); +} + +static int tegra_camera_set_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + return 0; +} + +static int tegra_camera_get_formats(struct soc_camera_device *icd, + unsigned int idx, + struct soc_camera_format_xlate *xlate) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + int formats = 0; + int ret; + enum v4l2_mbus_pixelcode code; + const struct soc_mbus_pixelfmt *fmt; + int k; + + ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + if (ret != 0) + /* No more formats */ + return 0; + + fmt = soc_mbus_get_fmtdesc(code); + if (!fmt) { + dev_err(dev, "Invalid format code #%u: %d\n", idx, code); + return 0; + } + + switch (code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: + formats += ARRAY_SIZE(tegra_camera_formats); + for (k = 0; + xlate && (k < ARRAY_SIZE(tegra_camera_formats)); + k++) { + xlate->host_fmt = &tegra_camera_formats[k]; + xlate->code = code; + xlate++; + + dev_info(dev, "Providing format %s using code %d\n", + tegra_camera_formats[k].name, code); + } + break; + default: + dev_info(dev, "Not supporting %s\n", fmt->name); + return 0; + } + + return formats; +} + +static void tegra_camera_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int tegra_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct device *dev = icd->parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); + struct tegra_camera_dev *pcdev = ici->priv; + + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate = NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + int ret; + + dev_dbg(dev, "In tegra_camera_set_fmt()\n"); + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(dev, "Format %x not found\n", pix->pixelformat); + return -EINVAL; + } + + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + if (IS_ERR_VALUE(ret)) { + dev_warn(dev, "Failed to configure for format %x\n", + pix->pixelformat); + return ret; + } + + if (mf.code != xlate->code) { + dev_warn(dev, "mf.code = %d, xlate->code = %d, mismatch\n", + mf.code, xlate->code); + return -EINVAL; + } + + icd->user_width = mf.width; + icd->user_height = mf.height; + icd->current_fmt = xlate; + + pcdev->field = pix->field; + + dev_dbg(dev, "Finished tegra_camera_set_fmt(), returning %d\n", ret); + + return ret; +} + +static int tegra_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + __u32 pixfmt = pix->pixelformat; + int ret; + + dev_dbg(icd->parent, "In tegra_camera_try_fmt()\n"); + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(icd->parent, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + pix->bytesperline = soc_mbus_bytes_per_line(pix->width, + xlate->host_fmt); + if (pix->bytesperline < 0) + return pix->bytesperline; + pix->sizeimage = pix->height * pix->bytesperline; + + /* limit to sensor capabilities */ + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + if (IS_ERR_VALUE(ret)) + return ret; + + pix->width = mf.width; + pix->height = mf.height; + pix->colorspace = mf.colorspace; + /* + * width and height could have been changed, therefore update the + * bytesperline and sizeimage here. + */ + pix->bytesperline = soc_mbus_bytes_per_line(pix->width, + xlate->host_fmt); + pix->sizeimage = pix->height * pix->bytesperline; + + switch (mf.field) { + case V4L2_FIELD_ANY: + case V4L2_FIELD_NONE: + pix->field = V4L2_FIELD_NONE; + break; + default: + /* TODO: support interlaced at least in pass-through mode */ + dev_err(icd->parent, "Field type %d unsupported.\n", + mf.field); + return -EINVAL; + } + + dev_dbg(icd->parent, + "Finished tegra_camera_try_fmt(), returning %d\n", ret); + + return ret; +} + +static int tegra_camera_reqbufs(struct soc_camera_device *icd, + struct v4l2_requestbuffers *p) +{ + return 0; +} + +static unsigned int tegra_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_poll(&icd->vb2_vidq, file, pt); +} + +static int tegra_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + strlcpy(cap->card, TEGRA_CAM_DRV_NAME, sizeof(cap->card)); + cap->version = TEGRA_CAM_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static struct soc_camera_host_ops tegra_soc_camera_host_ops = { + .owner = THIS_MODULE, + .init_videobuf2 = tegra_camera_init_videobuf, + .add = tegra_camera_add_device, + .remove = tegra_camera_remove_device, + .set_bus_param = tegra_camera_set_bus_param, + .get_formats = tegra_camera_get_formats, + .put_formats = tegra_camera_put_formats, + .set_fmt = tegra_camera_set_fmt, + .try_fmt = tegra_camera_try_fmt, + .reqbufs = tegra_camera_reqbufs, + .poll = tegra_camera_poll, + .querycap = tegra_camera_querycap, +}; + +static int __devinit tegra_camera_probe(struct nvhost_device *ndev, + struct nvhost_device_id *id_table) +{ + struct tegra_camera_dev *pcdev; + int err = 0; + + pcdev = kzalloc(sizeof(struct tegra_camera_dev), GFP_KERNEL); + if (!pcdev) { + dev_err(&ndev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit; + } + + pcdev->pdata = ndev->dev.platform_data; + pcdev->ndev = ndev; + + pcdev->ici.priv = pcdev; + pcdev->ici.v4l2_dev.dev = &ndev->dev; + pcdev->ici.nr = ndev->id; + pcdev->ici.drv_name = dev_name(&ndev->dev); + pcdev->ici.ops = &tegra_soc_camera_host_ops; + + INIT_LIST_HEAD(&pcdev->capture); + INIT_WORK(&pcdev->work, tegra_camera_work); + spin_lock_init(&pcdev->videobuf_queue_lock); + mutex_init(&pcdev->work_mutex); + + nvhost_set_drvdata(ndev, pcdev); + + if (!tegra_camera_port_is_valid(pcdev->pdata->port)) { + dev_err(&ndev->dev, "Invalid camera port %d in platform data\n", + pcdev->pdata->port); + goto exit_free_pcdev; + } + + pcdev->clk_vi = clk_get_sys("tegra_camera", "vi"); + if (IS_ERR_OR_NULL(pcdev->clk_vi)) { + dev_err(&ndev->dev, "Failed to get vi clock.\n"); + goto exit_free_pcdev; + } + + pcdev->clk_vi_sensor = clk_get_sys("tegra_camera", "vi_sensor"); + if (IS_ERR_OR_NULL(pcdev->clk_vi_sensor)) { + dev_err(&ndev->dev, "Failed to get vi_sensor clock.\n"); + goto exit_put_clk_vi; + } + + pcdev->clk_csi = clk_get_sys("tegra_camera", "csi"); + if (IS_ERR_OR_NULL(pcdev->clk_csi)) { + dev_err(&ndev->dev, "Failed to get csi clock.\n"); + goto exit_put_clk_vi_sensor; + } + + pcdev->clk_isp = clk_get_sys("tegra_camera", "isp"); + if (IS_ERR_OR_NULL(pcdev->clk_isp)) { + dev_err(&ndev->dev, "Failed to get isp clock.\n"); + goto exit_put_clk_csi; + } + + pcdev->clk_csus = clk_get_sys("tegra_camera", "csus"); + if (IS_ERR_OR_NULL(pcdev->clk_csus)) { + dev_err(&ndev->dev, "Failed to get csus clock.\n"); + goto exit_put_clk_isp; + } + + clk_set_rate(pcdev->clk_vi, 150000000); + clk_set_rate(pcdev->clk_vi_sensor, 24000000); + + err = nvhost_client_device_get_resources(ndev); + if (err) + goto exit_put_clk_csus; + + nvhost_client_device_init(ndev); + + pcdev->vi_base = ndev->aperture; + + pm_suspend_ignore_children(&ndev->dev, true); + pm_runtime_enable(&ndev->dev); + pm_runtime_resume(&ndev->dev); + + pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(NULL); + if (IS_ERR(pcdev->alloc_ctx)) { + err = PTR_ERR(pcdev->alloc_ctx); + goto exit_put_resources; + } + + err = soc_camera_host_register(&pcdev->ici); + if (IS_ERR_VALUE(err)) + goto exit_cleanup_alloc_ctx; + + dev_notice(&ndev->dev, "Tegra camera driver loaded.\n"); + + return err; + +exit_cleanup_alloc_ctx: + vb2_dma_nvmap_cleanup_ctx(&ndev->dev); +exit_put_resources: + pm_runtime_disable(&ndev->dev); + nvhost_client_device_put_resources(ndev); +exit_put_clk_csus: + clk_put(pcdev->clk_csus); +exit_put_clk_isp: + clk_put(pcdev->clk_isp); +exit_put_clk_csi: + clk_put(pcdev->clk_csi); +exit_put_clk_vi_sensor: + clk_put(pcdev->clk_vi_sensor); +exit_put_clk_vi: + clk_put(pcdev->clk_vi); +exit_free_pcdev: + kfree(pcdev); +exit: + return err; +} + +static int __devexit tegra_camera_remove(struct nvhost_device *ndev) +{ + struct soc_camera_host *ici = to_soc_camera_host(&ndev->dev); + struct tegra_camera_dev *pcdev = container_of(ici, + struct tegra_camera_dev, ici); + struct resource *res; + + res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "regs"); + if (!res) + return -EBUSY; + + soc_camera_host_unregister(ici); + + vb2_dma_nvmap_cleanup_ctx(&ndev->dev); + + pm_runtime_disable(&ndev->dev); + + nvhost_client_device_put_resources(ndev); + + clk_put(pcdev->clk_csus); + clk_put(pcdev->clk_isp); + clk_put(pcdev->clk_csi); + clk_put(pcdev->clk_vi_sensor); + clk_put(pcdev->clk_vi); + + kfree(pcdev); + + dev_notice(&ndev->dev, "Tegra camera host driver unloaded\n"); + + return 0; +} + +#ifdef CONFIG_PM_FISH +static int tegra_camera_suspend(struct nvhost_device *ndev, pm_message_t state) +{ + struct soc_camera_host *ici = to_soc_camera_host(&ndev->dev); + struct tegra_camera_dev *pcdev = container_of(ici, + struct tegra_camera_dev, ici); + + mutex_lock(&pcdev->work_mutex); + + /* We only need to do something if a camera sensor is attached. */ + if (pcdev->icd) { + /* Suspend the camera sensor. */ + WARN_ON(!pcdev->icd->ops->suspend); + pcdev->icd->ops->suspend(pcdev->icd, state); + + /* Power off the camera subsystem. */ + pcdev->pdata->disable_camera(pcdev->ndev); + + nvhost_module_idle_ext(nvhost_get_parent(ndev)); + } + + return 0; +} + +static int tegra_camera_resume(struct nvhost_device *ndev) +{ + struct soc_camera_host *ici = to_soc_camera_host(&ndev->dev); + struct tegra_camera_dev *pcdev = container_of(ici, + struct tegra_camera_dev, ici); + + /* We only need to do something if a camera sensor is attached. */ + if (pcdev->icd) { + nvhost_module_busy_ext(nvhost_get_parent(ndev)); + + /* Power on the camera subsystem. */ + pcdev->pdata->enable_camera(pcdev->ndev); + + /* Resume the camera host. */ + tegra_camera_save_syncpts(pcdev); + tegra_camera_capture_setup(pcdev); + + /* Resume the camera sensor. */ + WARN_ON(!pcdev->icd->ops->resume); + pcdev->icd->ops->resume(pcdev->icd); + } + + mutex_unlock(&pcdev->work_mutex); + + return 0; +} +#endif + +static struct nvhost_driver tegra_camera_driver = { + .driver = { + .name = TEGRA_CAM_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_camera_probe, + .remove = __devexit_p(tegra_camera_remove), +#ifdef CONFIG_PM_FISH + .suspend = tegra_camera_suspend, + .resume = tegra_camera_resume, +#endif +}; + + +static int __init tegra_camera_init(void) +{ + return nvhost_driver_register(&tegra_camera_driver); +} + +static void __exit tegra_camera_exit(void) +{ + nvhost_driver_unregister(&tegra_camera_driver); +} + +module_init(tegra_camera_init); +module_exit(tegra_camera_exit); + +MODULE_DESCRIPTION("TEGRA SoC Camera Host driver"); +MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("nvhost:" TEGRA_CAM_DRV_NAME); diff --git a/drivers/media/video/videobuf2-dma-nvmap.c b/drivers/media/video/videobuf2-dma-nvmap.c new file mode 100644 index 000000000000..27f43e5a3a57 --- /dev/null +++ b/drivers/media/video/videobuf2-dma-nvmap.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/nvmap.h> + +#include <media/videobuf2-core.h> +#include <media/videobuf2-memops.h> + +struct vb2_dc_conf { + struct device *dev; + struct nvmap_client *nvmap_client; +}; + +struct vb2_dc_buf { + struct vb2_dc_conf *conf; + void *vaddr; + dma_addr_t paddr; + unsigned long size; + struct vm_area_struct *vma; + atomic_t refcount; + struct vb2_vmarea_handler handler; + + struct nvmap_handle_ref *nvmap_ref; +}; + +static void vb2_dma_nvmap_put(void *buf_priv); + +static void *vb2_dma_nvmap_alloc(void *alloc_ctx, unsigned long size) +{ + struct vb2_dc_conf *conf = alloc_ctx; + struct vb2_dc_buf *buf; + int ret; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto exit; + } + + buf->nvmap_ref = nvmap_alloc(conf->nvmap_client, size, 32, + NVMAP_HANDLE_CACHEABLE, NVMAP_HEAP_SYSMEM); + if (IS_ERR(buf->nvmap_ref)) { + dev_err(conf->dev, "nvmap_alloc failed\n"); + ret = -ENOMEM; + goto exit_free; + } + + buf->paddr = nvmap_pin(conf->nvmap_client, buf->nvmap_ref); + if (IS_ERR_VALUE(buf->paddr)) { + dev_err(conf->dev, "nvmap_pin failed\n"); + ret = -ENOMEM; + goto exit_dealloc; + } + + buf->vaddr = nvmap_mmap(buf->nvmap_ref); + if (!buf->vaddr) { + dev_err(conf->dev, "nvmap_mmap failed\n"); + ret = -ENOMEM; + goto exit_unpin; + } + + buf->conf = conf; + buf->size = size; + + buf->handler.refcount = &buf->refcount; + buf->handler.put = vb2_dma_nvmap_put; + buf->handler.arg = buf; + + atomic_inc(&buf->refcount); + + return buf; + +exit_unpin: + nvmap_unpin(conf->nvmap_client, buf->nvmap_ref); +exit_dealloc: + nvmap_free(conf->nvmap_client, buf->nvmap_ref); +exit_free: + kfree(buf); +exit: + return ERR_PTR(ret); +} + +static void vb2_dma_nvmap_put(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + if (atomic_dec_and_test(&buf->refcount)) { + nvmap_munmap(buf->nvmap_ref, buf->vaddr); + nvmap_unpin(buf->conf->nvmap_client, buf->nvmap_ref); + nvmap_free(buf->conf->nvmap_client, buf->nvmap_ref); + kfree(buf); + } +} + +static void *vb2_dma_nvmap_cookie(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return &buf->paddr; +} + +static void *vb2_dma_nvmap_vaddr(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + if (!buf) + return 0; + + return buf->vaddr; +} + +static unsigned int vb2_dma_nvmap_num_users(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return atomic_read(&buf->refcount); +} + +static int vb2_dma_nvmap_mmap(void *buf_priv, struct vm_area_struct *vma) +{ + struct vb2_dc_buf *buf = buf_priv; + + if (!buf) { + printk(KERN_ERR "No buffer to map\n"); + return -EINVAL; + } + + return vb2_mmap_pfn_range(vma, buf->paddr, buf->size, + &vb2_common_vm_ops, &buf->handler); +} + +static void *vb2_dma_nvmap_get_userptr(void *alloc_ctx, unsigned long vaddr, + unsigned long size, int write) +{ + struct vb2_dc_buf *buf; + struct vm_area_struct *vma; + dma_addr_t paddr = 0; + int ret; + + buf = kzalloc(sizeof *buf, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr); + if (ret) { + printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", + vaddr); + kfree(buf); + return ERR_PTR(ret); + } + + buf->size = size; + buf->paddr = paddr; + buf->vma = vma; + + return buf; +} + +static void vb2_dma_nvmap_put_userptr(void *mem_priv) +{ + struct vb2_dc_buf *buf = mem_priv; + + if (!buf) + return; + + vb2_put_vma(buf->vma); + kfree(buf); +} + +const struct vb2_mem_ops vb2_dma_nvmap_memops = { + .alloc = vb2_dma_nvmap_alloc, + .put = vb2_dma_nvmap_put, + .cookie = vb2_dma_nvmap_cookie, + .vaddr = vb2_dma_nvmap_vaddr, + .mmap = vb2_dma_nvmap_mmap, + .get_userptr = vb2_dma_nvmap_get_userptr, + .put_userptr = vb2_dma_nvmap_put_userptr, + .num_users = vb2_dma_nvmap_num_users, +}; +EXPORT_SYMBOL_GPL(vb2_dma_nvmap_memops); + +void *vb2_dma_nvmap_init_ctx(struct device *dev) +{ + struct vb2_dc_conf *conf; + int ret; + + conf = kzalloc(sizeof *conf, GFP_KERNEL); + if (!conf) { + ret = -ENOMEM; + goto exit; + } + + conf->dev = dev; + + conf->nvmap_client = nvmap_create_client(nvmap_dev, + "videobuf2-dma-nvmap"); + if (!conf->nvmap_client) { + ret = -ENOMEM; + goto exit_free; + } + + return conf; + +exit_free: + kfree(conf); +exit: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(vb2_dma_nvmap_init_ctx); + +void vb2_dma_nvmap_cleanup_ctx(void *alloc_ctx) +{ + struct vb2_dc_conf *conf = alloc_ctx; + + nvmap_client_put(conf->nvmap_client); + + kfree(alloc_ctx); +} +EXPORT_SYMBOL_GPL(vb2_dma_nvmap_cleanup_ctx); + +MODULE_DESCRIPTION("DMA-nvmap memory handling routines for videobuf2"); +MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index e71ab64c2caa..8cb0f3af84ee 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -4339,6 +4339,7 @@ wl_iw_get_essid( if (!extra) return -EINVAL; + memset(&ssid, 0, sizeof(ssid)); if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) { WL_ERROR(("Error getting the SSID\n")); return error; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 44879e4a1170..a6b6dc8be91f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -3676,6 +3676,7 @@ void dhd_detach(dhd_pub_t *dhdp) #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + dhd->pub.hang_was_sent = 1; cancel_work_sync(&dhd->work_hang); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 0b1d2f2f73aa..5874c32958d9 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -1193,9 +1193,9 @@ static int __devinit isl29028_probe(struct i2c_client *client, "error %d\n", chip->irq, err); goto exit_free; } + chip->is_int_enable = true; } - chip->is_int_enable = true; chip->indio_dev = iio_allocate_device(0); if (!chip->indio_dev) { dev_err(&client->dev, "iio allocation fails\n"); diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c index 3f4f123da013..074a9b1ce6d9 100644 --- a/drivers/usb/gadget/tegra_udc.c +++ b/drivers/usb/gadget/tegra_udc.c @@ -38,6 +38,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/pm_qos_params.h> +#include <linux/platform_data/tegra_usb.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -649,7 +650,7 @@ static int tegra_ep_disable(struct usb_ep *_ep) ep_num = ep_index(ep); /* Touch the registers if cable is connected and phy is on */ - if (vbus_enabled(udc)) { + if (udc->vbus_active) { epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); if (ep_is_in(ep)) epctrl &= ~EPCTRL_TX_ENABLE; @@ -1009,7 +1010,7 @@ static int tegra_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ep_num = ep_index(ep); /* Touch the registers if cable is connected and phy is on */ - if (vbus_enabled(udc)) { + if (udc->vbus_active) { epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); if (ep_is_in(ep)) epctrl &= ~EPCTRL_TX_ENABLE; @@ -1060,7 +1061,7 @@ static int tegra_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) /* Enable EP */ out: /* Touch the registers if cable is connected and phy is on */ - if (vbus_enabled(udc)) { + if (udc->vbus_active) { epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); if (ep_is_in(ep)) epctrl |= EPCTRL_TX_ENABLE; @@ -1182,7 +1183,7 @@ static void tegra_ep_fifo_flush(struct usb_ep *_ep) bits = 1 << ep_num; /* Touch the registers if cable is connected and phy is on */ - if (!vbus_enabled(udc)) + if (!udc->vbus_active) return; timeout = jiffies + UDC_FLUSH_TIMEOUT_MS; @@ -2377,14 +2378,13 @@ static int tegra_udc_start(struct usb_gadget_driver *driver, goto out; } - /* Enable DR IRQ reg and Set usbcmd reg Run bit */ if (vbus_enabled(udc)) { dr_controller_run(udc); udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; udc->ep0_dir = 0; - udc->vbus_active = vbus_enabled(udc); + udc->vbus_active = 1; } printk(KERN_INFO "%s: bind to driver %s\n", @@ -2580,6 +2580,7 @@ static int __init tegra_udc_probe(struct platform_device *pdev) { struct tegra_udc *udc; struct resource *res; + struct tegra_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); int err = -ENODEV; DBG("%s(%d) BEGIN\n", __func__, __LINE__); @@ -2630,12 +2631,14 @@ static int __init tegra_udc_probe(struct platform_device *pdev) goto err_iounmap; } - err = enable_irq_wake(udc->irq); - if (err < 0) { - dev_warn(&pdev->dev, - "Couldn't enable USB udc mode wakeup, irq=%d, error=%d\n", - udc->irq, err); - err = 0; + if (pdata->u_data.dev.remote_wakeup_supported) { + err = enable_irq_wake(udc->irq); + if (err < 0) { + dev_warn(&pdev->dev, + "Couldn't enable USB udc mode wakeup, irq=%d," + " error=%d\n", udc->irq, err); + err = 0; + } } udc->phy = tegra_usb_phy_open(pdev); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 13d96d3b1320..095447d9e3a2 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -522,6 +522,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) struct resource *res; struct usb_hcd *hcd; struct tegra_ehci_hcd *tegra; + struct tegra_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); int err = 0; int irq; @@ -592,13 +593,15 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_phy; } - err = enable_irq_wake(tegra->irq); - if (err < 0) { - dev_warn(&pdev->dev, - "Couldn't enable USB host mode wakeup, irq=%d, " - "error=%d\n", irq, err); - err = 0; - tegra->irq = 0; + if (pdata->u_data.host.remote_wakeup_supported) { + err = enable_irq_wake(tegra->irq); + if (err < 0) { + dev_warn(&pdev->dev, + "Couldn't enable USB host mode wakeup," + " irq=%d error=%d\n", irq, err); + err = 0; + tegra->irq = 0; + } } tegra->ehci = hcd_to_ehci(hcd); @@ -646,6 +649,7 @@ static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state) static int tegra_ehci_remove(struct platform_device *pdev) { struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev); + struct tegra_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); if (tegra == NULL || hcd == NULL) @@ -658,7 +662,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) } #endif - if (tegra->irq) + if (tegra->irq && pdata->u_data.host.remote_wakeup_supported) disable_irq_wake(tegra->irq); /* Make sure phy is powered ON to access USB register */ diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 22e303ede24e..12c359bcbe46 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -173,8 +173,6 @@ static void tegra_stop_host(struct tegra_otg_data *tegra) if (pdev) { /* unregister host from otg */ - kfree(pdev->dev.platform_data); - pdev->dev.platform_data = NULL; platform_device_unregister(pdev); tegra->pdev = NULL; } @@ -472,12 +470,14 @@ static int tegra_otg_probe(struct platform_device *pdev) goto err_irq; } - err = enable_irq_wake(tegra->irq); - if (err < 0) { - dev_warn(&pdev->dev, - "Couldn't enable USB otg mode wakeup, irq=%d, error=%d\n", - tegra->irq, err); - err = 0; + if (pdata->ehci_pdata->u_data.host.remote_wakeup_supported) { + err = enable_irq_wake(tegra->irq); + if (err < 0) { + dev_warn(&pdev->dev, + "Couldn't enable USB otg mode wakeup," + " irq=%d, error=%d\n", tegra->irq, err); + err = 0; + } } INIT_WORK(&tegra->work, irq_work); diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index ca2103f73c8a..d4c3f8bc9a49 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -58,21 +58,6 @@ #define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL 0x01000000 #define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL 0x0 -static struct fb_videomode tegra_dc_hdmi_fallback_mode = { - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = KHZ2PICOS(25200), - .hsync_len = 96, /* h_sync_width */ - .vsync_len = 2, /* v_sync_width */ - .left_margin = 48, /* h_back_porch */ - .upper_margin = 33, /* v_back_porch */ - .right_margin = 16, /* h_front_porch */ - .lower_margin = 10, /* v_front_porch */ - .vmode = 0, - .sync = 0, -}; - static struct tegra_dc_mode override_disp_mode[3]; static void _tegra_dc_controller_disable(struct tegra_dc *dc); @@ -1462,34 +1447,6 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc) } #endif -static int _tegra_dc_set_default_videomode(struct tegra_dc *dc) -{ - if (dc->mode.pclk == 0) { - switch (dc->out->type) { - case TEGRA_DC_OUT_HDMI: - /* DC enable called but no videomode is loaded. - Check if HDMI is connected, then set fallback mdoe */ - if (tegra_dc_hpd(dc)) { - return tegra_dc_set_fb_mode(dc, - &tegra_dc_hdmi_fallback_mode, 0); - } else - return false; - - break; - - /* Do nothing for other outputs for now */ - case TEGRA_DC_OUT_RGB: - - case TEGRA_DC_OUT_DSI: - - default: - return false; - } - } - - return false; -} - static bool _tegra_dc_enable(struct tegra_dc *dc) { if (dc->mode.pclk == 0) @@ -1881,13 +1838,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev, dc->ext = NULL; } - mutex_lock(&dc->lock); - if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) { - _tegra_dc_set_default_videomode(dc); - dc->enabled = _tegra_dc_enable(dc); - } - mutex_unlock(&dc->lock); - /* interrupt handler must be registered before tegra_fb_register() */ if (request_irq(irq, tegra_dc_irq, 0, dev_name(&ndev->dev), dc)) { @@ -1895,6 +1845,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev, ret = -EBUSY; goto err_put_emc_clk; } + disable_dc_irq(irq); + + mutex_lock(&dc->lock); + if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) + dc->enabled = _tegra_dc_enable(dc); + mutex_unlock(&dc->lock); tegra_dc_create_debugfs(dc); @@ -1919,6 +1875,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev, } dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); + if (IS_ERR_OR_NULL(dc->fb)) dc->fb = NULL; } @@ -2033,10 +1990,8 @@ static int tegra_dc_resume(struct nvhost_device *ndev) mutex_lock(&dc->lock); dc->suspended = false; - if (dc->enabled) { - _tegra_dc_set_default_videomode(dc); + if (dc->enabled) _tegra_dc_enable(dc); - } if (dc->out && dc->out->hotplug_init) dc->out->hotplug_init(); diff --git a/drivers/video/tegra/dc/dc_config.c b/drivers/video/tegra/dc/dc_config.c index f238faddab12..90f505bba28a 100644 --- a/drivers/video/tegra/dc/dc_config.c +++ b/drivers/video/tegra/dc/dc_config.c @@ -23,7 +23,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_a[] = { { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -32,7 +32,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_a[] = { { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -40,7 +40,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_a[] = { { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -50,7 +50,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_a[] = { static struct tegra_dc_feature_entry t20_feature_entries_b[] = { { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -59,7 +59,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_b[] = { { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -67,7 +67,7 @@ static struct tegra_dc_feature_entry t20_feature_entries_b[] = { { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -85,7 +85,7 @@ struct tegra_dc_feature t20_feature_table_b = { static struct tegra_dc_feature_entry t30_feature_entries_a[] = { { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, @@ -94,7 +94,7 @@ static struct tegra_dc_feature_entry t30_feature_entries_a[] = { { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, @@ -102,7 +102,7 @@ static struct tegra_dc_feature_entry t30_feature_entries_a[] = { { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} }, @@ -112,7 +112,7 @@ static struct tegra_dc_feature_entry t30_feature_entries_a[] = { static struct tegra_dc_feature_entry t30_feature_entries_b[] = { { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} }, { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} }, { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -121,7 +121,7 @@ static struct tegra_dc_feature_entry t30_feature_entries_b[] = { { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} }, { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} }, { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, @@ -129,7 +129,7 @@ static struct tegra_dc_feature_entry t30_feature_entries_b[] = { { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} }, { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} }, - { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} }, + { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 1, 4095, 1,} }, { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} }, { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} }, { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} }, diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 59c162bb3b4c..f69048f62cc2 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -126,12 +126,9 @@ static int tegra_fb_set_par(struct fb_info *info) return -EINVAL; } /* if line_length unset, then pad the stride */ - if (!info->fix.line_length) { - info->fix.line_length = var->xres * var->bits_per_pixel - / 8; - info->fix.line_length = round_up(info->fix.line_length, - TEGRA_LINEAR_PITCH_ALIGNMENT); - } + info->fix.line_length = var->xres * var->bits_per_pixel / 8; + info->fix.line_length = round_up(info->fix.line_length, + TEGRA_LINEAR_PITCH_ALIGNMENT); tegra_fb->win->stride = info->fix.line_length; tegra_fb->win->stride_uv = 0; tegra_fb->win->phys_addr_u = 0; @@ -496,33 +493,15 @@ const struct fb_videomode *tegra_fb_find_best_mode( return best; } -static int tegra_fb_activate_mode(struct tegra_fb_info *fb_info, - struct fb_var_screeninfo *var) -{ - int err; - struct fb_info *info = fb_info->info; - - var->activate |= FB_ACTIVATE_FORCE; - console_lock(); - info->flags |= FBINFO_MISC_USEREVENT; - err = fb_set_var(info, var); - info->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - if (err) - return err; - return 0; -} - void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, struct fb_monspecs *specs, bool (*mode_filter)(const struct tegra_dc *dc, struct fb_videomode *mode)) { int i; - int ret = 0; + bool first = false; struct fb_event event; struct fb_info *info = fb_info->info; - const struct fb_videomode *best_mode = NULL; struct fb_var_screeninfo var = {0,}; mutex_lock(&fb_info->info->lock); @@ -561,6 +540,13 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, fb_var_to_videomode(&m, &var); fb_add_videomode(&m, &fb_info->info->modelist); + /* EDID stds recommend first detailed mode + to be applied as default,but if first mode + doesn't pass mode filter, we have to select + and apply other mode. So flag on if first + mode passes mode filter */ + if (!i) + first = true; } } else { fb_add_videomode(&specs->modedb[i], @@ -568,36 +554,31 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, } } - /* Get the best mode from modedb and apply on fb */ - var.xres = 0; - var.yres = 0; - best_mode = tegra_fb_find_best_mode(&var, &info->modelist); - - /* Update framebuffer with best mode */ - fb_videomode_to_var(&var, best_mode); - - /* TODO: Get proper way of getting rid of a 0 bpp */ - if (!var.bits_per_pixel) - var.bits_per_pixel = 32; - - memcpy(&info->var, &var, sizeof(struct fb_var_screeninfo)); - - ret = tegra_fb_activate_mode(fb_info, &var); - if (ret) - return; + /* We can't apply first detailed mode, so get the best mode + based on resolution and apply on fb */ + if (!first) { + var.xres = 0; + var.yres = 0; + info->mode = (struct fb_videomode *) + tegra_fb_find_best_mode(&var, &info->modelist); + } + /* Prepare fb info with new mode details */ + fb_videomode_to_var(&info->var, info->mode); event.info = fb_info->info; #ifdef CONFIG_FRAMEBUFFER_CONSOLE -/* Lock the console before sending the noti. Fbconsole - * on HDMI might be using console - */ + /* Send a noti to change fb_display[].mode for all vc's */ + console_lock(); + fb_notifier_call_chain(FB_EVENT_MODE_CHANGE_ALL, &event); + console_unlock(); + + /* Notify framebuffer console about mode change */ console_lock(); -#endif fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); -#ifdef CONFIG_FRAMEBUFFER_CONSOLE -/* Unlock the console */ console_unlock(); +#else + fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); #endif mutex_unlock(&fb_info->info->lock); @@ -649,12 +630,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, tegra_fb->valid = true; } - info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8; - - stride = tegra_dc_get_stride(dc, 0); - if (!stride) /* default to pad the stride */ - stride = round_up(info->fix.line_length, - TEGRA_LINEAR_PITCH_ALIGNMENT); + stride = fb_data->xres * fb_data->bits_per_pixel / 8; + stride = round_up(stride, TEGRA_LINEAR_PITCH_ALIGNMENT); info->fbops = &tegra_fb_ops; info->pseudo_palette = pseudo_palette; diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index aaa038221971..4614689ec37c 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -658,3 +658,15 @@ fail: return -ENXIO; } + +void nvhost_client_device_put_resources(struct nvhost_device *dev) +{ + struct resource *r; + + r = nvhost_get_resource(dev, IORESOURCE_MEM, 0); + BUG_ON(!r); + + iounmap(dev->aperture); + + release_mem_region(r->start, resource_size(r)); +} diff --git a/drivers/video/tegra/host/bus_client.h b/drivers/video/tegra/host/bus_client.h index e95ea0bc3401..8c7bdc9faefe 100644 --- a/drivers/video/tegra/host/bus_client.h +++ b/drivers/video/tegra/host/bus_client.h @@ -37,5 +37,6 @@ int nvhost_client_device_init(struct nvhost_device *dev); int nvhost_client_device_suspend(struct nvhost_device *dev); int nvhost_client_device_get_resources(struct nvhost_device *dev); +void nvhost_client_device_put_resources(struct nvhost_device *dev); #endif diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index a0dcf26337f8..19b821d41284 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -605,3 +605,16 @@ void nvmap_free(struct nvmap_client *client, struct nvmap_handle_ref *r) nvmap_free_handle_id(client, nvmap_ref_to_id(r)); } + +int nvmap_mark_global(struct nvmap_client *client, struct nvmap_handle_ref *r) +{ + struct nvmap_handle *h; + + h = nvmap_get_handle_id(client, (unsigned long)r->handle); + if (!h) + return -EINVAL; + r->handle->global = true; + nvmap_handle_put(h); + + return 0; +} diff --git a/include/linux/nvmap.h b/include/linux/nvmap.h index 692956b2fc4f..9cc978ec5a7f 100644 --- a/include/linux/nvmap.h +++ b/include/linux/nvmap.h @@ -124,6 +124,8 @@ void nvmap_unpin_handles(struct nvmap_client *client, struct nvmap_handle_ref *nvmap_duplicate_handle_id(struct nvmap_client *client, unsigned long id); +int nvmap_mark_global(struct nvmap_client *client, struct nvmap_handle_ref *r); + struct nvmap_platform_carveout { const char *name; unsigned int usage_mask; diff --git a/include/media/tegra_camera.h b/include/media/tegra_camera.h index 3c41864cc71f..d47839282382 100644 --- a/include/media/tegra_camera.h +++ b/include/media/tegra_camera.h @@ -56,7 +56,7 @@ struct tegra_camera_platform_data { #if defined(CONFIG_TEGRA_CAMERA) int is_tegra_camera_on(void); #else -int is_tegra_camera_on(void) { return 0; } +static inline int is_tegra_camera_on(void) { return 0; } #endif #define TEGRA_CAMERA_IOCTL_ENABLE _IOWR('i', 1, uint) diff --git a/include/media/tegra_v4l2_camera.h b/include/media/tegra_v4l2_camera.h new file mode 100644 index 000000000000..f6390b6e5609 --- /dev/null +++ b/include/media/tegra_v4l2_camera.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _TEGRA_CAMERA_H_ +#define _TEGRA_CAMERA_H_ + +#include <linux/regulator/consumer.h> +#include <linux/i2c.h> +#include <linux/nvhost.h> + +enum tegra_camera_port { + TEGRA_CAMERA_PORT_CSI_A = 1, + TEGRA_CAMERA_PORT_CSI_B, + TEGRA_CAMERA_PORT_VIP, +}; + +struct tegra_camera_platform_data { + int (*enable_camera)(struct nvhost_device *ndev); + void (*disable_camera)(struct nvhost_device *ndev); + bool flip_h; + bool flip_v; + enum tegra_camera_port port; + int lanes; /* For CSI port only */ + bool continuous_clk; /* For CSI port only */ +}; + +#endif /* _TEGRA_CAMERA_H_ */ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 63fd9d3db296..fb929eacd2e6 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -77,6 +77,7 @@ enum { V4L2_IDENT_OV2640 = 259, V4L2_IDENT_OV9740 = 260, V4L2_IDENT_OV5642 = 261, + V4L2_IDENT_OV5640 = 262, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, diff --git a/include/media/videobuf2-dma-nvmap.h b/include/media/videobuf2-dma-nvmap.h new file mode 100644 index 000000000000..39ae3d3a1d5d --- /dev/null +++ b/include/media/videobuf2-dma-nvmap.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _MEDIA_VIDEOBUF2_DMA_NVMAP_H +#define _MEDIA_VIDEOBUF2_DMA_NVMAP_H + +#include <media/videobuf2-core.h> +#include <linux/dma-mapping.h> + +static inline dma_addr_t +vb2_dma_nvmap_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no) +{ + dma_addr_t *paddr = vb2_plane_cookie(vb, plane_no); + + return *paddr; +} + +void *vb2_dma_nvmap_init_ctx(struct device *dev); +void vb2_dma_nvmap_cleanup_ctx(void *alloc_ctx); + +extern const struct vb2_mem_ops vb2_dma_nvmap_memops; + +#endif diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 915d7ae527b3..14bee5ade7c7 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1264,9 +1264,11 @@ struct hci_conn_info { __u8 out; __u16 state; __u32 link_mode; +#ifdef CONFIG_ANDROID __u32 mtu; __u32 cnt; __u32 pkts; +#endif }; struct hci_dev_req { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 33c4e0cd83b1..46f2c18a4cb8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -907,6 +907,7 @@ int hci_get_conn_list(void __user *arg) (ci + n)->out = c->out; (ci + n)->state = c->state; (ci + n)->link_mode = c->link_mode; +#ifdef CONFIG_ANDROID if (c->type == SCO_LINK) { (ci + n)->mtu = hdev->sco_mtu; (ci + n)->cnt = hdev->sco_cnt; @@ -916,6 +917,7 @@ int hci_get_conn_list(void __user *arg) (ci + n)->cnt = hdev->acl_cnt; (ci + n)->pkts = hdev->acl_pkts; } +#endif if (++n >= req.conn_num) break; } @@ -952,6 +954,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ci.out = conn->out; ci.state = conn->state; ci.link_mode = conn->link_mode; +#ifdef CONFIG_ANDROID if (req.type == SCO_LINK) { ci.mtu = hdev->sco_mtu; ci.cnt = hdev->sco_cnt; @@ -961,6 +964,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ci.cnt = hdev->acl_cnt; ci.pkts = hdev->acl_pkts; } +#endif } hci_dev_unlock_bh(hdev); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 1bfe202d0574..a47a746a2b7e 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -234,6 +234,7 @@ config SND_SOC_TEGRA_RT5640 select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_TEGRA30_SPDIF if ARCH_TEGRA_3x_SOC select SND_SOC_RT5640 + select SND_SOC_RT5639 select SND_SOC_SPDIF select SND_SOC_TEGRA30_DAM if ARCH_TEGRA_3x_SOC help |