M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,15 @@
GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
ccflags-y += -Idrivers/media/platform/msm/camera_v1 -Idrivers/media/platform/msm/camera_v1/server
ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v1/csi/include/csi2.0
else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
ccflags-y += -Idrivers/media/platform/msm/camera_v1/csi/include/csi3.0
endif
obj-$(CONFIG_MSM_CSI2_REGISTER) += msm_csi2_register.o
obj-$(CONFIG_MSM_CSIPHY) += msm_csiphy.o
obj-$(CONFIG_MSM_CSID) += msm_csid.o
obj-$(CONFIG_MSM_ISPIF) += msm_ispif.o
obj-$(CONFIG_ARCH_MSM8960) += msm_csi2_register.o msm_csiphy.o msm_csid.o msm_ispif.o
obj-$(CONFIG_ARCH_MSM7X27A) += msm_csic_register.o msm_csic.o
obj-$(CONFIG_ARCH_MSM8X60) += msm_csic_register.o msm_csic.o
obj-$(CONFIG_ARCH_MSM7X30) += msm_csic_register.o msm_csic.o
@@ -0,0 +1,52 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSID_HWREG_H
#define MSM_CSID_HWREG_H
/* MIPI CSID registers */
#define CSID_HW_VERSION_ADDR 0x0
#define CSID_CORE_CTRL_0_ADDR 0x4
#define CSID_CORE_CTRL_1_ADDR 0x4
#define CSID_RST_CMD_ADDR 0x8
#define CSID_CID_LUT_VC_0_ADDR 0xc
#define CSID_CID_LUT_VC_1_ADDR 0x10
#define CSID_CID_LUT_VC_2_ADDR 0x14
#define CSID_CID_LUT_VC_3_ADDR 0x18
#define CSID_CID_n_CFG_ADDR 0x1C
#define CSID_IRQ_CLEAR_CMD_ADDR 0x5c
#define CSID_IRQ_MASK_ADDR 0x60
#define CSID_IRQ_STATUS_ADDR 0x64
#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x68
#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x6c
#define CSID_CAPTURED_SHORT_PKT_ADDR 0x70
#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x74
#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x78
#define CSID_PIF_MISR_DL0_ADDR 0x7C
#define CSID_PIF_MISR_DL1_ADDR 0x80
#define CSID_PIF_MISR_DL2_ADDR 0x84
#define CSID_PIF_MISR_DL3_ADDR 0x88
#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x8C
#define CSID_STATS_ECC_ADDR 0x90
#define CSID_STATS_CRC_ADDR 0x94
#define CSID_TG_CTRL_ADDR 0x9C
#define CSID_TG_VC_CFG_ADDR 0xA0
#define CSID_TG_DT_n_CFG_0_ADDR 0xA8
#define CSID_TG_DT_n_CFG_1_ADDR 0xAC
#define CSID_TG_DT_n_CFG_2_ADDR 0xB0
#define CSID_RST_DONE_IRQ_BITSHIFT 11
#define CSID_RST_STB_ALL 0x7FFF
#define CSID_DL_INPUT_SEL_SHIFT 0x2
#define CSID_PHY_SEL_SHIFT 17
#define CSID_VERSION 0x02000011
#endif
@@ -0,0 +1,43 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSIPHY_HWREG_H
#define MSM_CSIPHY_HWREG_H
/*MIPI CSI PHY registers*/
#define MIPI_CSIPHY_HW_VERSION_ADDR 0x180
#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x180
#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1A0
#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x6F
#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1A4
#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1C0
#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1C4
#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1E0
#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1E8
#define CSIPHY_VERSION 0x0
#endif
@@ -0,0 +1,88 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_ISPIF_HWREG_H
#define MSM_ISPIF_HWREG_H
/* ISPIF registers */
#define ISPIF_RST_CMD_ADDR 0x00
#define ISPIF_RST_CMD_1_ADDR 0x00
#define ISPIF_INTF_CMD_ADDR 0x04
#define ISPIF_INTF_CMD_1_ADDR 0x30
#define ISPIF_CTRL_ADDR 0x08
#define ISPIF_INPUT_SEL_ADDR 0x0C
#define ISPIF_PIX_0_INTF_CID_MASK_ADDR 0x10
#define ISPIF_RDI_0_INTF_CID_MASK_ADDR 0x14
#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0x38
#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0x3C
#define ISPIF_RDI_2_INTF_CID_MASK_ADDR 0x44
#define ISPIF_PIX_0_STATUS_ADDR 0x24
#define ISPIF_RDI_0_STATUS_ADDR 0x28
#define ISPIF_PIX_1_STATUS_ADDR 0x60
#define ISPIF_RDI_1_STATUS_ADDR 0x64
#define ISPIF_RDI_2_STATUS_ADDR 0x6C
#define ISPIF_IRQ_MASK_ADDR 0x0100
#define ISPIF_IRQ_CLEAR_ADDR 0x0104
#define ISPIF_IRQ_STATUS_ADDR 0x0108
#define ISPIF_IRQ_MASK_1_ADDR 0x010C
#define ISPIF_IRQ_CLEAR_1_ADDR 0x0110
#define ISPIF_IRQ_STATUS_1_ADDR 0x0114
#define ISPIF_IRQ_MASK_2_ADDR 0x0118
#define ISPIF_IRQ_CLEAR_2_ADDR 0x011C
#define ISPIF_IRQ_STATUS_2_ADDR 0x0120
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x0124
/*ISPIF RESET BITS*/
#define VFE_CLK_DOMAIN_RST 31
#define RDI_CLK_DOMAIN_RST 30
#define PIX_CLK_DOMAIN_RST 29
#define AHB_CLK_DOMAIN_RST 28
#define RDI_1_CLK_DOMAIN_RST 27
#define RDI_2_VFE_RST_STB 19
#define RDI_2_CSID_RST_STB 18
#define RDI_1_VFE_RST_STB 13
#define RDI_1_CSID_RST_STB 12
#define RDI_0_VFE_RST_STB 7
#define RDI_0_CSID_RST_STB 6
#define PIX_1_VFE_RST_STB 10
#define PIX_1_CSID_RST_STB 9
#define PIX_0_VFE_RST_STB 4
#define PIX_0_CSID_RST_STB 3
#define SW_REG_RST_STB 2
#define MISC_LOGIC_RST_STB 1
#define STROBED_RST_EN 0
#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
#define PIX_INTF_0_OVERFLOW_IRQ 12
#define RAW_INTF_0_OVERFLOW_IRQ 25
#define RAW_INTF_1_OVERFLOW_IRQ 25
#define RAW_INTF_2_OVERFLOW_IRQ 12
#define RESET_DONE_IRQ 27
#define ISPIF_IRQ_STATUS_MASK 0x0A493249
#define ISPIF_IRQ_STATUS_1_MASK 0x02493249
#define ISPIF_IRQ_STATUS_2_MASK 0x00001249
#define ISPIF_IRQ_STATUS_PIX_SOF_MASK 0x249
#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000
#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000
#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x249
#define ISPIF_IRQ_STATUS_SOF_MASK 0x492249
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1
#endif
@@ -0,0 +1,52 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSID_HWREG_H
#define MSM_CSID_HWREG_H
/* MIPI CSID registers */
#define CSID_HW_VERSION_ADDR 0x0
#define CSID_CORE_CTRL_0_ADDR 0x4
#define CSID_CORE_CTRL_1_ADDR 0x8
#define CSID_RST_CMD_ADDR 0xC
#define CSID_CID_LUT_VC_0_ADDR 0x10
#define CSID_CID_LUT_VC_1_ADDR 0x14
#define CSID_CID_LUT_VC_2_ADDR 0x18
#define CSID_CID_LUT_VC_3_ADDR 0x1C
#define CSID_CID_n_CFG_ADDR 0x20
#define CSID_IRQ_CLEAR_CMD_ADDR 0x60
#define CSID_IRQ_MASK_ADDR 0x64
#define CSID_IRQ_STATUS_ADDR 0x68
#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x6C
#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x70
#define CSID_CAPTURED_SHORT_PKT_ADDR 0x74
#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x78
#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x7C
#define CSID_PIF_MISR_DL0_ADDR 0x80
#define CSID_PIF_MISR_DL1_ADDR 0x84
#define CSID_PIF_MISR_DL2_ADDR 0x88
#define CSID_PIF_MISR_DL3_ADDR 0x8C
#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x90
#define CSID_STATS_ECC_ADDR 0x94
#define CSID_STATS_CRC_ADDR 0x98
#define CSID_TG_CTRL_ADDR 0xA0
#define CSID_TG_VC_CFG_ADDR 0xA4
#define CSID_TG_DT_n_CFG_0_ADDR 0xAC
#define CSID_TG_DT_n_CFG_1_ADDR 0xB0
#define CSID_TG_DT_n_CFG_2_ADDR 0xB4
#define CSID_RST_DONE_IRQ_BITSHIFT 11
#define CSID_RST_STB_ALL 0x7FFF
#define CSID_DL_INPUT_SEL_SHIFT 0x4
#define CSID_PHY_SEL_SHIFT 17
#define CSID_VERSION 0x30000000
#endif
@@ -0,0 +1,43 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSIPHY_HWREG_H
#define MSM_CSIPHY_HWREG_H
/*MIPI CSI PHY registers*/
#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
#define MIPI_CSIPHY_HW_VERSION_ADDR 0x188
#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x18C
#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1AC
#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x3F
#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1AC
#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1CC
#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1CC
#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1EC
#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1F4
#define CSIPHY_VERSION 0x10
#endif
@@ -0,0 +1,102 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_ISPIF_HWREG_H
#define MSM_ISPIF_HWREG_H
/* ISPIF registers */
#define ISPIF_RST_CMD_ADDR 0x08
#define ISPIF_RST_CMD_1_ADDR 0x0C
#define ISPIF_INTF_CMD_ADDR 0x248
#define ISPIF_INTF_CMD_1_ADDR 0x24C
#define ISPIF_CTRL_ADDR 0x08
#define ISPIF_INPUT_SEL_ADDR 0x244
#define ISPIF_PIX_0_INTF_CID_MASK_ADDR 0x254
#define ISPIF_RDI_0_INTF_CID_MASK_ADDR 0x264
#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0x258
#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0x268
#define ISPIF_RDI_2_INTF_CID_MASK_ADDR 0x26C
#define ISPIF_PIX_0_STATUS_ADDR 0x2C0
#define ISPIF_RDI_0_STATUS_ADDR 0x2D0
#define ISPIF_PIX_1_STATUS_ADDR 0x2C4
#define ISPIF_RDI_1_STATUS_ADDR 0x2D4
#define ISPIF_RDI_2_STATUS_ADDR 0x2D8
#define ISPIF_IRQ_MASK_ADDR 0x208
#define ISPIF_IRQ_CLEAR_ADDR 0x230
#define ISPIF_IRQ_STATUS_ADDR 0x21C
#define ISPIF_IRQ_MASK_1_ADDR 0x20C
#define ISPIF_IRQ_CLEAR_1_ADDR 0x234
#define ISPIF_IRQ_STATUS_1_ADDR 0x220
#define ISPIF_IRQ_MASK_2_ADDR 0x210
#define ISPIF_IRQ_CLEAR_2_ADDR 0x238
#define ISPIF_IRQ_STATUS_2_ADDR 0x224
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x1C
/* new */
#define ISPIF_VFE_m_CTRL_0_ADDR 0x200
#define ISPIF_VFE_m_IRQ_MASK_0 0x208
#define ISPIF_VFE_m_IRQ_MASK_1 0x20C
#define ISPIF_VFE_m_IRQ_MASK_2 0x210
#define ISPIF_VFE_m_IRQ_STATUS_0 0x21C
#define ISPIF_VFE_m_IRQ_STATUS_1 0x220
#define ISPIF_VFE_m_IRQ_STATUS_2 0x224
#define ISPIF_VFE_m_IRQ_CLEAR_0 0x230
#define ISPIF_VFE_m_IRQ_CLEAR_1 0x234
#define ISPIF_VFE_m_IRQ_CLEAR_2 0x238
/*ISPIF RESET BITS*/
#define VFE_CLK_DOMAIN_RST 31
#define RDI_CLK_DOMAIN_RST 26
#define RDI_1_CLK_DOMAIN_RST 27
#define RDI_2_CLK_DOMAIN_RST 28
#define PIX_CLK_DOMAIN_RST 29
#define PIX_1_CLK_DOMAIN_RST 30
#define AHB_CLK_DOMAIN_RST 25
#define RDI_2_VFE_RST_STB 12
#define RDI_2_CSID_RST_STB 11
#define RDI_1_VFE_RST_STB 10
#define RDI_1_CSID_RST_STB 9
#define RDI_0_VFE_RST_STB 8
#define RDI_0_CSID_RST_STB 7
#define PIX_1_VFE_RST_STB 6
#define PIX_1_CSID_RST_STB 5
#define PIX_0_VFE_RST_STB 4
#define PIX_0_CSID_RST_STB 3
#define SW_REG_RST_STB 2
#define MISC_LOGIC_RST_STB 1
#define STROBED_RST_EN 0
#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
#define PIX_INTF_0_OVERFLOW_IRQ 12
#define RAW_INTF_0_OVERFLOW_IRQ 25
#define RAW_INTF_1_OVERFLOW_IRQ 25
#define RAW_INTF_2_OVERFLOW_IRQ 12
#define RESET_DONE_IRQ 27
#define ISPIF_IRQ_STATUS_MASK 0x0A493249
#define ISPIF_IRQ_STATUS_1_MASK 0x02493249
#define ISPIF_IRQ_STATUS_2_MASK 0x00001249
#define ISPIF_IRQ_STATUS_PIX_SOF_MASK 0x249
#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000
#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000
#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x249
#define ISPIF_IRQ_STATUS_SOF_MASK 0x492249
#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1
#endif
@@ -0,0 +1,50 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/device.h>
#include "msm.h"
#include "msm_csi_register.h"
int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
uint8_t csiphy_code_index, uint8_t csid_core_index,
struct msm_cam_server_dev *server_dev)
{
int rc = -ENODEV;
CDBG("%s csiphy sel %d csid sel %d\n", __func__, csiphy_code_index,
csid_core_index);
/* register csiphy subdev */
p_mctl->csiphy_sdev = server_dev->csiphy_device[csiphy_code_index];
if (!p_mctl->csiphy_sdev)
goto out;
v4l2_set_subdev_hostdata(p_mctl->csiphy_sdev, p_mctl);
/* register csid subdev */
p_mctl->csid_sdev = server_dev->csid_device[csid_core_index];
if (!p_mctl->csid_sdev)
goto out;
v4l2_set_subdev_hostdata(p_mctl->csid_sdev, p_mctl);
/* register ispif subdev */
p_mctl->ispif_sdev = server_dev->ispif_device[0];
if (!p_mctl->ispif_sdev)
goto out;
v4l2_set_subdev_hostdata(p_mctl->ispif_sdev, p_mctl);
rc = 0;
return rc;
out:
p_mctl->ispif_sdev = NULL;
return rc;
}
@@ -0,0 +1,16 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
uint8_t csiphy_code_index, uint8_t csid_core_index,
struct msm_cam_server_dev *server_dev);
@@ -0,0 +1,523 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <mach/clk.h>
#include <mach/board.h>
#include <mach/camera.h>
#include <media/msm_isp.h>
#include "msm_csic.h"
#include "msm.h"
#define DBG_CSIC 0
#define V4L2_IDENT_CSIC 50004
/* MIPI CSI controller registers */
#define MIPI_PHY_CONTROL 0x00000000
#define MIPI_PROTOCOL_CONTROL 0x00000004
#define MIPI_INTERRUPT_STATUS 0x00000008
#define MIPI_INTERRUPT_MASK 0x0000000C
#define MIPI_CAMERA_CNTL 0x00000024
#define MIPI_CALIBRATION_CONTROL 0x00000018
#define MIPI_PHY_D0_CONTROL2 0x00000038
#define MIPI_PHY_D1_CONTROL2 0x0000003C
#define MIPI_PHY_D2_CONTROL2 0x00000040
#define MIPI_PHY_D3_CONTROL2 0x00000044
#define MIPI_PHY_CL_CONTROL 0x00000048
#define MIPI_PHY_D0_CONTROL 0x00000034
#define MIPI_PHY_D1_CONTROL 0x00000020
#define MIPI_PHY_D2_CONTROL 0x0000002C
#define MIPI_PHY_D3_CONTROL 0x00000030
#define MIPI_PWR_CNTL 0x00000054
/*
* MIPI_PROTOCOL_CONTROL register bits to enable/disable the features of
* CSI Rx Block
*/
/* DPCM scheme */
#define MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT 0x1e
/* SW_RST to issue a SW reset to the CSI core */
#define MIPI_PROTOCOL_CONTROL_SW_RST_BMSK 0x8000000
/* To Capture Long packet Header Info in MIPI_PROTOCOL_STATUS register */
#define MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK 0x200000
/* Data format for unpacking purpose */
#define MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT 0x13
/* Enable decoding of payload based on data type filed of packet hdr */
#define MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK 0x40000
/* Enable error correction on packet headers */
#define MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK 0x20000
/*
* MIPI_CALIBRATION_CONTROL register contains control info for
* calibration impledence controller
*/
/* Enable bit for calibration pad */
#define MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT 0x16
/* With SWCAL_STRENGTH_OVERRIDE_EN, SW_CAL_EN and MANUAL_OVERRIDE_EN
* the hardware calibration circuitry associated with CAL_SW_HW_MODE
* is bypassed
*/
#define MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT 0x15
/* To indicate the Calibration process is in the control of HW/SW */
#define MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT 0x14
/* When this is set the strength value of the data and clk lane impedence
* termination is updated with MANUAL_STRENGTH settings and calibration
* sensing logic is idle.
*/
#define MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT 0x7
/* Data lane0 control */
/* T-hs Settle count value for Rx */
#define MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT 0x18
/* Rx termination control */
#define MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT 0x10
/* LP Rx enable */
#define MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT 0x4
/*
* Enable for error in sync sequence
* 1 - one bit error in sync seq
* 0 - requires all 8 bit correct seq
*/
#define MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
/* Comments are same as D0 */
#define MIPI_PHY_D1_CONTROL2_SETTLE_COUNT_SHFT 0x18
#define MIPI_PHY_D1_CONTROL2_HS_TERM_IMP_SHFT 0x10
#define MIPI_PHY_D1_CONTROL2_LP_REC_EN_SHFT 0x4
#define MIPI_PHY_D1_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
/* Comments are same as D0 */
#define MIPI_PHY_D2_CONTROL2_SETTLE_COUNT_SHFT 0x18
#define MIPI_PHY_D2_CONTROL2_HS_TERM_IMP_SHFT 0x10
#define MIPI_PHY_D2_CONTROL2_LP_REC_EN_SHFT 0x4
#define MIPI_PHY_D2_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
/* Comments are same as D0 */
#define MIPI_PHY_D3_CONTROL2_SETTLE_COUNT_SHFT 0x18
#define MIPI_PHY_D3_CONTROL2_HS_TERM_IMP_SHFT 0x10
#define MIPI_PHY_D3_CONTROL2_LP_REC_EN_SHFT 0x4
#define MIPI_PHY_D3_CONTROL2_ERR_SOT_HS_EN_SHFT 0x3
/* PHY_CL_CTRL programs the parameters of clk lane of CSIRXPHY */
/* HS Rx termination control */
#define MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT 0x18
/* Start signal for T-hs delay */
#define MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT 0x2
/* PHY DATA lane 0 control */
/*
* HS RX equalizer strength control
* 00 - 0db 01 - 3db 10 - 5db 11 - 7db
*/
#define MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT 0x1c
/* PHY DATA lane 1 control */
/* Shutdown signal for MIPI clk phy line */
#define MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT 0x9
/* Shutdown signal for MIPI data phy line */
#define MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT 0x8
#define MSM_AXI_QOS_PREVIEW 200000
#define MSM_AXI_QOS_SNAPSHOT 200000
#define MSM_AXI_QOS_RECORDING 200000
#define MIPI_PWR_CNTL_EN 0x07
#define MIPI_PWR_CNTL_DIS 0x0
static int msm_csic_config(struct v4l2_subdev *sd,
struct msm_camera_csi_params *csic_params)
{
int rc = 0;
uint32_t val = 0;
struct csic_device *csic_dev;
void __iomem *csicbase;
int i;
csic_dev = v4l2_get_subdevdata(sd);
csicbase = csic_dev->base;
/* Enable error correction for DATA lane. Applies to all data lanes */
msm_camera_io_w(0x4, csicbase + MIPI_PHY_CONTROL);
msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
csicbase + MIPI_PROTOCOL_CONTROL);
val = MIPI_PROTOCOL_CONTROL_LONG_PACKET_HEADER_CAPTURE_BMSK |
MIPI_PROTOCOL_CONTROL_DECODE_ID_BMSK |
MIPI_PROTOCOL_CONTROL_ECC_EN_BMSK;
val |= (uint32_t)(csic_params->data_format) <<
MIPI_PROTOCOL_CONTROL_DATA_FORMAT_SHFT;
val |= csic_params->dpcm_scheme <<
MIPI_PROTOCOL_CONTROL_DPCM_SCHEME_SHFT;
CDBG("%s MIPI_PROTOCOL_CONTROL val=0x%x\n", __func__, val);
msm_camera_io_w(val, csicbase + MIPI_PROTOCOL_CONTROL);
val = (csic_params->settle_cnt <<
MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
(0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
for (i = 0; i < csic_params->lane_cnt; i++)
msm_camera_io_w(val, csicbase + MIPI_PHY_D0_CONTROL2 + i * 4);
val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
msm_camera_io_w(val, csicbase + MIPI_PHY_CL_CONTROL);
val = 0 << MIPI_PHY_D0_CONTROL_HS_REC_EQ_SHFT;
msm_camera_io_w(val, csicbase + MIPI_PHY_D0_CONTROL);
val = (0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT) |
(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT);
CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
msm_camera_io_w(val, csicbase + MIPI_PHY_D1_CONTROL);
msm_camera_io_w(0x00000000, csicbase + MIPI_PHY_D2_CONTROL);
msm_camera_io_w(0x00000000, csicbase + MIPI_PHY_D3_CONTROL);
/* program number of lanes and lane mapping */
switch (csic_params->lane_cnt) {
case 1:
msm_camera_io_w(csic_params->lane_assign << 8 | 0x4,
csicbase + MIPI_CAMERA_CNTL);
break;
case 2:
msm_camera_io_w(csic_params->lane_assign << 8 | 0x5,
csicbase + MIPI_CAMERA_CNTL);
break;
case 3:
msm_camera_io_w(csic_params->lane_assign << 8 | 0x6,
csicbase + MIPI_CAMERA_CNTL);
break;
case 4:
msm_camera_io_w(csic_params->lane_assign << 8 | 0x7,
csicbase + MIPI_CAMERA_CNTL);
break;
}
msm_camera_io_w(0xF077F3C0, csicbase + MIPI_INTERRUPT_MASK);
/*clear IRQ bits*/
msm_camera_io_w(0xF077F3C0, csicbase + MIPI_INTERRUPT_STATUS);
return rc;
}
static irqreturn_t msm_csic_irq(int irq_num, void *data)
{
uint32_t irq;
struct csic_device *csic_dev = data;
pr_info("msm_csic_irq: %x\n", (unsigned int)csic_dev->base);
irq = msm_camera_io_r(csic_dev->base + MIPI_INTERRUPT_STATUS);
pr_info("%s MIPI_INTERRUPT_STATUS = 0x%x 0x%x\n",
__func__, irq,
msm_camera_io_r(csic_dev->base + MIPI_PROTOCOL_CONTROL));
msm_camera_io_w(irq, csic_dev->base + MIPI_INTERRUPT_STATUS);
/* TODO: Needs to send this info to upper layers */
if ((irq >> 19) & 0x1)
pr_info("Unsupported packet format is received\n");
return IRQ_HANDLED;
}
static int msm_csic_subdev_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
BUG_ON(!chip);
chip->ident = V4L2_IDENT_CSIC;
chip->revision = 0;
return 0;
}
static struct msm_cam_clk_info csic_8x_clk_info[] = {
{"csi_src_clk", 384000000},
{"csi_clk", -1},
{"csi_vfe_clk", -1},
{"csi_pclk", -1},
};
static struct msm_cam_clk_info csic_7x_clk_info[] = {
{"csi_clk", 400000000},
{"csi_vfe_clk", -1},
{"csi_pclk", -1},
};
static int msm_csic_init(struct v4l2_subdev *sd)
{
int rc = 0;
struct csic_device *csic_dev;
csic_dev = v4l2_get_subdevdata(sd);
if (csic_dev == NULL) {
rc = -ENOMEM;
return rc;
}
csic_dev->base = ioremap(csic_dev->mem->start,
resource_size(csic_dev->mem));
if (!csic_dev->base) {
rc = -ENOMEM;
return rc;
}
csic_dev->hw_version = CSIC_8X;
rc = msm_cam_clk_enable(&csic_dev->pdev->dev, csic_8x_clk_info,
csic_dev->csic_clk, ARRAY_SIZE(csic_8x_clk_info), 1);
if (rc < 0) {
csic_dev->hw_version = CSIC_7X;
rc = msm_cam_clk_enable(&csic_dev->pdev->dev, csic_7x_clk_info,
csic_dev->csic_clk, ARRAY_SIZE(csic_7x_clk_info), 1);
if (rc < 0) {
csic_dev->hw_version = 0;
iounmap(csic_dev->base);
csic_dev->base = NULL;
return rc;
}
}
if (csic_dev->hw_version == CSIC_7X)
msm_camio_vfe_blk_reset_3();
#if DBG_CSIC
enable_irq(csic_dev->irq->start);
#endif
return 0;
}
static void msm_csic_disable(struct v4l2_subdev *sd)
{
uint32_t val;
struct csic_device *csic_dev;
csic_dev = v4l2_get_subdevdata(sd);
val = 0x0;
if (csic_dev->base != NULL) {
CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D0_CONTROL2);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D1_CONTROL2);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D2_CONTROL2);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D3_CONTROL2);
CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_CL_CONTROL);
msleep(20);
val = msm_camera_io_r(csic_dev->base + MIPI_PHY_D1_CONTROL);
val &=
~((0x1 << MIPI_PHY_D1_CONTROL_MIPI_CLK_PHY_SHUTDOWNB_SHFT)
|(0x1 << MIPI_PHY_D1_CONTROL_MIPI_DATA_PHY_SHUTDOWNB_SHFT));
CDBG("%s MIPI_PHY_D1_CONTROL val=0x%x\n", __func__, val);
msm_camera_io_w(val, csic_dev->base + MIPI_PHY_D1_CONTROL);
usleep_range(5000, 6000);
msm_camera_io_w(0x0, csic_dev->base + MIPI_INTERRUPT_MASK);
msm_camera_io_w(0x0, csic_dev->base + MIPI_INTERRUPT_STATUS);
msm_camera_io_w(MIPI_PROTOCOL_CONTROL_SW_RST_BMSK,
csic_dev->base + MIPI_PROTOCOL_CONTROL);
msm_camera_io_w(0xE400, csic_dev->base + MIPI_CAMERA_CNTL);
}
}
static int msm_csic_release(struct v4l2_subdev *sd)
{
struct csic_device *csic_dev;
csic_dev = v4l2_get_subdevdata(sd);
msm_csic_disable(sd);
#if DBG_CSIC
disable_irq(csic_dev->irq->start);
#endif
if (csic_dev->hw_version == CSIC_8X) {
msm_cam_clk_enable(&csic_dev->pdev->dev, csic_8x_clk_info,
csic_dev->csic_clk, ARRAY_SIZE(csic_8x_clk_info), 0);
} else if (csic_dev->hw_version == CSIC_7X) {
msm_cam_clk_enable(&csic_dev->pdev->dev, csic_7x_clk_info,
csic_dev->csic_clk, ARRAY_SIZE(csic_7x_clk_info), 0);
}
iounmap(csic_dev->base);
csic_dev->base = NULL;
return 0;
}
static long msm_csic_cmd(struct v4l2_subdev *sd, void *arg)
{
long rc = 0;
struct csic_cfg_data cdata;
struct msm_camera_csi_params csic_params;
if (copy_from_user(&cdata,
(void *)arg,
sizeof(struct csic_cfg_data)))
return -EFAULT;
CDBG("%s cfgtype = %d\n", __func__, cdata.cfgtype);
switch (cdata.cfgtype) {
case CSIC_INIT:
rc = msm_csic_init(sd);
break;
case CSIC_CFG:
if (copy_from_user(&csic_params,
(void *)cdata.csic_params,
sizeof(struct msm_camera_csi_params)))
return -EFAULT;
rc = msm_csic_config(sd, &csic_params);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static long msm_csic_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
switch (cmd) {
case VIDIOC_MSM_CSIC_CFG:
return msm_csic_cmd(sd, arg);
case VIDIOC_MSM_CSIC_RELEASE:
return msm_csic_release(sd);
default:
return -ENOIOCTLCMD;
}
}
static struct v4l2_subdev_core_ops msm_csic_subdev_core_ops = {
.g_chip_ident = &msm_csic_subdev_g_chip_ident,
.ioctl = &msm_csic_subdev_ioctl,
};
static const struct v4l2_subdev_ops msm_csic_subdev_ops = {
.core = &msm_csic_subdev_core_ops,
};
static const struct v4l2_subdev_internal_ops msm_csic_internal_ops;
static int __devinit csic_probe(struct platform_device *pdev)
{
struct csic_device *new_csic_dev;
int rc = 0;
struct msm_cam_subdev_info sd_info;
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csic_dev = kzalloc(sizeof(struct csic_device), GFP_KERNEL);
if (!new_csic_dev) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&new_csic_dev->subdev, &msm_csic_subdev_ops);
new_csic_dev->subdev.internal_ops = &msm_csic_internal_ops;
new_csic_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(new_csic_dev->subdev.name,
ARRAY_SIZE(new_csic_dev->subdev.name), "msm_csic");
v4l2_set_subdevdata(&new_csic_dev->subdev, new_csic_dev);
platform_set_drvdata(pdev, &new_csic_dev->subdev);
mutex_init(&new_csic_dev->mutex);
new_csic_dev->mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "csic");
if (!new_csic_dev->mem) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto csic_no_resource;
}
new_csic_dev->irq = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "csic");
if (!new_csic_dev->irq) {
pr_err("%s: no irq resource?\n", __func__);
rc = -ENODEV;
goto csic_no_resource;
}
new_csic_dev->io = request_mem_region(new_csic_dev->mem->start,
resource_size(new_csic_dev->mem), pdev->name);
if (!new_csic_dev->io) {
pr_err("%s: no valid mem region\n", __func__);
rc = -EBUSY;
goto csic_no_resource;
}
rc = request_irq(new_csic_dev->irq->start, msm_csic_irq,
IRQF_TRIGGER_HIGH, "csic", new_csic_dev);
if (rc < 0) {
release_mem_region(new_csic_dev->mem->start,
resource_size(new_csic_dev->mem));
pr_err("%s: irq request fail\n", __func__);
rc = -EBUSY;
goto csic_no_resource;
}
disable_irq(new_csic_dev->irq->start);
new_csic_dev->pdev = pdev;
rc = msm_cam_clk_enable(&new_csic_dev->pdev->dev, &csic_7x_clk_info[2],
new_csic_dev->csic_clk, 1, 1);
new_csic_dev->base = ioremap(new_csic_dev->mem->start,
resource_size(new_csic_dev->mem));
if (!new_csic_dev->base) {
rc = -ENOMEM;
return rc;
}
msm_camera_io_w(MIPI_PWR_CNTL_DIS, new_csic_dev->base + MIPI_PWR_CNTL);
rc = msm_cam_clk_enable(&new_csic_dev->pdev->dev, &csic_7x_clk_info[2],
new_csic_dev->csic_clk, 1, 0);
iounmap(new_csic_dev->base);
new_csic_dev->base = NULL;
sd_info.sdev_type = CSIC_DEV;
sd_info.sd_index = pdev->id;
sd_info.irq_num = new_csic_dev->irq->start;
msm_cam_register_subdev_node(
&new_csic_dev->subdev, &sd_info);
media_entity_init(&new_csic_dev->subdev.entity, 0, NULL, 0);
new_csic_dev->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csic_dev->subdev.entity.group_id = CSIC_DEV;
new_csic_dev->subdev.entity.name = pdev->name;
new_csic_dev->subdev.entity.revision =
new_csic_dev->subdev.devnode->num;
return 0;
csic_no_resource:
mutex_destroy(&new_csic_dev->mutex);
kfree(new_csic_dev);
return 0;
}
static struct platform_driver csic_driver = {
.probe = csic_probe,
.driver = {
.name = MSM_CSIC_DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init msm_csic_init_module(void)
{
return platform_driver_register(&csic_driver);
}
static void __exit msm_csic_exit_module(void)
{
platform_driver_unregister(&csic_driver);
}
module_init(msm_csic_init_module);
module_exit(msm_csic_exit_module);
MODULE_DESCRIPTION("MSM csic driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,42 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSIC_H
#define MSM_CSIC_H
#include <linux/clk.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#define CSIC_7X 0x1
#define CSIC_8X (0x1 << 1)
struct csic_device {
struct platform_device *pdev;
struct v4l2_subdev subdev;
struct resource *mem;
struct resource *irq;
struct resource *io;
void __iomem *base;
struct mutex mutex;
uint32_t hw_version;
struct clk *csic_clk[5];
};
#define VIDIOC_MSM_CSIC_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csic_cfg_data*)
#define VIDIOC_MSM_CSIC_RELEASE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
#endif
@@ -0,0 +1,37 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/device.h>
#include "msm.h"
#include "msm_csi_register.h"
int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
uint8_t csiphy_code_index, uint8_t csid_core_index,
struct msm_cam_server_dev *server_dev)
{
int rc = -ENODEV;
p_mctl->csic_sdev = server_dev->csic_device[csid_core_index];
if (!p_mctl->csic_sdev)
goto out;
v4l2_set_subdev_hostdata(p_mctl->csic_sdev, p_mctl);
rc = 0;
p_mctl->ispif_sdev = NULL;
return rc;
out:
p_mctl->ispif_sdev = NULL;
return rc;
}
@@ -0,0 +1,649 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
#include <mach/board.h>
#include <mach/camera.h>
#include <media/msm_isp.h>
#include "msm_csid.h"
#include "msm_csid_hwreg.h"
#include "msm.h"
#include "msm_cam_server.h"
#define V4L2_IDENT_CSID 50002
#define CSID_VERSION_V2 0x02000011
#define CSID_VERSION_V3 0x30000000
#define DBG_CSID 0
#define TRUE 1
#define FALSE 0
static int msm_csid_cid_lut(
struct msm_camera_csid_lut_params *csid_lut_params,
void __iomem *csidbase)
{
int rc = 0, i = 0;
uint32_t val = 0;
if (!csid_lut_params) {
pr_err("%s:%d csid_lut_params NULL\n", __func__, __LINE__);
return -EINVAL;
}
for (i = 0; i < csid_lut_params->num_cid && i < 16; i++) {
CDBG("%s lut params num_cid = %d, cid = %d, dt = %x, df = %d\n",
__func__,
csid_lut_params->num_cid,
csid_lut_params->vc_cfg[i].cid,
csid_lut_params->vc_cfg[i].dt,
csid_lut_params->vc_cfg[i].decode_format);
if (csid_lut_params->vc_cfg[i].dt < 0x12 ||
csid_lut_params->vc_cfg[i].dt > 0x37) {
CDBG("%s: unsupported data type 0x%x\n",
__func__, csid_lut_params->vc_cfg[i].dt);
return rc;
}
val = msm_camera_io_r(csidbase + CSID_CID_LUT_VC_0_ADDR +
(csid_lut_params->vc_cfg[i].cid >> 2) * 4)
& ~(0xFF << ((csid_lut_params->vc_cfg[i].cid % 4) * 8));
val |= (csid_lut_params->vc_cfg[i].dt <<
((csid_lut_params->vc_cfg[i].cid % 4) * 8));
msm_camera_io_w(val, csidbase + CSID_CID_LUT_VC_0_ADDR +
(csid_lut_params->vc_cfg[i].cid >> 2) * 4);
val = (csid_lut_params->vc_cfg[i].decode_format << 4) | 0x3;
msm_camera_io_w(val, csidbase + CSID_CID_n_CFG_ADDR +
(csid_lut_params->vc_cfg[i].cid * 4));
}
return rc;
}
#if DBG_CSID
static void msm_csid_set_debug_reg(void __iomem *csidbase,
struct msm_camera_csid_params *csid_params)
{
uint32_t val = 0;
val = ((1 << csid_params->lane_cnt) - 1) << 20;
msm_camera_io_w(0x7f010800 | val, csidbase + CSID_IRQ_MASK_ADDR);
msm_camera_io_w(0x7f010800 | val, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
}
#else
static void msm_csid_set_debug_reg(void __iomem *csidbase,
struct msm_camera_csid_params *csid_params) {}
#endif
static int msm_csid_config(struct csid_device *csid_dev,
struct msm_camera_csid_params *csid_params)
{
int rc = 0;
uint32_t val = 0;
void __iomem *csidbase;
csidbase = csid_dev->base;
if (!csidbase || !csid_params) {
pr_err("%s:%d csidbase %p, csid params %p\n", __func__,
__LINE__, csidbase, csid_params);
return -EINVAL;
}
CDBG("%s csid_params, lane_cnt = %d, lane_assign = %x, phy sel = %d\n",
__func__,
csid_params->lane_cnt,
csid_params->lane_assign,
csid_params->phy_sel);
val = csid_params->lane_cnt - 1;
val |= csid_params->lane_assign << CSID_DL_INPUT_SEL_SHIFT;
if (csid_dev->hw_version < 0x30000000) {
val |= (0xF << 10);
msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
} else {
msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
val = csid_params->phy_sel << CSID_PHY_SEL_SHIFT;
val |= 0xF;
msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_1_ADDR);
}
rc = msm_csid_cid_lut(&csid_params->lut_params, csidbase);
if (rc < 0)
return rc;
msm_csid_set_debug_reg(csidbase, csid_params);
return rc;
}
static irqreturn_t msm_csid_irq(int irq_num, void *data)
{
uint32_t irq;
struct csid_device *csid_dev = data;
if (!csid_dev) {
pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__);
return IRQ_HANDLED;
}
irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
__func__, csid_dev->pdev->id, irq);
if (irq & (0x1 << CSID_RST_DONE_IRQ_BITSHIFT))
complete(&csid_dev->reset_complete);
msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
return IRQ_HANDLED;
}
int msm_csid_irq_routine(struct v4l2_subdev *sd, u32 status, bool *handled)
{
struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
irqreturn_t ret;
CDBG("%s E\n", __func__);
ret = msm_csid_irq(csid_dev->irq->start, csid_dev);
*handled = TRUE;
return 0;
}
static void msm_csid_reset(struct csid_device *csid_dev)
{
msm_camera_io_w(CSID_RST_STB_ALL, csid_dev->base + CSID_RST_CMD_ADDR);
wait_for_completion_interruptible(&csid_dev->reset_complete);
return;
}
static int msm_csid_subdev_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
BUG_ON(!chip);
chip->ident = V4L2_IDENT_CSID;
chip->revision = 0;
return 0;
}
static struct msm_cam_clk_info csid_8960_clk_info[] = {
{"csi_src_clk", 177780000},
{"csi_clk", -1},
{"csi_phy_clk", -1},
{"csi_pclk", -1},
};
static struct msm_cam_clk_info csid0_8974_clk_info[] = {
{"csi0_ahb_clk", -1},
{"csi0_src_clk", 200000000},
{"csi0_clk", -1},
{"csi0_phy_clk", -1},
{"csi0_pix_clk", -1},
{"csi0_rdi_clk", -1},
};
static struct msm_cam_clk_info csid1_8974_clk_info[] = {
{"csi1_ahb_clk", -1},
{"csi1_src_clk", 200000000},
{"csi1_clk", -1},
{"csi1_phy_clk", -1},
{"csi1_pix_clk", -1},
{"csi1_rdi_clk", -1},
};
static struct msm_cam_clk_info csid2_8974_clk_info[] = {
{"csi2_ahb_clk", -1},
{"csi2_src_clk", 200000000},
{"csi2_clk", -1},
{"csi2_phy_clk", -1},
{"csi2_pix_clk", -1},
{"csi2_rdi_clk", -1},
};
static struct msm_cam_clk_info csid3_8974_clk_info[] = {
{"csi3_ahb_clk", -1},
{"csi3_src_clk", 200000000},
{"csi3_clk", -1},
{"csi3_phy_clk", -1},
{"csi3_pix_clk", -1},
{"csi3_rdi_clk", -1},
};
static struct msm_cam_clk_setting csid_8974_clk_info[] = {
{&csid0_8974_clk_info[0], ARRAY_SIZE(csid0_8974_clk_info)},
{&csid1_8974_clk_info[0], ARRAY_SIZE(csid1_8974_clk_info)},
{&csid2_8974_clk_info[0], ARRAY_SIZE(csid2_8974_clk_info)},
{&csid3_8974_clk_info[0], ARRAY_SIZE(csid3_8974_clk_info)},
};
static struct camera_vreg_t csid_8960_vreg_info[] = {
{"mipi_csi_vdd", REG_LDO, 1200000, 1200000, 20000},
};
static struct camera_vreg_t csid_8974_vreg_info[] = {
{"mipi_csi_vdd", REG_LDO, 1800000, 1800000, 12000},
};
static int msm_csid_init(struct csid_device *csid_dev, uint32_t *csid_version)
{
int rc = 0;
uint8_t core_id = 0;
if (!csid_version) {
pr_err("%s:%d csid_version NULL\n", __func__, __LINE__);
rc = -EINVAL;
return rc;
}
if (csid_dev->csid_state == CSID_POWER_UP) {
pr_err("%s: csid invalid state %d\n", __func__,
csid_dev->csid_state);
rc = -EINVAL;
return rc;
}
csid_dev->base = ioremap(csid_dev->mem->start,
resource_size(csid_dev->mem));
if (!csid_dev->base) {
pr_err("%s csid_dev->base NULL\n", __func__);
rc = -ENOMEM;
return rc;
}
if (CSID_VERSION <= CSID_VERSION_V2) {
rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator on failed\n", __func__);
goto vreg_config_failed;
}
rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator enable failed\n", __func__);
goto vreg_enable_failed;
}
rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8960_clk_info, csid_dev->csid_clk,
ARRAY_SIZE(csid_8960_clk_info), 1);
if (rc < 0) {
pr_err("%s: clock enable failed\n", __func__);
goto clk_enable_failed;
}
} else if (CSID_VERSION == CSID_VERSION_V3) {
rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator on failed\n", __func__);
goto vreg_config_failed;
}
rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 1);
if (rc < 0) {
pr_err("%s: regulator enable failed\n", __func__);
goto vreg_enable_failed;
}
rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
csid_8974_clk_info[0].num_clk_info, 1);
if (rc < 0) {
pr_err("%s: clock enable failed\n", __func__);
goto csid0_clk_enable_failed;
}
core_id = csid_dev->pdev->id;
if (core_id) {
rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8974_clk_info[core_id].clk_info,
csid_dev->csid_clk,
csid_8974_clk_info[core_id].num_clk_info, 1);
if (rc < 0) {
pr_err("%s: clock enable failed\n",
__func__);
goto clk_enable_failed;
}
}
}
csid_dev->hw_version =
msm_camera_io_r(csid_dev->base + CSID_HW_VERSION_ADDR);
*csid_version = csid_dev->hw_version;
init_completion(&csid_dev->reset_complete);
enable_irq(csid_dev->irq->start);
msm_csid_reset(csid_dev);
csid_dev->csid_state = CSID_POWER_UP;
return rc;
clk_enable_failed:
if (CSID_VERSION == CSID_VERSION_V3) {
msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
csid_8974_clk_info[0].num_clk_info, 0);
}
csid0_clk_enable_failed:
if (CSID_VERSION <= CSID_VERSION_V2) {
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
} else if (CSID_VERSION == CSID_VERSION_V3) {
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
}
vreg_enable_failed:
if (CSID_VERSION <= CSID_VERSION_V2) {
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
} else if (CSID_VERSION == CSID_VERSION_V3) {
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
}
vreg_config_failed:
iounmap(csid_dev->base);
csid_dev->base = NULL;
return rc;
}
static int msm_csid_release(struct csid_device *csid_dev)
{
uint32_t irq;
uint8_t core_id = 0;
if (csid_dev->csid_state != CSID_POWER_UP) {
pr_err("%s: csid invalid state %d\n", __func__,
csid_dev->csid_state);
return -EINVAL;
}
irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
msm_camera_io_w(0, csid_dev->base + CSID_IRQ_MASK_ADDR);
disable_irq(csid_dev->irq->start);
if (csid_dev->hw_version <= CSID_VERSION_V2) {
msm_cam_clk_enable(&csid_dev->pdev->dev, csid_8960_clk_info,
csid_dev->csid_clk, ARRAY_SIZE(csid_8960_clk_info), 0);
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
} else if (csid_dev->hw_version == CSID_VERSION_V3) {
core_id = csid_dev->pdev->id;
if (core_id)
msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8974_clk_info[core_id].clk_info,
csid_dev->csid_clk,
csid_8974_clk_info[core_id].num_clk_info, 0);
msm_cam_clk_enable(&csid_dev->pdev->dev,
csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
csid_8974_clk_info[0].num_clk_info, 0);
msm_camera_enable_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
msm_camera_config_vreg(&csid_dev->pdev->dev,
csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
NULL, 0, &csid_dev->csi_vdd, 0);
}
iounmap(csid_dev->base);
csid_dev->base = NULL;
csid_dev->csid_state = CSID_POWER_DOWN;
return 0;
}
static long msm_csid_cmd(struct csid_device *csid_dev, void *arg)
{
int rc = 0;
struct csid_cfg_data cdata;
if (!csid_dev) {
pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__);
return -EINVAL;
}
if (copy_from_user(&cdata,
(void *)arg,
sizeof(struct csid_cfg_data))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
return -EFAULT;
}
CDBG("%s cfgtype = %d\n", __func__, cdata.cfgtype);
switch (cdata.cfgtype) {
case CSID_INIT:
rc = msm_csid_init(csid_dev, &cdata.cfg.csid_version);
if (copy_to_user((void *)arg,
&cdata,
sizeof(struct csid_cfg_data))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
}
break;
case CSID_CFG: {
struct msm_camera_csid_params csid_params;
struct msm_camera_csid_vc_cfg *vc_cfg = NULL;
if (copy_from_user(&csid_params,
(void *)cdata.cfg.csid_params,
sizeof(struct msm_camera_csid_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
break;
}
vc_cfg = kzalloc(csid_params.lut_params.num_cid *
sizeof(struct msm_camera_csid_vc_cfg),
GFP_KERNEL);
if (!vc_cfg) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOMEM;
break;
}
if (copy_from_user(vc_cfg,
(void *)csid_params.lut_params.vc_cfg,
(csid_params.lut_params.num_cid *
sizeof(struct msm_camera_csid_vc_cfg)))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
kfree(vc_cfg);
rc = -EFAULT;
break;
}
csid_params.lut_params.vc_cfg = vc_cfg;
rc = msm_csid_config(csid_dev, &csid_params);
kfree(vc_cfg);
break;
}
default:
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOIOCTLCMD;
break;
}
return rc;
}
static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
int rc = -ENOIOCTLCMD;
struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
mutex_lock(&csid_dev->mutex);
switch (cmd) {
case VIDIOC_MSM_CSID_CFG:
rc = msm_csid_cmd(csid_dev, arg);
break;
case VIDIOC_MSM_CSID_RELEASE:
rc = msm_csid_release(csid_dev);
break;
default:
pr_err("%s: command not found\n", __func__);
}
mutex_unlock(&csid_dev->mutex);
return rc;
}
static const struct v4l2_subdev_internal_ops msm_csid_internal_ops;
static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = {
.g_chip_ident = &msm_csid_subdev_g_chip_ident,
.ioctl = &msm_csid_subdev_ioctl,
.interrupt_service_routine = msm_csid_irq_routine,
};
static const struct v4l2_subdev_ops msm_csid_subdev_ops = {
.core = &msm_csid_subdev_core_ops,
};
static int __devinit csid_probe(struct platform_device *pdev)
{
struct csid_device *new_csid_dev;
struct msm_cam_subdev_info sd_info;
struct intr_table_entry irq_req;
int rc = 0;
CDBG("%s:%d called\n", __func__, __LINE__);
new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
if (!new_csid_dev) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&new_csid_dev->subdev, &msm_csid_subdev_ops);
new_csid_dev->subdev.internal_ops = &msm_csid_internal_ops;
new_csid_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(new_csid_dev->subdev.name,
ARRAY_SIZE(new_csid_dev->subdev.name), "msm_csid");
v4l2_set_subdevdata(&new_csid_dev->subdev, new_csid_dev);
platform_set_drvdata(pdev, &new_csid_dev->subdev);
mutex_init(&new_csid_dev->mutex);
if (pdev->dev.of_node)
of_property_read_u32((&pdev->dev)->of_node,
"cell-index", &pdev->id);
CDBG("%s device id %d\n", __func__, pdev->id);
new_csid_dev->mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "csid");
if (!new_csid_dev->mem) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto csid_no_resource;
}
new_csid_dev->irq = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "csid");
if (!new_csid_dev->irq) {
pr_err("%s: no irq resource?\n", __func__);
rc = -ENODEV;
goto csid_no_resource;
}
new_csid_dev->io = request_mem_region(new_csid_dev->mem->start,
resource_size(new_csid_dev->mem), pdev->name);
if (!new_csid_dev->io) {
pr_err("%s: no valid mem region\n", __func__);
rc = -EBUSY;
goto csid_no_resource;
}
new_csid_dev->pdev = pdev;
sd_info.sdev_type = CSID_DEV;
sd_info.sd_index = pdev->id;
sd_info.irq_num = new_csid_dev->irq->start;
msm_cam_register_subdev_node(&new_csid_dev->subdev, &sd_info);
media_entity_init(&new_csid_dev->subdev.entity, 0, NULL, 0);
new_csid_dev->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csid_dev->subdev.entity.group_id = CSID_DEV;
new_csid_dev->subdev.entity.name = pdev->name;
new_csid_dev->subdev.entity.revision =
new_csid_dev->subdev.devnode->num;
/* Request for this device irq from the camera server. If the
* IRQ Router is present on this target, the interrupt will be
* handled by the camera server and the interrupt service
* routine called. If the request_irq call returns ENXIO, then
* the IRQ Router hardware is not present on this target. We
* have to request for the irq ourselves and register the
* appropriate interrupt handler. */
irq_req.cam_hw_idx = MSM_CAM_HW_CSI0 + pdev->id;
irq_req.dev_name = "csid";
irq_req.irq_idx = CAMERA_SS_IRQ_2 + pdev->id;
irq_req.irq_num = new_csid_dev->irq->start;
irq_req.is_composite = 0;
irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
irq_req.num_hwcore = 1;
irq_req.subdev_list[0] = &new_csid_dev->subdev;
irq_req.data = (void *)new_csid_dev;
rc = msm_cam_server_request_irq(&irq_req);
if (rc == -ENXIO) {
/* IRQ Router hardware is not present on this hardware.
* Request for the IRQ and register the interrupt handler. */
rc = request_irq(new_csid_dev->irq->start, msm_csid_irq,
IRQF_TRIGGER_RISING, "csid", new_csid_dev);
if (rc < 0) {
release_mem_region(new_csid_dev->mem->start,
resource_size(new_csid_dev->mem));
pr_err("%s: irq request fail\n", __func__);
rc = -EBUSY;
goto csid_no_resource;
}
disable_irq(new_csid_dev->irq->start);
} else if (rc < 0) {
release_mem_region(new_csid_dev->mem->start,
resource_size(new_csid_dev->mem));
pr_err("%s Error registering irq ", __func__);
goto csid_no_resource;
}
new_csid_dev->csid_state = CSID_POWER_DOWN;
return 0;
csid_no_resource:
mutex_destroy(&new_csid_dev->mutex);
kfree(new_csid_dev);
return 0;
}
static const struct of_device_id msm_csid_dt_match[] = {
{.compatible = "qcom,csid"},
{}
};
MODULE_DEVICE_TABLE(of, msm_csid_dt_match);
static struct platform_driver csid_driver = {
.probe = csid_probe,
.driver = {
.name = MSM_CSID_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = msm_csid_dt_match,
},
};
static int __init msm_csid_init_module(void)
{
return platform_driver_register(&csid_driver);
}
static void __exit msm_csid_exit_module(void)
{
platform_driver_unregister(&csid_driver);
}
module_init(msm_csid_init_module);
module_exit(msm_csid_exit_module);
MODULE_DESCRIPTION("MSM CSID driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,49 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSID_H
#define MSM_CSID_H
#include <linux/clk.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include <media/msm_camera.h>
enum msm_csid_state_t {
CSID_POWER_UP,
CSID_POWER_DOWN,
};
struct csid_device {
struct platform_device *pdev;
struct v4l2_subdev subdev;
struct resource *mem;
struct resource *irq;
struct resource *io;
struct regulator *csi_vdd;
void __iomem *base;
struct mutex mutex;
struct completion reset_complete;
uint32_t hw_version;
enum msm_csid_state_t csid_state;
struct clk *csid0_clk[6];
struct clk *csid_clk[6];
};
#define VIDIOC_MSM_CSID_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csic_cfg_data*)
#define VIDIOC_MSM_CSID_RELEASE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
#endif
@@ -0,0 +1,465 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <mach/board.h>
#include <mach/vreg.h>
#include <media/msm_isp.h>
#include "msm_csiphy.h"
#include "msm.h"
#include "msm_csiphy_hwreg.h"
#define DBG_CSIPHY 0
#define V4L2_IDENT_CSIPHY 50003
#define CSIPHY_VERSION_V3 0x10
int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
struct msm_camera_csiphy_params *csiphy_params)
{
int rc = 0;
int j = 0;
uint32_t val = 0;
uint8_t lane_cnt = 0;
uint16_t lane_mask = 0;
void __iomem *csiphybase;
csiphybase = csiphy_dev->base;
if (!csiphybase) {
pr_err("%s: csiphybase NULL\n", __func__);
return -EINVAL;
}
csiphy_dev->lane_mask[csiphy_dev->pdev->id] |= csiphy_params->lane_mask;
lane_mask = csiphy_dev->lane_mask[csiphy_dev->pdev->id];
lane_cnt = csiphy_params->lane_cnt;
if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
pr_err("%s: unsupported lane cnt %d\n",
__func__, csiphy_params->lane_cnt);
return rc;
}
CDBG("%s csiphy_params, mask = %x, cnt = %d, settle cnt = %x\n",
__func__,
csiphy_params->lane_mask,
csiphy_params->lane_cnt,
csiphy_params->settle_cnt);
msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
val = 0x3;
msm_camera_io_w((lane_mask << 2) | val,
csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
msm_camera_io_w(0x10, csiphybase + MIPI_CSIPHY_LNCK_CFG2_ADDR);
msm_camera_io_w(csiphy_params->settle_cnt,
csiphybase + MIPI_CSIPHY_LNCK_CFG3_ADDR);
msm_camera_io_w(0x24,
csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR);
msm_camera_io_w(0x24,
csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
} else {
val = 0x1;
msm_camera_io_w((lane_mask << 1) | val,
csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
msm_camera_io_w(csiphy_params->combo_mode <<
MIPI_CSIPHY_MODE_CONFIG_SHIFT,
csiphybase + MIPI_CSIPHY_GLBL_RESET_ADDR);
}
lane_mask &= 0x1f;
while (lane_mask & 0x1f) {
if (!(lane_mask & 0x1)) {
j++;
lane_mask >>= 1;
continue;
}
msm_camera_io_w(0x10,
csiphybase + MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*j);
msm_camera_io_w(csiphy_params->settle_cnt,
csiphybase + MIPI_CSIPHY_LNn_CFG3_ADDR + 0x40*j);
msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
MIPI_CSIPHY_INTERRUPT_MASK_ADDR + 0x4*j);
msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR + 0x4*j);
j++;
lane_mask >>= 1;
}
msleep(20);
return rc;
}
static irqreturn_t msm_csiphy_irq(int irq_num, void *data)
{
uint32_t irq;
int i;
struct csiphy_device *csiphy_dev = data;
for (i = 0; i < 8; i++) {
irq = msm_camera_io_r(
csiphy_dev->base +
MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR + 0x4*i);
msm_camera_io_w(irq,
csiphy_dev->base +
MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
pr_err("%s MIPI_CSIPHY%d_INTERRUPT_STATUS%d = 0x%x\n",
__func__, csiphy_dev->pdev->id, i, irq);
msm_camera_io_w(0x1, csiphy_dev->base +
MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
msm_camera_io_w(0x0, csiphy_dev->base +
MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
msm_camera_io_w(0x0,
csiphy_dev->base +
MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
}
return IRQ_HANDLED;
}
static void msm_csiphy_reset(struct csiphy_device *csiphy_dev)
{
msm_camera_io_w(0x1, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
usleep_range(5000, 8000);
msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
}
static int msm_csiphy_subdev_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
BUG_ON(!chip);
chip->ident = V4L2_IDENT_CSIPHY;
chip->revision = 0;
return 0;
}
static struct msm_cam_clk_info csiphy_8960_clk_info[] = {
{"csiphy_timer_src_clk", 177780000},
{"csiphy_timer_clk", -1},
};
static struct msm_cam_clk_info csiphy_8974_clk_info[] = {
{"ispif_ahb_clk", -1},
{"csiphy_timer_src_clk", 200000000},
{"csiphy_timer_clk", -1},
};
static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
{
int rc = 0;
if (csiphy_dev == NULL) {
pr_err("%s: csiphy_dev NULL\n", __func__);
rc = -ENOMEM;
return rc;
}
if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
pr_err("%s: csiphy invalid state %d\n", __func__,
csiphy_dev->csiphy_state);
rc = -EINVAL;
return rc;
}
if (csiphy_dev->ref_count++) {
CDBG("%s csiphy refcount = %d\n", __func__,
csiphy_dev->ref_count);
return rc;
}
csiphy_dev->base = ioremap(csiphy_dev->mem->start,
resource_size(csiphy_dev->mem));
if (!csiphy_dev->base) {
pr_err("%s: csiphy_dev->base NULL\n", __func__);
csiphy_dev->ref_count--;
rc = -ENOMEM;
return rc;
}
if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 1);
else
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 1);
if (rc < 0) {
pr_err("%s: csiphy clk enable failed\n", __func__);
csiphy_dev->ref_count--;
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
return rc;
}
#if DBG_CSIPHY
enable_irq(csiphy_dev->irq->start);
#endif
msm_csiphy_reset(csiphy_dev);
csiphy_dev->hw_version =
msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
}
static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
{
int i = 0;
struct msm_camera_csi_lane_params *csi_lane_params;
uint16_t csi_lane_mask;
csi_lane_params = (struct msm_camera_csi_lane_params *)arg;
csi_lane_mask = csi_lane_params->csi_lane_mask;
if (!csiphy_dev || !csiphy_dev->ref_count) {
pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
return 0;
}
if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) {
pr_err("%s: csiphy invalid state %d\n", __func__,
csiphy_dev->csiphy_state);
return -EINVAL;
}
CDBG("%s csiphy_params, lane assign %x mask = %x\n",
__func__,
csi_lane_params->csi_lane_assign,
csi_lane_params->csi_lane_mask);
if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
for (i = 0; i < 4; i++)
msm_camera_io_w(0x0, csiphy_dev->base +
MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
} else {
csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
~(csi_lane_params->csi_lane_mask);
i = 0;
while (csi_lane_mask & 0x1F) {
if (csi_lane_mask & 0x1) {
msm_camera_io_w(0x0, csiphy_dev->base +
MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
}
csi_lane_mask >>= 1;
i++;
}
}
if (--csiphy_dev->ref_count) {
CDBG("%s csiphy refcount = %d\n", __func__,
csiphy_dev->ref_count);
return 0;
}
msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
#if DBG_CSIPHY
disable_irq(csiphy_dev->irq->start);
#endif
if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 0);
else
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 0);
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
}
static long msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
{
int rc = 0;
struct csiphy_cfg_data cdata;
struct msm_camera_csiphy_params csiphy_params;
if (!csiphy_dev) {
pr_err("%s: csiphy_dev NULL\n", __func__);
return -EINVAL;
}
if (copy_from_user(&cdata,
(void *)arg,
sizeof(struct csiphy_cfg_data))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
return -EFAULT;
}
switch (cdata.cfgtype) {
case CSIPHY_INIT:
rc = msm_csiphy_init(csiphy_dev);
break;
case CSIPHY_CFG:
if (copy_from_user(&csiphy_params,
(void *)cdata.csiphy_params,
sizeof(struct msm_camera_csiphy_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
break;
}
rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params);
break;
default:
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOIOCTLCMD;
break;
}
return rc;
}
static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
int rc = -ENOIOCTLCMD;
struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
mutex_lock(&csiphy_dev->mutex);
switch (cmd) {
case VIDIOC_MSM_CSIPHY_CFG:
rc = msm_csiphy_cmd(csiphy_dev, arg);
break;
case VIDIOC_MSM_CSIPHY_RELEASE:
rc = msm_csiphy_release(csiphy_dev, arg);
break;
default:
pr_err("%s: command not found\n", __func__);
}
mutex_unlock(&csiphy_dev->mutex);
return rc;
}
static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops;
static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = {
.g_chip_ident = &msm_csiphy_subdev_g_chip_ident,
.ioctl = &msm_csiphy_subdev_ioctl,
};
static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = {
.core = &msm_csiphy_subdev_core_ops,
};
static int __devinit csiphy_probe(struct platform_device *pdev)
{
struct csiphy_device *new_csiphy_dev;
int rc = 0;
struct msm_cam_subdev_info sd_info;
new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
if (!new_csiphy_dev) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&new_csiphy_dev->subdev, &msm_csiphy_subdev_ops);
new_csiphy_dev->subdev.internal_ops = &msm_csiphy_internal_ops;
new_csiphy_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(new_csiphy_dev->subdev.name,
ARRAY_SIZE(new_csiphy_dev->subdev.name), "msm_csiphy");
v4l2_set_subdevdata(&new_csiphy_dev->subdev, new_csiphy_dev);
platform_set_drvdata(pdev, &new_csiphy_dev->subdev);
mutex_init(&new_csiphy_dev->mutex);
if (pdev->dev.of_node)
of_property_read_u32((&pdev->dev)->of_node,
"cell-index", &pdev->id);
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csiphy_dev->mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "csiphy");
if (!new_csiphy_dev->mem) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto csiphy_no_resource;
}
new_csiphy_dev->irq = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "csiphy");
if (!new_csiphy_dev->irq) {
pr_err("%s: no irq resource?\n", __func__);
rc = -ENODEV;
goto csiphy_no_resource;
}
new_csiphy_dev->io = request_mem_region(new_csiphy_dev->mem->start,
resource_size(new_csiphy_dev->mem), pdev->name);
if (!new_csiphy_dev->io) {
pr_err("%s: no valid mem region\n", __func__);
rc = -EBUSY;
goto csiphy_no_resource;
}
rc = request_irq(new_csiphy_dev->irq->start, msm_csiphy_irq,
IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev);
if (rc < 0) {
release_mem_region(new_csiphy_dev->mem->start,
resource_size(new_csiphy_dev->mem));
pr_err("%s: irq request fail\n", __func__);
rc = -EBUSY;
goto csiphy_no_resource;
}
disable_irq(new_csiphy_dev->irq->start);
new_csiphy_dev->pdev = pdev;
sd_info.sdev_type = CSIPHY_DEV;
sd_info.sd_index = pdev->id;
sd_info.irq_num = new_csiphy_dev->irq->start;
msm_cam_register_subdev_node(
&new_csiphy_dev->subdev, &sd_info);
media_entity_init(&new_csiphy_dev->subdev.entity, 0, NULL, 0);
new_csiphy_dev->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csiphy_dev->subdev.entity.group_id = CSIPHY_DEV;
new_csiphy_dev->subdev.entity.name = pdev->name;
new_csiphy_dev->subdev.entity.revision =
new_csiphy_dev->subdev.devnode->num;
new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
csiphy_no_resource:
mutex_destroy(&new_csiphy_dev->mutex);
kfree(new_csiphy_dev);
return 0;
}
static const struct of_device_id msm_csiphy_dt_match[] = {
{.compatible = "qcom,csiphy"},
{}
};
MODULE_DEVICE_TABLE(of, msm_csiphy_dt_match);
static struct platform_driver csiphy_driver = {
.probe = csiphy_probe,
.driver = {
.name = MSM_CSIPHY_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = msm_csiphy_dt_match,
},
};
static int __init msm_csiphy_init_module(void)
{
return platform_driver_register(&csiphy_driver);
}
static void __exit msm_csiphy_exit_module(void)
{
platform_driver_unregister(&csiphy_driver);
}
module_init(msm_csiphy_init_module);
module_exit(msm_csiphy_exit_module);
MODULE_DESCRIPTION("MSM CSIPHY driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,49 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_CSIPHY_H
#define MSM_CSIPHY_H
#include <linux/clk.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include <media/msm_camera.h>
#define MAX_CSIPHY 3
enum msm_csiphy_state_t {
CSIPHY_POWER_UP,
CSIPHY_POWER_DOWN,
};
struct csiphy_device {
struct platform_device *pdev;
struct v4l2_subdev subdev;
struct resource *mem;
struct resource *irq;
struct resource *io;
void __iomem *base;
struct mutex mutex;
uint32_t hw_version;
enum msm_csiphy_state_t csiphy_state;
struct clk *csiphy_clk[3];
uint8_t ref_count;
uint16_t lane_mask[MAX_CSIPHY];
};
#define VIDIOC_MSM_CSIPHY_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct csiphy_cfg_data*)
#define VIDIOC_MSM_CSIPHY_RELEASE \
_IOWR('V', BASE_VIDIOC_PRIVATE + 9, void *)
#endif
@@ -0,0 +1,928 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <mach/gpio.h>
#include <mach/camera.h>
#include "msm_ispif.h"
#include "msm.h"
#include "msm_ispif_hwreg.h"
#define V4L2_IDENT_ISPIF 50001
#define CSID_VERSION_V2 0x02000011
#define CSID_VERSION_V3 0x30000000
#define MAX_CID 15
static atomic_t ispif_irq_cnt;
static spinlock_t ispif_tasklet_lock;
static struct list_head ispif_tasklet_q;
static int msm_ispif_intf_reset(struct ispif_device *ispif,
uint16_t intfmask, uint8_t vfe_intf)
{
int rc = 0;
uint32_t data = (0x1 << STROBED_RST_EN);
uint16_t intfnum = 0, mask = intfmask;
while (mask != 0) {
if (!(intfmask & (0x1 << intfnum))) {
mask >>= 1;
intfnum++;
continue;
}
switch (intfnum) {
case PIX0:
data |= (0x1 << PIX_0_VFE_RST_STB) |
(0x1 << PIX_0_CSID_RST_STB);
ispif->pix_sof_count = 0;
break;
case RDI0:
data |= (0x1 << RDI_0_VFE_RST_STB) |
(0x1 << RDI_0_CSID_RST_STB);
break;
case PIX1:
data |= (0x1 << PIX_1_VFE_RST_STB) |
(0x1 << PIX_1_CSID_RST_STB);
break;
case RDI1:
data |= (0x1 << RDI_1_VFE_RST_STB) |
(0x1 << RDI_1_CSID_RST_STB);
break;
case RDI2:
data |= (0x1 << RDI_2_VFE_RST_STB) |
(0x1 << RDI_2_CSID_RST_STB);
break;
default:
rc = -EINVAL;
break;
}
mask >>= 1;
intfnum++;
} /*end while */
if (data > 0x1) {
if (vfe_intf == VFE0)
msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
else
msm_camera_io_w(data, ispif->base +
ISPIF_RST_CMD_1_ADDR);
rc = wait_for_completion_interruptible(&ispif->reset_complete);
}
return rc;
}
static int msm_ispif_reset(struct ispif_device *ispif)
{
int rc = 0;
ispif->pix_sof_count = 0;
msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR);
if (ispif->csid_version == CSID_VERSION_V3)
msm_camera_io_w(ISPIF_RST_CMD_1_MASK, ispif->base +
ISPIF_RST_CMD_1_ADDR);
rc = wait_for_completion_interruptible(&ispif->reset_complete);
return rc;
}
static int msm_ispif_subdev_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
BUG_ON(!chip);
chip->ident = V4L2_IDENT_ISPIF;
chip->revision = 0;
return 0;
}
static void msm_ispif_sel_csid_core(struct ispif_device *ispif,
uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
{
int rc = 0;
uint32_t data = 0;
if (ispif->csid_version <= CSID_VERSION_V2) {
if (ispif->ispif_clk[intftype] == NULL) {
pr_err("%s: ispif NULL clk\n", __func__);
return;
}
rc = clk_set_rate(ispif->ispif_clk[intftype], csid);
if (rc < 0)
pr_err("%s: clk_set_rate failed %d\n", __func__, rc);
}
data = msm_camera_io_r(ispif->base + ISPIF_INPUT_SEL_ADDR +
(0x200 * vfe_intf));
switch (intftype) {
case PIX0:
data &= ~(0x3);
data |= csid;
break;
case RDI0:
data &= ~(0x3 << 4);
data |= (csid << 4);
break;
case PIX1:
data &= ~(0x3 << 8);
data |= (csid << 8);
break;
case RDI1:
data &= ~(0x3 << 12);
data |= (csid << 12);
break;
case RDI2:
data &= ~(0x3 << 20);
data |= (csid << 20);
break;
}
if (data) {
msm_camera_io_w(data, ispif->base + ISPIF_INPUT_SEL_ADDR +
(0x200 * vfe_intf));
}
}
static void msm_ispif_enable_intf_cids(struct ispif_device *ispif,
uint8_t intftype, uint16_t cid_mask, uint8_t vfe_intf)
{
uint32_t data = 0;
mutex_lock(&ispif->mutex);
switch (intftype) {
case PIX0:
data = msm_camera_io_r(ispif->base +
ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
data |= cid_mask;
msm_camera_io_w(data, ispif->base +
ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI0:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
data |= cid_mask;
msm_camera_io_w(data, ispif->base +
ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case PIX1:
data = msm_camera_io_r(ispif->base +
ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
data |= cid_mask;
msm_camera_io_w(data, ispif->base +
ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI1:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
data |= cid_mask;
msm_camera_io_w(data, ispif->base +
ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI2:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
data |= cid_mask;
msm_camera_io_w(data, ispif->base +
ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
}
mutex_unlock(&ispif->mutex);
}
static int32_t msm_ispif_validate_intf_status(struct ispif_device *ispif,
uint8_t intftype, uint8_t vfe_intf)
{
int32_t rc = 0;
uint32_t data = 0;
mutex_lock(&ispif->mutex);
switch (intftype) {
case PIX0:
data = msm_camera_io_r(ispif->base +
ISPIF_PIX_0_STATUS_ADDR + (0x200 * vfe_intf));
break;
case RDI0:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_0_STATUS_ADDR + (0x200 * vfe_intf));
break;
case PIX1:
data = msm_camera_io_r(ispif->base +
ISPIF_PIX_1_STATUS_ADDR + (0x200 * vfe_intf));
break;
case RDI1:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_1_STATUS_ADDR + (0x200 * vfe_intf));
break;
case RDI2:
data = msm_camera_io_r(ispif->base +
ISPIF_RDI_2_STATUS_ADDR + (0x200 * vfe_intf));
break;
}
if ((data & 0xf) != 0xf)
rc = -EBUSY;
mutex_unlock(&ispif->mutex);
return rc;
}
static int msm_ispif_config(struct ispif_device *ispif,
struct msm_ispif_params_list *params_list)
{
uint32_t params_len;
struct msm_ispif_params *ispif_params;
int rc = 0, i = 0;
uint8_t intftype;
uint8_t vfe_intf;
params_len = params_list->len;
ispif_params = params_list->params;
CDBG("Enable interface\n");
msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_ADDR);
msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_1_ADDR);
msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_2_ADDR);
for (i = 0; i < params_len; i++) {
intftype = ispif_params[i].intftype;
vfe_intf = ispif_params[i].vfe_intf;
CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__,
intftype, vfe_intf, ispif_params[i].csid);
if ((intftype >= INTF_MAX) ||
(ispif->csid_version <= CSID_VERSION_V2 &&
vfe_intf > VFE0) ||
(ispif->csid_version == CSID_VERSION_V3 &&
vfe_intf >= VFE_MAX)) {
pr_err("%s: intftype / vfe intf not valid\n",
__func__);
return -EINVAL;
}
rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
if (rc < 0) {
pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
return rc;
}
msm_ispif_sel_csid_core(ispif, intftype, ispif_params[i].csid,
vfe_intf);
msm_ispif_enable_intf_cids(ispif, intftype,
ispif_params[i].cid_mask, vfe_intf);
}
msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
ISPIF_IRQ_MASK_ADDR);
msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
ISPIF_IRQ_CLEAR_ADDR);
msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
ISPIF_IRQ_MASK_1_ADDR);
msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
ISPIF_IRQ_CLEAR_1_ADDR);
msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
ISPIF_IRQ_MASK_2_ADDR);
msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
ISPIF_IRQ_CLEAR_2_ADDR);
msm_camera_io_w(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
return rc;
}
static uint32_t msm_ispif_get_cid_mask(struct ispif_device *ispif,
uint16_t intftype, uint8_t vfe_intf)
{
uint32_t mask = 0;
switch (intftype) {
case PIX0:
mask = msm_camera_io_r(ispif->base +
ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI0:
mask = msm_camera_io_r(ispif->base +
ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case PIX1:
mask = msm_camera_io_r(ispif->base +
ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI1:
mask = msm_camera_io_r(ispif->base +
ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
case RDI2:
mask = msm_camera_io_r(ispif->base +
ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
break;
default:
break;
}
return mask;
}
static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint16_t intfmask,
uint8_t intf_cmd_mask, uint8_t vfe_intf)
{
uint8_t vc = 0, val = 0;
uint16_t mask = intfmask, intfnum = 0;
uint32_t cid_mask = 0;
uint32_t global_intf_cmd_mask1 = 0xFFFFFFFF;
while (mask != 0) {
if (!(intfmask & (0x1 << intfnum))) {
mask >>= 1;
intfnum++;
continue;
}
cid_mask = msm_ispif_get_cid_mask(ispif, intfnum, vfe_intf);
vc = 0;
while (cid_mask != 0) {
if ((cid_mask & 0xf) != 0x0) {
if (intfnum != RDI2) {
val = (intf_cmd_mask>>(vc*2)) & 0x3;
ispif->global_intf_cmd_mask |=
(0x3 << ((vc * 2) +
(intfnum * 8)));
ispif->global_intf_cmd_mask &=
~((0x3 & ~val) << ((vc * 2) +
(intfnum * 8)));
} else
global_intf_cmd_mask1 &=
~((0x3 & ~intf_cmd_mask)
<< ((vc * 2) + 8));
}
vc++;
cid_mask >>= 4;
}
mask >>= 1;
intfnum++;
}
msm_camera_io_w(ispif->global_intf_cmd_mask,
ispif->base + ISPIF_INTF_CMD_ADDR + (0x200 * vfe_intf));
if (global_intf_cmd_mask1 != 0xFFFFFFFF)
msm_camera_io_w(global_intf_cmd_mask1,
ispif->base + ISPIF_INTF_CMD_1_ADDR +
(0x200 * vfe_intf));
}
static int msm_ispif_abort_intf_transfer(struct ispif_device *ispif,
uint16_t intfmask, uint8_t vfe_intf)
{
int rc = 0;
uint8_t intf_cmd_mask = 0xAA;
uint16_t intfnum = 0, mask = intfmask;
mutex_lock(&ispif->mutex);
CDBG("%s intfmask %x intf_cmd_mask %x\n", __func__, intfmask,
intf_cmd_mask);
msm_ispif_intf_cmd(ispif, intfmask, intf_cmd_mask, vfe_intf);
while (mask != 0) {
if (intfmask & (0x1 << intfnum))
ispif->global_intf_cmd_mask |= (0xFF << (intfnum * 8));
mask >>= 1;
intfnum++;
if (intfnum == RDI2)
break;
}
mutex_unlock(&ispif->mutex);
return rc;
}
static int msm_ispif_start_intf_transfer(struct ispif_device *ispif,
uint16_t intfmask, uint8_t vfe_intf)
{
uint8_t intf_cmd_mask = 0x55;
int rc = 0;
mutex_lock(&ispif->mutex);
rc = msm_ispif_intf_reset(ispif, intfmask, vfe_intf);
CDBG("%s intfmask start after%x intf_cmd_mask %x\n", __func__, intfmask,
intf_cmd_mask);
msm_ispif_intf_cmd(ispif, intfmask, intf_cmd_mask, vfe_intf);
mutex_unlock(&ispif->mutex);
return rc;
}
static int msm_ispif_stop_intf_transfer(struct ispif_device *ispif,
uint16_t intfmask, uint8_t vfe_intf)
{
int rc = 0;
uint8_t intf_cmd_mask = 0x00;
uint16_t intfnum = 0, mask = intfmask;
mutex_lock(&ispif->mutex);
CDBG("%s intfmask %x intf_cmd_mask %x\n", __func__, intfmask,
intf_cmd_mask);
msm_ispif_intf_cmd(ispif, intfmask, intf_cmd_mask, vfe_intf);
while (mask != 0) {
if (intfmask & (0x1 << intfnum)) {
switch (intfnum) {
case PIX0:
while ((msm_camera_io_r(ispif->base +
ISPIF_PIX_0_STATUS_ADDR +
(0x200 * vfe_intf))
& 0xf) != 0xf) {
CDBG("Wait for pix0 Idle\n");
}
break;
case RDI0:
while ((msm_camera_io_r(ispif->base +
ISPIF_RDI_0_STATUS_ADDR +
(0x200 * vfe_intf))
& 0xf) != 0xf) {
CDBG("Wait for rdi0 Idle\n");
}
break;
case PIX1:
while ((msm_camera_io_r(ispif->base +
ISPIF_PIX_1_STATUS_ADDR +
(0x200 * vfe_intf))
& 0xf) != 0xf) {
CDBG("Wait for pix1 Idle\n");
}
break;
case RDI1:
while ((msm_camera_io_r(ispif->base +
ISPIF_RDI_1_STATUS_ADDR +
(0x200 * vfe_intf))
& 0xf) != 0xf) {
CDBG("Wait for rdi1 Idle\n");
}
break;
case RDI2:
while ((msm_camera_io_r(ispif->base +
ISPIF_RDI_2_STATUS_ADDR +
(0x200 * vfe_intf))
& 0xf) != 0xf) {
CDBG("Wait for rdi2 Idle\n");
}
break;
default:
break;
}
if (intfnum != RDI2)
ispif->global_intf_cmd_mask |= (0xFF <<
(intfnum * 8));
}
mask >>= 1;
intfnum++;
}
mutex_unlock(&ispif->mutex);
return rc;
}
static int msm_ispif_subdev_video_s_stream(struct v4l2_subdev *sd,
int enable)
{
struct ispif_device *ispif =
(struct ispif_device *)v4l2_get_subdevdata(sd);
uint32_t cmd = enable & ((1<<ISPIF_S_STREAM_SHIFT)-1);
uint16_t intf = enable >> ISPIF_S_STREAM_SHIFT;
uint8_t vfe_intf = enable >> ISPIF_VFE_INTF_SHIFT;
int rc = -EINVAL;
CDBG("%s enable %x, cmd %x, intf %x\n", __func__, enable, cmd, intf);
BUG_ON(!ispif);
if ((ispif->csid_version <= CSID_VERSION_V2 && vfe_intf > VFE0) ||
(ispif->csid_version == CSID_VERSION_V3 &&
vfe_intf >= VFE_MAX)) {
pr_err("%s invalid csid version %x && vfe intf %d\n", __func__,
ispif->csid_version, vfe_intf);
return rc;
}
switch (cmd) {
case ISPIF_ON_FRAME_BOUNDARY:
rc = msm_ispif_start_intf_transfer(ispif, intf, vfe_intf);
break;
case ISPIF_OFF_FRAME_BOUNDARY:
rc = msm_ispif_stop_intf_transfer(ispif, intf, vfe_intf);
break;
case ISPIF_OFF_IMMEDIATELY:
rc = msm_ispif_abort_intf_transfer(ispif, intf, vfe_intf);
break;
default:
break;
}
return rc;
}
static void send_rdi_sof(struct ispif_device *ispif,
enum msm_ispif_intftype interface, int count)
{
struct rdi_count_msg sof_msg;
sof_msg.rdi_interface = interface;
sof_msg.count = count;
v4l2_subdev_notify(&ispif->subdev, NOTIFY_AXI_RDI_SOF_COUNT,
(void *)&sof_msg);
}
static void ispif_do_tasklet(unsigned long data)
{
unsigned long flags;
struct ispif_isr_queue_cmd *qcmd = NULL;
struct ispif_device *ispif;
ispif = (struct ispif_device *)data;
while (atomic_read(&ispif_irq_cnt)) {
spin_lock_irqsave(&ispif_tasklet_lock, flags);
qcmd = list_first_entry(&ispif_tasklet_q,
struct ispif_isr_queue_cmd, list);
atomic_sub(1, &ispif_irq_cnt);
if (!qcmd) {
spin_unlock_irqrestore(&ispif_tasklet_lock,
flags);
return;
}
list_del(&qcmd->list);
spin_unlock_irqrestore(&ispif_tasklet_lock,
flags);
kfree(qcmd);
}
}
static void ispif_process_irq(struct ispif_device *ispif,
struct ispif_irq_status *out)
{
unsigned long flags;
struct ispif_isr_queue_cmd *qcmd;
qcmd = kzalloc(sizeof(struct ispif_isr_queue_cmd),
GFP_ATOMIC);
if (!qcmd) {
pr_err("ispif_process_irq: qcmd malloc failed!\n");
return;
}
qcmd->ispifInterruptStatus0 = out->ispifIrqStatus0;
qcmd->ispifInterruptStatus1 = out->ispifIrqStatus1;
qcmd->ispifInterruptStatus2 = out->ispifIrqStatus2;
if (qcmd->ispifInterruptStatus0 &
ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
CDBG("%s: ispif PIX irq status", __func__);
ispif->pix_sof_count++;
v4l2_subdev_notify(&ispif->subdev,
NOTIFY_VFE_PIX_SOF_COUNT,
(void *)&ispif->pix_sof_count);
}
if (qcmd->ispifInterruptStatus0 &
ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
CDBG("%s: ispif RDI0 irq status", __func__);
ispif->rdi0_sof_count++;
send_rdi_sof(ispif, RDI_0, ispif->rdi0_sof_count);
}
if (qcmd->ispifInterruptStatus1 &
ISPIF_IRQ_STATUS_RDI1_SOF_MASK) {
CDBG("%s: ispif RDI1 irq status", __func__);
ispif->rdi1_sof_count++;
send_rdi_sof(ispif, RDI_1, ispif->rdi1_sof_count);
}
if (qcmd->ispifInterruptStatus2 &
ISPIF_IRQ_STATUS_RDI2_SOF_MASK) {
CDBG("%s: ispif RDI2 irq status", __func__);
ispif->rdi2_sof_count++;
send_rdi_sof(ispif, RDI_2, ispif->rdi2_sof_count);
}
spin_lock_irqsave(&ispif_tasklet_lock, flags);
list_add_tail(&qcmd->list, &ispif_tasklet_q);
atomic_add(1, &ispif_irq_cnt);
spin_unlock_irqrestore(&ispif_tasklet_lock, flags);
tasklet_schedule(&ispif->ispif_tasklet);
return;
}
static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
void *data)
{
uint32_t status0 = 0, status1 = 0, status2 = 0;
struct ispif_device *ispif = (struct ispif_device *)data;
out->ispifIrqStatus0 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_ADDR);
out->ispifIrqStatus1 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_1_ADDR);
out->ispifIrqStatus2 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_2_ADDR);
msm_camera_io_w(out->ispifIrqStatus0,
ispif->base + ISPIF_IRQ_CLEAR_ADDR);
msm_camera_io_w(out->ispifIrqStatus1,
ispif->base + ISPIF_IRQ_CLEAR_1_ADDR);
msm_camera_io_w(out->ispifIrqStatus2,
ispif->base + ISPIF_IRQ_CLEAR_2_ADDR);
CDBG("%s: irq vfe0 Irq_status0 = 0x%x, 1 = 0x%x, 2 = 0x%x\n",
__func__, out->ispifIrqStatus0, out->ispifIrqStatus1,
out->ispifIrqStatus2);
if (out->ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
if (out->ispifIrqStatus0 & (0x1 << RESET_DONE_IRQ))
complete(&ispif->reset_complete);
if (out->ispifIrqStatus0 & (0x1 << PIX_INTF_0_OVERFLOW_IRQ))
pr_err("%s: pix intf 0 overflow.\n", __func__);
if (out->ispifIrqStatus0 & (0x1 << RAW_INTF_0_OVERFLOW_IRQ))
pr_err("%s: rdi intf 0 overflow.\n", __func__);
if (out->ispifIrqStatus1 & (0x1 << RAW_INTF_1_OVERFLOW_IRQ))
pr_err("%s: rdi intf 1 overflow.\n", __func__);
if (out->ispifIrqStatus2 & (0x1 << RAW_INTF_2_OVERFLOW_IRQ))
pr_err("%s: rdi intf 2 overflow.\n", __func__);
if ((out->ispifIrqStatus0 & ISPIF_IRQ_STATUS_SOF_MASK) ||
(out->ispifIrqStatus1 & ISPIF_IRQ_STATUS_SOF_MASK) ||
(out->ispifIrqStatus2 & ISPIF_IRQ_STATUS_RDI2_SOF_MASK))
ispif_process_irq(ispif, out);
}
if (ispif->csid_version == CSID_VERSION_V3) {
status0 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_ADDR + 0x200);
msm_camera_io_w(status0,
ispif->base + ISPIF_IRQ_CLEAR_ADDR + 0x200);
status1 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_1_ADDR + 0x200);
msm_camera_io_w(status1,
ispif->base + ISPIF_IRQ_CLEAR_1_ADDR + 0x200);
status2 = msm_camera_io_r(ispif->base +
ISPIF_IRQ_STATUS_2_ADDR + 0x200);
msm_camera_io_w(status2,
ispif->base + ISPIF_IRQ_CLEAR_2_ADDR + 0x200);
CDBG("%s: irq vfe1 Irq_status0 = 0x%x, 1 = 0x%x, 2 = 0x%x\n",
__func__, status0, status1, status2);
}
msm_camera_io_w(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
}
static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
{
struct ispif_irq_status irq;
msm_ispif_read_irq_status(&irq, data);
return IRQ_HANDLED;
}
static struct msm_cam_clk_info ispif_8960_clk_info[] = {
{"csi_pix_clk", 0},
{"csi_rdi_clk", 0},
{"csi_pix1_clk", 0},
{"csi_rdi1_clk", 0},
{"csi_rdi2_clk", 0},
};
static int msm_ispif_init(struct ispif_device *ispif,
const uint32_t *csid_version)
{
int rc = 0;
CDBG("%s called %d\n", __func__, __LINE__);
if (ispif->ispif_state == ISPIF_POWER_UP) {
pr_err("%s: ispif invalid state %d\n", __func__,
ispif->ispif_state);
rc = -EINVAL;
return rc;
}
spin_lock_init(&ispif_tasklet_lock);
INIT_LIST_HEAD(&ispif_tasklet_q);
rc = request_irq(ispif->irq->start, msm_io_ispif_irq,
IRQF_TRIGGER_RISING, "ispif", ispif);
ispif->global_intf_cmd_mask = 0xFFFFFFFF;
init_completion(&ispif->reset_complete);
tasklet_init(&ispif->ispif_tasklet,
ispif_do_tasklet, (unsigned long)ispif);
ispif->csid_version = *csid_version;
if (ispif->csid_version < CSID_VERSION_V2) {
rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
ispif->ispif_clk, 2, 1);
if (rc < 0)
return rc;
} else if (ispif->csid_version == CSID_VERSION_V2) {
rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 1);
if (rc < 0)
return rc;
}
rc = msm_ispif_reset(ispif);
ispif->ispif_state = ISPIF_POWER_UP;
return rc;
}
static void msm_ispif_release(struct ispif_device *ispif)
{
if (ispif->ispif_state != ISPIF_POWER_UP) {
pr_err("%s: ispif invalid state %d\n", __func__,
ispif->ispif_state);
return;
}
CDBG("%s, free_irq\n", __func__);
free_irq(ispif->irq->start, ispif);
tasklet_kill(&ispif->ispif_tasklet);
if (ispif->csid_version < CSID_VERSION_V2) {
msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
ispif->ispif_clk, 2, 0);
} else if (ispif->csid_version == CSID_VERSION_V2) {
msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0);
}
ispif->ispif_state = ISPIF_POWER_DOWN;
}
static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
{
long rc = 0;
struct ispif_cfg_data cdata;
struct ispif_device *ispif =
(struct ispif_device *)v4l2_get_subdevdata(sd);
if (copy_from_user(&cdata, (void *)arg, sizeof(struct ispif_cfg_data)))
return -EFAULT;
CDBG("%s cfgtype = %d\n", __func__, cdata.cfgtype);
switch (cdata.cfgtype) {
case ISPIF_INIT:
CDBG("%s csid_version = %x\n", __func__,
cdata.cfg.csid_version);
rc = msm_ispif_init(ispif, &cdata.cfg.csid_version);
break;
case ISPIF_SET_CFG:
CDBG("%s len = %d, intftype = %d,.cid_mask = %d, csid = %d\n",
__func__,
cdata.cfg.ispif_params.len,
cdata.cfg.ispif_params.params[0].intftype,
cdata.cfg.ispif_params.params[0].cid_mask,
cdata.cfg.ispif_params.params[0].csid);
rc = msm_ispif_config(ispif, &cdata.cfg.ispif_params);
break;
case ISPIF_SET_ON_FRAME_BOUNDARY:
case ISPIF_SET_OFF_FRAME_BOUNDARY:
case ISPIF_SET_OFF_IMMEDIATELY:
rc = msm_ispif_subdev_video_s_stream(sd, cdata.cfg.cmd);
break;
case ISPIF_RELEASE:
msm_ispif_release(ispif);
break;
default:
break;
}
return rc;
}
static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
void *arg)
{
switch (cmd) {
case VIDIOC_MSM_ISPIF_CFG:
return msm_ispif_cmd(sd, arg);
default:
return -ENOIOCTLCMD;
}
}
static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
.g_chip_ident = &msm_ispif_subdev_g_chip_ident,
.ioctl = &msm_ispif_subdev_ioctl,
};
static struct v4l2_subdev_video_ops msm_ispif_subdev_video_ops = {
.s_stream = &msm_ispif_subdev_video_s_stream,
};
static const struct v4l2_subdev_ops msm_ispif_subdev_ops = {
.core = &msm_ispif_subdev_core_ops,
.video = &msm_ispif_subdev_video_ops,
};
static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops;
static int __devinit ispif_probe(struct platform_device *pdev)
{
int rc = 0;
struct msm_cam_subdev_info sd_info;
struct ispif_device *ispif;
CDBG("%s\n", __func__);
ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
if (!ispif) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
}
v4l2_subdev_init(&ispif->subdev, &msm_ispif_subdev_ops);
ispif->subdev.internal_ops = &msm_ispif_internal_ops;
ispif->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(ispif->subdev.name,
ARRAY_SIZE(ispif->subdev.name), "msm_ispif");
v4l2_set_subdevdata(&ispif->subdev, ispif);
platform_set_drvdata(pdev, &ispif->subdev);
snprintf(ispif->subdev.name, sizeof(ispif->subdev.name),
"ispif");
mutex_init(&ispif->mutex);
if (pdev->dev.of_node)
of_property_read_u32((&pdev->dev)->of_node,
"cell-index", &pdev->id);
ispif->mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "ispif");
if (!ispif->mem) {
pr_err("%s: no mem resource?\n", __func__);
rc = -ENODEV;
goto ispif_no_resource;
}
ispif->irq = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "ispif");
if (!ispif->irq) {
pr_err("%s: no irq resource?\n", __func__);
rc = -ENODEV;
goto ispif_no_resource;
}
ispif->io = request_mem_region(ispif->mem->start,
resource_size(ispif->mem), pdev->name);
if (!ispif->io) {
pr_err("%s: no valid mem region\n", __func__);
rc = -EBUSY;
goto ispif_no_resource;
}
ispif->base = ioremap(ispif->mem->start,
resource_size(ispif->mem));
if (!ispif->base) {
rc = -ENOMEM;
goto ispif_no_mem;
}
ispif->pdev = pdev;
sd_info.sdev_type = ISPIF_DEV;
sd_info.sd_index = pdev->id;
sd_info.irq_num = ispif->irq->start;
msm_cam_register_subdev_node(&ispif->subdev, &sd_info);
media_entity_init(&ispif->subdev.entity, 0, NULL, 0);
ispif->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ispif->subdev.entity.group_id = ISPIF_DEV;
ispif->subdev.entity.name = pdev->name;
ispif->subdev.entity.revision = ispif->subdev.devnode->num;
ispif->ispif_state = ISPIF_POWER_DOWN;
return 0;
ispif_no_mem:
release_mem_region(ispif->mem->start,
resource_size(ispif->mem));
ispif_no_resource:
mutex_destroy(&ispif->mutex);
kfree(ispif);
return rc;
}
static const struct of_device_id msm_ispif_dt_match[] = {
{.compatible = "qcom,ispif"},
};
MODULE_DEVICE_TABLE(of, msm_ispif_dt_match);
static struct platform_driver ispif_driver = {
.probe = ispif_probe,
.driver = {
.name = MSM_ISPIF_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = msm_ispif_dt_match,
},
};
static int __init msm_ispif_init_module(void)
{
return platform_driver_register(&ispif_driver);
}
static void __exit msm_ispif_exit_module(void)
{
platform_driver_unregister(&ispif_driver);
}
module_init(msm_ispif_init_module);
module_exit(msm_ispif_exit_module);
MODULE_DESCRIPTION("MSM ISP Interface driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,62 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_ISPIF_H
#define MSM_ISPIF_H
#include <linux/clk.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
struct ispif_irq_status {
uint32_t ispifIrqStatus0;
uint32_t ispifIrqStatus1;
uint32_t ispifIrqStatus2;
};
enum msm_ispif_state_t {
ISPIF_POWER_UP,
ISPIF_POWER_DOWN,
};
struct ispif_device {
struct platform_device *pdev;
struct v4l2_subdev subdev;
struct resource *mem;
struct resource *irq;
struct resource *io;
void __iomem *base;
struct mutex mutex;
uint8_t start_ack_pending;
struct completion reset_complete;
uint32_t csid_version;
struct clk *ispif_clk[5];
uint32_t pix_sof_count;
uint32_t rdi0_sof_count;
uint32_t rdi1_sof_count;
uint32_t rdi2_sof_count;
uint32_t global_intf_cmd_mask;
struct tasklet_struct ispif_tasklet;
enum msm_ispif_state_t ispif_state;
};
struct ispif_isr_queue_cmd {
struct list_head list;
uint32_t ispifInterruptStatus0;
uint32_t ispifInterruptStatus1;
uint32_t ispifInterruptStatus2;
};
#define VIDIOC_MSM_ISPIF_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 18, struct ispif_cfg_data*)
#endif