blob: 0fbe23843179840f1babd82aa6f54b06c7535d98 [file] [log] [blame]
/* Copyright (c) 2011-2013, 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 <linux/irqreturn.h>
#include <mach/vreg.h>
#include "msm_csiphy.h"
#include "msm_sd.h"
#include "msm_csiphy_hwreg.h"
#include "msm_camera_io_util.h"
#define DBG_CSIPHY 0
#define V4L2_IDENT_CSIPHY 50003
#define CSIPHY_VERSION_V22 0x01
#define CSIPHY_VERSION_V20 0x00
#define CSIPHY_VERSION_V30 0x10
#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
#undef CDBG
#ifdef CONFIG_MSMB_CAMERA_DEBUG
#define CDBG(fmt, args...) pr_err(fmt, ##args)
#else
#define CDBG(fmt, args...) do { } while (0)
#endif
static 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;
uint8_t csiphy_id = csiphy_dev->pdev->id;
csiphybase = csiphy_dev->base;
if (!csiphybase) {
pr_err("%s: csiphybase NULL\n", __func__);
return -EINVAL;
}
csiphy_dev->lane_mask[csiphy_id] |= csiphy_params->lane_mask;
lane_mask = csiphy_dev->lane_mask[csiphy_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 csid %d\n",
__func__,
csiphy_params->lane_mask,
csiphy_params->lane_cnt,
csiphy_params->settle_cnt,
csiphy_params->csid_core);
if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30) {
val = msm_camera_io_r(csiphy_dev->clk_mux_base);
if (csiphy_params->combo_mode &&
(csiphy_params->lane_mask & 0x18)) {
val &= ~0xf0;
val |= csiphy_params->csid_core << 4;
} else {
val &= ~0xf;
val |= csiphy_params->csid_core;
}
msm_camera_io_w(val, csiphy_dev->clk_mux_base);
CDBG("%s clk mux addr %p val 0x%x\n", __func__,
csiphy_dev->clk_mux_base, val);
mb();
}
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_V30) {
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_8610_clk_info[] = {
{"csiphy_timer_src_clk", 200000000},
{"csiphy_timer_clk", -1},
};
static struct msm_cam_clk_info csiphy_8974_clk_info[] = {
{"camss_top_ahb_clk", -1},
{"ispif_ahb_clk", -1},
{"csiphy_timer_src_clk", 200000000},
{"csiphy_timer_clk", -1},
};
#if DBG_CSIPHY
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
if (csiphy_dev->ref_count++) {
CDBG("%s csiphy refcount = %d\n", __func__,
csiphy_dev->ref_count);
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
CDBG("%s:%d called\n", __func__, __LINE__);
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 if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8610_clk_info), 1);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
if (!csiphy_dev->clk_mux_mem || !csiphy_dev->clk_mux_io) {
pr_err("%s clk mux mem %p io %p\n", __func__,
csiphy_dev->clk_mux_mem,
csiphy_dev->clk_mux_io);
rc = -ENOMEM;
return rc;
}
csiphy_dev->clk_mux_base = ioremap(
csiphy_dev->clk_mux_mem->start,
resource_size(csiphy_dev->clk_mux_mem));
if (!csiphy_dev->clk_mux_base) {
pr_err("%s: ERROR %d\n", __func__, __LINE__);
rc = -ENOMEM;
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 1);
} else {
pr_err("%s: ERROR Invalid CSIPHY Version %d",
__func__, __LINE__);
rc = -EINVAL;
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
enable_irq(csiphy_dev->irq->start);
msm_csiphy_reset(csiphy_dev);
CDBG("%s:%d called\n", __func__, __LINE__);
if (CSIPHY_VERSION == CSIPHY_VERSION_V30)
csiphy_dev->hw_version =
msm_camera_io_r(csiphy_dev->base +
MIPI_CSIPHY_HW_VERSION_ADDR);
else
csiphy_dev->hw_version = CSIPHY_VERSION;
CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
}
#else
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
if (csiphy_dev->ref_count++) {
CDBG("%s csiphy refcount = %d\n", __func__,
csiphy_dev->ref_count);
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
CDBG("%s:%d called\n", __func__, __LINE__);
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 if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8610_clk_info), 1);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
if (!csiphy_dev->clk_mux_mem || !csiphy_dev->clk_mux_io) {
pr_err("%s clk mux mem %p io %p\n", __func__,
csiphy_dev->clk_mux_mem,
csiphy_dev->clk_mux_io);
rc = -ENOMEM;
return rc;
}
csiphy_dev->clk_mux_base = ioremap(
csiphy_dev->clk_mux_mem->start,
resource_size(csiphy_dev->clk_mux_mem));
if (!csiphy_dev->clk_mux_base) {
pr_err("%s: ERROR %d\n", __func__, __LINE__);
rc = -ENOMEM;
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8974_clk_info), 1);
} else {
pr_err("%s: ERROR Invalid CSIPHY Version %d",
__func__, __LINE__);
rc = -EINVAL;
return rc;
}
CDBG("%s:%d called\n", __func__, __LINE__);
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;
}
CDBG("%s:%d called\n", __func__, __LINE__);
msm_csiphy_reset(csiphy_dev);
CDBG("%s:%d called\n", __func__, __LINE__);
if (CSIPHY_VERSION == CSIPHY_VERSION_V30)
csiphy_dev->hw_version =
msm_camera_io_r(csiphy_dev->base +
MIPI_CSIPHY_HW_VERSION_ADDR);
else
csiphy_dev->hw_version = CSIPHY_VERSION;
CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
}
#endif
#if DBG_CSIPHY
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_V30) {
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);
disable_irq(csiphy_dev->irq->start);
if (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 0);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8610_clk_info), 0);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
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->clk_mux_base);
}
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
}
#else
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_V30) {
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 (CSIPHY_VERSION == CSIPHY_VERSION_V20) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8960_clk_info), 0);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V22) {
msm_cam_clk_enable(&csiphy_dev->pdev->dev,
csiphy_8610_clk_info, csiphy_dev->csiphy_clk,
ARRAY_SIZE(csiphy_8610_clk_info), 0);
} else if (CSIPHY_VERSION == CSIPHY_VERSION_V30) {
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->clk_mux_base);
}
iounmap(csiphy_dev->base);
csiphy_dev->base = NULL;
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
return 0;
}
#endif
static long msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
{
int rc = 0;
struct csiphy_cfg_data *cdata = (struct csiphy_cfg_data *)arg;
struct msm_camera_csiphy_params csiphy_params;
struct msm_camera_csi_lane_params csi_lane_params;
if (!csiphy_dev || !cdata) {
pr_err("%s: csiphy_dev NULL\n", __func__);
return -EINVAL;
}
switch (cdata->cfgtype) {
case CSIPHY_INIT:
rc = msm_csiphy_init(csiphy_dev);
break;
case CSIPHY_CFG:
if (copy_from_user(&csiphy_params,
(void *)cdata->cfg.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;
case CSIPHY_RELEASE:
if (copy_from_user(&csi_lane_params,
(void *)cdata->cfg.csi_lane_params,
sizeof(struct msm_camera_csi_lane_params))) {
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -EFAULT;
break;
}
rc = msm_csiphy_release(csiphy_dev, &csi_lane_params);
break;
default:
pr_err("%s: %d failed\n", __func__, __LINE__);
rc = -ENOIOCTLCMD;
break;
}
return rc;
}
static int32_t msm_csiphy_get_subdev_id(struct csiphy_device *csiphy_dev,
void *arg)
{
uint32_t *subdev_id = (uint32_t *)arg;
if (!subdev_id) {
pr_err("%s:%d failed\n", __func__, __LINE__);
return -EINVAL;
}
*subdev_id = csiphy_dev->pdev->id;
pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
return 0;
}
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);
CDBG("%s:%d id %d\n", __func__, __LINE__, csiphy_dev->pdev->id);
mutex_lock(&csiphy_dev->mutex);
switch (cmd) {
case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
rc = msm_csiphy_get_subdev_id(csiphy_dev, arg);
break;
case VIDIOC_MSM_CSIPHY_IO_CFG:
rc = msm_csiphy_cmd(csiphy_dev, arg);
break;
case VIDIOC_MSM_CSIPHY_RELEASE:
case MSM_SD_SHUTDOWN:
rc = msm_csiphy_release(csiphy_dev, arg);
break;
default:
pr_err("%s: command not found\n", __func__);
}
mutex_unlock(&csiphy_dev->mutex);
CDBG("%s:%d\n", __func__, __LINE__);
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;
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->msm_sd.sd, &msm_csiphy_subdev_ops);
v4l2_set_subdevdata(&new_csiphy_dev->msm_sd.sd, new_csiphy_dev);
platform_set_drvdata(pdev, &new_csiphy_dev->msm_sd.sd);
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->clk_mux_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "csiphy_clk_mux");
if (new_csiphy_dev->clk_mux_mem) {
new_csiphy_dev->clk_mux_io = request_mem_region(
new_csiphy_dev->clk_mux_mem->start,
resource_size(new_csiphy_dev->clk_mux_mem),
new_csiphy_dev->clk_mux_mem->name);
if (!new_csiphy_dev->clk_mux_io)
pr_err("%s: ERROR %d\n", __func__, __LINE__);
}
new_csiphy_dev->pdev = pdev;
new_csiphy_dev->msm_sd.sd.internal_ops = &msm_csiphy_internal_ops;
new_csiphy_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(new_csiphy_dev->msm_sd.sd.name,
ARRAY_SIZE(new_csiphy_dev->msm_sd.sd.name), "msm_csiphy");
media_entity_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL, 0);
new_csiphy_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
new_csiphy_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSIPHY;
new_csiphy_dev->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x4;
msm_sd_register(&new_csiphy_dev->msm_sd);
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");