diff options
Diffstat (limited to 'drivers/media/i2c/soc_camera')
-rw-r--r-- | drivers/media/i2c/soc_camera/Kconfig | 18 | ||||
-rw-r--r-- | drivers/media/i2c/soc_camera/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/i2c/soc_camera/adv7280.c | 834 | ||||
-rw-r--r-- | drivers/media/i2c/soc_camera/ap1302.c | 558 | ||||
-rw-r--r-- | drivers/media/i2c/soc_camera/ov5640.c | 1842 | ||||
-rw-r--r-- | drivers/media/i2c/soc_camera/tc358743.c | 1104 |
6 files changed, 3334 insertions, 1025 deletions
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 4e8b50bf64ec..085553dc2d00 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -12,6 +12,12 @@ config SOC_CAMERA_AR0330 help This driver supports AR0330 camera from Aptina +config SOC_CAMERA_AP1302 + tristate "AP1302 ISP support" + depends on SOC_CAMERA && I2C + help + This driver supports AP1302 ISP from Aptina + config SOC_CAMERA_IMX074 tristate "imx074 support" depends on SOC_CAMERA && I2C @@ -115,3 +121,15 @@ config SOC_CAMERA_TW9910 depends on SOC_CAMERA && I2C help This is a tw9910 video driver + +config SOC_CAMERA_TC358743 + tristate "tc358743 support" + depends on SOC_CAMERA && I2C + help + This is a tc358743 video driver + +config SOC_CAMERA_ADV7280 + tristate "adv7280m support" + depends on SOC_CAMERA && I2C + help + This is a adv7280 video decoder driver diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index faea5137dc83..9bf568334d84 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -17,3 +17,6 @@ obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +obj-$(CONFIG_SOC_CAMERA_AP1302) += ap1302.o +obj-$(CONFIG_SOC_CAMERA_TC358743) += tc358743.o +obj-$(CONFIG_SOC_CAMERA_ADV7280) += adv7280.o diff --git a/drivers/media/i2c/soc_camera/adv7280.c b/drivers/media/i2c/soc_camera/adv7280.c new file mode 100644 index 000000000000..d5e7391b6b75 --- /dev/null +++ b/drivers/media/i2c/soc_camera/adv7280.c @@ -0,0 +1,834 @@ +/* + * ADV7280 camera decoder driver, based on ADV7180 driver + * + * Copyright (c) 2014 Antmicro Ltd <www.antmicro.com> + * Based on ADV7180 video decoder driver, + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <media/v4l2-ioctl.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#define DRIVER_NAME "adv7280" + +#define HW_DEINT /* Enable hardware deinterlacer */ +#define CSI +#define VPP_SLAVE_ADDRESS 0x42 +#define CSI_SLAVE_ADDRESS 0x44 + +/* User Sub Map Regs */ +#define ADV7280_INPUT_CONTROL 0x00 +#define ADV7280_VIDEO_SELECTION_1 0x01 +#define ADV7280_VIDEO_SELECTION_2 0x02 +#define ADV7280_OUTPUT_CONTROL 0x03 +#define ADV7280_EXTENDED_OUTPUT_CONTROL 0x04 +#define ADV7280_AUTODETECT_ENABLE 0x07 +#define ADV7280_ADI_CONTROL_1 0x0E +#define ADV7280_POWER_MANAGEMENT 0x0F +#define ADV7280_STATUS_1 0x10 +#define ADV7280_IDENT 0x11 +#define ADV7280_SHAPING_FILTER_CONTROL_1 0x17 +#define ADV7280_ADI_CONTROL_2 0x1D +#define ADV7280_PIXEL_DELAY_CONTROL 0x27 +#define ADV7280_VPP_SLAVE_ADDRESS 0xFD +#define ADV7280_CSI_TX_SLAVE_ADDRESS 0xFE +#define ADV7280_OUTPUT_SYNC_SELECT_2 0x6B + +/* VPP regs (Video Postprocessor) */ +#define VPP_DEINT_RESET 0x41 +#define VPP_I2C_DEINT_ENABLE 0x55 +#define VPP_ADV_TIMING_MODE_EN 0x5B + +/* CSI regs (Camera Serial Interface, MIPI-CSI2) */ +#define CSI_CSITX_PWRDN 0x00 +#define CSI_DPHY_PWRDN_CTL 0xDE + +/* other */ +#define ADV7280_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 +#define ADV7280_AUTODETECT_DEFAULT 0x7f + +#define ADV7280_INPUT_CONTROL_COMPOSITE_IN1 0x00 +#define ADV7280_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 + +#define ADV7280_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7280_INPUT_CONTROL_PAL60 0x60 +#define ADV7280_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7280_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7280_INPUT_CONTROL_PAL_N 0x90 +#define ADV7280_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7280_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7280_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7280_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7280_INPUT_CONTROL_PAL_SECAM 0xe0 + +#define ADV7280_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7280_STATUS1_IN_LOCK 0x01 +#define ADV7280_STATUS1_AUTOD_MASK 0x70 +#define ADV7280_STATUS1_AUTOD_NTSM_M_J 0x00 +#define ADV7280_STATUS1_AUTOD_NTSC_4_43 0x10 +#define ADV7280_STATUS1_AUTOD_PAL_M 0x20 +#define ADV7280_STATUS1_AUTOD_PAL_60 0x30 +#define ADV7280_STATUS1_AUTOD_PAL_B_G 0x40 +#define ADV7280_STATUS1_AUTOD_SECAM 0x50 +#define ADV7280_STATUS1_AUTOD_PAL_COMB 0x60 +#define ADV7280_STATUS1_AUTOD_SECAM_525 0x70 + +#define ADV7280_ICONF1_ADI 0x40 +#define ADV7280_ICONF1_ACTIVE_LOW 0x01 +#define ADV7280_ICONF1_PSYNC_ONLY 0x10 + +#define ADV7280_IMR1_ADI 0x44 +#define ADV7280_IMR2_ADI 0x48 +#define ADV7280_IRQ3_AD_CHANGE 0x08 +#define ADV7280_ISR3_ADI 0x4A +#define ADV7280_ICR3_ADI 0x4B +#define ADV7280_IMR3_ADI 0x4C +#define ADV7280_IMR4_ADI 0x50 + +struct adv7280_state { + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; + int active_input; +}; + +static v4l2_std_id adv7280_std_to_v4l2(u8 status1) +{ + switch (status1 & ADV7280_STATUS1_AUTOD_MASK) { + case ADV7280_STATUS1_AUTOD_NTSM_M_J: + return V4L2_STD_NTSC; + case ADV7280_STATUS1_AUTOD_NTSC_4_43: + return V4L2_STD_NTSC_443; + case ADV7280_STATUS1_AUTOD_PAL_M: + return V4L2_STD_PAL_M; + case ADV7280_STATUS1_AUTOD_PAL_60: + return V4L2_STD_PAL_60; + case ADV7280_STATUS1_AUTOD_PAL_B_G: + return V4L2_STD_PAL; + case ADV7280_STATUS1_AUTOD_SECAM: + return V4L2_STD_SECAM; + case ADV7280_STATUS1_AUTOD_PAL_COMB: + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + case ADV7280_STATUS1_AUTOD_SECAM_525: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int v4l2_std_to_adv7280(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7280_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7280_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7280_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7280_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7280_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7280_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7280_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7280_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static int adv7280_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + i2c_smbus_write_byte_data(client, reg, val); + return 0; +} + +static u32 adv7280_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7280_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7280_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7280_STATUS_1); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7280_status_to_v4l2(status1); + if (std) + *std = adv7280_std_to_v4l2(status1); + + return 0; +} + +static inline struct adv7280_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7280_state, sd); +} + +static int adv7280_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7280_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7280_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} + +static int adv7280_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7280_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7280_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; +} + +/* +static int adv7280_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7280, 0); +} +*/ + +static int adv7280_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7280_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = adv7280_write_reg(client, + ADV7280_INPUT_CONTROL, + ADV7280_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM | + (ADV7280_INPUT_CONTROL_COMPOSITE_IN1 + + state->active_input)); + if (ret < 0) + goto out; + + __adv7280_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7280(std) | + (ADV7280_INPUT_CONTROL_COMPOSITE_IN1 + + state->active_input); + if (ret < 0) + goto out; + + ret = adv7280_write_reg(client, + ADV7280_INPUT_CONTROL, ret); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + +/* +static int adv7280_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} +*/ + +/* Request bus settings on camera side */ +/* +static unsigned long adv7280_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} +*/ + +static enum v4l2_mbus_pixelcode adv7280_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static int adv7280_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + enum v4l2_colorspace cspace; + enum v4l2_mbus_pixelcode code = mf->code; + + switch (code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + cspace = V4L2_COLORSPACE_SMPTE170M; + break; + default: + return -EINVAL; + } + + mf->code = code; + mf->colorspace = cspace; + + return adv7280_s_std(sd, V4L2_STD_ALL); +} + +static int adv7280_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ +#ifdef HW_DEINT + mf->field = V4L2_FIELD_NONE; +#else + mf->field = V4L2_FIELD_INTERLACED_TB; +#endif + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + + /* PAL, line aligned to 64B */ + mf->width = 736; + mf->height = 576; + + return 0; +} + +static int adv7280_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *scsd = soc_camera_i2c_to_desc(client); + + return soc_camera_set_power(&client->dev, scsd, on); +} + +static int adv7280_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(adv7280_codes)) + return -EINVAL; + + *code = adv7280_codes[index]; + + return 0; +} + +/* +static struct soc_camera_ops adv7280_ops = { + .set_bus_param = adv7280_set_bus_param, + .query_bus_param = adv7280_query_bus_param, +}; +*/ + +static const struct v4l2_subdev_video_ops adv7280_video_ops = { + .s_mbus_fmt = adv7280_s_fmt, + .try_mbus_fmt = adv7280_try_fmt, + .enum_mbus_fmt = adv7280_enum_fmt, + .querystd = adv7280_querystd, + .g_input_status = adv7280_g_input_status, +}; + +static const struct v4l2_subdev_core_ops adv7280_core_ops = { + //.g_chip_ident = adv7280_g_chip_ident, + .s_std = adv7280_s_std, + .s_power = adv7280_s_power, +}; + +static const struct v4l2_subdev_ops adv7280_subdev_ops = { + .core = &adv7280_core_ops, + .video = &adv7280_video_ops, +}; + +static void adv7280_work(struct work_struct *work) +{ + struct adv7280_state *state = container_of(work, struct adv7280_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + adv7280_write_reg(client, ADV7280_ADI_CONTROL_1, + ADV7280_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7280_ISR3_ADI); + /* clear */ + adv7280_write_reg(client, ADV7280_ICR3_ADI, isr3); + adv7280_write_reg(client, ADV7280_ADI_CONTROL_1, 0); + + if (isr3 & ADV7280_IRQ3_AD_CHANGE && state->autodetect) + __adv7280_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7280_irq(int irq, void *devid) +{ + struct adv7280_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct adv7280_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 val; + + if (i < 6) { + state->active_input = i; + val = i2c_smbus_read_byte_data(client, + ADV7280_INPUT_CONTROL); + val &= 0xf0; + val |= (ADV7280_INPUT_CONTROL_COMPOSITE_IN1 + + state->active_input); + return adv7280_write_reg(client, + ADV7280_INPUT_CONTROL, val); + } + return -EINVAL; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct adv7280_state *state = to_state(sd); + + *i = state->active_input; + + return 0; +} + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static int adv7280_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7280_state *state; + struct soc_camera_device *icd = client->dev.platform_data; + struct v4l2_subdev *sd; + u8 ident; + int ret; + struct v4l2_ioctl_ops *ops; + struct i2c_client vpp_client = { + .flags = client->flags, + .addr = VPP_SLAVE_ADDRESS, + .name = "ADV7180_VPP_SLAVE", + .adapter = client->adapter, + .dev = client->dev, + }; + struct i2c_client csi_client = { + .flags = client->flags, + .addr = CSI_SLAVE_ADDRESS, + .name = "ADV7180_CSI_SLAVE", + .adapter = client->adapter, + .dev = client->dev, + }; + + printk("probe, id=%s\n", id->name); + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7280_state), GFP_KERNEL); + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + ident = i2c_smbus_read_byte_data(client, ADV7280_IDENT); + v4l_info(client, "ident reg is 0x%02x\n", ident); + + state->irq = client->irq; + INIT_WORK(&state->work, adv7280_work); + mutex_init(&state->mutex); + state->autodetect = true; + state->active_input = 0; // input 1 + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7280_subdev_ops); + //icd->ops = &adv7280_ops; + + /* Reset */ + ret = adv7280_write_reg(client, + ADV7280_POWER_MANAGEMENT, 0xA0); + if (ret < 0) + goto err_unreg_subdev; + msleep(10); + + /* Initialize adv7280 */ + /* Exit Power Down Mode */ + ret = adv7280_write_reg(client, + ADV7280_POWER_MANAGEMENT, 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + ADV7280_ADI_CONTROL_1, 0x80); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x9C, 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x9C, 0xFF); + if (ret < 0) + goto err_unreg_subdev; + + /* Enter User Sub Map */ + ret = adv7280_write_reg(client, + ADV7280_ADI_CONTROL_1, 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* Enable Pixel & Sync output drivers */ + ret = adv7280_write_reg(client, + ADV7280_OUTPUT_CONTROL, 0x0C); + if (ret < 0) + goto err_unreg_subdev; + + /* Power-up INTRQ, HS & VS pads */ + ret = adv7280_write_reg(client, + ADV7280_EXTENDED_OUTPUT_CONTROL, 0x07); + if (ret < 0) + goto err_unreg_subdev; + + /* Enable SH1 */ + /* + ret = adv7280_write_reg(client, + ADV7280_SHAPING_FILTER_CONTROL_1, 0x41); + if (ret < 0) + goto err_unreg_subdev; + */ + + /* Disable comb filtering */ + /* + ret = adv7280_write_reg(client, + 0x39, 0x24); + if (ret < 0) + goto err_unreg_subdev; + */ + + /* Enable LLC output driver */ + ret = adv7280_write_reg(client, + ADV7280_ADI_CONTROL_2, 0x40); + if (ret < 0) + goto err_unreg_subdev; + + /* VSYNC on VS/FIELD/SFL pin */ + ret = adv7280_write_reg(client, + ADV7280_OUTPUT_SYNC_SELECT_2, 0x01); + if (ret < 0) + goto err_unreg_subdev; + + /* Enable autodetection */ + ret = adv7280_write_reg(client, ADV7280_INPUT_CONTROL, + ADV7280_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM | + (ADV7280_INPUT_CONTROL_COMPOSITE_IN1 + + state->active_input)); + if (ret < 0) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_AUTODETECT_ENABLE, + ADV7280_AUTODETECT_DEFAULT); + if (ret < 0) + goto err_unreg_subdev; + + /* ITU-R BT.656-4 compatible + ret = adv7280_write_reg(client, + ADV7280_EXTENDED_OUTPUT_CONTROL, + ADV7280_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + goto err_unreg_subdev; + */ + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x52, 0xCD); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x80, 0x51); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x81, 0x51); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(client, + 0x82, 0x68); + if (ret < 0) + goto err_unreg_subdev; + +#ifdef HW_DEINT + /* Set VPP Map */ + ret = adv7280_write_reg(client, + ADV7280_VPP_SLAVE_ADDRESS, (VPP_SLAVE_ADDRESS << 1)); + if (ret < 0) + goto err_unreg_subdev; + + /* VPP - not documented */ + ret = adv7280_write_reg(&vpp_client, + 0xA3, 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* VPP - Enbable Advanced Timing Mode */ + ret = adv7280_write_reg(&vpp_client, + VPP_ADV_TIMING_MODE_EN, 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* VPP - Enable Deinterlacer */ + ret = adv7280_write_reg(&vpp_client, + VPP_I2C_DEINT_ENABLE, 0x80); + if (ret < 0) + goto err_unreg_subdev; +#endif + +#ifdef CSI + /* Set CSI Map */ + ret = adv7280_write_reg(client, + ADV7280_CSI_TX_SLAVE_ADDRESS, (CSI_SLAVE_ADDRESS << 1)); + if (ret < 0) + goto err_unreg_subdev; + + /* Power up MIPI D-PHY */ + ret = adv7280_write_reg(&csi_client, + CSI_DPHY_PWRDN_CTL, 0x02); + if (ret < 0) + goto err_unreg_subdev; + + + ret = adv7280_write_reg(&csi_client, + 0x02 , 0x18); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(&csi_client, + 0xD2 , 0xF7); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(&csi_client, + 0xD8 , 0x65); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(&csi_client, + 0xE0 , 0x09); + if (ret < 0) + goto err_unreg_subdev; + + /* analog devices recommends */ + ret = adv7280_write_reg(&csi_client, + 0x2C , 0x00); + if (ret < 0) + goto err_unreg_subdev; + + /* MIPI-CSI2 power up */ + ret = adv7280_write_reg(&csi_client, + CSI_CSITX_PWRDN , 0x00); + if (ret < 0) + goto err_unreg_subdev; +#endif + + /* read current norm */ + __adv7280_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7280_irq, 0, DRIVER_NAME, + state); + if (ret) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_ADI_CONTROL_1, + ADV7280_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + goto err_unreg_subdev; + + /* config the Interrupt pin to be active low */ + ret = adv7280_write_reg(client, ADV7280_ICONF1_ADI, + ADV7280_ICONF1_ACTIVE_LOW | ADV7280_ICONF1_PSYNC_ONLY); + if (ret < 0) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_IMR1_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_IMR2_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + /* enable AD change interrupts interrupts */ + ret = adv7280_write_reg(client, ADV7280_IMR3_ADI, + ADV7280_IRQ3_AD_CHANGE); + if (ret < 0) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_IMR4_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = adv7280_write_reg(client, ADV7280_ADI_CONTROL_1, + 0); + if (ret < 0) + goto err_unreg_subdev; + } + + /* + * this is the only way to support more than one input as soc_camera + * assumes in its own vidioc_s(g)_input implementation that only one + * input is present we have to override that with our own handlers. + */ + /* + ops = (struct v4l2_ioctl_ops*)icd->vdev->ioctl_ops; + ops->vidioc_s_input = &vidioc_s_input; + ops->vidioc_g_input = &vidioc_g_input; + */ + + return 0; + +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); + return ret; +} + +static int adv7280_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7280_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id adv7280_id[] = { + {"adv7280", 0}, + {"adv7280-M", 1}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7280_id); + +static struct i2c_driver adv7280_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = adv7280_probe, + .remove = adv7280_remove, + .id_table = adv7280_id, +}; + +static __init int adv7280_init(void) +{ + return i2c_add_driver(&adv7280_driver); +} + +static __exit void adv7280_exit(void) +{ + i2c_del_driver(&adv7280_driver); +} + +module_init(adv7280_init); +module_exit(adv7280_exit); + +MODULE_DESCRIPTION("Analog Devices ADV7280/ADV7280-M video decoder driver"); +MODULE_AUTHOR("Wojciech Bieganski <wbieganski@antmicro.com>"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/soc_camera/ap1302.c b/drivers/media/i2c/soc_camera/ap1302.c new file mode 100644 index 000000000000..00a4315bcc39 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ap1302.c @@ -0,0 +1,558 @@ +/* + * Copyright (c) Antmicro Ltd. All rights reserved. + * based on ov5640 driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> +#include <linux/firmware.h> +#include <linux/gpio.h> + +#define AP1302_NAME "ap1302" +#define AP1302_CHIP_ID 0x0265 +#define AP1302_FW_WINDOW_OFFSET 0x8000 +#define AP1302_FW_WINDOW_SIZE 0x2000 + +#define REG_CHIP_VERSION 0x0000 +#define REG_CHIP_REV 0x0050 + +#define REG_ERROR 0x0006 +#define REG_CTRL 0x1000 +#define REG_ENABLE 0x1008 +#define REG_PREVIEW_WIDTH 0x2000 +#define REG_PREVIEW_HEIGHT 0x2002 +#define REG_PREVIEW_ROI_X0 0x2004 +#define REG_PREVIEW_ROI_Y0 0x2006 +#define REG_PREVIEW_ROI_X1 0x2008 +#define REG_PREVIEW_ENABLE 0x2010 +#define REG_PREVIEW_OUT_FMT 0x2012 +#define REG_PREVIEW_SENSOR_MODE 0x2014 +#define REG_PREVIEW_MAX_FPS 0x2020 +#define REG_PREVIEW_AE_USG 0x2022 +#define REG_PREVIEW_HINF_CTRL 0x2030 +#define REG_PREVIEW_HINF_SPOOF_W 0x2032 +#define REG_PREVIEW_HINF_SPOOF_H 0x2034 +#define REG_AE_CTRL 0x5002 +#define REG_AE_MANUAL_GAIN 0x5006 +#define REG_AE_MANUAL_EXP_TIME 0x500C +#define REG_AE_BV_MIN 0x5010 +#define REG_AF_CTRL 0x5058 +#define REG_AWB_MANUAL_TEMP 0x510A +#define REG_BOOTDATA_STAGE 0x6002 +#define REG_GAMMA 0x700A +#define REG_CLS_LOCK_CONN 0x54C2 +#define REG_PREVIEW_DIV_HINF_MIPI 0x2064 +#define REG_SIPS_CRC 0xF052 + +#define FW_OFFSET 0x8000 +#define FW_PLL_SIZE 832 +#define FW_CHUNK_SIZE 0x600 + +#define REG_16B 2 +#define REG_32B 4 + +static int ap1302_write_reg(struct i2c_client*, u16, u32, int); + +enum { + AP1302_MODE_640x480, + AP1302_MODE_1280x720, + AP1302_MODE_1920x1080, + AP1302_MODE_4224x3156, + AP1302_SIZE_LAST, +}; + +#define to_ap1302(sd) container_of(sd, struct ap1302_priv, subdev) + +struct ap1302_priv { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt mf; + + int ident; + u16 chip_id; + u8 revision; + + int mode; + + struct i2c_client *client; + const struct firmware *fw; +}; + +static enum v4l2_mbus_pixelcode ap1302_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static const struct v4l2_frmsize_discrete ap1302_frmsizes[AP1302_SIZE_LAST] = { + {640, 480}, + {1280, 720}, + {1920, 1080}, + {4224, 3156}, +}; + +static int ap1302_find_mode(u32 width, u32 height) +{ + int i; + + for (i = 0; i < AP1302_SIZE_LAST; i++) { + if ((ap1302_frmsizes[i].width >= width) && + (ap1302_frmsizes[i].height >= height)) + break; + } + + /* If not found, select biggest */ + if (i >= AP1302_SIZE_LAST) + i = AP1302_SIZE_LAST - 1; + + return i; +} + +static void ap1302_set_default_fmt(struct ap1302_priv *priv) +{ + struct v4l2_mbus_framefmt *mf = &priv->mf; + + mf->width = ap1302_frmsizes[AP1302_MODE_640x480].width; + mf->height = ap1302_frmsizes[AP1302_MODE_640x480].height; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* Start/Stop streaming from the device */ +static int ap1302_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ap1302_priv *priv = to_ap1302(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (!enable) { + ap1302_set_default_fmt(priv); + return 0; + } + + ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B); + ap1302_write_reg(client, REG_PREVIEW_WIDTH, priv->mf.width, REG_16B); + ap1302_write_reg(client, REG_PREVIEW_HEIGHT, priv->mf.height, REG_16B); + ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, (priv->mf.height > 1080) ? 0x0040 : 0x0041, REG_16B); + ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B); + ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B); + + return ret; +} + +static int ap1302_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int mode; + + mode = ap1302_find_mode(mf->width, mf->height); + mf->width = ap1302_frmsizes[mode].width; + mf->height = ap1302_frmsizes[mode].height; + + mf->field = V4L2_FIELD_NONE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +/* set the format we will capture in */ +static int ap1302_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct ap1302_priv *priv = to_ap1302(sd); + int ret; + + ret = ap1302_try_fmt(sd, mf); + if (ret < 0) + return ret; + + priv->mode = ap1302_find_mode(mf->width, mf->height); + + memcpy(&priv->mf, mf, sizeof(struct v4l2_mbus_framefmt)); + + return 0; +} + +static int ap1302_s_power(struct v4l2_subdev *sd, int on) +{ + struct ap1302_priv *priv = to_ap1302(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *scsd = soc_camera_i2c_to_desc(client); + + if (on) + ap1302_s_fmt(sd, &priv->mf); + + return soc_camera_set_power(&client->dev, scsd, on); +} + +static int ap1302_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(ap1302_codes)) + return -EINVAL; + + *code = ap1302_codes[index]; + + return 0; +} + +static int ap1302_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + return 0; +} + +static int ap1302_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + return 0; +} + +static int ap1302_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return 0; +} + +/* Get chip identification */ +static int ap1302_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct ap1302_priv *priv = to_ap1302(sd); + + id->ident = priv->ident; + id->revision = priv->revision; + + return 0; +} + +static struct v4l2_subdev_video_ops ap1302_video_ops = { + .s_stream = ap1302_s_stream, + .s_mbus_fmt = ap1302_s_fmt, + .try_mbus_fmt = ap1302_try_fmt, + .enum_mbus_fmt = ap1302_enum_fmt, + .cropcap = ap1302_cropcap, + .g_crop = ap1302_g_crop, + .querystd = ap1302_querystd, +}; + + int (*reset)(struct v4l2_subdev *sd, u32 val); + +static struct v4l2_subdev_core_ops ap1302_core_ops = { + .g_chip_ident = ap1302_g_chip_ident, + .s_power = ap1302_s_power, +}; + +static struct v4l2_subdev_ops ap1302_subdev_ops = { + .core = &ap1302_core_ops, + .video = &ap1302_video_ops, +}; + +static int ap1302_read_reg(struct i2c_client *client, u16 addr, u16 *val) +{ + int err; + struct i2c_msg msg[2]; + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = data; + + /* high byte goes out first */ + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = data + 2; + + err = i2c_transfer(client->adapter, msg, REG_16B); + + if (err != 2) + return -EINVAL; + + *val = (data[2] << 8) | data[3]; + + return 0; +} + +static int ap1302_write_reg(struct i2c_client *client, u16 addr, u32 value, int size) +{ + int count; + struct i2c_msg msg[1]; + unsigned char data[6]; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + + if (size == 1) { + data[2] = (u8) (value & 0xff); + } + else if (size == 2) { + data[2] = (u8) (value >> 8); + data[3] = (u8) (value & 0xff); + } + else if (size == 4) { + data[2] = (u8) (value >> 24); + data[3] = (u8) (value >> 16); + data[4] = (u8) (value >> 8); + data[5] = (u8) (value & 0xff); + } + else + return -1; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = (size + 2); + msg[0].buf = data; + + count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (count == ARRAY_SIZE(msg)) { + return 0; + } + dev_err(&client->dev, + "ap1302: i2c transfer failed, addr: %x, value: %02x\n", + addr, (u32)value); + return -EIO; +} + +static int ap1302_write_bulk_reg(struct i2c_client *client, u16 reg_addr, u8 *data, int len) +{ + int err; + struct i2c_msg msg; + u8 data_with_addr[len+2]; + + if (!client->adapter) + return -ENODEV; + + data_with_addr[0] = (u8)(reg_addr >> 8); + data_with_addr[1] = (u8)(reg_addr & 0xFF); + memcpy((u8*)&data_with_addr[2], data, len); + + len = (len + 2); + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data_with_addr; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + dev_err(&client->dev, "ap1302: i2c transfer failed at %x\n", + (int)data[0] << 8 | data[1]); + + return err; +} + +static int ap1302_request_firmware(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ap1302_priv *priv = to_ap1302(sd); + int ret; + ret = request_firmware(&priv->fw, "ap1302_fw.bin", &client->dev); + if (ret) + dev_err(&client->dev, + "ap1302_request_firmware failed. ret=%d\n", ret); + return ret; +} + +static int ap1302_write_firmware(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ap1302_priv *priv = to_ap1302(sd); + int err = 0; + u16 pos = 0, ppos = 0, smpos = 0; + int fw_size = priv->fw->size; + u8 *fw_data = (u8 *)(priv->fw->data); + u32 packet_size = 0, sm_packet_size = 0; + + err += ap1302_write_reg(client, REG_SIPS_CRC, 0xFFFF, REG_16B); + + /* write pll firmware */ + err += ap1302_write_bulk_reg(client, 0x8000, fw_data, FW_PLL_SIZE); + + /* write the rest of the firmware */ + err += ap1302_write_reg(client, REG_BOOTDATA_STAGE, 0x0002, REG_16B); + pos = FW_PLL_SIZE; + ppos = FW_PLL_SIZE; + + /* split fw into packets (8192B) */ + for (pos = FW_PLL_SIZE; pos < fw_size; pos += packet_size) { + if (fw_size - pos < AP1302_FW_WINDOW_SIZE - ppos) + packet_size = fw_size - pos; + else + packet_size = AP1302_FW_WINDOW_SIZE - ppos; + + /* split each packet into chunks (each having FW_CHUNK_SIZE) */ + sm_packet_size = FW_CHUNK_SIZE; + for (smpos = 0; smpos < packet_size; smpos += sm_packet_size) { + if ((smpos + sm_packet_size) > packet_size) + sm_packet_size = packet_size - smpos; + err += ap1302_write_bulk_reg(client, (FW_OFFSET + ppos + smpos), + (u8 *)(fw_data + pos + smpos), sm_packet_size); + } + + msleep(10); + + ppos += packet_size; + if (ppos >= AP1302_FW_WINDOW_SIZE) + ppos = 0; + } + + /* TODO: check CRC and ERROR regs */ + ap1302_write_reg(client, REG_BOOTDATA_STAGE, 0xFFFF, REG_16B); + + return err; +} + +static int ap1302_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ap1302_priv *priv; + struct soc_camera_subdev_desc *scsd; + u16 chip_id = 0; + int ret = 0; + + /* Checking soc-camera interface */ + scsd = soc_camera_i2c_to_desc(client); + if (!scsd) { + dev_err(&client->dev, "Missing soc_camera_link for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(struct ap1302_priv), + GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "Failed to allocate private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ap1302_subdev_ops); + + priv->client = client; + + soc_camera_power_on(&client->dev, scsd); + + /* TODO: this should be moved to boardfile, because + * now the driver works only on Jetson TK1 */ + gpio_set_value(219, 0); + msleep(100); + gpio_set_value(219, 1); + msleep(100); + + ap1302_set_default_fmt(priv); + + ap1302_read_reg(client, REG_CHIP_VERSION, &chip_id); + printk(KERN_INFO "AP1302 chip id = 0x%04x\n", chip_id); + if (chip_id != 0x265) { + soc_camera_power_off(&client->dev, scsd); + return -1; + } + + ret = ap1302_request_firmware(&(priv->subdev)); + if (ret) { + dev_err(&client->dev, "Cannot request ap1302 firmware.\n"); + return -1; + } + + /* loading firmware */ + ret += ap1302_write_firmware(&(priv->subdev)); + + /* configuring isp */ + ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_MAX_FPS, 0xF000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HINF_CTRL, 0x0034, REG_16B); + ret += ap1302_write_reg(client, REG_AF_CTRL, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_ENABLE, 0x0086, REG_16B); + ret += ap1302_write_reg(client, REG_AE_BV_MIN, 0x0100, REG_16B); + ret += ap1302_write_reg(client, REG_AE_MANUAL_GAIN, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_AE_CTRL, 0x002A, REG_16B); + ret += ap1302_write_reg(client, REG_ENABLE, 0x0001, REG_16B); + ret += ap1302_write_reg(client, REG_AE_MANUAL_EXP_TIME, 0x00003CF0, REG_32B); + ret += ap1302_write_reg(client, REG_AWB_MANUAL_TEMP, 0x11F8, REG_16B); + ret += ap1302_write_reg(client, REG_GAMMA, 0x2000, REG_16B); + ret += ap1302_write_reg(client, REG_CLS_LOCK_CONN, 0x0024, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_ROI_X0, 0x8000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_ROI_X1, 0x7FFF, REG_16B); + ret += ap1302_write_reg(client, REG_CLS_LOCK_CONN, 0x0024, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_DIV_HINF_MIPI, 0x00030000, REG_32B); + ret += ap1302_write_reg(client, REG_CTRL, 0x0001, REG_16B); + ret += ap1302_write_reg(client, REG_CTRL, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_AE_USG, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, 0x0041, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B); + + /* setting default mode: 640x480 */ + ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, 0x0041, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B); + ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B); + + return ret; +} + +static int ap1302_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id ap1302_id[] = { + { "ap1302", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ap1302_id); + +static struct i2c_driver ap1302_i2c_driver = { + .driver = { + .name = "ap1302", + }, + .probe = ap1302_probe, + .remove = ap1302_remove, + .id_table = ap1302_id, +}; + +static int __init ap1302_module_init(void) +{ + return i2c_add_driver(&ap1302_i2c_driver); +} + +static void __exit ap1302_module_exit(void) +{ + i2c_del_driver(&ap1302_i2c_driver); +} + +module_init(ap1302_module_init); +module_exit(ap1302_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for AP1302 ISP from ON Semiconductor"); +MODULE_AUTHOR("Wojciech Bieganski <wbieganski@antmicro.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/soc_camera/ov5640.c b/drivers/media/i2c/soc_camera/ov5640.c index 38315b514407..acd532b8b7d3 100644 --- a/drivers/media/i2c/soc_camera/ov5640.c +++ b/drivers/media/i2c/soc_camera/ov5640.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2013 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2017 Antmicro Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -30,1073 +31,853 @@ struct ov5640_reg { #define OV5640_TABLE_WAIT_MS 0 #define OV5640_TABLE_END 1 +/* System and IO Pad Control [0x3000 ~ 0x3052] */ +#define OV5640_SYSTEM_RESET_00 0x3000 /* default 0x30 -> all 0x00 */ +#define OV5640_SYSTEM_RESET_01 0x3001 /* default 0x08 */ +#define OV5640_SYSTEM_RESET_02 0x3002 /* default 0x1c */ +#define OV5640_SYSTEM_RESET_03 0x3003 /* default 0x00 */ +#define OV5640_SYSTEM_CLK_EN_00 0x3004 /* default 0xcf -> all 0xff */ +#define OV5640_SYSTEM_CLK_EN_01 0x3005 /* default 0xf7 */ +#define OV5640_SYSTEM_CLK_EN_02 0x3006 /* default 0xe3 -> all 0xc3 */ +#define OV5640_SYSTEM_CLK_EN_03 0x3007 /* default 0xff */ +#define OV5640_SYSTEM_CTRL 0x3008 /* default 0x02 all -> (0x82, 0x42, 0x02) */ +#define OV5640_CHIP_ID_H 0x300a /* default 0x56 */ +#define OV5640_CHIP_ID_L 0x300b /* default 0x40 */ +#define OV5640_SYSTEM_MIPI_CTRL 0x300e /* default 0x58 -> all 0x45 */ +#define OV5640_IO_CTRL_01 0x3017 /* default 0x00 */ +#define OV5640_IO_CTRL_02 0x3018 /* default 0x00 */ +#define OV5640_SYSTEM_CTRL_2D 0x302d /* default ---- -> all 0x60 !! */ +#define OV5640_SYSTEM_CTRL_2E 0x302e /* default ---- -> all 0x08 !! */ +#define OV5640_SC_PLL_CTRL0 0x3034 /* default 0x1a -> all 0x18 MIPI_MODE */ +#define OV5640_SC_PLL_CTRL1 0x3035 /* default 0x11 -> 0x11 0x11 0x21 0x14 */ +#define OV5640_SC_PLL_CTRL2 0x3036 /* default 0x69 -> 0x7d 0x54 0x70 0x70 */ +#define OV5640_SC_PLL_CTRL3 0x3037 /* default 0x03 -> all 0x13 */ +#define OV5640_SC_PLL_CTRL5 0x3039 /* not use */ + +#define SYSTEM_CTRL_SRST 0x80 /* Bit[7]: Software reset */ +#define SYSTEM_CTRL_PDOWN 0x40 /* Bit[6]: Software power down */ +#define MIPI_MODE_8BIT 0x08 /* Bit[3:0]: MIPI bit mode - 8-bit mode */ +#define MIPI_MODE_10BIT 0x0a /* Bit[3:0]: MIPI bit mode - 10-bit mode */ + +/* SCCB Control [0x3100 ~ 0x3108] */ +#define OV5640_SCCB_SYSTEM_CTRL 0x3103 /* default 0x00 -> all 0x03 */ +#define OV5640_SCCB_SCLK 0x3108 /* default 0x16 -> all 0x01 */ + +/* VCM Control [0x3600 ~ 0x3606] */ +#define OV5640_VCM_DEBUG_MODE_0 0x3600 /* default ---- -> all 0x08 */ +#define OV5640_VCM_DEBUG_MODE_1 0x3601 /* default ---- -> all 0x33 */ + +/* Timing Control [0x3800 ~ 0x3821] */ +#define OV5640_TIMING_HS_H 0x3800 /* default 0x00 -> 0x00 0x01 0x00 0x00 X_ADDR_ST */ +#define OV5640_TIMING_HS_L 0x3801 /* default 0x00 -> 0x00 0x50 0x00 0x00 */ +#define OV5640_TIMING_VS_H 0x3802 /* default 0x00 -> 0x00 0x01 0x00 0x00 Y_ADDR_ST */ +#define OV5640_TIMING_VS_L 0x3803 /* default 0x00 -> 0x00 0xaa 0x00 0x04 */ +#define OV5640_TIMING_HW_H 0x3804 /* default 0x0a -> 0x0a 0x08 0x0a 0x0a X_ADDR_END */ +#define OV5640_TIMING_HW_L 0x3805 /* default 0x3f -> 0x3f 0xef 0x3f 0x3f 2623 */ +#define OV5640_TIMING_VH_H 0x3806 /* default 0x07 -> 0x07 0x05 0x07 0x07 Y_ADDR_END */ +#define OV5640_TIMING_VH_L 0x3807 /* default 0x9f -> 0x9f 0xf9 0x9f 0x9b */ +#define OV5640_TIMING_DVPHO_H 0x3808 /* default 0x0a -> 0x0a 0x07 0x05 0x02 width[11:8] */ +#define OV5640_TIMING_DVPHO_L 0x3809 /* default 0x20 -> 0x30 0x90 0x18 0x80 width[7:0] */ +#define OV5640_TIMING_DVPVO_H 0x380a /* default 0x07 -> 0x07 0x04 0x03 0x01 height[11:8] */ +#define OV5640_TIMING_DVPVO_L 0x380b /* default 0x98 -> 0x9c 0x48 0xcc 0xe0 height[7:0] */ +#define OV5640_TIMING_HTS_H 0x380c /* default 0x0b -> 0x0b 0x09 0x07 0x07 */ +#define OV5640_TIMING_HTS_L 0x380d /* default 0x1c -> 0x1c 0xc4 0x5e 0x68 */ +#define OV5640_TIMING_VTS_H 0x380e /* default 0x07 -> 0x07 0x04 0x03 0x03 */ +#define OV5640_TIMING_VTS_L 0x380f /* default 0xb0 -> 0xb0 0x60 0xde 0xd8 */ +#define OV5640_TIMING_HOFFSET_H 0x3810 /* default 0x00 */ +#define OV5640_TIMING_HOFFSET_L 0x3811 /* default 0x10 -> 0x08 0x08 0x06 0x10 */ +#define OV5640_TIMING_VOFFSET_H 0x3812 /* default 0x00 */ +#define OV5640_TIMING_VOFFSET_L 0x3813 /* default 0x04 -> 0x02 0x04 0x02 0x06 */ +#define OV5640_TIMING_X_INC 0x3814 /* default 0x11 -> 0x11 0x11 0x31 0x31 */ +#define OV5640_TIMING_Y_INC 0x3815 /* default 0x11 -> 0x11 0x11 0x31 0x31 */ +#define OV5640_TIMING_REG20 0x3820 /* default 0x40 */ +#define OV5640_TIMING_REG21 0x3821 /* default 0x00 -> 0x06 0x06 0x07 0x07 */ +#define REG20_SENSOR_VFLIP 0x02 +#define REG20_SENSOR_ISP_FLIP 0x04 +#define REG21_BINNING_EN 0x01 +#define REG21_SENSOR_MIRROR 0x02 +#define REG21_ISP_MIRROR 0x04 + +#define OV5640_ADDR_H(x) ((x >> 8 ) & 0x0f) +#define OV5640_ADDR_L(x) (x & 0xff) +#define OV5640_WIDTH_H(x) ((x >> 8 ) & 0x0f) +#define OV5640_WIDTH_L(x) (x & 0xff) +#define OV5640_HEIGHT_H(x) ((x >> 8 ) & 0x0f) +#define OV5640_HEIGHT_L(x) (x & 0xff) +#define OV5640_HTS_H(x) ((x >> 8 ) & 0x1f) +#define OV5640_HTS_L(x) (x & 0xff) +#define OV5640_VTS_H(x) ((x >> 8 ) & 0xff) +#define OV5640_VTS_L(x) (x & 0xff) +#define OV5640_H_OFFSET_H(x) ((x >> 8 ) & 0x0F) +#define OV5640_H_OFFSET_L(x) (x & 0xff) +#define OV5640_V_OFFSET_H(x) ((x >> 8 ) & 0x03) +#define OV5640_V_OFFSET_L(x) (x & 0xff) + +/* AEC/AGC power down domain control [0x3A00 ~ 0x3A25] */ +#define OV5640_AEC_CTRL_0F 0x3a0f /* default 0x78 -> all 0x30 */ +#define OV5640_AEC_CTRL_10 0x3a10 /* default 0x68 -> all 0x28 */ +#define OV5640_AEC_CTRL_11 0x3a11 /* default 0xd0 -> all 0x60 */ +#define OV5640_AEC_CTRL_1B 0x3a1b /* default 0x78 -> all 0x30 */ +#define OV5640_AEC_CTRL_1E 0x3a1e /* default 0x68 -> all 0x26 */ +#define OV5640_AEC_CTRL_1F 0x3a1f /* default 0x40 -> all 0x14 */ +#define OV5640_AEC_MAX_EXPO60_H 0x3a02 /* default 0x3d -> 0x07 0x04 0x03 0x03 */ +#define OV5640_AEC_MAX_EXPO60_L 0x3a03 /* default 0x80 -> 0xb6 0x60 0xd8 0xd8 */ +#define OV5640_AEC_B50_STEP_H 0x3a08 /* default 0x01 */ +#define OV5640_AEC_B50_STEP_L 0x3a09 /* default 0x27 -> 0x27 0x50 0x27 0x27 */ +#define OV5640_AEC_B60_STEP_H 0x3a0a /* default 0x00 -> 0x00 0x01 0x00 0x00 */ +#define OV5640_AEC_B60_STEP_L 0x3a0b /* default 0xf6 -> 0xf6 0x18 0xf6 0xf6 */ +#define OV5640_AEC_CTRL_0D 0x3a0d /* default 0x08 -> 0x06 0x03 0x03 0x03 */ +#define OV5640_AEC_CTRL_0E 0x3a0e /* default 0x06 -> 0x06 0x04 0x04 0x04 */ +#define OV5640_AEC_CTRL_13 0x3a13 /* default 0x40 -> all 0x43 */ +#define OV5640_AEC_MAX_EXPO_H 0x3a14 /* default 0x0e -> 0x07 0x04 0x03 0x03 */ +#define OV5640_AEC_MAX_EXPO_L 0x3a15 /* default 0x40 -> 0xb0 0xd0 0xd8 0xd8 */ +#define OV5640_AEC_GAIN_CEILING_0 0x3a18 /* default 0x03 -> all 0x00 */ +#define OV5640_AEC_GAIN_CEILING_1 0x3a19 /* default 0xE0 -> all 0xf8 */ + +/* 50/60Hz detector control [0x3C00 ~ 0x3C1E] */ +#define OV5640_5060_CTRL_01 0x3c01 /* default 0x00 -> all 0x34 */ +#define OV5640_5060_CTRL_04 0x3c04 /* default 0x20 -> all 0x28 */ +#define OV5640_5060_CTRL_05 0x3c05 /* default 0x70 -> all 0x98 */ + +#define OV5640_5060_THRESHOLD_1_H 0x3c06 /* default 0x00 -> all 0x00 */ +#define OV5640_5060_THRESHOLD_1_L 0x3c07 /* default 0x00 -> all 0x07 0x07 0x07 0x07 0x08 */ +#define OV5640_5060_THRESHOLD_2_H 0x3c08 /* default 0x01 -> all 0x00 */ +#define OV5640_5060_THRESHOLD_2_L 0x3c09 /* default 0x2c -> all 0x1c */ +#define OV5640_5060_SAMPLE_NUM_H 0x3c0a /* default 0x4e -> all 0x9c */ +#define OV5640_5060_SAMPLE_NUM_L 0x3c0b /* default 0x1f -> all 0x40 */ + +/* BLC Control [0x4000 ~ 0x4033] */ +#define OV5640_BLC_CTRL_01 0x4001 /* default 0x00 -> 0x02 */ +#define OV5640_BLC_CTRL_04 0x4004 /* default 0x08 -> 0x06 0x06 0x02 0x02 */ + +/* MIPI control [0x4800 ~ 0x4837] */ +#define OV5640_MIPI_CTRL_00 0x4800 /* default 0x04 */ +#define OV5640_MIPI_CTRL_01 0x4801 /* not use */ +#define OV5640_MIPI_CTRL_05 0x4805 /* not use */ +#define OV5640_MIPI_PCLK_PERIOD 0x4837 /* default 0x10 -> 0x0a 0x0a 0x10 0x44 */ + +/* Format Control [0x4300 ~ 0x430D] */ +#define FMT_CTRL_00 0x4300 /* default 0xF8 -> all 0x32 */ + +/* Format Control */ +#define FMT_YUV422 0x30 +/* Output sequence */ +#define OFMT_YUYV 0x00 +#define OFMT_YVYU 0x01 +#define OFMT_UYVY 0x02 +#define OFMT_VYUY 0x03 + +/* ISP Control [0x5000 ~ 0x5063] */ +#define OV5640_ISP_CTRL_00 0x5000 /* default 0x06 -> all 0xa7 */ +#define OV5640_ISP_CTRL_01 0x5001 /* default 0x01 -> 0x83 0x83 0x83 0xa3 */ +#define OV5640_ISP_MUX_CTRL 0x501F /* default 0x00 -> all 0x00 (000: ISP YUV422) */ +#define OV5640_ISP_DEBUG_25 0x5025 /* default ---- -> all 0x00 */ +#define OV5640_ISP_TEST 0x503D /* default 0x00 */ +#define OV5640_ISP_DEBUG_3E 0x503E /* default ---- */ +#define OV5640_ISP_DEBUG_46 0x5046 /* default ---- */ + +#define ISP_SCALE_DIGITAL 0x80 /* Special digital effect */ +#define ISP_SCALE_EN 0x20 /* Scale enable */ +#define ISP_AUTO_BALANCE_EN 0x01 /* Auto white balance enable */ +#define ISP_COLOR_MATRIX_EN 0x02 /* Color matrix enable */ +#define ISP_TEST_EN 0x80 +#define ISP_TEST_00 0x00 /* 00: Standard eight color bar */ +#define ISP_TEST_01 0x01 /* 01: Gradual change at vertical mode 1 */ +#define ISP_TEST_10 0x02 /* 10: Gradual change at horizontal */ +#define ISP_TEST_11 0x03 /* 11: Gradual change at vertical mode 2 */ +#define ISP_TEST_TRANSPARENT 0x20 /* Transparent */ +#define ISP_TEST_ROLLING 0x40 /* Rolling */ + +/* AWB Control [0x5180 ~ 0x51D0] */ +#define OV5640_AWB_CTRL_00 0x5180 /* default 0xff */ +#define OV5640_AWB_CTRL_01 0x5181 /* default 0x58 -> all 0xf2 */ +#define OV5640_AWB_CTRL_02 0x5182 /* default 0x11 -> all 0x00 */ +#define OV5640_AWB_CTRL_03 0x5183 /* default 0x90 -> all 0x14 */ +#define OV5640_AWB_CTRL_04 0x5184 /* default 0x25 */ +#define OV5640_AWB_CTRL_05 0x5185 /* default 0x24 */ +#define OV5640_AWB_CTRL_06 0x5186 /* default ---- -> all 0x09 */ +#define OV5640_AWB_CTRL_07 0x5187 /* default ---- -> all 0x09 */ +#define OV5640_AWB_CTRL_08 0x5188 /* default ---- -> all 0x09 */ +#define OV5640_AWB_CTRL_09 0x5189 /* default ---- -> all 0x75 */ +#define OV5640_AWB_CTRL_10 0x518a /* default ---- -> all 0x54 */ +#define OV5640_AWB_CTRL_11 0x518b /* default ---- -> all 0xe0 */ +#define OV5640_AWB_CTRL_12 0x518c /* default ---- -> all 0xb2 */ +#define OV5640_AWB_CTRL_13 0x518d /* default ---- -> all 0x42 */ +#define OV5640_AWB_CTRL_14 0x518e /* default ---- -> all 0x3d */ +#define OV5640_AWB_CTRL_15 0x518f /* default ---- -> all 0x56 */ +#define OV5640_AWB_CTRL_16 0x5190 /* default ---- -> all 0x46 */ +#define OV5640_AWB_CTRL_17 0x5191 /* default 0xff -> all 0xf8 */ +#define OV5640_AWB_CTRL_18 0x5192 /* default 0x00 -> all 0x04 */ +#define OV5640_AWB_CTRL_19 0x5193 /* default 0xf0 -> all 0x70 */ +#define OV5640_AWB_CTRL_20 0x5194 /* default 0xf0 */ +#define OV5640_AWB_CTRL_21 0x5195 /* default 0xf0 */ +#define OV5640_AWB_CTRL_22 0x5196 /* default 0x03 */ +#define OV5640_AWB_CTRL_23 0x5197 /* default 0x02 -> all 0x01 */ +#define OV5640_AWB_CTRL_24 0x5198 /* default ---- -> all 0x04 */ +#define OV5640_AWB_CTRL_25 0x5199 /* default ---- -> all 0x12 */ +#define OV5640_AWB_CTRL_26 0x519a /* default ---- -> all 0x04 */ +#define OV5640_AWB_CTRL_27 0x519b /* default ---- -> all 0x00 */ +#define OV5640_AWB_CTRL_28 0x519c /* default ---- -> all 0x06 */ +#define OV5640_AWB_CTRL_29 0x519d /* default ---- -> all 0x82 */ +#define OV5640_AWB_CTRL_30 0x519e /* default 0x02 -> all 0x38 */ + +/* CIP Control [0x5300 ~ 0x530F] */ +#define OV5640_CIP_SH_MT_THRES_1 0x5300 /* default 0x08 */ +#define OV5640_CIP_SH_MT_THRES_2 0x5301 /* default 0x48 -> all 0x30 */ +#define OV5640_CIP_SH_MT_OFFSET_1 0x5302 /* default 0x18 -> all 0x10 */ +#define OV5640_CIP_SH_MT_OFFSET_2 0x5303 /* default 0x0e -> all 0x00 */ +#define OV5640_CIP_DNS_THRES_1 0x5304 /* default 0x08 */ +#define OV5640_CIP_DNS_THRES_2 0x5305 /* default 0x48 -> all 0x30 */ +#define OV5640_CIP_DNS_OFFSET_1 0x5306 /* default 0x09 -> all 0x08 */ +#define OV5640_CIP_DNS_OFFSET_2 0x5307 /* default 0x16 */ +#define OV5640_CIP_SH_TH_THRES_1 0x5309 /* default 0x08 */ +#define OV5640_CIP_SH_TH_THRES_2 0x530a /* default 0x48 -> all 0x30 */ +#define OV5640_CIP_SH_TH_OFFSET_1 0x530b /* default 0x04 */ +#define OV5640_CIP_SH_TH_OFFSET_2 0x530c /* default 0x06 */ + +/* CMX Control [0x5380 ~ 0x538B] */ +#define OV5640_CMX1 0x5381 /* default 0x20 -> all 0x1e */ +#define OV5640_CMX2 0x5382 /* default 0x64 -> all 0x5b */ +#define OV5640_CMX3 0x5383 /* default 0x08 */ +#define OV5640_CMX4 0x5384 /* default 0x30 -> all 0x0a */ +#define OV5640_CMX5 0x5385 /* default 0x80 -> all 0x7e */ +#define OV5640_CMX6 0x5386 /* default 0xc0 -> all 0x88 */ +#define OV5640_CMX7 0x5387 /* default 0xa0 -> all 0x7c */ +#define OV5640_CMX8 0x5388 /* default 0x98 -> all 0x6c */ +#define OV5640_CMX9 0x5389 /* default 0x08 -> all 0x10 */ +#define OV5640_CMX_SIGN_0 0x538A /* default 0x01 */ +#define OV5640_CMX_SIGN_1 0x538B /* default 0x98 */ + +/* Gamma Control [0x5480 ~ 0x5490] */ +#define OV5640_GAMMA_CTRL_00 0x5480 /* default 0x00 -> all 0x01 */ +#define OV5640_GAMMA_YST_00 0x5481 /* default 0x26 -> all 0x08 */ +#define OV5640_GAMMA_YST_01 0x5482 /* default 0x35 -> all 0x14 */ + +#define OV5640_GAMMA_YST_02 0x5483 /* default 0x48 -> all 0x28 */ +#define OV5640_GAMMA_YST_03 0x5484 /* default 0x57 -> all 0x51 */ +#define OV5640_GAMMA_YST_04 0x5485 /* default 0x63 -> all 0x65 */ +#define OV5640_GAMMA_YST_05 0x5486 /* default 0x6e -> all 0x71 */ + +#define OV5640_GAMMA_YST_06 0x5487 /* default 0x77 -> all 0x7d */ +#define OV5640_GAMMA_YST_07 0x5488 /* default 0x80 -> all 0x87 */ +#define OV5640_GAMMA_YST_08 0x5489 /* default 0x88 -> all 0x91 */ +#define OV5640_GAMMA_YST_09 0x548a /* default 0x96 -> all 0x9a */ +#define OV5640_GAMMA_YST_0A 0x548b /* default 0xa3 -> all 0xaa */ +#define OV5640_GAMMA_YST_0B 0x548c /* default 0xaf -> all 0xb8 */ +#define OV5640_GAMMA_YST_0C 0x548d /* default 0xc5 -> all 0xcd */ +#define OV5640_GAMMA_YST_0D 0x548e /* default 0xd7 -> all 0xdd */ +#define OV5640_GAMMA_YST_0E 0x548f /* default 0xe8 -> all 0xea */ +#define OV5640_GAMMA_YST_0F 0x5490 /* default 0x0f -> all 0x1d */ + +/* SDE Control [0x5580 ~ 0x558C] */ +#define OV5640_SDE_CTRL_0 0x5580 /* default 0x00 -> all 0x02 */ +#define OV5640_SDE_CTRL_3 0x5583 /* default 0x40 */ +#define OV5640_SDE_CTRL_4 0x5584 /* default 0x40 -> all 0x10 */ +#define OV5640_SDE_CTRL_9 0x5589 /* default 0x01 -> all 0x10 */ +#define OV5640_SDE_CTRL_10 0x558a /* default 0x01 -> all 0x00 */ +#define OV5640_SDE_CTRL_11 0x558b /* default 0xff -> all 0xf8 */ + +/* LENC Control [0x5800 ~ 0x5849] */ +#define OV5640_LENC_GMTRX_00 0x5800 /* default 0x10 -> all 0x23 */ +#define OV5640_LENC_GMTRX_01 0x5801 /* default 0x10 -> all 0x14 */ +#define OV5640_LENC_GMTRX_02 0x5802 /* default 0x10 -> all 0x0f */ +#define OV5640_LENC_GMTRX_03 0x5803 /* default 0x10 -> all 0x0f */ +#define OV5640_LENC_GMTRX_04 0x5804 /* default 0x10 -> all 0x12 */ +#define OV5640_LENC_GMTRX_05 0x5805 /* default 0x10 -> all 0x26 */ +#define OV5640_LENC_GMTRX_10 0x5806 /* default 0x10 -> all 0x0c */ +#define OV5640_LENC_GMTRX_11 0x5807 /* default 0x08 */ +#define OV5640_LENC_GMTRX_12 0x5808 /* default 0x08 -> all 0x05 */ +#define OV5640_LENC_GMTRX_13 0x5809 /* default 0x08 -> all 0x05 */ +#define OV5640_LENC_GMTRX_14 0x580a /* default 0x08 -> all */ +#define OV5640_LENC_GMTRX_15 0x580b /* default 0x10 -> all 0x0d */ +#define OV5640_LENC_GMTRX_20 0x580c /* default 0x10 -> all 0x08 */ +#define OV5640_LENC_GMTRX_21 0x580d /* default 0x08 -> all 0x03 */ +#define OV5640_LENC_GMTRX_22 0x580e /* default 0x00 */ +#define OV5640_LENC_GMTRX_23 0x580f /* default 0x00 */ +#define OV5640_LENC_GMTRX_24 0x5810 /* default 0x08 -> all 0x03 */ +#define OV5640_LENC_GMTRX_25 0x5811 /* default 0x10 -> all 0x09 */ +#define OV5640_LENC_GMTRX_30 0x5812 /* default 0x10 -> all 0x07 */ +#define OV5640_LENC_GMTRX_31 0x5813 /* default 0x08 -> all 0x03 */ +#define OV5640_LENC_GMTRX_32 0x5814 /* default 0x00 */ +#define OV5640_LENC_GMTRX_33 0x5815 /* default 0x00 -> all 0x01 */ +#define OV5640_LENC_GMTRX_34 0x5816 /* default 0x08 -> all 0x03 */ +#define OV5640_LENC_GMTRX_35 0x5817 /* default 0x10 -> all 0x08 */ +#define OV5640_LENC_GMTRX_40 0x5818 /* default 0x10 -> all 0x0d */ +#define OV5640_LENC_GMTRX_41 0x5819 /* default 0x08 */ +#define OV5640_LENC_GMTRX_42 0x581a /* default 0x08 -> all 0x05 */ +#define OV5640_LENC_GMTRX_43 0x581b /* default 0x08 -> all 0x06 */ +#define OV5640_LENC_GMTRX_44 0x581c /* default 0x08 */ +#define OV5640_LENC_GMTRX_45 0x581d /* default 0x10 -> all 0x0e */ +#define OV5640_LENC_GMTRX_50 0x581e /* default 0x10 -> all 0x29 */ +#define OV5640_LENC_GMTRX_51 0x581f /* default 0x10 -> all 0x17 */ +#define OV5640_LENC_GMTRX_52 0x5820 /* default 0x10 -> all 0x11 */ +#define OV5640_LENC_GMTRX_53 0x5821 /* default 0x10 -> all 0x11 */ +#define OV5640_LENC_GMTRX_54 0x5822 /* default 0x10 -> all 0x15 */ +#define OV5640_LENC_GMTRX_55 0x5823 /* default 0x10 -> all 0x28 */ +#define OV5640_LENC_BRMATRX_00 0x5824 /* default 0xaa -> all 0x46 */ +#define OV5640_LENC_BRMATRX_01 0x5825 /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_02 0x5826 /* default 0xaa -> all 0x08 */ +#define OV5640_LENC_BRMATRX_03 0x5827 /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_04 0x5828 /* default 0xaa -> all 0x64 */ +#define OV5640_LENC_BRMATRX_05 0x5829 /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_06 0x582a /* default 0x99 -> all 0x24 */ +#define OV5640_LENC_BRMATRX_07 0x582b /* default 0x99 -> all 0x22 */ +#define OV5640_LENC_BRMATRX_08 0x582c /* default 0x99 -> all 0x24 */ +#define OV5640_LENC_BRMATRX_09 0x582d /* default 0xaa -> all 0x24 */ +#define OV5640_LENC_BRMATRX_20 0x582e /* default 0xaa -> all 0x06 */ +#define OV5640_LENC_BRMATRX_21 0x582f /* default 0x99 -> all 0x22 */ +#define OV5640_LENC_BRMATRX_22 0x5830 /* default 0x88 -> all 0x40 */ +#define OV5640_LENC_BRMATRX_23 0x5831 /* default 0x99 -> all 0x42 */ +#define OV5640_LENC_BRMATRX_24 0x5832 /* default 0xaa -> all 0x24 */ +#define OV5640_LENC_BRMATRX_30 0x5833 /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_31 0x5834 /* default 0x99 -> all 0x24 */ +#define OV5640_LENC_BRMATRX_32 0x5835 /* default 0x99 -> all 0x22 */ +#define OV5640_LENC_BRMATRX_33 0x5836 /* default 0x99 -> all 0x22 */ +#define OV5640_LENC_BRMATRX_34 0x5837 /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_40 0x5838 /* default 0xaa -> all 0x44 */ +#define OV5640_LENC_BRMATRX_41 0x5839 /* default 0xaa -> all 0x24 */ +#define OV5640_LENC_BRMATRX_42 0x583a /* default 0xaa -> all 0x26 */ +#define OV5640_LENC_BRMATRX_43 0x583b /* default 0xaa -> all 0x28 */ +#define OV5640_LENC_BRMATRX_44 0x583c /* default 0xaa -> all 0x42 */ +#define OV5640_LENC_BR_OFFSET 0x583d /* default 0x88 -> all 0xce */ + + static struct ov5640_reg mode_2592x1944[] = { /* PLL Control MIPI bit rate/lane = 672MHz, 16-bit mode. - * Output size: 2608x1948 (0, 0) - (2623, 1951), + * Output size: 2592x1944 (0, 0) - (2623, 1951), * Line Length = 2844, Frame Length = 1968 + * Scaling method: full resolution (dummy 16 pixel + * horizontal, 8 lines) 2608x1952 with dummy */ - {0x3103, 0x11}, - {0x3008, 0x82}, - {OV5640_TABLE_WAIT_MS, 5}, - {0x3008, 0x42}, - {0x3103, 0x03}, - {0x3017, 0x00}, - {0x3018, 0x00}, - {0x3034, 0x18}, - {0x3035, 0x11}, - {0x3036, 0x7d}, - {0x3037, 0x13}, - {0x3108, 0x01}, - {0x3630, 0x36}, - {0x3631, 0x0e}, - {0x3632, 0xe2}, - {0x3633, 0x12}, - {0x3621, 0xe0}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3905, 0x02}, - {0x3906, 0x10}, - {0x3901, 0x0a}, - {0x3731, 0x12}, - {0x3600, 0x08}, - {0x3601, 0x33}, - {0x302d, 0x60}, - {0x3620, 0x52}, - {0x371b, 0x20}, - {0x471c, 0x50}, - {0x3a13, 0x43}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3635, 0x13}, - {0x3636, 0x03}, - {0x3634, 0x40}, - {0x3622, 0x01}, - {0x3c01, 0x34}, - {0x3c04, 0x28}, - {0x3c05, 0x98}, - {0x3c06, 0x00}, - {0x3c07, 0x07}, - {0x3c08, 0x00}, - {0x3c09, 0x1c}, - {0x3c0a, 0x9c}, - {0x3c0b, 0x40}, - {0x3820, 0x40}, - {0x3821, 0x06}, - {0x3814, 0x11}, - {0x3815, 0x11}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0x9f}, - {0x3808, 0x0a}, - {0x3809, 0x30}, - {0x380a, 0x07}, - {0x380b, 0x9c}, - {0x380c, 0x0b}, - {0x380d, 0x1c}, - {0x380e, 0x07}, - {0x380f, 0xb0}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x02}, - {0x3618, 0x04}, + + {OV5640_SC_PLL_CTRL0, 0x10 | MIPI_MODE_8BIT }, + {OV5640_SC_PLL_CTRL1, 0x11}, + {OV5640_SC_PLL_CTRL2, 0x7d}, /* default 0x69 PLL multipier */ + {OV5640_SC_PLL_CTRL3, 0x03 | (1<<4) }, /* default 0x03 PLL divided by 2 */ + + {OV5640_MIPI_CTRL_00, 0x04}, + {OV5640_MIPI_PCLK_PERIOD, 0x0a}, + + {OV5640_ISP_CTRL_00, 0xa7}, + {OV5640_ISP_CTRL_01, 0x83}, + {OV5640_ISP_MUX_CTRL, 0x00}, + + /* Timing Control */ + {OV5640_TIMING_HS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_HS_L, OV5640_ADDR_L(0) }, + {OV5640_TIMING_VS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_VS_L, OV5640_ADDR_L(0) }, + {OV5640_TIMING_HW_H, OV5640_ADDR_H(2623) }, + {OV5640_TIMING_HW_L, OV5640_ADDR_L(2623) }, + {OV5640_TIMING_VH_H, OV5640_ADDR_H(1951) }, + {OV5640_TIMING_VH_L, OV5640_ADDR_L(1951) }, + {OV5640_TIMING_DVPHO_H, OV5640_WIDTH_H(2592) }, + {OV5640_TIMING_DVPHO_L, OV5640_WIDTH_L(2592) }, + {OV5640_TIMING_DVPVO_H, OV5640_HEIGHT_H(1944) }, + {OV5640_TIMING_DVPVO_L, OV5640_HEIGHT_L(1944) }, + {OV5640_TIMING_HTS_H, OV5640_HTS_H(2844) }, + {OV5640_TIMING_HTS_L, OV5640_HTS_L(2844) }, + {OV5640_TIMING_VTS_H, OV5640_VTS_H(1968) }, + {OV5640_TIMING_VTS_L, OV5640_VTS_L(1968) }, + {OV5640_TIMING_HOFFSET_H, OV5640_H_OFFSET_H(16) }, // (2623-2592-1)/2 + {OV5640_TIMING_HOFFSET_L, OV5640_H_OFFSET_L(16) }, + {OV5640_TIMING_VOFFSET_H, OV5640_V_OFFSET_H(4) }, // (1951-1944-1)/2 + {OV5640_TIMING_VOFFSET_L, OV5640_V_OFFSET_L(4) }, + {OV5640_TIMING_X_INC, 0x11}, + {OV5640_TIMING_Y_INC, 0x11}, + {OV5640_TIMING_REG20, 0x40}, + {OV5640_TIMING_REG21, 0}, + + /* magic registers */ {0x3612, 0x2b}, + {0x3618, 0x04}, {0x3708, 0x64}, {0x3709, 0x12}, {0x370c, 0x00}, - {0x3a02, 0x07}, - {0x3a03, 0xb0}, - {0x3a08, 0x01}, - {0x3a09, 0x27}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0e, 0x06}, - {0x3a0d, 0x08}, - {0x3a14, 0x07}, - {0x3a15, 0xb0}, - {0x4001, 0x02}, - {0x4004, 0x06}, - {0x3000, 0x00}, - {0x3002, 0x1c}, - {0x3004, 0xff}, - {0x3006, 0xc3}, - {0x300e, 0x45}, - {0x302e, 0x08}, - {0x4300, 0x32}, - {0x4800, 0x24}, - {0x4837, 0x0a}, - {0x501f, 0x00}, - {0x440e, 0x00}, - {0x5000, 0xa7}, - {0x5001, 0x83}, - {0x5180, 0xff}, - {0x5181, 0xf2}, - {0x5182, 0x00}, - {0x5183, 0x14}, - {0x5184, 0x25}, - {0x5185, 0x24}, - {0x5186, 0x09}, - {0x5187, 0x09}, - {0x5188, 0x09}, - {0x5189, 0x75}, - {0x518a, 0x54}, - {0x518b, 0xe0}, - {0x518c, 0xb2}, - {0x518d, 0x42}, - {0x518e, 0x3d}, - {0x518f, 0x56}, - {0x5190, 0x46}, - {0x5191, 0xf8}, - {0x5192, 0x04}, - {0x5193, 0x70}, - {0x5194, 0xf0}, - {0x5195, 0xf0}, - {0x5196, 0x03}, - {0x5197, 0x01}, - {0x5198, 0x04}, - {0x5199, 0x12}, - {0x519a, 0x04}, - {0x519b, 0x00}, - {0x519c, 0x06}, - {0x519d, 0x82}, - {0x519e, 0x38}, - {0x5381, 0x1e}, - {0x5382, 0x5b}, - {0x5383, 0x08}, - {0x5384, 0x0a}, - {0x5385, 0x7e}, - {0x5386, 0x88}, - {0x5387, 0x7c}, - {0x5388, 0x6c}, - {0x5389, 0x10}, - {0x538a, 0x01}, - {0x538b, 0x98}, - {0x5300, 0x08}, - {0x5301, 0x30}, - {0x5302, 0x10}, - {0x5303, 0x00}, - {0x5304, 0x08}, - {0x5305, 0x30}, - {0x5306, 0x08}, - {0x5307, 0x16}, - {0x5309, 0x08}, - {0x530a, 0x30}, - {0x530b, 0x04}, - {0x530c, 0x06}, - {0x5480, 0x01}, - {0x5481, 0x08}, - {0x5482, 0x14}, - {0x5483, 0x28}, - {0x5484, 0x51}, - {0x5485, 0x65}, - {0x5486, 0x71}, - {0x5487, 0x7d}, - {0x5488, 0x87}, - {0x5489, 0x91}, - {0x548a, 0x9a}, - {0x548b, 0xaa}, - {0x548c, 0xb8}, - {0x548d, 0xcd}, - {0x548e, 0xdd}, - {0x548f, 0xea}, - {0x5490, 0x1d}, - {0x5580, 0x02}, - {0x5583, 0x40}, - {0x5584, 0x10}, - {0x5589, 0x10}, - {0x558a, 0x00}, - {0x558b, 0xf8}, - {0x5800, 0x23}, - {0x5801, 0x14}, - {0x5802, 0x0f}, - {0x5803, 0x0f}, - {0x5804, 0x12}, - {0x5805, 0x26}, - {0x5806, 0x0c}, - {0x5807, 0x08}, - {0x5808, 0x05}, - {0x5809, 0x05}, - {0x580a, 0x08}, - {0x580b, 0x0d}, - {0x580c, 0x08}, - {0x580d, 0x03}, - {0x580e, 0x00}, - {0x580f, 0x00}, - {0x5810, 0x03}, - {0x5811, 0x09}, - {0x5812, 0x07}, - {0x5813, 0x03}, - {0x5814, 0x00}, - {0x5815, 0x01}, - {0x5816, 0x03}, - {0x5817, 0x08}, - {0x5818, 0x0d}, - {0x5819, 0x08}, - {0x581a, 0x05}, - {0x581b, 0x06}, - {0x581c, 0x08}, - {0x581d, 0x0e}, - {0x581e, 0x29}, - {0x581f, 0x17}, - {0x5820, 0x11}, - {0x5821, 0x11}, - {0x5822, 0x15}, - {0x5823, 0x28}, - {0x5824, 0x46}, - {0x5825, 0x26}, - {0x5826, 0x08}, - {0x5827, 0x26}, - {0x5828, 0x64}, - {0x5829, 0x26}, - {0x582a, 0x24}, - {0x582b, 0x22}, - {0x582c, 0x24}, - {0x582d, 0x24}, - {0x582e, 0x06}, - {0x582f, 0x22}, - {0x5830, 0x40}, - {0x5831, 0x42}, - {0x5832, 0x24}, - {0x5833, 0x26}, - {0x5834, 0x24}, - {0x5835, 0x22}, - {0x5836, 0x22}, - {0x5837, 0x26}, - {0x5838, 0x44}, - {0x5839, 0x24}, - {0x583a, 0x26}, - {0x583b, 0x28}, - {0x583c, 0x42}, - {0x583d, 0xce}, - {0x5025, 0x00}, - {0x3a0f, 0x30}, - {0x3a10, 0x28}, - {0x3a1b, 0x30}, - {0x3a1e, 0x26}, - {0x3a11, 0x60}, - {0x3a1f, 0x14}, - {0x3008, 0x02}, + + /* AEC/AGC Power Down Domain Control */ + {OV5640_AEC_MAX_EXPO60_H, 0x07}, + {OV5640_AEC_MAX_EXPO60_L, 0xb0}, + {OV5640_AEC_B50_STEP_H, 0x01}, + {OV5640_AEC_B50_STEP_L, 0x27}, + {OV5640_AEC_B60_STEP_H, 0x00}, + {OV5640_AEC_B60_STEP_L, 0xf6}, + {OV5640_AEC_CTRL_0E, 0x06}, + {OV5640_AEC_CTRL_0D, 0x08}, + {OV5640_AEC_MAX_EXPO_H, 0x07}, + {OV5640_AEC_MAX_EXPO_L, 0xb0}, + {OV5640_AEC_CTRL_13, 0x43}, + {OV5640_AEC_GAIN_CEILING_0, 0x00}, + {OV5640_AEC_GAIN_CEILING_1, 0xf8}, + {OV5640_AEC_CTRL_0F, 0x30}, + {OV5640_AEC_CTRL_10, 0x28}, + {OV5640_AEC_CTRL_1B, 0x30}, + {OV5640_AEC_CTRL_1E, 0x26}, + {OV5640_AEC_CTRL_11, 0x60}, + {OV5640_AEC_CTRL_1F, 0x14}, + + {OV5640_SYSTEM_CTRL, 0x02}, {OV5640_TABLE_END, 0x0000} }; static struct ov5640_reg mode_1920x1080[] = { /* PLL Control MIPI bit rate/lane = 672MHz, 16-bit mode. - * Output size: 1936x1096 (336, 426) - (2287, 1529), + * Output size: 1936x1088 (336, 426) - (2287, 1529), * Line Length = 2500, Frame Length = 1120. + * Scaling method: cropping from full resolution + * 1936x1088 with dummy pixels */ - {0x3103, 0x11}, - {0x3008, 0x82}, - {OV5640_TABLE_WAIT_MS, 5}, - {0x3008, 0x42}, - {0x3103, 0x03}, - {0x3017, 0x00}, - {0x3018, 0x00}, - {0x3034, 0x18}, - {0x3035, 0x11}, - {0x3036, 0x54}, - {0x3037, 0x13}, - {0x3108, 0x01}, - {0x3630, 0x36}, - {0x3631, 0x0e}, - {0x3632, 0xe2}, - {0x3633, 0x12}, - {0x3621, 0xe0}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3905, 0x02}, - {0x3906, 0x10}, - {0x3901, 0x0a}, - {0x3731, 0x12}, - {0x3600, 0x08}, - {0x3601, 0x33}, - {0x302d, 0x60}, - {0x3620, 0x52}, - {0x371b, 0x20}, - {0x471c, 0x50}, - {0x3a13, 0x43}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3635, 0x13}, - {0x3636, 0x03}, - {0x3634, 0x40}, - {0x3622, 0x01}, - {0x3c01, 0x34}, - {0x3c04, 0x28}, - {0x3c05, 0x98}, - {0x3c06, 0x00}, - {0x3c07, 0x07}, - {0x3c08, 0x00}, - {0x3c09, 0x1c}, - {0x3c0a, 0x9c}, - {0x3c0b, 0x40}, - {0x3820, 0x40}, - {0x3821, 0x06}, - {0x3814, 0x11}, - {0x3815, 0x11}, - {0x3800, 0x01}, - {0x3801, 0x50}, - {0x3802, 0x01}, - {0x3803, 0xaa}, - {0x3804, 0x08}, - {0x3805, 0xef}, - {0x3806, 0x05}, - {0x3807, 0xf9}, - {0x3808, 0x07}, - {0x3809, 0x90}, - {0x380a, 0x04}, - {0x380b, 0x48}, - {0x380c, 0x09}, - {0x380d, 0xc4}, - {0x380e, 0x04}, - {0x380f, 0x60}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x04}, - {0x3618, 0x04}, + + {OV5640_SC_PLL_CTRL0, 0x10 | MIPI_MODE_8BIT }, + {OV5640_SC_PLL_CTRL1, 0x11}, + {OV5640_SC_PLL_CTRL2, 0x54}, /* default 0x69 PLL multipier */ + {OV5640_SC_PLL_CTRL3, 0x03 | (1<<4) }, /* default 0x03 PLL divided by 2 */ + + {OV5640_MIPI_CTRL_00, 0x04}, + {OV5640_MIPI_PCLK_PERIOD, 0x0a}, + + {OV5640_ISP_CTRL_00, 0xa7}, + {OV5640_ISP_CTRL_01, 0x83}, + {OV5640_ISP_MUX_CTRL, 0x00}, + + /* Timing Control */ + {OV5640_TIMING_HS_H, OV5640_ADDR_H(336) }, + {OV5640_TIMING_HS_L, OV5640_ADDR_L(336) }, + {OV5640_TIMING_VS_H, OV5640_ADDR_H(426) }, + {OV5640_TIMING_VS_L, OV5640_ADDR_L(426) }, + {OV5640_TIMING_HW_H, OV5640_ADDR_H(2287) }, + {OV5640_TIMING_HW_L, OV5640_ADDR_L(2287) }, + {OV5640_TIMING_VH_H, OV5640_ADDR_H(1529) }, + {OV5640_TIMING_VH_L, OV5640_ADDR_L(1529) }, + {OV5640_TIMING_DVPHO_H, OV5640_WIDTH_H(1920) }, //1936 + {OV5640_TIMING_DVPHO_L, OV5640_WIDTH_L(1920) }, + {OV5640_TIMING_DVPVO_H, OV5640_HEIGHT_H(1080) }, //1088 + {OV5640_TIMING_DVPVO_L, OV5640_HEIGHT_L(1080) }, + {OV5640_TIMING_HTS_H, OV5640_HTS_H(2500) }, + {OV5640_TIMING_HTS_L, OV5640_HTS_L(2500) }, + {OV5640_TIMING_VTS_H, OV5640_VTS_H(1120) }, + {OV5640_TIMING_VTS_L, OV5640_VTS_L(1120) }, + {OV5640_TIMING_HOFFSET_H, OV5640_H_OFFSET_H(15) }, // (2287-336-1936-1)/2 + {OV5640_TIMING_HOFFSET_L, OV5640_H_OFFSET_L(15) }, + {OV5640_TIMING_VOFFSET_H, OV5640_V_OFFSET_H(11) }, // (1529-426-1088-1)/2 + {OV5640_TIMING_VOFFSET_L, OV5640_V_OFFSET_L(11) }, + {OV5640_TIMING_X_INC, 0x11}, + {OV5640_TIMING_Y_INC, 0x11}, + {OV5640_TIMING_REG20, 0x40}, + {OV5640_TIMING_REG21, 0}, + + /* magic registers */ {0x3612, 0x2b}, + {0x3618, 0x04}, {0x3708, 0x64}, {0x3709, 0x12}, {0x370c, 0x00}, - {0x3a02, 0x04}, - {0x3a03, 0x60}, - {0x3a08, 0x01}, - {0x3a09, 0x50}, - {0x3a0a, 0x01}, - {0x3a0b, 0x18}, - {0x3a0e, 0x03}, - {0x3a0d, 0x04}, - {0x3a14, 0x04}, - {0x3a15, 0x60}, - {0x4001, 0x02}, - {0x4004, 0x06}, - {0x3000, 0x00}, - {0x3002, 0x1c}, - {0x3004, 0xff}, - {0x3006, 0xc3}, - {0x300e, 0x45}, - {0x302e, 0x08}, - {0x4300, 0x32}, - {0x501f, 0x00}, - {0x4713, 0x02}, - {0x4407, 0x04}, - {0x440e, 0x00}, - {0x460b, 0x37}, - {0x460c, 0x20}, - {0x4800, 0x24}, - {0x4837, 0x0a}, - {0x3824, 0x04}, - {0x5000, 0xa7}, - {0x5001, 0x83}, - {0x5180, 0xff}, - {0x5181, 0xf2}, - {0x5182, 0x00}, - {0x5183, 0x14}, - {0x5184, 0x25}, - {0x5185, 0x24}, - {0x5186, 0x09}, - {0x5187, 0x09}, - {0x5188, 0x09}, - {0x5189, 0x75}, - {0x518a, 0x54}, - {0x518b, 0xe0}, - {0x518c, 0xb2}, - {0x518d, 0x42}, - {0x518e, 0x3d}, - {0x518f, 0x56}, - {0x5190, 0x46}, - {0x5191, 0xf8}, - {0x5192, 0x04}, - {0x5193, 0x70}, - {0x5194, 0xf0}, - {0x5195, 0xf0}, - {0x5196, 0x03}, - {0x5197, 0x01}, - {0x5198, 0x04}, - {0x5199, 0x12}, - {0x519a, 0x04}, - {0x519b, 0x00}, - {0x519c, 0x06}, - {0x519d, 0x82}, - {0x519e, 0x38}, - {0x5381, 0x1e}, - {0x5382, 0x5b}, - {0x5383, 0x08}, - {0x5384, 0x0a}, - {0x5385, 0x7e}, - {0x5386, 0x88}, - {0x5387, 0x7c}, - {0x5388, 0x6c}, - {0x5389, 0x10}, - {0x538a, 0x01}, - {0x538b, 0x98}, - {0x5300, 0x08}, - {0x5301, 0x30}, - {0x5302, 0x10}, - {0x5303, 0x00}, - {0x5304, 0x08}, - {0x5305, 0x30}, - {0x5306, 0x08}, - {0x5307, 0x16}, - {0x5309, 0x08}, - {0x530a, 0x30}, - {0x530b, 0x04}, - {0x530c, 0x06}, - {0x5480, 0x01}, - {0x5481, 0x08}, - {0x5482, 0x14}, - {0x5483, 0x28}, - {0x5484, 0x51}, - {0x5485, 0x65}, - {0x5486, 0x71}, - {0x5487, 0x7d}, - - {0x5488, 0x87}, - {0x5489, 0x91}, - {0x548a, 0x9a}, - {0x548b, 0xaa}, - {0x548c, 0xb8}, - {0x548d, 0xcd}, - {0x548e, 0xdd}, - {0x548f, 0xea}, - {0x5490, 0x1d}, - {0x5580, 0x02}, - {0x5583, 0x40}, - {0x5584, 0x10}, - {0x5589, 0x10}, - {0x558a, 0x00}, - {0x558b, 0xf8}, - {0x5800, 0x23}, - {0x5801, 0x14}, - {0x5802, 0x0f}, - {0x5803, 0x0f}, - {0x5804, 0x12}, - {0x5805, 0x26}, - {0x5806, 0x0c}, - {0x5807, 0x08}, - {0x5808, 0x05}, - {0x5809, 0x05}, - {0x580a, 0x08}, - {0x580b, 0x0d}, - {0x580c, 0x08}, - {0x580d, 0x03}, - {0x580e, 0x00}, - {0x580f, 0x00}, - {0x5810, 0x03}, - {0x5811, 0x09}, - {0x5812, 0x07}, - {0x5813, 0x03}, - {0x5814, 0x00}, - {0x5815, 0x01}, - {0x5816, 0x03}, - {0x5817, 0x08}, - {0x5818, 0x0d}, - {0x5819, 0x08}, - {0x581a, 0x05}, - {0x581b, 0x06}, - {0x581c, 0x08}, - {0x581d, 0x0e}, - {0x581e, 0x29}, - {0x581f, 0x17}, - {0x5820, 0x11}, - {0x5821, 0x11}, - {0x5822, 0x15}, - {0x5823, 0x28}, - {0x5824, 0x46}, - {0x5825, 0x26}, - {0x5826, 0x08}, - {0x5827, 0x26}, - {0x5828, 0x64}, - {0x5829, 0x26}, - {0x582a, 0x24}, - {0x582b, 0x22}, - {0x582c, 0x24}, - {0x582d, 0x24}, - {0x582e, 0x06}, - {0x582f, 0x22}, - {0x5830, 0x40}, - {0x5831, 0x42}, - {0x5832, 0x24}, - {0x5833, 0x26}, - {0x5834, 0x24}, - {0x5835, 0x22}, - {0x5836, 0x22}, - {0x5837, 0x26}, - {0x5838, 0x44}, - {0x5839, 0x24}, - {0x583a, 0x26}, - {0x583b, 0x28}, - {0x583c, 0x42}, - {0x583d, 0xce}, - {0x5025, 0x00}, - {0x3a0f, 0x30}, - {0x3a10, 0x28}, - {0x3a1b, 0x30}, - {0x3a1e, 0x26}, - {0x3a11, 0x60}, - {0x3a1f, 0x14}, - {0x3008, 0x02}, + + /* AEC/AGC Power Down Domain Control */ + {OV5640_AEC_MAX_EXPO60_H, 0x04}, + {OV5640_AEC_MAX_EXPO60_L, 0x60}, + {OV5640_AEC_B50_STEP_H, 0x01}, + {OV5640_AEC_B50_STEP_L, 0x50}, + {OV5640_AEC_B60_STEP_H, 0x01}, + {OV5640_AEC_B60_STEP_L, 0x18}, + {OV5640_AEC_CTRL_0E, 0x03}, + {OV5640_AEC_CTRL_0D, 0x04}, + {OV5640_AEC_MAX_EXPO_H, 0x04}, + {OV5640_AEC_MAX_EXPO_L, 0x60}, + {OV5640_AEC_CTRL_13, 0x43}, + {OV5640_AEC_GAIN_CEILING_0, 0x00}, + {OV5640_AEC_GAIN_CEILING_1, 0xf8}, + {OV5640_AEC_CTRL_0F, 0x30}, + {OV5640_AEC_CTRL_10, 0x28}, + {OV5640_AEC_CTRL_1B, 0x30}, + {OV5640_AEC_CTRL_1E, 0x26}, + {OV5640_AEC_CTRL_11, 0x60}, + {OV5640_AEC_CTRL_1F, 0x14}, + + {OV5640_SYSTEM_CTRL, 0x02}, {OV5640_TABLE_END, 0x0000} }; -static struct ov5640_reg mode_1296x972[] = { +static struct ov5640_reg mode_1280x960[] = { /* PLL Control MIPI bit rate/lane = 448MHz, 16-bit mode. * Output size: 1304x972 (0, 0) - (2623, 1951), * Line Length = 1886, Frame Length = 990. + * Scaling method: subsampling in vertical and horizontal + * 1296x968 supports 2x2 binning */ - {0x3103, 0x11}, - {0x3008, 0x82}, - {OV5640_TABLE_WAIT_MS, 5}, - {0x3008, 0x42}, - {0x3103, 0x03}, - {0x3017, 0x00}, - {0x3018, 0x00}, - {0x3034, 0x18}, - {0x3035, 0x21}, - {0x3036, 0x70}, - {0x3037, 0x13}, - {0x3108, 0x01}, - {0x3630, 0x36}, - {0x3631, 0x0e}, - {0x3632, 0xe2}, - {0x3633, 0x12}, - {0x3621, 0xe0}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3905, 0x02}, - {0x3906, 0x10}, - {0x3901, 0x0a}, - {0x3731, 0x12}, - {0x3600, 0x08}, - {0x3601, 0x33}, - {0x302d, 0x60}, - {0x3620, 0x52}, - {0x371b, 0x20}, - {0x471c, 0x50}, - {0x3a13, 0x43}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3635, 0x13}, - {0x3636, 0x03}, - {0x3634, 0x40}, - {0x3622, 0x01}, - {0x3c01, 0x34}, - {0x3c04, 0x28}, - {0x3c05, 0x98}, - {0x3c06, 0x00}, - {0x3c07, 0x07}, - {0x3c08, 0x00}, - {0x3c09, 0x1c}, - {0x3c0a, 0x9c}, - {0x3c0b, 0x40}, - {0x3820, 0x41}, - {0x3821, 0x07}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0x9f}, - {0x3808, 0x05}, - {0x3809, 0x18}, - {0x380a, 0x03}, - {0x380b, 0xcc}, - {0x380c, 0x07}, - {0x380d, 0x5e}, - {0x380e, 0x03}, - {0x380f, 0xde}, - {0x3810, 0x00}, - {0x3811, 0x06}, - {0x3812, 0x00}, - {0x3813, 0x02}, - {0x3618, 0x00}, + + {OV5640_SC_PLL_CTRL0, 0x10 | MIPI_MODE_8BIT }, + {OV5640_SC_PLL_CTRL1, 0x21}, + {OV5640_SC_PLL_CTRL2, 0x70}, /* default 0x69 PLL multipier */ + {OV5640_SC_PLL_CTRL3, 0x03 | (1<<4) }, /* default 0x03 PLL divided by 2 */ + + {OV5640_MIPI_CTRL_00, 0x04}, + {OV5640_MIPI_PCLK_PERIOD, 0x10}, + + {OV5640_ISP_CTRL_00, 0xa7}, + {OV5640_ISP_CTRL_01, 0x83}, + {OV5640_ISP_MUX_CTRL, 0x00}, + + /* Timing Control */ + {OV5640_TIMING_HS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_HS_L, OV5640_ADDR_L(0) }, + {OV5640_TIMING_VS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_VS_L, OV5640_ADDR_L(0) }, + {OV5640_TIMING_HW_H, OV5640_ADDR_H(2623) }, + {OV5640_TIMING_HW_L, OV5640_ADDR_L(2623) }, + {OV5640_TIMING_VH_H, OV5640_ADDR_H(1951) }, + {OV5640_TIMING_VH_L, OV5640_ADDR_L(1951) }, + {OV5640_TIMING_DVPHO_H, OV5640_WIDTH_H(1280) }, + {OV5640_TIMING_DVPHO_L, OV5640_WIDTH_L(1280) }, + {OV5640_TIMING_DVPVO_H, OV5640_HEIGHT_H(960) }, + {OV5640_TIMING_DVPVO_L, OV5640_HEIGHT_L(960) }, + {OV5640_TIMING_HTS_H, OV5640_HTS_H(1886) }, + {OV5640_TIMING_HTS_L, OV5640_HTS_L(1886) }, + {OV5640_TIMING_VTS_H, OV5640_VTS_H(990) }, + {OV5640_TIMING_VTS_L, OV5640_VTS_L(990) }, + {OV5640_TIMING_HOFFSET_H, OV5640_H_OFFSET_H(31) }, // (2623-0-(1280*2)-1)/2 + {OV5640_TIMING_HOFFSET_L, OV5640_H_OFFSET_L(31) }, + {OV5640_TIMING_VOFFSET_H, OV5640_V_OFFSET_H(15) }, // (1951-0-(960*2)-1)/2 + {OV5640_TIMING_VOFFSET_L, OV5640_V_OFFSET_L(15) }, + {OV5640_TIMING_X_INC, 0x11 | (3<<4) }, + {OV5640_TIMING_Y_INC, 0x11 | (3<<4) }, + {OV5640_TIMING_REG20, 0x40}, + {OV5640_TIMING_REG21, REG21_BINNING_EN}, + + /* magic registers */ {0x3612, 0x29}, + {0x3618, 0x00}, {0x3708, 0x62}, {0x3709, 0x52}, {0x370c, 0x03}, - {0x3a02, 0x03}, - {0x3a03, 0xd8}, - {0x3a08, 0x01}, - {0x3a09, 0x27}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0e, 0x03}, - {0x3a0d, 0x04}, - {0x3a14, 0x03}, - {0x3a15, 0xd8}, - {0x4001, 0x02}, - {0x4004, 0x02}, - {0x3000, 0x00}, - {0x3002, 0x1c}, - {0x3004, 0xff}, - {0x3006, 0xc3}, - {0x300e, 0x45}, - {0x302e, 0x08}, - {0x4300, 0x32}, - {0x501f, 0x00}, - {0x4713, 0x02}, - {0x4407, 0x04}, - {0x440e, 0x00}, - {0x460b, 0x37}, - {0x460c, 0x20}, - {0x4800, 0x24}, - {0x4837, 0x10}, - {0x3824, 0x04}, - {0x5000, 0xa7}, - {0x5001, 0x83}, - {0x5180, 0xff}, - {0x5181, 0xf2}, - {0x5182, 0x00}, - {0x5183, 0x14}, - {0x5184, 0x25}, - {0x5185, 0x24}, - {0x5186, 0x09}, - {0x5187, 0x09}, - {0x5188, 0x09}, - {0x5189, 0x75}, - {0x518a, 0x54}, - {0x518b, 0xe0}, - {0x518c, 0xb2}, - {0x518d, 0x42}, - {0x518e, 0x3d}, - {0x518f, 0x56}, - {0x5190, 0x46}, - {0x5191, 0xf8}, - {0x5192, 0x04}, - {0x5193, 0x70}, - {0x5194, 0xf0}, - {0x5195, 0xf0}, - {0x5196, 0x03}, - {0x5197, 0x01}, - {0x5198, 0x04}, - {0x5199, 0x12}, - {0x519a, 0x04}, - {0x519b, 0x00}, - {0x519c, 0x06}, - {0x519d, 0x82}, - {0x519e, 0x38}, - {0x5381, 0x1e}, - {0x5382, 0x5b}, - {0x5383, 0x08}, - {0x5384, 0x0a}, - {0x5385, 0x7e}, - {0x5386, 0x88}, - {0x5387, 0x7c}, - {0x5388, 0x6c}, - {0x5389, 0x10}, - {0x538a, 0x01}, - {0x538b, 0x98}, - {0x5300, 0x08}, - {0x5301, 0x30}, - {0x5302, 0x10}, - {0x5303, 0x00}, - {0x5304, 0x08}, - {0x5305, 0x30}, - {0x5306, 0x08}, - {0x5307, 0x16}, - {0x5309, 0x08}, - {0x530a, 0x30}, - {0x530b, 0x04}, - {0x530c, 0x06}, - {0x5480, 0x01}, - {0x5481, 0x08}, - {0x5482, 0x14}, - {0x5483, 0x28}, - {0x5484, 0x51}, - {0x5485, 0x65}, - {0x5486, 0x71}, - {0x5487, 0x7d}, - {0x5488, 0x87}, - {0x5489, 0x91}, - {0x548a, 0x9a}, - {0x548b, 0xaa}, - {0x548c, 0xb8}, - {0x548d, 0xcd}, - {0x548e, 0xdd}, - {0x548f, 0xea}, - {0x5490, 0x1d}, - {0x5580, 0x02}, - {0x5583, 0x40}, - {0x5584, 0x10}, - {0x5589, 0x10}, - {0x558a, 0x00}, - {0x558b, 0xf8}, - {0x5800, 0x23}, - {0x5801, 0x14}, - {0x5802, 0x0f}, - {0x5803, 0x0f}, - {0x5804, 0x12}, - {0x5805, 0x26}, - {0x5806, 0x0c}, - {0x5807, 0x08}, - {0x5808, 0x05}, - {0x5809, 0x05}, - {0x580a, 0x08}, - {0x580b, 0x0d}, - {0x580c, 0x08}, - {0x580d, 0x03}, - {0x580e, 0x00}, - {0x580f, 0x00}, - {0x5810, 0x03}, - {0x5811, 0x09}, - {0x5812, 0x07}, - {0x5813, 0x03}, - {0x5814, 0x00}, - {0x5815, 0x01}, - {0x5816, 0x03}, - {0x5817, 0x08}, - {0x5818, 0x0d}, - {0x5819, 0x08}, - {0x581a, 0x05}, - {0x581b, 0x06}, - {0x581c, 0x08}, - {0x581d, 0x0e}, - {0x581e, 0x29}, - {0x581f, 0x17}, - {0x5820, 0x11}, - {0x5821, 0x11}, - {0x5822, 0x15}, - {0x5823, 0x28}, - {0x5824, 0x46}, - {0x5825, 0x26}, - {0x5826, 0x08}, - {0x5827, 0x26}, - {0x5828, 0x64}, - {0x5829, 0x26}, - {0x582a, 0x24}, - {0x582b, 0x22}, - {0x582c, 0x24}, - {0x582d, 0x24}, - {0x582e, 0x06}, - {0x582f, 0x22}, - {0x5830, 0x40}, - {0x5831, 0x42}, - {0x5832, 0x24}, - {0x5833, 0x26}, - {0x5834, 0x24}, - {0x5835, 0x22}, - {0x5836, 0x22}, - {0x5837, 0x26}, - {0x5838, 0x44}, - {0x5839, 0x24}, - {0x583a, 0x26}, - {0x583b, 0x28}, - {0x583c, 0x42}, - {0x583d, 0xce}, - {0x5025, 0x00}, - {0x3a0f, 0x30}, - {0x3a10, 0x28}, - {0x3a1b, 0x30}, - {0x3a1e, 0x26}, - {0x3a11, 0x60}, - {0x3a1f, 0x14}, - {0x3008, 0x02}, + + /* AEC/AGC Power Down Domain Control */ + {OV5640_AEC_MAX_EXPO60_H, 0x03}, + {OV5640_AEC_MAX_EXPO60_L, 0xd8}, + {OV5640_AEC_B50_STEP_H, 0x01}, + {OV5640_AEC_B50_STEP_L, 0x27}, + {OV5640_AEC_B60_STEP_H, 0x00}, + {OV5640_AEC_B60_STEP_L, 0xf6}, + {OV5640_AEC_CTRL_0E, 0x03}, + {OV5640_AEC_CTRL_0D, 0x04}, + {OV5640_AEC_MAX_EXPO_H, 0x03}, + {OV5640_AEC_MAX_EXPO_L, 0xd8}, + {OV5640_AEC_CTRL_13, 0x43}, + {OV5640_AEC_GAIN_CEILING_0, 0x00}, + {OV5640_AEC_GAIN_CEILING_1, 0xf8}, + {OV5640_AEC_CTRL_0F, 0x30}, + {OV5640_AEC_CTRL_10, 0x28}, + {OV5640_AEC_CTRL_1B, 0x30}, + {OV5640_AEC_CTRL_1E, 0x26}, + {OV5640_AEC_CTRL_11, 0x60}, + {OV5640_AEC_CTRL_1F, 0x14}, + + {OV5640_SYSTEM_CTRL, 0x02}, {OV5640_TABLE_END, 0x0000} }; static struct ov5640_reg mode_640x480[] = { - {0x3103, 0x11}, - {0x3008, 0x82}, - {OV5640_TABLE_WAIT_MS, 5}, - {0x3008, 0x42}, - {0x3103, 0x03}, - {0x3017, 0x00}, - {0x3018, 0x00}, - {0x3034, 0x18}, - {0x3035, 0x14}, - {0x3036, 0x70}, - {0x3037, 0x13}, - {0x4800, 0x24}, /* noncontinuous clock */ - {0x3108, 0x01}, - {0x3630, 0x36}, - {0x3631, 0x0e}, - {0x3632, 0xe2}, - {0x3633, 0x12}, - {0x3621, 0xe0}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3905, 0x02}, - {0x3906, 0x10}, - {0x3901, 0x0a}, - {0x3731, 0x12}, - {0x3600, 0x08}, - {0x3601, 0x33}, - {0x302d, 0x60}, - {0x3620, 0x52}, - {0x371b, 0x20}, - {0x471c, 0x50}, - {0x3a13, 0x43}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3635, 0x13}, - {0x3636, 0x03}, - {0x3634, 0x40}, - {0x3622, 0x01}, - {0x3c01, 0x34}, - {0x3c04, 0x28}, - {0x3c05, 0x98}, - {0x3c06, 0x00}, - {0x3c07, 0x08}, - {0x3c08, 0x00}, - {0x3c09, 0x1c}, - {0x3c0a, 0x9c}, - {0x3c0b, 0x40}, - {0x3820, 0x41}, - {0x3821, 0x07}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0x9b}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0xe0}, - {0x380c, 0x07}, - {0x380d, 0x68}, - {0x380e, 0x03}, - {0x380f, 0xd8}, - {0x3810, 0x00}, - {0x3811, 0x10}, - {0x3812, 0x00}, - {0x3813, 0x06}, - {0x3618, 0x00}, + /* PLL Control MIPI bit rate/lane = ?MHz, 16-bit mode. + * Output size: 640x480 (0, 4) - (2623, 1951), + * Line Length = 1896, Frame Length = 984. + * Scaling method: subsampling from 1280x960 + * 648x484 with dummy + * supports 2x2 binning + */ + + {OV5640_SC_PLL_CTRL0, 0x10 | MIPI_MODE_8BIT }, + {OV5640_SC_PLL_CTRL1, 0x14}, + {OV5640_SC_PLL_CTRL2, 0x70}, /* default 0x69 PLL multipier */ + {OV5640_SC_PLL_CTRL3, 0x03 | (1<<4) }, /* default 0x03 PLL divided by 2 */ + + {OV5640_MIPI_CTRL_00, 0x04}, + {OV5640_MIPI_PCLK_PERIOD, 0x44}, + + {OV5640_ISP_CTRL_00, 0xa7}, + {OV5640_ISP_CTRL_01, 0x83 | ISP_SCALE_EN}, + {OV5640_ISP_MUX_CTRL, 0x00}, + + /* Timing Control */ + {OV5640_TIMING_HS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_HS_L, OV5640_ADDR_L(0) }, + {OV5640_TIMING_VS_H, OV5640_ADDR_H(0) }, + {OV5640_TIMING_VS_L, OV5640_ADDR_L(4) }, + {OV5640_TIMING_HW_H, OV5640_ADDR_H(2623) }, + {OV5640_TIMING_HW_L, OV5640_ADDR_L(2623) }, + {OV5640_TIMING_VH_H, OV5640_ADDR_H(1947) }, + {OV5640_TIMING_VH_L, OV5640_ADDR_L(1947) }, + {OV5640_TIMING_DVPHO_H, OV5640_WIDTH_H(640) }, + {OV5640_TIMING_DVPHO_L, OV5640_WIDTH_L(640) }, + {OV5640_TIMING_DVPVO_H, OV5640_HEIGHT_H(480) }, + {OV5640_TIMING_DVPVO_L, OV5640_HEIGHT_L(480) }, + {OV5640_TIMING_HTS_H, OV5640_HTS_H(1896) }, + {OV5640_TIMING_HTS_L, OV5640_HTS_L(1896) }, + {OV5640_TIMING_VTS_H, OV5640_VTS_H(984) }, + {OV5640_TIMING_VTS_L, OV5640_VTS_L(984) }, + {OV5640_TIMING_HOFFSET_H, OV5640_H_OFFSET_H(31) }, // (2623-0-(640*2*2)-1)/2 + {OV5640_TIMING_HOFFSET_L, OV5640_H_OFFSET_L(31) }, + {OV5640_TIMING_VOFFSET_H, OV5640_V_OFFSET_H(11) }, // (1947-4-(480*2*2)-1)/2 + {OV5640_TIMING_VOFFSET_L, OV5640_V_OFFSET_L(11) }, + {OV5640_TIMING_X_INC, 0x11 | (3<<4) }, + {OV5640_TIMING_Y_INC, 0x11 | (3<<4) }, + {OV5640_TIMING_REG20, 0x40}, + {OV5640_TIMING_REG21, REG21_BINNING_EN}, + + /* magic registers */ {0x3612, 0x29}, + {0x3618, 0x00}, {0x3708, 0x64}, {0x3709, 0x52}, {0x370c, 0x03}, /* AEC/AGC Power Down Domain Control */ - {0x3a02, 0x03}, - {0x3a03, 0xd8}, - {0x3a08, 0x01}, - {0x3a09, 0x27}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0e, 0x03}, - {0x3a0d, 0x04}, - {0x3a14, 0x03}, - {0x3a15, 0xd8}, - - {0x4001, 0x02}, - {0x4004, 0x02}, - {0x3000, 0x00}, - {0x3002, 0x1c}, - {0x3004, 0xff}, - {0x3006, 0xc3}, - {0x300e, 0x45}, - {0x302e, 0x08}, - /* org:30 bit[3:0] - 0x0:YUYV 0x1:YVYU 0x2:UYVY - 0x3:VYUY 0xF:UYVY 0x4~0xE:Not-allowed - */ - {0x4300, 0x32}, - {0x501f, 0x00}, - {0x4713, 0x03}, - {0x4407, 0x04}, - {0x440e, 0x00}, - {0x460b, 0x35}, - {0x460c, 0x22}, - {0x4837, 0x44}, - {0x3824, 0x02}, - {0x5000, 0xa7}, - {0x5001, 0xa3}, + {OV5640_AEC_MAX_EXPO60_H, 0x03}, + {OV5640_AEC_MAX_EXPO60_L, 0xd8}, + {OV5640_AEC_B50_STEP_H, 0x01}, + {OV5640_AEC_B50_STEP_L, 0x27}, + {OV5640_AEC_B60_STEP_H, 0x00}, + {OV5640_AEC_B60_STEP_L, 0xf6}, + {OV5640_AEC_CTRL_0E, 0x03}, + {OV5640_AEC_CTRL_0D, 0x04}, + {OV5640_AEC_MAX_EXPO_H, 0x03}, + {OV5640_AEC_MAX_EXPO_L, 0xd8}, + {OV5640_AEC_CTRL_13, 0x43}, + {OV5640_AEC_GAIN_CEILING_0, 0x00}, + {OV5640_AEC_GAIN_CEILING_1, 0xf8}, + {OV5640_AEC_CTRL_0F, 0x30}, + {OV5640_AEC_CTRL_10, 0x28}, + {OV5640_AEC_CTRL_1B, 0x30}, + {OV5640_AEC_CTRL_1E, 0x26}, + {OV5640_AEC_CTRL_11, 0x60}, + {OV5640_AEC_CTRL_1F, 0x14}, + + {OV5640_SYSTEM_CTRL, 0x02}, + {OV5640_TABLE_END, 0x0000}, +}; + +static struct ov5640_reg mode_common_registers[] = { + + /* SCCB Control */ + {OV5640_SCCB_SYSTEM_CTRL, 0x03}, /* Clock from PLL */ + {OV5640_SCCB_SCLK, 0x01}, /* PCLK = pll_clki, SCLK = pll_clki/2 */ + + /* System Control */ + {OV5640_SYSTEM_CTRL, 0x02 | SYSTEM_CTRL_SRST }, /* Software reset */ + {OV5640_TABLE_WAIT_MS, 5}, + {OV5640_SYSTEM_CTRL, 0x02 | SYSTEM_CTRL_PDOWN }, /* Software power down */ + {OV5640_SYSTEM_RESET_00, 0x00}, + {OV5640_SYSTEM_RESET_01, 0x00}, + {OV5640_SYSTEM_RESET_02, 0x1c}, + {OV5640_SYSTEM_CLK_EN_00, 0xff}, + {OV5640_SYSTEM_CLK_EN_02, 0xc3}, + {OV5640_SYSTEM_MIPI_CTRL, 0x45}, + + {OV5640_SYSTEM_CTRL_2D, 0x60}, /* magic values */ + {OV5640_SYSTEM_CTRL_2E, 0x08}, /* magic values */ + + /* Format Control */ + {FMT_CTRL_00, FMT_YUV422 | OFMT_UYVY }, + + /* VCM Control */ +/* {OV5640_VCM_DEBUG_MODE_0, 0x08},*/ +/* {OV5640_VCM_DEBUG_MODE_1, 0x33},*/ + + /* 50/60Hz detector control */ + {OV5640_5060_CTRL_01, 0x34}, + {OV5640_5060_CTRL_04, 0x28}, + {OV5640_5060_CTRL_05, 0x98}, + {OV5640_5060_THRESHOLD_1_H, 0x00}, + {OV5640_5060_THRESHOLD_1_L, 0x07}, + {OV5640_5060_THRESHOLD_2_H, 0x00}, + {OV5640_5060_THRESHOLD_2_L, 0x1c}, + {OV5640_5060_SAMPLE_NUM_H, 0x9c}, + {OV5640_5060_SAMPLE_NUM_L, 0x40}, + + /* BLC Control */ + {OV5640_BLC_CTRL_01, 0x02}, + {OV5640_BLC_CTRL_04, 0x06}, /* AWB Control */ - {0x5180, 0xff}, - {0x5181, 0xf2}, - {0x5182, 0x00}, - {0x5183, 0x14}, - {0x5184, 0x25}, - {0x5185, 0x24}, - {0x5186, 0x09}, - {0x5187, 0x09}, - {0x5188, 0x09}, - {0x5189, 0x75}, - {0x518a, 0x54}, - {0x518b, 0xe0}, - {0x518c, 0xb2}, - {0x518d, 0x42}, - {0x518e, 0x3d}, - {0x518d, 0x56}, - {0x5190, 0x46}, - {0x5191, 0xf8}, - {0x5192, 0x04}, - {0x5193, 0x70}, - {0x5194, 0xf0}, - {0x5195, 0xf0}, - {0x5196, 0x03}, - {0x5197, 0x01}, - {0x5198, 0x04}, - {0x5199, 0x12}, - {0x519a, 0x04}, - {0x519b, 0x00}, - {0x519c, 0x06}, - {0x519d, 0x82}, - {0x519e, 0x38}, + {OV5640_AWB_CTRL_00, 0xff}, + {OV5640_AWB_CTRL_01, 0xf2}, + {OV5640_AWB_CTRL_02, 0x00}, + {OV5640_AWB_CTRL_03, 0x14}, + {OV5640_AWB_CTRL_04, 0x25}, + {OV5640_AWB_CTRL_05, 0x24}, + {OV5640_AWB_CTRL_06, 0x09}, + {OV5640_AWB_CTRL_07, 0x09}, + {OV5640_AWB_CTRL_08, 0x09}, + {OV5640_AWB_CTRL_09, 0x75}, + {OV5640_AWB_CTRL_10, 0x54}, + {OV5640_AWB_CTRL_11, 0xe0}, + {OV5640_AWB_CTRL_12, 0xb2}, + {OV5640_AWB_CTRL_13, 0x42}, + {OV5640_AWB_CTRL_14, 0x3d}, + {OV5640_AWB_CTRL_15, 0x56}, + {OV5640_AWB_CTRL_16, 0x46}, + {OV5640_AWB_CTRL_17, 0xf8}, + {OV5640_AWB_CTRL_18, 0x04}, + {OV5640_AWB_CTRL_19, 0x70}, + {OV5640_AWB_CTRL_20, 0xf0}, + {OV5640_AWB_CTRL_21, 0xf0}, + {OV5640_AWB_CTRL_22, 0x03}, + {OV5640_AWB_CTRL_23, 0x01}, + {OV5640_AWB_CTRL_24, 0x04}, + {OV5640_AWB_CTRL_25, 0x12}, + {OV5640_AWB_CTRL_26, 0x04}, + {OV5640_AWB_CTRL_27, 0x00}, + {OV5640_AWB_CTRL_28, 0x06}, + {OV5640_AWB_CTRL_29, 0x82}, + {OV5640_AWB_CTRL_30, 0x38}, /* CMX Control */ - {0x5381, 0x1e}, - {0x5382, 0x5b}, - {0x5383, 0x08}, - {0x5384, 0x0a}, - {0x5385, 0x7e}, - {0x5386, 0x88}, - {0x5387, 0x7c}, - {0x5388, 0x6c}, - {0x5389, 0x10}, - {0x538a, 0x01}, - {0x538b, 0x98}, + {OV5640_CMX1, 0x1e}, + {OV5640_CMX2, 0x5b}, + {OV5640_CMX3, 0x08}, + {OV5640_CMX4, 0x0a}, + {OV5640_CMX5, 0x7e}, + {OV5640_CMX6, 0x88}, + {OV5640_CMX7, 0x7c}, + {OV5640_CMX8, 0x6c}, + {OV5640_CMX9, 0x10}, + {OV5640_CMX_SIGN_0, 0x01}, + {OV5640_CMX_SIGN_1, 0x98}, /* CIP Control */ - {0x5300, 0x08}, - {0x5301, 0x30}, - {0x5302, 0x10}, - {0x5303, 0x00}, - {0x5304, 0x08}, - {0x5305, 0x30}, - {0x5306, 0x08}, - {0x5307, 0x16}, - {0x5309, 0x08}, - {0x530a, 0x30}, - {0x530b, 0x04}, - {0x530c, 0x06}, + {OV5640_CIP_SH_MT_THRES_1, 0x08}, + {OV5640_CIP_SH_MT_THRES_2, 0x30}, + {OV5640_CIP_SH_MT_OFFSET_1, 0x10}, + {OV5640_CIP_SH_MT_OFFSET_2, 0x00}, + {OV5640_CIP_DNS_THRES_1, 0x08}, + {OV5640_CIP_DNS_THRES_2, 0x30}, + {OV5640_CIP_DNS_OFFSET_1, 0x08}, + {OV5640_CIP_DNS_OFFSET_2, 0x16}, + {OV5640_CIP_SH_TH_THRES_1, 0x08}, + {OV5640_CIP_SH_TH_THRES_2, 0x30}, + {OV5640_CIP_SH_TH_OFFSET_1, 0x04}, + {OV5640_CIP_SH_TH_OFFSET_2, 0x06}, /* Gamma Control */ - {0x5480, 0x01}, - {0x5481, 0x08}, - {0x5482, 0x14}, - {0x5483, 0x28}, - {0x5484, 0x51}, - {0x5485, 0x65}, - {0x5486, 0x71}, - {0x5487, 0x7d}, - {0x5488, 0x87}, - {0x5489, 0x91}, - {0x548a, 0x9a}, - {0x548b, 0xaa}, - {0x548c, 0xb8}, - {0x548d, 0xcd}, - {0x548e, 0xdd}, - {0x548f, 0xea}, - {0x5490, 0x1d}, + {OV5640_GAMMA_CTRL_00, 0x01}, + {OV5640_GAMMA_YST_00, 0x08}, + {OV5640_GAMMA_YST_01, 0x14}, + {OV5640_GAMMA_YST_02, 0x28}, + {OV5640_GAMMA_YST_03, 0x51}, + {OV5640_GAMMA_YST_04, 0x65}, + {OV5640_GAMMA_YST_05, 0x71}, + {OV5640_GAMMA_YST_06, 0x7d}, + {OV5640_GAMMA_YST_07, 0x87}, + {OV5640_GAMMA_YST_08, 0x91}, + {OV5640_GAMMA_YST_09, 0x9a}, + {OV5640_GAMMA_YST_0A, 0xaa}, + {OV5640_GAMMA_YST_0B, 0xb8}, + {OV5640_GAMMA_YST_0C, 0xcd}, + {OV5640_GAMMA_YST_0D, 0xdd}, + {OV5640_GAMMA_YST_0E, 0xea}, + {OV5640_GAMMA_YST_0F, 0x1d}, /* SDE Control */ - {0x5580, 0x02}, - {0x5583, 0x40}, - {0x5584, 0x10}, - {0x5589, 0x10}, - {0x558a, 0x00}, - {0x558b, 0xf8}, + {OV5640_SDE_CTRL_0, 0x02}, + {OV5640_SDE_CTRL_3, 0x40}, + {OV5640_SDE_CTRL_4, 0x10}, + {OV5640_SDE_CTRL_9, 0x10}, + {OV5640_SDE_CTRL_10, 0x00}, + {OV5640_SDE_CTRL_11, 0xf8}, /* LENC Control */ - {0x5800, 0x23}, - {0x5801, 0x14}, - {0x5802, 0x0f}, - {0x5803, 0x0f}, - {0x5804, 0x12}, - {0x5805, 0x26}, - {0x5806, 0x0c}, - {0x5807, 0x08}, - {0x5808, 0x05}, - {0x5809, 0x05}, - {0x580a, 0x08}, - {0x580b, 0x0d}, - {0x580c, 0x08}, - {0x580d, 0x03}, - {0x580e, 0x00}, - {0x580f, 0x00}, - {0x5810, 0x03}, - {0x5811, 0x09}, - {0x5812, 0x07}, - {0x5813, 0x03}, - {0x5814, 0x00}, - {0x5815, 0x01}, - {0x5816, 0x03}, - {0x5817, 0x08}, - {0x5818, 0x0d}, - {0x5819, 0x08}, - {0x581a, 0x05}, - {0x581b, 0x06}, - {0x581c, 0x08}, - {0x581d, 0x0e}, - {0x581e, 0x29}, - {0x581f, 0x17}, - {0x5820, 0x11}, - {0x5821, 0x11}, - {0x5822, 0x15}, - {0x5823, 0x28}, - {0x5824, 0x46}, - {0x5825, 0x26}, - {0x5826, 0x08}, - {0x5827, 0x26}, - {0x5828, 0x64}, - {0x5829, 0x26}, - {0x582a, 0x24}, - {0x582b, 0x22}, - {0x582c, 0x24}, - {0x582d, 0x24}, - {0x582e, 0x06}, - {0x582f, 0x22}, - {0x5830, 0x40}, - {0x5831, 0x42}, - {0x5832, 0x24}, - {0x5833, 0x26}, - {0x5834, 0x24}, - {0x5835, 0x22}, - {0x5836, 0x22}, - {0x5837, 0x26}, - {0x5838, 0x44}, - {0x5839, 0x24}, - {0x583a, 0x26}, - {0x583b, 0x28}, - {0x583c, 0x42}, - {0x583d, 0xce}, - - {0x5025, 0x00}, - {0x3a0f, 0x30}, - {0x3a10, 0x28}, - {0x3a1b, 0x30}, - {0x3a1e, 0x26}, - {0x3a11, 0x60}, - {0x3a1f, 0x14}, - {0x3008, 0x02}, + {OV5640_LENC_GMTRX_00, 0x23}, + {OV5640_LENC_GMTRX_01, 0x14}, + {OV5640_LENC_GMTRX_02, 0x0f}, + {OV5640_LENC_GMTRX_03, 0x0f}, + {OV5640_LENC_GMTRX_04, 0x12}, + {OV5640_LENC_GMTRX_05, 0x26}, + {OV5640_LENC_GMTRX_10, 0x0c}, + {OV5640_LENC_GMTRX_11, 0x08}, + {OV5640_LENC_GMTRX_12, 0x05}, + {OV5640_LENC_GMTRX_13, 0x05}, + {OV5640_LENC_GMTRX_14, 0x08}, + {OV5640_LENC_GMTRX_15, 0x0d}, + {OV5640_LENC_GMTRX_20, 0x08}, + {OV5640_LENC_GMTRX_21, 0x03}, + {OV5640_LENC_GMTRX_22, 0x00}, + {OV5640_LENC_GMTRX_23, 0x00}, + {OV5640_LENC_GMTRX_24, 0x03}, + {OV5640_LENC_GMTRX_25, 0x09}, + {OV5640_LENC_GMTRX_30, 0x07}, + {OV5640_LENC_GMTRX_31, 0x03}, + {OV5640_LENC_GMTRX_32, 0x00}, + {OV5640_LENC_GMTRX_33, 0x01}, + {OV5640_LENC_GMTRX_34, 0x03}, + {OV5640_LENC_GMTRX_35, 0x08}, + {OV5640_LENC_GMTRX_40, 0x0d}, + {OV5640_LENC_GMTRX_41, 0x08}, + {OV5640_LENC_GMTRX_42, 0x05}, + {OV5640_LENC_GMTRX_43, 0x06}, + {OV5640_LENC_GMTRX_44, 0x08}, + {OV5640_LENC_GMTRX_45, 0x0e}, + {OV5640_LENC_GMTRX_50, 0x29}, + {OV5640_LENC_GMTRX_51, 0x17}, + {OV5640_LENC_GMTRX_52, 0x11}, + {OV5640_LENC_GMTRX_53, 0x11}, + {OV5640_LENC_GMTRX_54, 0x15}, + {OV5640_LENC_GMTRX_55, 0x28}, + {OV5640_LENC_BRMATRX_00, 0x46}, + {OV5640_LENC_BRMATRX_01, 0x26}, + {OV5640_LENC_BRMATRX_02, 0x08}, + {OV5640_LENC_BRMATRX_03, 0x26}, + {OV5640_LENC_BRMATRX_04, 0x64}, + {OV5640_LENC_BRMATRX_05, 0x26}, + {OV5640_LENC_BRMATRX_06, 0x24}, + {OV5640_LENC_BRMATRX_07, 0x22}, + {OV5640_LENC_BRMATRX_08, 0x24}, + {OV5640_LENC_BRMATRX_09, 0x24}, + {OV5640_LENC_BRMATRX_20, 0x06}, + {OV5640_LENC_BRMATRX_21, 0x22}, + {OV5640_LENC_BRMATRX_22, 0x40}, + {OV5640_LENC_BRMATRX_23, 0x42}, + {OV5640_LENC_BRMATRX_24, 0x24}, + {OV5640_LENC_BRMATRX_30, 0x26}, + {OV5640_LENC_BRMATRX_31, 0x24}, + {OV5640_LENC_BRMATRX_32, 0x22}, + {OV5640_LENC_BRMATRX_33, 0x22}, + {OV5640_LENC_BRMATRX_34, 0x26}, + {OV5640_LENC_BRMATRX_40, 0x44}, + {OV5640_LENC_BRMATRX_41, 0x24}, + {OV5640_LENC_BRMATRX_42, 0x26}, + {OV5640_LENC_BRMATRX_43, 0x28}, + {OV5640_LENC_BRMATRX_44, 0x42}, + {OV5640_LENC_BR_OFFSET, 0xce}, + + /* magic registers */ + {0x3620, 0x52}, + {0x3621, 0xe0}, + {0x3622, 0x01}, + {0x3630, 0x36}, + {0x3631, 0x0e}, + {0x3632, 0xe2}, + {0x3633, 0x12}, + {0x3634, 0x40}, + {0x3635, 0x13}, + {0x3636, 0x03}, + {0x3703, 0x5a}, + {0x3704, 0xa0}, + {0x3705, 0x1a}, + {0x370b, 0x60}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x371b, 0x20}, + {0x3731, 0x12}, + {0x3901, 0x0a}, + {0x3905, 0x02}, + {0x3906, 0x10}, + {0x471c, 0x50}, + {OV5640_TABLE_END, 0x0000}, }; enum { OV5640_MODE_640x480, - OV5640_MODE_1296x972, + OV5640_MODE_1280x960, OV5640_MODE_1920x1080, OV5640_MODE_2592x1944, OV5640_SIZE_LAST, @@ -1104,7 +885,7 @@ enum { static struct ov5640_reg *mode_table[] = { [OV5640_MODE_640x480] = mode_640x480, - [OV5640_MODE_1296x972] = mode_1296x972, + [OV5640_MODE_1280x960] = mode_1280x960, [OV5640_MODE_1920x1080] = mode_1920x1080, [OV5640_MODE_2592x1944] = mode_2592x1944, }; @@ -1113,9 +894,9 @@ static int test_pattern; module_param(test_pattern, int, 0644); static struct ov5640_reg tp_cbars[] = { - {0x503D, 0x80}, - {0x503E, 0x00}, - {0x5046, 0x01}, + {OV5640_ISP_TEST, ISP_TEST_EN | ISP_TEST_00 | ISP_TEST_TRANSPARENT | ISP_TEST_ROLLING }, + {OV5640_ISP_DEBUG_3E, 0x00}, + {OV5640_ISP_DEBUG_46, 0x01}, {OV5640_TABLE_END, 0x0000} }; @@ -1143,7 +924,7 @@ static enum v4l2_mbus_pixelcode ov5640_codes[] = { static const struct v4l2_frmsize_discrete ov5640_frmsizes[OV5640_SIZE_LAST] = { {640, 480}, - {1296, 972}, + {1280, 960}, {1920, 1080}, {2592, 1944}, }; @@ -1217,8 +998,9 @@ static int ov5640_write_reg(struct i2c_client *client, u16 addr, u8 value) msg[0].buf = data; count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); - if (count == ARRAY_SIZE(msg)) + if (count == ARRAY_SIZE(msg)) { return 0; + } dev_err(&client->dev, "ov5840: i2c transfer failed, addr: %x, value: %02x\n", addr, (u32)value); @@ -1314,6 +1096,10 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) return 0; } + ret = ov5640_write_table(priv, mode_common_registers); + if (ret) + return ret; + ret = ov5640_write_table(priv, mode_table[priv->mode]); if (ret) return ret; @@ -1365,12 +1151,12 @@ static int ov5640_s_power(struct v4l2_subdev *sd, int on) { struct ov5640_priv *priv = to_ov5640(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *scsd = soc_camera_i2c_to_desc(client); if (on) ov5640_s_fmt(sd, &priv->mf); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, scsd, on); } static int ov5640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, @@ -1409,6 +1195,11 @@ static int ov5640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) return 0; } +static int ov5640_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return 0; +} + /* Get chip identification */ static int ov5640_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *id) @@ -1462,6 +1253,7 @@ static struct v4l2_subdev_video_ops ov5640_video_ops = { .enum_mbus_fmt = ov5640_enum_fmt, .cropcap = ov5640_cropcap, .g_crop = ov5640_g_crop, + .querystd = ov5640_querystd, }; @@ -1486,13 +1278,13 @@ static int ov5640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5640_priv *priv; - struct soc_camera_link *icl; + struct soc_camera_subdev_desc *scsd; u8 chip_id_hi, chip_id_lo; int ret; /* Checking soc-camera interface */ - icl = soc_camera_i2c_to_link(client); - if (!icl) { + scsd = soc_camera_i2c_to_desc(client); + if (!scsd) { dev_err(&client->dev, "Missing soc_camera_link for driver\n"); return -EINVAL; } @@ -1513,14 +1305,14 @@ static int ov5640_probe(struct i2c_client *client, /* * check and show product ID and manufacturer ID */ - soc_camera_power_on(&client->dev, icl); - ret = ov5640_read_reg(client, 0x300A, &chip_id_hi); + soc_camera_power_on(&client->dev, scsd); + ret = ov5640_read_reg(client, OV5640_CHIP_ID_H, &chip_id_hi); if (ret < 0) { dev_err(&client->dev, "Failure to read Chip ID (high byte)\n"); return ret; } - ret = ov5640_read_reg(client, 0x300B, &chip_id_lo); + ret = ov5640_read_reg(client, OV5640_CHIP_ID_L, &chip_id_lo); if (ret < 0) { dev_err(&client->dev, "Failure to read Chip ID (low byte)\n"); return ret; @@ -1534,7 +1326,7 @@ static int ov5640_probe(struct i2c_client *client, ret = -ENODEV; return ret; } - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, scsd); ov5640_set_default_fmt(priv); diff --git a/drivers/media/i2c/soc_camera/tc358743.c b/drivers/media/i2c/soc_camera/tc358743.c new file mode 100644 index 000000000000..7a63b1ec019a --- /dev/null +++ b/drivers/media/i2c/soc_camera/tc358743.c @@ -0,0 +1,1104 @@ +/* + * based on OV5640 driver and TC358743 driver for i.MX6 + * + * author: Antmicro Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#define PLLCTL0 0x0020 +#define PLLCTL1 0x0022 + +#define MASK_PLL_PRD 0xf000 +#define SET_PLL_PRD(prd) ((((prd) - 1) << 12) & MASK_PLL_PRD) + +#define MASK_PLL_FBD 0x01ff +#define SET_PLL_FBD(fbd) (((fbd) - 1) & MASK_PLL_FBD) + +#define MASK_CKEN 0x0010 +#define MASK_RESETB 0x0002 +#define MASK_PLL_EN 0x0001 + +#define MASK_NOL_1 0 +#define MASK_NOL_2 2 +#define MASK_NOL_3 4 +#define MASK_NOL_4 6 + +/* this is true for 27 MHz refclk */ +#define REFCLK_PRE_DIVIDER 9 +#define SET_PLL_FREQ(x) (SET_PLL_PRD(REFCLK_PRE_DIVIDER) | SET_PLL_FBD(DIV_ROUND_UP(x,3))) + +#define FRS_BW_25 0x0 +#define FRS_BW_33 0x1 +#define FRS_BW_50 0x2 +#define FRS_BW_MAX 0x3 + +#define MASK_PLL_FRS 0x0f00 +#define SET_PLL_FRS(frs,bw) (((frs | bw) << 8) & MASK_PLL_FRS) +#define GET_FRS(x) (x < 125) ? 0xC : (x < 250) ? 0x8 : (x < 500) ? 0x4 : 0x0 + +#define HCNT_MASK 0x3f +#define CALC_HDR_CNT(prep, zero) ((zero & HCNT_MASK) << 8) | (prep & HCNT_MASK) + +#define SYS_STATUS 0x8520 +#define CSI_STATUS 0x0410 +#define MASK_S_WSYNC 0x0400 +#define VI_STATUS0 0x8521 +#define VI_STATUS1 0x8522 +#define AU_STATUS0 0x8523 +#define VI_STATUS2 0x8525 +#define VI_STATUS3 0x8528 + +#define EDID_MODE 0x85C7 +#define MASK_EDID_SPEED 0x40 +#define MASK_EDID_MODE 0x03 +#define MASK_EDID_MODE_DISABLE 0x00 +#define MASK_EDID_MODE_DDC2B 0x01 +#define MASK_EDID_MODE_E_DDC 0x02 +#define EDID_LEN1 0x85CA +#define EDID_LEN2 0x85CB + + +#define SYSCTL 0x0002 +#define CONFCTL 0x0004 +#define MASK_AUTOINDEX 0x0004 +#define FIFOCTL 0x0006 +#define INTSTATUS 0x0014 +#define INTMASK 0x0016 +#define PHY_RST 0x8535 +#define MASK_RESET_CTRL 0x01 /* Reset active low */ + +#define HPD_CTL 0x8544 +#define MASK_HPD_CTL0 0x10 +#define MASK_HPD_OUT0 0x01 + +#define BKSV 0x8800 + +#define CSI_CONFW 0x0500 +#define MASK_MODE_SET 0xa0000000 +#define MASK_ADDRESS_CSI_CONTROL 0x03000000 +#define MASK_CSI_MODE 0x8000 +#define MASK_HTXTOEN 0x0400 +#define MASK_TXHSMD 0x0080 +#define MASK_HSCKMD 0x0020 +#define HSTXVREGEN 0x0234 +#define TXOPTIONCNTRL 0x0238 +#define MASK_CONTCLKMODE 0x00000001 +#define STARTCNTRL 0x0204 +#define MASK_START 0x00000001 +#define CLW_CNTRL 0x0140 +#define MASK_CLW_LANEDISABLE 0x0001 + +#define D0W_CNTRL 0x0144 +#define MASK_D0W_LANEDISABLE 0x0001 + +#define D1W_CNTRL 0x0148 +#define MASK_D1W_LANEDISABLE 0x0001 + +#define D2W_CNTRL 0x014C +#define MASK_D2W_LANEDISABLE 0x0001 + +#define D3W_CNTRL 0x0150 +#define MASK_D3W_LANEDISABLE 0x0001 + +#define LINEINITCNT 0x0210 +#define LPTXTIMECNT 0x0214 +#define TCLK_HEADERCNT 0x0218 +#define TCLK_TRAILCNT 0x021C +#define THS_HEADERCNT 0x0220 +#define TWAKEUP 0x0224 +#define TCLK_POSTCNT 0x0228 +#define THS_TRAILCNT 0x022C + +#define CSI_START 0x0518 +#define MASK_STRT 0x00000001 + +#define PHY_CTL1 0x8532 +#define PHY_CTL2 0x8533 /* Not in REF_01 */ +#define PHY_BIAS 0x8536 /* Not in REF_01 */ +#define PHY_CSQ 0x853F /* Not in REF_01 */ +#define DDC_CTL 0x8543 + +#define SYS_FREQ0 0x8540 +#define SYS_FREQ1 0x8541 + +#define NCO_F0_MOD 0x8670 +#define MASK_NCO_F0_MOD_27MHZ 0x01 + + +#define LOCKDET_REF0 0x8630 +#define LOCKDET_REF1 0x8631 +#define LOCKDET_REF2 0x8632 + +#define SYS_INT 0x8502 +#define MASK_I_DDC 0x01 +#define SYS_INTM 0x8512 +#define PACKET_INTM 0x8514 +#define CBIT_INTM 0x8515 +#define AUDIO_INTM 0x8516 +#define PHY_CTL0 0x8531 +#define MASK_PHY_CTL 0x01 +#define ANA_CTL 0x8545 +#define MASK_APPL_PCSX 0x30 +#define MASK_APPL_PCSX_NORMAL 0x30 + +#define MASK_ANALOG_ON 0x01 + + +#define AVM_CTL 0x8546 + + +#define MASK_PHY_AUTO_RST4 0x04 +#define MASK_PHY_AUTO_RST3 0x02 +#define MASK_PHY_AUTO_RST2 0x01 + +#define VOUT_SET2 0x8573 +#define MASK_VOUT_422FIL_100 0x40 +#define MASK_SEL422 0x80 +#define MASK_VOUTCOLORMODE_AUTO 0x01 +#define MASK_VOUTCOLORMODE_THROUGH 0x00 +#define MASK_VOUTCOLORMODE_MANUAL 0x03 + +#define VOUT_SET3 0x8574 +#define MASK_VOUT_EXTCNT 0x08 + +#define PK_INT_MODE 0x8709 +#define NO_PKT_LIMIT 0x870B +#define NO_PKT_CLR 0x870C +#define ERR_PK_LIMIT 0x870D +#define NO_GDB_LIMIT 0x9007 +#define NO_PKT_LIMIT2 0x870E + +#define INIT_END 0x854A +#define MASK_INIT_END 0x01 + + +#define HDCP_MODE 0x8560 +#define HDCP_REG1 0x8563 +#define HDCP_REG2 0x8564 +#define HDCP_REG3 0x85D1 + +#define EDID_LEN2 0x85CB +#define EDID_MODE 0x85C7 + +#define FH_MIN0 0x85AA +#define FH_MIN1 0x85AB +#define FH_MAX0 0x85AC +#define FH_MAX1 0x85AD + +#define DE_WIDTH_H_LO 0x8582 +#define DE_WIDTH_H_HI 0x8583 +#define DE_WIDTH_V_LO 0x8588 +#define DE_WIDTH_V_HI 0x8589 +#define H_SIZE_LO 0x858A +#define H_SIZE_HI 0x858B +#define V_SIZE_LO 0x858C +#define V_SIZE_HI 0x858D +#define FV_CNT_LO 0x85A1 +#define FV_CNT_HI 0x85A2 + + +#define MASK_IRRST 0x0800 +#define MASK_CECRST 0x0400 +#define MASK_CTXRST 0x0200 +#define MASK_HDMIRST 0x0100 + +#define MASK_AUDCHNUM_2 0x0c00 +#define MASK_AUTOINDEX 0x0004 +#define MASK_ABUFEN 0x0002 +#define MASK_VBUFEN 0x0001 +#define MASK_AUDOUTSEL_I2S 0x0010 + +#define MASK_YCBCRFMT 0x00c0 + +#define MASK_YCBCRFMT_422_12_BIT 0x0040 +#define MASK_YCBCRFMT_COLORBAR 0x0080 +#define MASK_YCBCRFMT_422_8_BIT 0x00c0 + +#define MASK_YCBCRFMT_444 0x0000 + +#define MASK_INFRMEN 0x0020 +#define PHY_EN 0x8534 +#define MASK_ENABLE_PHY 0x01 + +#define MASK_PWRISO 0x8000 + +#define VI_MODE 0x8570 +#define MASK_RGB_DVI 0x8 + +#define VI_REP 0x8576 +#define MASK_VOUT_COLOR_SEL 0xe0 +#define MASK_VOUT_COLOR_RGB_FULL 0x00 +#define MASK_VOUT_COLOR_RGB_LIMITED 0x20 +#define MASK_VOUT_COLOR_601_YCBCR_FULL 0x40 +#define MASK_VOUT_COLOR_601_YCBCR_LIMITED 0x60 +#define MASK_VOUT_COLOR_709_YCBCR_FULL 0x80 +#define MASK_VOUT_COLOR_709_YCBCR_LIMITED 0xa0 +#define MASK_VOUT_COLOR_FULL_TO_LIMITED 0xc0 +#define MASK_VOUT_COLOR_LIMITED_TO_FULL 0xe0 +#define MASK_IN_REP_HEN 0x10 +#define MASK_IN_REP 0x0f + +/* status regs */ +#define SYS_STATUS 0x8520 +#define MASK_S_SYNC 0x80 +#define MASK_S_AVMUTE 0x40 +#define MASK_S_HDCP 0x20 +#define MASK_S_HDMI 0x10 +#define MASK_S_PHY_SCDT 0x08 +#define MASK_S_PHY_PLL 0x04 +#define MASK_S_TMDS 0x02 +#define MASK_S_DDC5V 0x01 +#define CSI_STATUS 0x0410 +#define MASK_S_WSYNC 0x0400 +#define MASK_S_TXACT 0x0200 +#define MASK_S_RXACT 0x0100 +#define MASK_S_HLT 0x0001 +#define VI_STATUS1 0x8522 +#define MASK_S_V_GBD 0x08 +#define MASK_S_DEEPCOLOR 0x0c +#define MASK_S_V_422 0x02 +#define MASK_S_V_INTERLACE 0x01 +#define AU_STATUS0 0x8523 +#define MASK_S_A_SAMPLE 0x01 +#define VI_STATUS3 0x8528 +#define MASK_S_V_COLOR 0x1e +#define MASK_LIMITED 0x01 + +#define HDMI_DET 0x8552 /* Not in REF_01 */ +#define MASK_HDMI_DET_MOD1 0x80 +#define MASK_HDMI_DET_MOD0 0x40 +#define MASK_HDMI_DET_V 0x30 +#define MASK_HDMI_DET_V_SYNC 0x00 +#define MASK_HDMI_DET_V_ASYNC_25MS 0x10 +#define MASK_HDMI_DET_V_ASYNC_50MS 0x20 +#define MASK_HDMI_DET_V_ASYNC_100MS 0x30 +#define MASK_HDMI_DET_NUM 0x0f + +#define HV_RST 0x85AF /* Not in REF_01 */ +#define MASK_H_PI_RST 0x20 +#define MASK_V_PI_RST 0x10 + +#define MASK_DDC5V_MODE_100MS 2 + +#define VI_MUTE 0x857F +#define MASK_AUTO_MUTE 0xc0 +#define MASK_VI_MUTE 0x10 + +#define FORCE_MUTE 0x8600 +#define MASK_FORCE_AMUTE 0x10 + +#define BCAPS 0x8840 +#define BSTATUS1 0x8842 + +#define MASK_MAX_EXCED 0x08 +#define MASK_REPEATER 0x40 +#define MASK_READY 0x20 + +#define MASK_HDMI_RSVD 0x80 + +#define VOUT_SET0 0x8571 +#define VOUT_SET1 0x8572 + +#define CMD_AUD 0x8601 +#define MASK_CMD_BUFINIT 0x04 +#define MASK_CMD_LOCKDET 0x02 +#define MASK_CMD_MUTE 0x01 + +#define MASK_FORCE_DMUTE 0x01 + +#define ERR_PK_LIMIT 0x870D +#define NO_PKT_LIMIT2 0x870E +#define PK_AVI_0HEAD 0x8710 +#define PK_AVI_1HEAD 0x8711 +#define PK_AVI_2HEAD 0x8712 +#define PK_AVI_0BYTE 0x8713 +#define PK_AVI_1BYTE 0x8714 +#define PK_AVI_2BYTE 0x8715 +#define PK_AVI_3BYTE 0x8716 +#define PK_AVI_4BYTE 0x8717 +#define PK_AVI_5BYTE 0x8718 +#define PK_AVI_6BYTE 0x8719 +#define PK_AVI_7BYTE 0x871A +#define PK_AVI_8BYTE 0x871B +#define PK_AVI_9BYTE 0x871C +#define PK_AVI_10BYTE 0x871D +#define PK_AVI_11BYTE 0x871E +#define PK_AVI_12BYTE 0x871F +#define PK_AVI_13BYTE 0x8720 +#define PK_AVI_14BYTE 0x8721 +#define PK_AVI_15BYTE 0x8722 +#define PK_AVI_16BYTE 0x8723 + +struct reg_value { + u16 addr; + u32 val; + u32 flags; +}; + +struct _reg_size +{ + u16 startaddr, endaddr; + int size; +} + +tc358743_read_reg_size [] = +{ + {0x0000, 0x005a, 2}, + {0x0140, 0x0150, 4}, + {0x0204, 0x0238, 4}, + {0x040c, 0x0418, 4}, + {0x044c, 0x0454, 4}, + {0x0500, 0x0518, 4}, + {0x0600, 0x06cc, 4}, + {0x7000, 0x7100, 2}, + {0x8500, 0x8bff, 1}, + {0x8c00, 0x8fff, 4}, + {0x9000, 0x90ff, 1}, + {0x9100, 0x92ff, 1}, + {0, 0, 0}, +}; + +typedef struct timings_regs { + uint32_t frequency; + uint32_t line_init_cnt; + uint32_t tlpx_time_cnt; + + uint32_t tclk_prepare_cnt; + uint32_t tclk_zero_cnt; + + uint32_t tclk_trail_cnt; /* Don't care in continous clk mode */ + + uint32_t ths_prepare_cnt; + uint32_t ths_zero_cnt; + + uint32_t twakeup_cnt; + uint32_t tclk_post_cnt; /* Don't care in continous clk mode */ + + uint32_t ths_trail_cnt; + + uint32_t fifo_delay; +} timings_regs; + +timings_regs timings[] = { + /* 640x480 @ 75 */ + { 500, 3328, 1, 1, 7, 0, 1, 0, 16384, 5, 0, 256}, + /* 800x600 @ 75 */ + { 594, 3712, 3, 3, 20, 0, 3, 1, 18562, 8, 2, 180}, + /* 1024x768 @ 75 */ + { 600, 7689, 3, 2, 20, 0, 3, 0, 18944, 8, 2, 128}, + /* 1280x720 @ 60 */ + { 825, 5160, 5, 4, 29, 0, 5, 5, 18000, 0, 4, 180}, + /* 1280x1024 @ 75 and 1920x1080 @ 60 */ + { 1075, 6000, 5, 4, 29, 0, 5, 80, 18000, 0, 4, 256}, + {}, +}; + +static struct reg_value tc358743_reset[] = { + {CONFCTL, 0, 100}, + + {PHY_RST, 0x0, 100}, + {PHY_RST, 0x1, 100}, + {PHY_EN, 0, 100}, + {PHY_EN, 1, 100}, + + /* reset */ + {SYSCTL, MASK_IRRST | MASK_CECRST | MASK_CTXRST | MASK_HDMIRST, 100}, + {SYSCTL, 0x00000000, 1000}, + + /* set ref frequency to 27 MHz */ + {SYS_FREQ0, (2700) & 0xFF, 0}, + {SYS_FREQ1, (2700 >> 8) & 0xFF, 0}, + + {LOCKDET_REF0, (270000) & 0xFF, 0}, + {LOCKDET_REF1, (270000 >> 8) & 0xFF, 0}, + {LOCKDET_REF2, (270000 >> 16) & 0xFF, 0}, + {FH_MIN0, (270) & 0xFF, 0}, + {FH_MIN1, (270 >> 8) & 0xFF, 0}, + {FH_MAX0, (27 * 66) & 0xFF, 0}, + {FH_MAX1, ((27 * 66) >> 8) & 0xFF, 0}, + {NCO_F0_MOD, MASK_NCO_F0_MOD_27MHZ, 0}, +}; + +static timings_regs tc358743_get_best_timings(uint32_t frequency) { + int count = 0; + timings_regs best_timings = {}; + while (1) { + if (timings[count].frequency == 0) break; + best_timings = timings[count]; + if (timings[count].frequency >= frequency) break; + count++; + } + return best_timings; +} + +static struct reg_value tc358743_2lanes_start[] = { + {CLW_CNTRL, 0x0000, 0}, + + /* Make all lanes active */ + {D0W_CNTRL, 0x0000, 0}, + {D1W_CNTRL, 0x0000, 0}, + + {D2W_CNTRL, 0x0000, 0}, + {D3W_CNTRL, 0x0000, 0}, + {HSTXVREGEN, 0x001f, 0}, + + /* Continuous clock mode */ + {TXOPTIONCNTRL, MASK_CONTCLKMODE, 0}, + {STARTCNTRL, MASK_START, 0}, + {CSI_START, MASK_STRT, 0}, + + /* Use two lanes */ + {CSI_CONFW, MASK_MODE_SET | MASK_ADDRESS_CSI_CONTROL | MASK_CSI_MODE | MASK_TXHSMD | MASK_HSCKMD | MASK_NOL_2, 0}, + + /* Output Control */ + {CONFCTL, MASK_VBUFEN | MASK_INFRMEN | MASK_YCBCRFMT_422_8_BIT | MASK_AUTOINDEX, 0}, +}; + +static struct reg_value tc358743_setting_hdmi[] = { + /* HDMI interrupt mask */ + {SYS_INT, 0x0000, 100}, + {SYS_INTM, 0x0000, 0}, + {PACKET_INTM, 0x0000, 0}, + {CBIT_INTM, 0x0000, 0}, + {AUDIO_INTM, 0x0000, 0}, + + {PHY_CTL0, MASK_PHY_CTL, 0}, + /* HDMI PHY */ + {PHY_CTL1, 0x0080, 0}, + {PHY_BIAS, 0x0040, 0}, + {PHY_CSQ, 0x000a, 0}, + + {HDMI_DET, MASK_HDMI_DET_V_SYNC | MASK_HDMI_DET_MOD0 | MASK_HDMI_DET_MOD1, 0}, + {HV_RST, MASK_H_PI_RST | MASK_V_PI_RST, 0}, + {PHY_CTL2, MASK_PHY_AUTO_RST2 | MASK_PHY_AUTO_RST3 | MASK_PHY_AUTO_RST4, 0}, + + /* HDMI system */ + {DDC_CTL, 0x0030 | MASK_DDC5V_MODE_100MS, 0}, + {ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON, 0}, + {AVM_CTL, 0x002d, 0}, + {VI_MODE, MASK_RGB_DVI, 0}, + {VI_MUTE, MASK_AUTO_MUTE, 0}, + {CMD_AUD, MASK_CMD_MUTE, 0}, + + /* EDID */ + {EDID_MODE, 0x0001, 2}, + {EDID_LEN2, 0x0001, 0}, + + {BCAPS, MASK_REPEATER | MASK_READY, 0}, /* Turn off HDMI */ + {BSTATUS1, MASK_MAX_EXCED, 0}, + + /* HDCP Settings */ + {HDCP_REG3, 0x0001, 0}, + {HDCP_MODE, 0x0024, 0}, + {HDCP_REG1, 0x0011, 0}, + {HDCP_REG2, 0x000f, 0}, + + /* RGB to YUV Conversion */ + {VOUT_SET0, 0x0002,0}, + {VOUT_SET2, MASK_VOUTCOLORMODE_AUTO | MASK_SEL422 | MASK_VOUT_422FIL_100, 0}, + {VOUT_SET3, MASK_VOUT_EXTCNT, 0}, + {VI_REP, MASK_VOUT_COLOR_601_YCBCR_LIMITED, 0}, + + /* InfoFrame extraction */ + {PK_INT_MODE, 0x00ff, 0}, + {NO_PKT_LIMIT, 0x002c, 0}, + {NO_PKT_CLR, 0x0053, 0}, + {ERR_PK_LIMIT, 0x0001, 0}, + {NO_PKT_LIMIT2, 0x0030, 0}, + {NO_GDB_LIMIT, 0x0010, 0}, + {INIT_END, MASK_INIT_END, 0}, +}; + +enum { + TC358743_MODE_640x480, + TC358743_MODE_800x600, + TC358743_MODE_1024x768, + TC358743_MODE_1280x720, + TC358743_MODE_1280x1024, + TC358743_MODE_1680x1050, + TC358743_MODE_1920x1080, + TC358743_SIZE_LAST, +}; + +#define to_tc358743(sd) container_of(sd, struct tc358743_priv, subdev) + +struct tc358743_priv { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt mf; + int ident; + u16 chip_id; + u8 revision; + int mode; + struct i2c_client *client; +}; + +static enum v4l2_mbus_pixelcode tc358743_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static const struct v4l2_frmsize_discrete tc358743_frmsizes[TC358743_SIZE_LAST] = { + {640, 480}, + {800, 600}, + {1024, 768}, + {1280, 720}, + {1280, 1024}, + {1680, 1050}, + {1920, 1080}, +}; + +/* EDID taken from BENQ G2220HD display */ +static u8 hdmi_edid[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x09, 0xd1, 0x21, 0x78, 0x45, 0x54, 0x00, 0x00, + 0x26, 0x14, 0x01, 0x03, 0x80, 0x30, 0x1b, 0x78, 0x2e, 0x35, 0x81, 0xa6, 0x56, 0x48, 0x9a, 0x24, + 0x12, 0x50, 0x54, 0xa5, 0x6b, 0x80, 0x71, 0x00, 0x81, 0xc0, 0x81, 0x40, 0x81, 0x80, 0xa9, 0xc0, + 0xb3, 0x00, 0xd1, 0xc0, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xdd, 0x0c, 0x11, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xff, 0x00, 0x48, 0x39, 0x41, + 0x30, 0x30, 0x34, 0x35, 0x35, 0x53, 0x4c, 0x30, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x4c, 0x18, 0x53, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x42, 0x65, 0x6e, 0x51, 0x20, 0x47, 0x32, 0x32, 0x32, 0x30, 0x48, 0x44, 0x0a, 0x00, 0xd7, +}; + +static int tc358743_find_mode(u32 width, u32 height) +{ + int i; + + for (i = 0; i < TC358743_SIZE_LAST; i++) { + if ((tc358743_frmsizes[i].width >= width) && + (tc358743_frmsizes[i].height >= height)) + break; + } + + /* If not found, select the biggest */ + if (i >= TC358743_SIZE_LAST) + i = TC358743_SIZE_LAST - 1; + + return i; +} + +static int get_register_size(u16 reg) { + int i = 0; + int size = 0; + while(0 != tc358743_read_reg_size[i].startaddr || + 0 != tc358743_read_reg_size[i].endaddr || + 0 != tc358743_read_reg_size[i].size) + { + if(tc358743_read_reg_size[i].startaddr <= reg && tc358743_read_reg_size[i].endaddr >= reg) + { + size = tc358743_read_reg_size[i].size; + break; + } + i++; + } + if (size == 0) { + printk(KERN_ERR "Unkown register 0x%04x size!!", reg); + } + return size; +} + +static uint32_t i2c_rd(struct i2c_client *client, u16 reg) +{ + int err; + uint32_t result = 0; + uint8_t *values = (uint8_t*)&result; + u8 buf[2] = { reg >> 8, reg & 0xff }; + int n = get_register_size(reg); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = n, + .buf = values, + }, + }; + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) { + pr_err("%s: reading register 0x%x from 0x%x failed\n", __func__, reg, client->addr); + } + return result; +} + +static int i2c_wr(struct i2c_client *client, u16 reg, u32 val) +{ + int i = 0; + u32 data = val; + u8 au8Buf[6] = {0}; + int size = get_register_size(reg); + int len = get_register_size(reg); + + if(!size) { + pr_err("%s:write reg error:reg=%x is not found\n",__func__, reg); + return -1; + } + + if(size == 3) { + size = 2; + } else if(size != len) { + pr_err("%s:write reg len error:reg=%x %d instead of %d\n", + __func__, reg, len, size); + return 0; + } + + while(len > 0) { + i = 0; + au8Buf[i++] = (reg >> 8) & 0xff; + au8Buf[i++] = reg & 0xff; + while(size-- > 0) { + au8Buf[i++] = (u8)data; + data >>= 8; + } + + if (i2c_master_send(client, au8Buf, i) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + len -= (u8)size; + reg += (u16)size; + } + + return 0; +} + +static int tc358743_set_pll(struct i2c_client *client, uint32_t frequency) +{ + int ret = 0; + + timings_regs timings = tc358743_get_best_timings(frequency); + if (timings.frequency == 0) return 1; + + printk(KERN_DEBUG "Setting pll to frequency %d (%d) MHz\n", + frequency, timings.frequency); + + ret += i2c_wr(client, FIFOCTL, timings.fifo_delay); + + /* Disable all interrupts except the hdmi-rx */ + ret += i2c_wr(client, INTSTATUS, 0x0); + ret += i2c_wr(client, INTMASK, 0x5ff); + + ret += i2c_wr(client, PLLCTL0, SET_PLL_FREQ(frequency)); + + ret += i2c_wr(client, PLLCTL1, + SET_PLL_FRS(GET_FRS(frequency), FRS_BW_50) | MASK_RESETB | MASK_PLL_EN); + mdelay(1); + ret += i2c_wr(client, PLLCTL1, + SET_PLL_FRS(GET_FRS(frequency), FRS_BW_50) | MASK_RESETB | MASK_PLL_EN | MASK_CKEN); + + ret += i2c_wr(client, LINEINITCNT, timings.line_init_cnt); + ret += i2c_wr(client, LPTXTIMECNT, timings.tlpx_time_cnt); + + ret += i2c_wr(client, TCLK_HEADERCNT, + CALC_HDR_CNT(timings.tclk_prepare_cnt, timings.tclk_zero_cnt)); + + ret += i2c_wr(client, TCLK_TRAILCNT, timings.tclk_trail_cnt); + + ret += i2c_wr(client, THS_HEADERCNT, + CALC_HDR_CNT(timings.ths_prepare_cnt, timings.ths_zero_cnt)); + + ret += i2c_wr(client, TWAKEUP, timings.twakeup_cnt); + ret += i2c_wr(client, TCLK_POSTCNT, timings.tclk_post_cnt); + ret += i2c_wr(client, THS_TRAILCNT, timings.ths_trail_cnt); + + return ret; +} + +static int tc358743_write_table(struct i2c_client *client, struct reg_value table[], int table_length) +{ + u32 lines_to_repeat = 0; + u32 repeats = 0; + u32 delay_ms = 0; + u16 addr = 0; + u32 val = 0; + int ret = 0; + int i; + + for (i = 0; i < table_length; ++i) { + addr = table[i].addr; + val = table[i].val; + delay_ms = (table[i].flags & 0xffff); + + ret = i2c_wr(client, addr, val); + if (ret < 0) + break; + + if (delay_ms) + msleep(delay_ms); + + if(((table[i].flags >> 16) & (0xff)) != 0) { + if(!repeats) { + repeats = ((table[i].flags >> 16) & 0xff); + lines_to_repeat = ((table[i].flags >> 24) & 0xff); + } + if(--repeats > 0) { + i -= lines_to_repeat; + } + } + } + + return ret; +} + +static int tc358743_write_edid(struct i2c_client *client, u8 *edid, int len) +{ + int i = 0, off = 0; + u8 au8Buf[8+2] = {0}; + int size = 0; + u16 reg; + + reg = 0x8C00; + off = 0; + size = ARRAY_SIZE(au8Buf)-2; + printk(KERN_DEBUG "Write EDID: %d (%d)\n", len, size); + while(len > 0) + { + i = 0; + au8Buf[i++] = (reg >> 8) & 0xff; + au8Buf[i++] = reg & 0xff; + while(i < ARRAY_SIZE(au8Buf)) + { + au8Buf[i++] = edid[off++]; + } + + if (i2c_master_send(client, au8Buf, i) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, off); + return -1; + } + len -= (u8)size; + reg += (u16)size; + } + printk(KERN_DEBUG "Activate EDID\n"); + i2c_wr(client, EDID_MODE, MASK_EDID_MODE_DDC2B); + i2c_wr(client, EDID_LEN1, 0x00); + i2c_wr(client, EDID_LEN2, 0x01); + return 0; +} + +static int tc358743_toggle_hpd(struct i2c_client *client, int active) +{ + int ret = 0; + + if(active) + { + ret += i2c_wr(client, HPD_CTL, 0x00); + mdelay(500); + ret += i2c_wr(client, HPD_CTL, MASK_HPD_CTL0); + } else { + ret += i2c_wr(client, HPD_CTL, MASK_HPD_CTL0); + mdelay(500); + ret += i2c_wr(client, HPD_CTL, 0x00); + } + return ret; +} + +static void tc358743_set_default_fmt(struct tc358743_priv *priv) +{ + struct v4l2_mbus_framefmt *mf = &priv->mf; + + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = V4L2_COLORSPACE_SRGB; +} + +/* Start/Stop streaming from the device */ +static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct tc358743_priv *priv = to_tc358743(sd); + + if (!enable) { + printk(KERN_DEBUG "Disabling stream"); + i2c_wr(priv->client, CONFCTL, 0); + return 0; + } + + return 0; +} + +static int tc358743_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + int mode; + + mode = tc358743_find_mode(mf->width, mf->height); + mf->width = tc358743_frmsizes[mode].width; + mf->height = tc358743_frmsizes[mode].height; + mf->field = V4L2_FIELD_NONE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + + +struct aviInfoFrame { + u8 f17; + u8 y10; + u8 a0; + u8 b10; + u8 s10; + u8 c10; + u8 m10; + u8 r3210; + u8 itc; + u8 ec210; + u8 q10; + u8 sc10; + u8 f47; + u8 vic; + u8 yq10; + u8 cn10; + u8 pr3210; + u16 etb; + u16 sbb; + u16 elb; + u16 srb; +}; + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + struct tc358743_priv *priv = to_tc358743(sd); + return i2c_rd(priv->client, SYS_STATUS) & MASK_S_HDMI; +} + +/* set the format we will capture in */ +static int tc358743_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct tc358743_priv *priv = to_tc358743(sd); + int ret; + uint32_t v2, v, width, height, v_size, h_size, fv_cnt, fps; + + ret = tc358743_try_fmt(sd, mf); + if (ret < 0) + return ret; + + priv->mode = tc358743_find_mode(mf->width, mf->height); + + memcpy(&priv->mf, mf, sizeof(struct v4l2_mbus_framefmt)); + + tc358743_write_table(priv->client, tc358743_reset, ARRAY_SIZE(tc358743_reset)); + tc358743_write_table(priv->client, tc358743_setting_hdmi, ARRAY_SIZE(tc358743_setting_hdmi)); + msleep(100); + + v = i2c_rd(priv->client, DE_WIDTH_H_LO); + v2 = i2c_rd(priv->client, DE_WIDTH_H_HI) & 0x1f; + width = (v2 << 8) + v; + v = i2c_rd(priv->client, DE_WIDTH_V_LO); + v2 = i2c_rd(priv->client, DE_WIDTH_V_HI) & 0x1f; + height = (v2 << 8) + v; + v = i2c_rd(priv->client, H_SIZE_LO); + v2 = i2c_rd(priv->client, H_SIZE_HI) & 0x1f; + h_size = (v2 << 8) + v; + v = i2c_rd(priv->client, V_SIZE_LO); + v2 = i2c_rd(priv->client, V_SIZE_HI) & 0x3f; + v_size = ((v2 << 8) + v) / 2; + v = i2c_rd(priv->client, FV_CNT_LO); + v2 = i2c_rd(priv->client, FV_CNT_HI) & 0x3; + fv_cnt = (v2 << 8) + v; + fps = fv_cnt > 0 ? DIV_ROUND_CLOSEST(10000, fv_cnt) : 0; + + printk(KERN_DEBUG "Image is %dx%d@%d, ~%d Gbps bandwidth (~%dMHz/lane)", + width, height, fps, + DIV_ROUND_UP(fps*width*height*16, 1000*1000), + DIV_ROUND_UP(fps*width*height*8, 1000*1000)); + + if (width * fps == 0) { + printk(KERN_ERR "width or fps 0"); + return 0; + } + + if ((width != mf->width)) { + printk(KERN_ERR "Wrong width (%d vs %d)", width, mf->width); + return 0; + } + + /* XXX the chip sometimes miscalculates the height of the image, + * therefore, we base the output mostly on the width of the image */ + if (width == 1920) { + /* works for 1920x1080 @ 60 */ + ret = tc358743_set_pll(priv->client, 1075); + /* 872 is in the middle between 1024 and 720, should be a safe + * value for both 1280x720 and 1280x1024 */ + } else if (width == 1280 && height >= 872) { + /* works on 1280x1024 @ 75 */ + ret = tc358743_set_pll(priv->client, 1075); + } else if (width == 1280 && height < 872) { + /* works on 1280x720 @ 60 */ + ret = tc358743_set_pll(priv->client, 825); + } else if (width == 1024) { + /* works on 1024x768 @ 75 */ + ret = tc358743_set_pll(priv->client, 600); + } else if (width >= 800) { + /* works for 800x600@75 */ + ret = tc358743_set_pll(priv->client, 594); + } else if (width == 640) { + /* works on 640x480 @ 75 */ + ret = tc358743_set_pll(priv->client, 500); + } else { + return 0; + } + + tc358743_write_table(priv->client, + tc358743_2lanes_start, + ARRAY_SIZE(tc358743_2lanes_start)); + + return ret; +} + +static int tc358743_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(tc358743_codes)) + return -EINVAL; + + *code = tc358743_codes[index]; + + return 0; +} + +/* Get chip identification */ +static int tc358743_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct tc358743_priv *priv = to_tc358743(sd); + + id->ident = priv->ident; + id->revision = priv->revision; + + return 0; +} + +static int tc358743_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_subdev_desc *scsd = soc_camera_i2c_to_desc(client); + + return soc_camera_set_power(&client->dev, scsd, on); +} + +static struct v4l2_subdev_video_ops tc358743_video_ops = { + .s_stream = tc358743_s_stream, + .s_mbus_fmt = tc358743_s_fmt, + .try_mbus_fmt = tc358743_try_fmt, + .enum_mbus_fmt = tc358743_enum_fmt, +}; + +static struct v4l2_subdev_core_ops tc358743_core_ops = { + .g_chip_ident = tc358743_g_chip_ident, + .s_power = tc358743_s_power, +}; + +static struct v4l2_subdev_ops tc358743_subdev_ops = { + .core = &tc358743_core_ops, + .video = &tc358743_video_ops, +}; + +/* + * i2c_driver function + */ +static int tc358743_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct tc358743_priv *priv; + struct soc_camera_subdev_desc *scsd; + int ret = 0; + + /* Checking soc-camera interface */ + scsd = client->dev.platform_data; + if (!scsd) { + dev_err(&client->dev, "Missing soc_camera_link for driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&client->dev, sizeof(struct tc358743_priv), + GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "Failed to allocate private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &tc358743_subdev_ops); + + priv->client = client; + priv->ident = V4L2_IDENT_OV5640; + + /* + * check and show product ID and manufacturer ID + */ + soc_camera_power_on(&client->dev, scsd); + + tc358743_set_default_fmt(priv); + tc358743_write_table(client, tc358743_reset, ARRAY_SIZE(tc358743_reset)); + tc358743_write_table(client, tc358743_setting_hdmi, ARRAY_SIZE(tc358743_setting_hdmi)); + + if((ret = tc358743_write_edid(client, hdmi_edid, ARRAY_SIZE(hdmi_edid)))) + printk(KERN_ERR "%s: Fail to write EDID to tc35874!\n", __FUNCTION__); + + tc358743_toggle_hpd(client, 1); + + soc_camera_power_off(&client->dev, scsd); + + return ret; +} + +static int tc358743_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tc358743_id[] = { + { "tc358743", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc358743_id); + +static struct i2c_driver tc358743_i2c_driver = { + .driver = { + .name = "tc358743", + .owner = THIS_MODULE, + }, + .probe = tc358743_probe, + .remove = tc358743_remove, + .id_table = tc358743_id, +}; + +static int __init tc358743_module_init(void) +{ + return i2c_add_driver(&tc358743_i2c_driver); +} + +static void __exit tc358743_module_exit(void) +{ + i2c_del_driver(&tc358743_i2c_driver); +} + +module_init(tc358743_module_init); +module_exit(tc358743_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for Toshiba TC358743 HDMI to CSI-2 bridge"); +MODULE_AUTHOR("Wojciech Bieganski <wbieganski@antmicro.com>"); +MODULE_LICENSE("GPL v2"); |