blob: e62ee5050df704316e204e74ab650160516e39e7 [file] [log] [blame]
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl6040.h>
#include <plat/i2c.h>
#include <plat/omap-pm.h>
#include <asm/mach-types.h>
#include <media/ov5640.h>
#include <media/ov5650.h>
#include "devices.h"
#include "../../../drivers/media/video/omap4iss/iss.h"
#include "control.h"
#include "mux.h"
#define OMAP4430SDP_GPIO_CAM_PDN_C 39
static struct clk *sdp4430_cam_aux_clk;
static int sdp4430_ov5640_power(struct v4l2_subdev *subdev, int on)
{
struct iss_device *iss = v4l2_dev_to_iss_device(subdev->v4l2_dev);
int ret = 0;
struct iss_csiphy_dphy_cfg dphy;
struct iss_csiphy_lanes_cfg lanes;
#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
unsigned int ddr_freq = 480; /* FIXME: Do an actual query for this */
#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
unsigned int ddr_freq = 336; /* FIXME: Do an actual query for this */
#endif
memset(&lanes, 0, sizeof(lanes));
memset(&dphy, 0, sizeof(dphy));
lanes.clk.pos = 1;
lanes.clk.pol = 0;
lanes.data[0].pos = 2;
lanes.data[0].pol = 0;
#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
lanes.data[1].pos = 3;
lanes.data[1].pol = 0;
#endif
dphy.ths_term = ((((12500 * ddr_freq + 1000000) / 1000000) - 1) & 0xFF);
dphy.ths_settle = ((((90000 * ddr_freq + 1000000) / 1000000) + 3) & 0xFF);
dphy.tclk_term = 0;
dphy.tclk_miss = 1;
dphy.tclk_settle = 14;
if (on) {
u8 gpoctl = 0;
/* TWL6030_BASEADD_AUX */
twl_i2c_write_u8(15, 0x00, 0xB);
twl_i2c_write_u8(15, 0x80, 0x1);
mdelay(50);
/* TWL6030_BASEADD_PM_SLAVE_MISC */
twl_i2c_write_u8(21, 0xFF, 0x5E);
twl_i2c_write_u8(21, 0x13, 0x5F);
mdelay(50);
twl_i2c_write_u8(15, 0x40, 0x1);
twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &gpoctl,
TWL6040_REG_GPOCTL);
twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, gpoctl | TWL6040_GPO3,
TWL6040_REG_GPOCTL);
mdelay(10);
gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 1);
mdelay(10);
clk_enable(sdp4430_cam_aux_clk); /* Enable XCLK */
mdelay(10);
iss->platform_cb.csiphy_config(&iss->csiphy1, &dphy, &lanes);
} else {
clk_disable(sdp4430_cam_aux_clk);
mdelay(1);
gpio_set_value(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
}
return ret;
}
#define OV5640_I2C_ADDRESS (0x3C)
#define OV5650_I2C_ADDRESS (0x36)
#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
static struct ov5650_platform_data ov_platform_data = {
#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
static struct ov5640_platform_data ov_platform_data = {
#endif
.s_power = sdp4430_ov5640_power,
};
static struct i2c_board_info ov_camera_i2c_device = {
#ifdef CONFIG_MACH_OMAP_4430SDP_CAM_OV5650
I2C_BOARD_INFO("ov5650", OV5650_I2C_ADDRESS),
#elif defined(CONFIG_MACH_OMAP_4430SDP_CAM_OV5640)
I2C_BOARD_INFO("ov5640", OV5640_I2C_ADDRESS),
#endif
.platform_data = &ov_platform_data,
};
static struct iss_subdev_i2c_board_info ov_camera_subdevs[] = {
{
.board_info = &ov_camera_i2c_device,
.i2c_adapter_id = 3,
},
{ NULL, 0, },
};
static struct iss_v4l2_subdevs_group sdp4430_camera_subdevs[] = {
{
.subdevs = ov_camera_subdevs,
.interface = ISS_INTERFACE_CSI2A_PHY1,
},
{ },
};
static void sdp4430_omap4iss_set_constraints(struct iss_device *iss, bool enable)
{
if (!iss)
return;
/* FIXME: Look for something more precise as a good throughtput limit */
omap_pm_set_min_bus_tput(iss->dev, OCP_INITIATOR_AGENT,
enable ? 800000 : -1);
}
static struct iss_platform_data sdp4430_iss_platform_data = {
.subdevs = sdp4430_camera_subdevs,
.set_constraints = sdp4430_omap4iss_set_constraints,
};
static struct omap_device_pad omap4iss_pads[] = {
{
.name = "csi21_dx0.csi21_dx0",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
{
.name = "csi21_dy0.csi21_dy0",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
{
.name = "csi21_dx1.csi21_dx1",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
{
.name = "csi21_dy1.csi21_dy1",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
{
.name = "csi21_dx2.csi21_dx2",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
{
.name = "csi21_dy2.csi21_dy2",
.enable = OMAP_MUX_MODE0 | OMAP_INPUT_EN,
},
};
static struct omap_board_data omap4iss_data = {
.id = 1,
.pads = omap4iss_pads,
.pads_cnt = ARRAY_SIZE(omap4iss_pads),
};
static int __init sdp4430_camera_init(void)
{
if (!machine_is_omap_4430sdp())
return 0;
sdp4430_cam_aux_clk = clk_get(NULL, "auxclk3_ck");
if (IS_ERR(sdp4430_cam_aux_clk)) {
printk(KERN_ERR "Unable to get auxclk3_ck\n");
return -ENODEV;
}
if (clk_set_rate(sdp4430_cam_aux_clk,
clk_round_rate(sdp4430_cam_aux_clk, 24000000)))
return -EINVAL;
/*
* CSI2 1(A):
* LANEENABLE[4:0] = 00111(0x7) - Lanes 0, 1 & 2 enabled
* CTRLCLKEN = 1 - Active high enable for CTRLCLK
* CAMMODE = 0 - DPHY mode
*/
omap4_ctrl_pad_writel((omap4_ctrl_pad_readl(
OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX) &
~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
OMAP4_CAMERARX_CSI21_CAMMODE_MASK)) |
(0x7 << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT) |
OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK,
OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
omap_mux_init_gpio(OMAP4430SDP_GPIO_CAM_PDN_C, OMAP_PIN_OUTPUT);
/* Init FREF_CLK3_OUT */
omap_mux_init_signal("fref_clk3_out", OMAP_PIN_OUTPUT);
if (gpio_request(OMAP4430SDP_GPIO_CAM_PDN_C, "CAM_PDN_C"))
printk(KERN_WARNING "Cannot request GPIO %d\n",
OMAP4430SDP_GPIO_CAM_PDN_C);
else
gpio_direction_output(OMAP4430SDP_GPIO_CAM_PDN_C, 0);
omap4_init_camera(&sdp4430_iss_platform_data, &omap4iss_data);
return 0;
}
late_initcall(sdp4430_camera_init);