summaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig7
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/adv7180.c172
-rw-r--r--drivers/media/video/max9526.c1102
-rw-r--r--drivers/media/video/tegra_v4l2_camera.c1198
5 files changed, 1785 insertions, 695 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index dfff30255a92..0fe628f5912d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -60,7 +60,6 @@ config VIDEOBUF2_VMALLOC
select VIDEOBUF2_MEMOPS
tristate
-
config VIDEOBUF2_DMA_SG
#depends on HAS_DMA
select VIDEOBUF2_CORE
@@ -797,6 +796,12 @@ config SOC_CAMERA_IMX074
help
This driver supports IMX074 cameras from Sony
+config SOC_CAMERA_MAX9526
+ tristate "max9526 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MAX9526 video decoders from Maxim Integrated
+
config SOC_CAMERA_MT9M001
tristate "mt9m001 support"
depends on SOC_CAMERA && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ce79916258ef..28362059e105 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
+obj-$(CONFIG_SOC_CAMERA_MAX9526) += max9526.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index d2138d06bcad..14b48edb39b2 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -27,13 +27,20 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
#include <linux/mutex.h>
#define DRIVER_NAME "adv7180"
#define ADV7180_INPUT_CONTROL_REG 0x00
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN1 0x00
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN2 0x01
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN3 0x02
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN4 0x03
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN5 0x04
+#define ADV7180_INPUT_CONTROL_COMPOSITE_IN6 0x05
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_M_SECAM 0x10
#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
@@ -71,7 +78,8 @@
#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
#define ADV7180_IDENT_REG 0x11
-#define ADV7180_ID_7180 0x18
+#define ADV7180_ID_7180 0x1C /* 64-lead and 40-lead models only */
+#define ADV7180_ID2_7180 0x1E /* 48-lead and 32-lead devices only */
#define ADV7180_ICONF1_ADI 0x40
#define ADV7180_ICONF1_ACTIVE_LOW 0x01
@@ -97,6 +105,7 @@ struct adv7180_state {
int irq;
v4l2_std_id curr_norm;
bool autodetect;
+ int active_input;
};
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
@@ -224,14 +233,18 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
if (std == V4L2_STD_ALL) {
ret = i2c_smbus_write_byte_data(client,
ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM |
+ (ADV7180_INPUT_CONTROL_COMPOSITE_IN1 +
+ state->active_input));
if (ret < 0)
goto out;
__adv7180_status(client, NULL, &state->curr_norm);
state->autodetect = true;
} else {
- ret = v4l2_std_to_adv7180(std);
+ ret = v4l2_std_to_adv7180(std) |
+ (ADV7180_INPUT_CONTROL_COMPOSITE_IN1 +
+ state->active_input);
if (ret < 0)
goto out;
@@ -249,19 +262,99 @@ out:
return ret;
}
+static int adv7180_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+/* Request bus settings on camera side */
+static unsigned long adv7180_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 adv7180_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+};
+
+static int adv7180_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
+// struct i2c_client *client = v4l2_get_subdevdata(sd);
+// u8 status1;
+
+// status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+// printk(KERN_ERR "*********************************** status1 = 0x%02x\n", status1);
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mf->code = code;
+ mf->colorspace = cspace;
+
+ return adv7180_s_std(sd, V4L2_STD_ALL);
+}
+
+static int adv7180_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ mf->field = V4L2_FIELD_INTERLACED_TB;
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ // PAL
+ mf->width = 720;
+ mf->height = 576;
+
+ return 0;
+}
+
+static int adv7180_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(adv7180_codes))
+ return -EINVAL;
+
+ *code = adv7180_codes[index];
+
+ return 0;
+}
+
+static struct soc_camera_ops adv7180_ops = {
+ .set_bus_param = adv7180_set_bus_param,
+ .query_bus_param = adv7180_query_bus_param,
+};
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
- .querystd = adv7180_querystd,
- .g_input_status = adv7180_g_input_status,
+ .s_mbus_fmt = adv7180_s_fmt,
+ .try_mbus_fmt = adv7180_try_fmt,
+ .enum_mbus_fmt = adv7180_enum_fmt,
+ .querystd = adv7180_querystd,
+ .g_input_status = adv7180_g_input_status,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
- .g_chip_ident = adv7180_g_chip_ident,
- .s_std = adv7180_s_std,
+ .g_chip_ident = adv7180_g_chip_ident,
+ .s_std = adv7180_s_std,
};
-static const struct v4l2_subdev_ops adv7180_ops = {
- .core = &adv7180_core_ops,
- .video = &adv7180_video_ops,
+static const struct v4l2_subdev_ops adv7180_subdev_ops = {
+ .core = &adv7180_core_ops,
+ .video = &adv7180_video_ops,
};
static void adv7180_work(struct work_struct *work)
@@ -297,6 +390,36 @@ static irqreturn_t adv7180_irq(int irq, void *devid)
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 adv7180_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,
+ ADV7180_INPUT_CONTROL_REG);
+ val &= 0xf0;
+ val |= (ADV7180_INPUT_CONTROL_COMPOSITE_IN1 +
+ state->active_input);
+ return i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG, 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 adv7180_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'
@@ -306,8 +429,11 @@ static __devinit int adv7180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7180_state *state;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct v4l2_subdev *sd;
+ u8 ident;
int ret;
+ struct v4l2_ioctl_ops *ops;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -322,17 +448,25 @@ static __devinit int adv7180_probe(struct i2c_client *client,
goto err;
}
+ ident = i2c_smbus_read_byte_data(client, ADV7180_IDENT_REG);
+ WARN_ON((ident != ADV7180_ID_7180) && (ident != ADV7180_ID2_7180));
+ v4l_info(client, "ident reg is 0x%02x\n", ident);
+
state->irq = client->irq;
INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
+ state->active_input = 0; // input 1
sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
+ v4l2_i2c_subdev_init(sd, client, &adv7180_subdev_ops);
+ icd->ops = &adv7180_ops;
/* Initialize adv7180 */
/* Enable autodetection */
ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM |
+ (ADV7180_INPUT_CONTROL_COMPOSITE_IN1 +
+ state->active_input));
if (ret < 0)
goto err_unreg_subdev;
@@ -393,6 +527,15 @@ static __devinit int adv7180_probe(struct i2c_client *client,
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:
@@ -432,7 +575,7 @@ static const struct i2c_device_id adv7180_id[] = {
{},
};
-MODULE_DEVICE_TABLE(i2c, adv7180_id);
+//MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_driver = {
.driver = {
@@ -460,4 +603,3 @@ module_exit(adv7180_exit);
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/video/max9526.c b/drivers/media/video/max9526.c
new file mode 100644
index 000000000000..1f13dcb29c5a
--- /dev/null
+++ b/drivers/media/video/max9526.c
@@ -0,0 +1,1102 @@
+/*
+ * drivers/media/video/max9526.c
+ *
+ * MAXIM MAX9526 decoder driver
+ *
+ * Copyright (c) 2011 Ming-Yao Chen <mychen0518@gmail.com>
+ * (based on tvp514x.c)
+ *
+ * Copyright (c) 2013 Ant Micro <www.antmicro.com>
+ *
+ * This package 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/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+
+/*MODULE NAME*/
+#define MAX9526_MODULE_NAME "max9526"
+
+/*Private macros for MAX9526*/
+
+#define LOCK_RETRY_DELAY (200)
+#define LOCK_RETRY_COUNT (5)
+#define I2C_RETRY_COUNT (5)
+
+
+ /* registers */
+#define REG_STATUS_0 0x00
+#define REG_STATUS_1 0x01
+#define REG_IRQMASK_0 0x02
+#define REG_IRQMASK_1 0x03
+#define REG_STANDARD_SELECT_SHUTDOWN_CONTROL 0x04
+#define REG_CONTRAST 0x05
+#define REG_BRIGHTNESS 0x06
+#define REG_HUE 0x07
+#define REG_SATURATION 0x08
+#define REG_VIDEO_INPUT_SELECT_AND_CLAMP 0x09
+#define REG_GAIN_CONTROL 0x0A
+#define REG_COLOR_KILL 0x0B
+#define REG_OUTPUT_TEST_SIGNAL 0x0C
+#define REG_CLOCK_AND_OUTPUT 0x0D
+#define REG_PLL_CONTROL 0x0E
+#define REG_MISCELLANEOUS 0x0F
+
+#define PAL_NUM_ACTIVE_PIXELS (720)
+#define PAL_NUM_ACTIVE_LINES (576)
+
+#define NTSC_NUM_ACTIVE_PIXELS (720)
+#define NTSC_NUM_ACTIVE_LINES (480)
+
+#define REG_VIDEO_INPUT_SELECT_IN1 0x00
+#define REG_VIDEO_INPUT_SELECT_IN2 0x40
+#define REG_VIDEO_INPUT_SELECT_AUTO 0x80
+
+
+struct max9526_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+enum max9526_tokens {
+ TOK_TERM,
+ TOK_SKIP,
+ TOK_DELAY,
+ TOK_WRITE,
+};
+
+enum {
+ VIDEO_STDSEL_NTSC_M_BIT,
+ VIDEO_STDSEL_PAL_BGHID_BIT,
+};
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("MAX9526 linux decoder driver");
+MODULE_LICENSE("GPL");
+
+
+/* enum max9526_std - enum for supported standards*/
+
+enum max9526_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+
+/**
+ * struct max9526_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+
+
+struct max9526_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+// TODO: redo this
+static const struct max9526_reg max9526_reg_list_default[] = {
+ {TOK_SKIP , REG_STATUS_0, 0x84},
+ {TOK_SKIP , REG_STATUS_1, 0x40},
+ {TOK_SKIP , REG_IRQMASK_0, 0x00},
+ {TOK_SKIP , REG_IRQMASK_1, 0x00},
+ /*Standard Select, Shutdown, and Control Register*/
+ {TOK_WRITE, REG_STANDARD_SELECT_SHUTDOWN_CONTROL, 0x10}, // was 0x10 (autodetect), 0x0=PAL, 0x40=NTSC
+ {TOK_SKIP, REG_CONTRAST, 0x80},
+ {TOK_SKIP, REG_BRIGHTNESS, 0x00},
+ {TOK_SKIP, REG_HUE, 0x80},
+ {TOK_SKIP, REG_SATURATION, 0x88},
+ {TOK_WRITE, REG_VIDEO_INPUT_SELECT_AND_CLAMP, 0x80}, // auto-select
+ // between input 1 and 2
+ {TOK_SKIP, REG_GAIN_CONTROL, 0x00},
+ {TOK_SKIP, REG_COLOR_KILL, 0x23},
+ {TOK_WRITE, REG_OUTPUT_TEST_SIGNAL, 0x03}, // select 100% color bars
+ {TOK_WRITE, REG_CLOCK_AND_OUTPUT, 0x04}, // select HSVS
+ {TOK_SKIP, REG_PLL_CONTROL, 0x03},
+ {TOK_SKIP, REG_MISCELLANEOUS, 0x18},
+ {TOK_TERM, 0, 0},
+};
+
+
+//static struct max9526_reg max9526_reg_list_default[0x11];
+
+/*MAX9526 default register values*/
+
+static int max9526_s_stream(struct v4l2_subdev *sd, int enable);
+
+/**
+ * struct max9526_decoder - MAX9526 decoder object
+ * @sd: Subdevice Slave handle
+ * @max9526_regs: copy of hw's regs with preset values.
+ * @pdata: Board specific
+ * @ver: Chip version
+ * @streaming: MAX9526 decoder streaming - enabled or disabled.
+ * @pix: Current pixel format
+ * @num_fmts: Number of formats
+ * @fmt_list: Format list
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @input: Input routing at chip level
+ * @output: Output routing at chip level
+ */
+struct max9526_decoder {
+ struct v4l2_subdev sd;
+ struct max9526_reg max9526_regs[ARRAY_SIZE(max9526_reg_list_default)];
+ const struct max9526_platform_data *pdata;
+
+ int ver;
+ int streaming;
+
+ struct v4l2_pix_format pix;
+ int num_fmts;
+ const struct v4l2_fmtdesc *fmt_list;
+
+ enum max9526_std current_std;
+ int num_stds;
+ struct max9526_std_info *std_list;
+ /* Input and Output Routing parameters */
+ u32 input;
+ u32 output;
+
+ int active_input;
+};
+
+/**
+ * List of image formats supported by max9526 decoder
+ * Currently we are using 8 bit mode only, but can be
+ * extended to 10/20 bit mode.
+ */
+static const struct v4l2_fmtdesc max9526_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit YUYV 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+/**
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static struct max9526_std_info max9526_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STDSEL_NTSC_M_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STDSEL_PAL_BGHID_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+
+static inline struct max9526_decoder *to_decoder(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct max9526_decoder, sd);
+}
+
+/**
+ * max9526_read_reg() - Read a value from a register in an MAX9526.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: max9526 register address
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int max9526_read_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err < 0) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+#if 0
+/**
+ * dump_reg() - dump the register content of MAX9526.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: MAX9526 register address
+ */
+static void dump_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ u32 val;
+
+ val = max9526_read_reg(sd, reg);
+ v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val);
+}
+#endif
+
+/**
+ * max9526_write_reg() - Write a value to a register in MAX9526
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: MAX9526 register address
+ * @val: value to be written to the register
+ *
+ * Write a value to a register in an MAX9526 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int max9526_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+// if (!err) {
+// v4l2_warn(sd, "max9526: wrote %X to register %X.\n", val, reg);
+// }
+ return err;
+}
+
+
+/**
+ * max9526_write_regs() : Initializes a list of MAX9526 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @reglist: list of MAX9526 registers and values
+ *
+ * Initializes a list of MAX9526 registers: token is state flag
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int max9526_write_regs(struct v4l2_subdev *sd,
+ const struct max9526_reg reglist[])
+{
+ int err;
+ const struct max9526_reg *next = reglist;
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = max9526_write_reg(sd, next->reg, (u8) next->val);
+ if (err) {
+ v4l2_err(sd, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * max9526_get_current_std() : Get the current standard detected by max9526
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Get current standard detected by MAX9526, STD_INVALID if there is no
+ * standard detected.
+ */
+static enum max9526_std max9526_get_current_std(struct v4l2_subdev *sd)
+{
+ u8 std, std_status;
+
+ std = max9526_read_reg(sd, REG_STANDARD_SELECT_SHUTDOWN_CONTROL);
+
+ std_status = std>>5;
+ switch (std_status) {
+ case VIDEO_STDSEL_NTSC_M_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STDSEL_PAL_BGHID_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+
+/**
+ * max9526_configure() - Configure the MAX9526 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @decoder: ptr to max9526_decoder structure
+ *
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int max9526_configure(struct v4l2_subdev *sd, struct max9526_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ max9526_write_regs(sd, decoder->max9526_regs);
+ if (err)
+ return err;
+
+// if (debug)
+// max9526_reg_dump(sd);
+
+ return 0;
+}
+
+
+/**
+ * max9526_querystd() - V4L2 decoder interface handler for querystd
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by MAX9526. If no active input is
+ * detected, returns -EINVAL
+ */
+static int max9526_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+ enum max9526_std current_std;
+ //enum max9526_input input_sel;
+ //u8 sync_lock_status, lock_mask;
+ //int err;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ msleep(LOCK_RETRY_DELAY);
+
+ /* get the current standard */
+ current_std = max9526_get_current_std(sd);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ *std_id = decoder->std_list[current_std].standard.id;
+
+// v4l2_dbg(1, debug, sd, "Current STD: %s",
+// decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * max9526_s_std() - V4L2 decoder interface handler for s_std
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int max9526_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+ int err, i;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = max9526_write_reg(sd, REG_STANDARD_SELECT_SHUTDOWN_CONTROL,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ decoder->max9526_regs[REG_STANDARD_SELECT_SHUTDOWN_CONTROL].val =
+ decoder->std_list[i].video_std;
+
+// v4l2_dbg(1, debug, sd, "Standard set to: %s",
+// decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * max9526_s_routing() - V4L2 decoder interface handler for s_routing
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @input: input selector for routing the signal
+ * @output: output selector for routing the signal
+ * @config: config value. Not used
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int max9526_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ //struct max9526_decoder *decoder = to_decoder(sd);
+
+ /*
+ * For the sequence streamon -> streamoff and again s_input, most of
+ * the time it fails to lock the signal, since streamoff puts MAX9526
+ * into power off state which leads to failure in sub-sequent s_input.
+ */
+ max9526_s_stream(sd, 1);
+ return 0;
+}
+
+
+/**
+ * max9526_queryctrl() - V4L2 decoder interface handler for queryctrl
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @qctrl: standard V4L2 v4l2_queryctrl structure
+ *
+ * If the requested control is supported, returns the control information.
+ * Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+max9526_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+{
+ int err = -EINVAL;
+
+ if (qctrl == NULL)
+ return err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* Brightness supported is (0-255), */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ /**
+ * Saturation and Contrast supported is -
+ * Contrast: 0 - 255 (Default - 128)
+ * Saturation: 0 - 255 (Default - 128)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue Supported is -
+ * Hue - -180 - +180 (Default - 0, Step - +180)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /**
+ * Auto Gain supported is -
+ * 0 - 1 (Default - 1)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
+ break;
+ default:
+ v4l2_err(sd, "invalid control id %d\n", qctrl->id);
+ return err;
+ }
+
+// v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d",
+// qctrl->name, qctrl->minimum, qctrl->maximum,
+// qctrl->default_value);
+
+ return err;
+}
+
+
+/**
+ * max9526_g_ctrl() - V4L2 decoder interface handler for g_ctrl
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the decoder. Otherwise, returns -EINVAL if the control is not
+ * supported.
+ */
+static int
+max9526_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = decoder->max9526_regs[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = decoder->max9526_regs[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = decoder->max9526_regs[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = decoder->max9526_regs[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+ ctrl->value = -180;
+ else
+ ctrl->value = 0;
+
+ break;
+ default:
+ v4l2_err(sd, "invalid control id %d\n", ctrl->id);
+ return -EINVAL;
+ }
+
+// v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d",
+// ctrl->id, ctrl->value);
+ return 0;
+}
+
+/**
+ * max9526_s_ctrl() - V4L2 decoder interface handler for s_ctrl
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+max9526_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+ int err = -EINVAL, value;
+
+ if (ctrl == NULL)
+ return err;
+
+ value = ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l2_err(sd, "invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = max9526_write_reg(sd, REG_BRIGHTNESS,
+ value);
+ if (err)
+ return err;
+
+ decoder->max9526_regs[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l2_err(sd, "invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = max9526_write_reg(sd, REG_CONTRAST, value);
+ if (err)
+ return err;
+
+ decoder->max9526_regs[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l2_err(sd, "invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = max9526_write_reg(sd, REG_SATURATION, value);
+ if (err)
+ return err;
+
+ decoder->max9526_regs[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ else if (value == 0)
+ value = 0;
+ else {
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+ err = max9526_write_reg(sd, REG_HUE, value);
+ if (err)
+ return err;
+
+ decoder->max9526_regs[REG_HUE].val = value;
+ break;
+ default:
+ v4l2_err(sd, "invalid control id %d\n", ctrl->id);
+ return err;
+ }
+
+// v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d",
+// ctrl->id, ctrl->value);
+
+ return err;
+}
+
+
+static int max9526_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+
+ if (index < 0 || index >= decoder->num_fmts)
+ return -EINVAL;
+ switch (index) {
+ case 0:
+ *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ break;
+ case 1:
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int max9526_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+/* pr_info("%s width:%d\n", __func__, mf->width);
+ pr_info("%s height:%d\n", __func__, mf->height);
+ pr_info("%s field:0x%X (V4L2_FIELD_NONE==0x%X)\n", __func__, mf->field, V4L2_FIELD_NONE);
+ pr_info("%s code:0x%X (V4L2_MBUS_FMT_YUYV8_2X8==0x%X)\n", __func__, mf->code, V4L2_MBUS_FMT_YUYV8_2X8);
+ pr_info("%s colorspace:0x%X (V4L2_COLORSPACE_SRGB==0x%X)\n", __func__, mf->colorspace, V4L2_COLORSPACE_SRGB);*/
+
+ if (mf->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ mf->width = PAL_NUM_ACTIVE_PIXELS;
+ mf->height = PAL_NUM_ACTIVE_LINES;
+ } else if (mf->code == V4L2_MBUS_FMT_YUYV8_2X8) {
+ mf->width = PAL_NUM_ACTIVE_PIXELS;
+ mf->height = PAL_NUM_ACTIVE_LINES;
+ }
+ mf->field = V4L2_FIELD_INTERLACED_TB;
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+
+/**
+ * max9526_g_parm() - V4L2 decoder interface handler for g_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+max9526_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+ struct v4l2_captureparm *cparm;
+ enum max9526_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* get the current standard */
+ current_std = max9526_get_current_std(sd);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+
+/**
+ * max9526_s_parm() - V4L2 decoder interface handler for s_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+max9526_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct max9526_decoder *decoder = to_decoder(sd);
+ struct v4l2_fract *timeperframe;
+ enum max9526_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = max9526_get_current_std(sd);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * max9526_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int max9526_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ int err = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct max9526_decoder *decoder = to_decoder(sd);
+
+ if (decoder->streaming == enable)
+ return 0;
+
+ switch (enable) {
+ case 0:
+ {
+ decoder->streaming = enable;
+ break;
+ }
+ case 1:
+ {
+ struct max9526_reg *int_seq = (struct max9526_reg *)client->driver->id_table->driver_data;
+
+ err = max9526_write_regs(sd, int_seq);
+ if (err) {
+ v4l2_err(sd, "Unable to turn on decoder\n");
+ return err;
+ }
+ err = max9526_configure(sd, decoder);
+ if (err) {
+ v4l2_err(sd, "Unable to configure decoder\n");
+ return err;
+ }
+ decoder->streaming = enable;
+ break;
+ }
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+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 max9526_decoder *decoder = to_decoder(sd);
+ u8 val;
+
+ if (i < 3) {
+ decoder->active_input = i;
+ switch (decoder->active_input) {
+ case 0:
+ val = REG_VIDEO_INPUT_SELECT_IN1;
+ break;
+ case 1:
+ val = REG_VIDEO_INPUT_SELECT_IN2;
+ break;
+ default:
+ val = REG_VIDEO_INPUT_SELECT_AUTO;
+ }
+ return max9526_write_reg(sd, REG_VIDEO_INPUT_SELECT_AND_CLAMP,
+ 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 max9526_decoder *decoder = to_decoder(sd);
+
+ *i = decoder->active_input;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops max9526_core_ops = {
+ .queryctrl = max9526_queryctrl,
+ .g_ctrl = max9526_g_ctrl,
+ .s_ctrl = max9526_s_ctrl,
+ .s_std = max9526_s_std,
+};
+
+
+static int max9526_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+#if 0
+ pr_info("%s width:%d\n", __func__, mf->width);
+ pr_info("%s height:%d\n", __func__, mf->height);
+ pr_info("%s field:0x%X (V4L2_FIELD_NONE==0x%X)\n", __func__, mf->field, V4L2_FIELD_NONE);
+ pr_info("%s code:0x%X (V4L2_MBUS_FMT_YUYV8_2X8==0x%X)\n", __func__, mf->code, V4L2_MBUS_FMT_YUYV8_2X8);
+ pr_info("%s colorspace:0x%X (V4L2_COLORSPACE_SRGB==0x%X)\n", __func__, mf->colorspace, V4L2_COLORSPACE_SRGB);
+
+ mf->width = PAL_NUM_ACTIVE_PIXELS;
+ mf->height = PAL_NUM_ACTIVE_LINES;
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+#endif
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops max9526_video_ops = {
+ .s_routing = max9526_s_routing,
+ .querystd = max9526_querystd,
+ .enum_mbus_fmt = max9526_enum_mbus_fmt,
+ .try_mbus_fmt = max9526_try_mbus_fmt,
+ .s_mbus_fmt = max9526_s_mbus_fmt,
+ .g_parm = max9526_g_parm,
+ .s_parm = max9526_s_parm,
+ .s_stream = max9526_s_stream,
+};
+
+/* Alter bus settings on camera side */
+static int max9526_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long max9526_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 struct soc_camera_ops max9526_soc_camera_ops = {
+ .set_bus_param = max9526_set_bus_param,
+ .query_bus_param = max9526_query_bus_param,
+ .num_controls = 0,
+};
+
+static const struct v4l2_subdev_ops max9526_ops = {
+ .core = &max9526_core_ops,
+ .video = &max9526_video_ops,
+};
+
+static struct max9526_decoder max9526_dev = {
+ .streaming = 0,
+
+ .fmt_list = max9526_fmt_list,
+ .num_fmts = ARRAY_SIZE(max9526_fmt_list),
+
+ .pix = {
+ /* Default to PAL 8-bit YUV 422 */
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED_TB,
+ .bytesperline = PAL_NUM_ACTIVE_PIXELS * 2,
+ .sizeimage =
+ PAL_NUM_ACTIVE_PIXELS * 2 * PAL_NUM_ACTIVE_LINES,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },
+
+ .current_std = STD_PAL_BDGHIN,
+ .std_list = max9526_std_list,
+ .num_stds = ARRAY_SIZE(max9526_std_list),
+
+ .active_input = 2, // auto-select between input 1 and 2
+};
+
+/**
+ * max9526_probe() - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ * @id: i2c driver id table
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+
+static int max9526_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct max9526_decoder *decoder;
+ struct v4l2_subdev *sd;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct v4l2_ioctl_ops *ops;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!client->dev.platform_data) {
+ v4l2_err(client, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+
+ /* Initialize the max9526_decoder with default configuration */
+ *decoder = max9526_dev;
+ /* Copy default register configuration */
+ memcpy(decoder->max9526_regs, max9526_reg_list_default,
+ sizeof(max9526_reg_list_default));
+
+ /* Copy board specific information here */
+ decoder->pdata = client->dev.platform_data;
+
+
+ /* Register with V4L2 layer as slave device */
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &max9526_ops);
+
+ icd->ops = &max9526_soc_camera_ops;
+
+ /*
+ * 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;
+
+ v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
+
+ return 0;
+
+}
+
+
+/**
+ * max9526_remove() - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of max9526_probe().
+ */
+static int max9526_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct max9526_decoder *decoder = to_decoder(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(decoder);
+ return 0;
+}
+
+/**
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id max9526_id[] = {
+ {"max9526", (unsigned long)max9526_reg_list_default},
+};
+
+
+static struct i2c_driver max9526_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MAX9526_MODULE_NAME,
+ },
+ .probe = max9526_probe,
+ .remove = max9526_remove,
+ .id_table = max9526_id,
+};
+
+static int __init max9526_init(void)
+{
+ return i2c_add_driver(&max9526_driver);
+}
+
+static void __exit max9526_exit(void)
+{
+ i2c_del_driver(&max9526_driver);
+}
+
+module_init(max9526_init);
+module_exit(max9526_exit);
diff --git a/drivers/media/video/tegra_v4l2_camera.c b/drivers/media/video/tegra_v4l2_camera.c
index 8f71d1702537..14a74cd6b7cc 100644
--- a/drivers/media/video/tegra_v4l2_camera.c
+++ b/drivers/media/video/tegra_v4l2_camera.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -23,13 +22,14 @@
#include <linux/nvhost.h>
#include <mach/iomap.h>
-#include <mach/powergate.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/videobuf2-dma-nvmap.h>
#include <media/tegra_v4l2_camera.h>
+#include <mach/powergate.h>
+
#include "dev.h"
#include "bus_client.h"
#include "host1x/host1x_syncpt.h"
@@ -37,18 +37,22 @@
#define TEGRA_CAM_DRV_NAME "vi"
#define TEGRA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
-#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 200
+static unsigned int internal_sync = 0;
+module_param(internal_sync, int, 0644);
+MODULE_PARM_DESC(internal_sync, "enable internal vsync and hsync decoded " \
+ "from data");
+
+#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 25
#define TEGRA_SYNCPT_CSI_WAIT_TIMEOUT 200
#define TEGRA_SYNCPT_RETRY_COUNT 10
-#define TEGRA_VIP_H_ACTIVE_START 0x98
-#define TEGRA_VIP_V_ACTIVE_START 0x10
+#define TEGRA_VIP_H_ACTIVE_START 0x8F //0x98
+#define TEGRA_VIP_V_ACTIVE_START 0x12 //0x10
/* SYNCPTs 12-17 are reserved for VI. */
#define TEGRA_VI_SYNCPT_VI NVSYNCPT_VI_ISP_2
-#define TEGRA_VI_SYNCPT_CSI_A NVSYNCPT_VI_ISP_3
-#define TEGRA_VI_SYNCPT_CSI_B NVSYNCPT_VI_ISP_4
+#define TEGRA_VI_SYNCPT_CSI NVSYNCPT_VI_ISP_3
/* Tegra CSI-MIPI registers. */
#define TEGRA_VI_OUT_1_INCR_SYNCPT 0x0000
@@ -235,7 +239,10 @@
#define TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME 0x08c8
#define TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME 0x08cc
#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x08d0
-#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG0 0x08d4
+
+#define IS_INTERLACED ((pcdev->field == V4L2_FIELD_INTERLACED)\
+ || (pcdev->field == V4L2_FIELD_INTERLACED_BT)\
+ || (pcdev->field == V4L2_FIELD_INTERLACED_TB))
#define TC_VI_REG_RD(DEV, REG) readl(DEV->vi_base + REG)
#define TC_VI_REG_WT(DEV, REG, VAL) writel(VAL, DEV->vi_base + REG)
@@ -256,8 +263,6 @@
struct tegra_buffer {
struct vb2_buffer vb; /* v4l buffer must be first */
struct list_head queue;
- struct soc_camera_device *icd;
- int output_channel;
/*
* Various buffer addresses shadowed so we don't have to recalculate
@@ -269,21 +274,20 @@ struct tegra_buffer {
dma_addr_t start_addr;
dma_addr_t start_addr_u;
dma_addr_t start_addr_v;
+ void* virtual_addr;
};
struct tegra_camera_dev {
struct soc_camera_host ici;
+ struct soc_camera_device *icd;
struct nvhost_device *ndev;
+ struct tegra_camera_platform_data *pdata;
struct clk *clk_vi;
struct clk *clk_vi_sensor;
struct clk *clk_csi;
struct clk *clk_isp;
struct clk *clk_csus;
- struct clk *clk_sclk;
- struct clk *clk_emc;
-
- struct regulator *reg;
void __iomem *vi_base;
spinlock_t videobuf_queue_lock;
@@ -291,22 +295,16 @@ struct tegra_camera_dev {
struct vb2_buffer *active;
struct vb2_alloc_ctx *alloc_ctx;
enum v4l2_field field;
- int sequence_a;
- int sequence_b;
+ int sequence;
struct work_struct work;
struct mutex work_mutex;
u32 syncpt_vi;
- u32 syncpt_csi_a;
- u32 syncpt_csi_b;
+ u32 syncpt_csi;
/* Debug */
int num_frames;
- int enable_refcnt;
-
- /* CSI pad calibration flag */
- int cal_done;
};
static const struct soc_mbus_pixelfmt tegra_camera_formats[] = {
@@ -352,23 +350,6 @@ static const struct soc_mbus_pixelfmt tegra_camera_formats[] = {
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
-
- /* For RAW8 and RAW10 output, we always output 16-bit (2 bytes). */
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .name = "Bayer 8 BGBG.. GRGR..",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_EXTEND16,
- .order = SOC_MBUS_ORDER_LE,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGBG.. GRGR..",
- .bits_per_sample = 16,
- .packing = SOC_MBUS_PACKING_EXTEND16,
- .order = SOC_MBUS_ORDER_LE,
- },
-
};
static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb)
@@ -378,13 +359,9 @@ static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb)
static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev)
{
- pcdev->syncpt_csi_a =
+ pcdev->syncpt_csi =
nvhost_syncpt_read_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_A);
-
- pcdev->syncpt_csi_b =
- nvhost_syncpt_read_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_B);
+ TEGRA_VI_SYNCPT_CSI);
pcdev->syncpt_vi =
nvhost_syncpt_read_ext(pcdev->ndev,
@@ -394,65 +371,60 @@ static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev)
static void tegra_camera_incr_syncpts(struct tegra_camera_dev *pcdev)
{
nvhost_syncpt_cpu_incr_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_A);
-
- nvhost_syncpt_cpu_incr_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_B);
+ TEGRA_VI_SYNCPT_CSI);
nvhost_syncpt_cpu_incr_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_VI);
}
-static void tegra_camera_capture_clean(struct tegra_camera_dev *pcdev)
+static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev,
+ int input_format,
+ int yuv_input_format)
{
- TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000);
+ struct soc_camera_device *icd = pcdev->icd;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_READONLY_STATUS, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_ESCAPE_MODE_DATA, 0x0);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x02000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL,
+ (yuv_input_format << 8) |
+ (input_format << 2));
- TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_CONTROL, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_0, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_1, 0x0);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_2, 0x0);
-}
+ TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000004);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000004);
-static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev,
- struct soc_camera_device *icd,
- u32 hdr)
-{
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
+ /* CSI-A H_ACTIVE and V_ACTIVE */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_H_ACTIVE,
+ (icd->user_width << 16));
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_V_ACTIVE,
+ (icd->user_height << 16));
+ /* CSI A */
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_GAP, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0, 0x00000000);
+
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG0, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG1, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME, 0x0);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x02000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000);
- /* CSI-A H_ACTIVE and V_ACTIVE */
- TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_H_ACTIVE,
- (icd->user_width << 16));
- TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_V_ACTIVE,
- (icd->user_height << 16));
+ /* pad1s enabled, virtual channel ID 00 */
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0,
+ (0x1 << 16) | /* Output 1 pixel per clock */
+ (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */
+ (0x1 << 7) | /* Check header CRC */
+ (0x1 << 6) | /* Use word count field in the header */
+ (0x1 << 5) | /* Look at data identifier byte in hdr */
+ (0x1 << 4)); /* Expect packet header */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1,
0x1); /* Frame # for top field detect for interlaced */
@@ -466,27 +438,22 @@ static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev,
(0x100 << 4) | /* Wait 0x100 vi clks for timeout */
0x1); /* Enable line timeout */
- /* pad 0s enabled, virtual channel ID 00 */
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0,
- (0x1 << 16) | /* Output 1 pixel per clock */
- (hdr << 8) | /* If hdr shows wrong fmt, use right value */
- (0x1 << 7) | /* Check header CRC */
- (0x1 << 6) | /* Use word count field in the header */
- (0x1 << 5) | /* Look at data identifier byte in hdr */
- (0x1 << 4)); /* Expect packet header */
-
TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL,
(0x3f << 16) | /* Skip packet threshold */
- (pdata->lanes - 1));
+ (pcdev->pdata->lanes - 1));
/* Use 0x00000022 for continuous clock mode. */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0,
- (pdata->continuous_clk << 5) |
+ (pcdev->pdata->continuous_clk << 5) |
0x5); /* Clock settle time */
TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_END,
(0x1 << 8) | /* Enable continuous syncpt */
- TEGRA_VI_SYNCPT_CSI_A);
+ TEGRA_VI_SYNCPT_CSI);
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1,
+ (0x1 << 8) | /* Enable continuous syncpt */
+ TEGRA_VI_SYNCPT_VI);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00020001);
@@ -494,37 +461,49 @@ static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev,
}
static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev,
- struct soc_camera_device *icd,
- u32 hdr)
+ int input_format,
+ int yuv_input_format)
{
- struct tegra_camera_platform_data *pdata = icd->link->priv;
+ struct soc_camera_device *icd = pcdev->icd;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x04000000);
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL,
+ (yuv_input_format << 8) |
+ (input_format << 2));
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000008);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000008);
+
+ /* CSI-B H_ACTIVE and V_ACTIVE */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_H_ACTIVE,
+ (icd->user_width << 16));
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_V_ACTIVE,
+ (icd->user_height << 16));
+
+ /* CSI B */
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL1, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_GAP, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0, 0x00000000);
+
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG0, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG1, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME, 0x0);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x04000000);
-
- /* CSI-B H_ACTIVE and V_ACTIVE */
- TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_H_ACTIVE,
- (icd->user_width << 16));
- TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_V_ACTIVE,
- (icd->user_height << 16));
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000);
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000);
- /* pad 0s enabled, virtual channel ID 00 */
+ /* pad1s enabled, virtual channel ID 00 */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0,
(0x1 << 16) | /* Output 1 pixel per clock */
- (hdr << 8) | /* If hdr shows wrong fmt, use right value */
+ (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */
(0x1 << 7) | /* Check header CRC */
(0x1 << 6) | /* Use word count field in the header */
(0x1 << 5) | /* Look at data identifier byte in hdr */
@@ -545,16 +524,20 @@ static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev,
TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL,
(0x3f << 16) | /* Skip packet threshold */
- (pdata->lanes - 1));
+ (pcdev->pdata->lanes - 1));
/* Use 0x00000022 for continuous clock mode. */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0,
- (pdata->continuous_clk << 5) |
+ (pcdev->pdata->continuous_clk << 5) |
0x5); /* Clock settle time */
TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_END,
(0x1 << 8) | /* Enable continuous syncpt */
- TEGRA_VI_SYNCPT_CSI_B);
+ TEGRA_VI_SYNCPT_CSI);
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1,
+ (0x1 << 8) | /* Enable continuous syncpt */
+ TEGRA_VI_SYNCPT_VI);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00010002);
@@ -562,17 +545,22 @@ static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev,
}
static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev,
- struct soc_camera_device *icd,
- u32 input_control)
+ int input_format,
+ int yuv_input_format)
{
+ struct soc_camera_device *icd = pcdev->icd;
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL,
- (1 << 27) | /* field detect */
- (1 << 25) | /* hsync/vsync decoded from data (BT.656) */
+// (1 << 27) | /* field detect */
+ (0 << 28) | /* 1 == top field is even field, 00 == odd */
+ ((internal_sync == 1) << 25) | /* 1 == hsync/vsync decoded
+ internally from data
+ (BT.656) */
+ (yuv_input_format << 8) |
(1 << 1) | /* VIP_INPUT_ENABLE */
- input_control);
+ (input_format << 2));
TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000000);
@@ -580,180 +568,80 @@ static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev,
/* VIP H_ACTIVE and V_ACTIVE */
TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_H_ACTIVE,
(icd->user_width << 16) |
- TEGRA_VIP_H_ACTIVE_START);
+ (TEGRA_VIP_H_ACTIVE_START - ((internal_sync == 1)?1:0)));
TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_V_ACTIVE,
- (icd->user_height << 16) |
+ ( ( IS_INTERLACED ? (icd->user_height/2) : (icd->user_height) ) << 16) |
TEGRA_VIP_V_ACTIVE_START);
/*
* For VIP, D9..D2 is mapped to the video decoder's P7..P0.
* Disable/mask out the other Dn wires.
*/
- TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INPUT_ENABLE, 0x000003fc);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INPUT_ENABLE, 0x000003fc | 0x6000); // D2..D9 + VSYNC + HSYNC
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_DATA_INPUT_CONTROL, 0x000003fc);
TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INVERSION, 0x00000000);
- TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_VIP_VSYNC,
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1,
(0x1 << 8) | /* Enable continuous syncpt */
TEGRA_VI_SYNCPT_VI);
- TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004);
+// TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004);
}
-static int tegra_camera_capture_output_channel_setup(
- struct tegra_camera_dev *pcdev,
- struct soc_camera_device *icd)
+static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
{
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int port = pdata->port;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
+ struct soc_camera_device *icd = pcdev->icd;
const struct soc_camera_format_xlate *current_fmt = icd->current_fmt;
+ enum v4l2_mbus_pixelcode input_code = current_fmt->code;
u32 output_fourcc = current_fmt->host_fmt->fourcc;
- u32 output_format, output_control;
- struct tegra_buffer *buf = to_tegra_vb(pcdev->active);
+ int yuv_input_format = 0x0;
+ int input_format = 0x0; /* Default to YUV422 */
+ int yuv_output_format = 0x0;
+ int output_format = 0x3; /* Default to YUV422 */
+ int port = pcdev->pdata->port;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ int frame_count = 1;
+
+ switch (input_code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ yuv_input_format = 0x2;
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ yuv_input_format = 0x3;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ yuv_input_format = 0x0;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ yuv_input_format = 0x1;
+ break;
+ default:
+ BUG_ON(1);
+ }
switch (output_fourcc) {
case V4L2_PIX_FMT_UYVY:
- output_format = 0x3; /* Default to YUV422 */
+ yuv_output_format = 0x0;
break;
case V4L2_PIX_FMT_VYUY:
- output_format = (0x1 << 17) | 0x3;
+ yuv_output_format = 0x1;
break;
case V4L2_PIX_FMT_YUYV:
- output_format = (0x2 << 17) | 0x3;
+ yuv_output_format = 0x2;
break;
case V4L2_PIX_FMT_YVYU:
- output_format = (0x3 << 17) | 0x3;
+ yuv_output_format = 0x3;
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
output_format = 0x6; /* YUV420 planar */
break;
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SBGGR10:
- /* Use second output channel for RAW8/RAW10 */
- buf->output_channel = 1;
-
- if (port == TEGRA_CAMERA_PORT_CSI_A)
- output_format = 0x7;
- else if (port == TEGRA_CAMERA_PORT_CSI_B)
- output_format = 0x8;
- else
- output_format = 0x9;
- break;
default:
- dev_err(&pcdev->ndev->dev, "Wrong output format %d\n",
- output_fourcc);
- return -EINVAL;
+ BUG_ON(1);
}
- output_control = (pdata->flip_v ? (0x1 << 20) : 0) |
- (pdata->flip_h ? (0x1 << 19) : 0) |
- output_format;
-
- if (buf->output_channel == 0) {
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL,
- output_control);
- /*
- * Set up frame size. Bits 31:16 are the number of lines, and
- * bits 15:0 are the number of pixels per line.
- */
- TC_VI_REG_WT(pcdev, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE,
- (icd->user_height << 16) | icd->user_width);
-
- /* First output memory enabled */
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000);
-
- /* Set the number of frames in the buffer. */
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_FIRST, 0x00000001);
-
- /* Set up buffer frame size. */
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_FIRST,
- (icd->user_height << 16) | icd->user_width);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST,
- (icd->user_height * bytes_per_line));
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1,
- (0x1 << 8) | /* Enable continuous syncpt */
- TEGRA_VI_SYNCPT_VI);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000);
- } else if (buf->output_channel == 1) {
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_SECOND_OUTPUT_CONTROL,
- output_control);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_SECOND_OUTPUT_FRAME_SIZE,
- (icd->user_height << 16) | icd->user_width);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE_2, 0x00000000);
-
- /* Set the number of frames in the buffer. */
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_SECOND, 0x00000001);
-
- /* Set up buffer frame size. */
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_SECOND,
- (icd->user_height << 16) | icd->user_width);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_SECOND,
- (icd->user_height * bytes_per_line));
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_2,
- (0x1 << 8) | /* Enable continuous syncpt */
- TEGRA_VI_SYNCPT_VI);
-
- TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE_2, 0x00000000);
- } else {
- dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n",
- buf->output_channel);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
-{
- struct vb2_buffer *vb = pcdev->active;
- struct tegra_buffer *buf = to_tegra_vb(vb);
- struct soc_camera_device *icd = buf->icd;
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int port = pdata->port;
- const struct soc_camera_format_xlate *current_fmt = icd->current_fmt;
- enum v4l2_mbus_pixelcode input_code = current_fmt->code;
- u32 hdr, input_control = 0x0;
-
- switch (input_code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
- input_control |= 0x2 << 8;
- hdr = 30;
- break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
- input_control |= 0x3 << 8;
- hdr = 30;
- break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- input_control |= 0x0;
- hdr = 30;
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- input_control |= 0x1 << 8;
- hdr = 30;
- break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- input_control |= 0x2 << 2; /* Input Format = Bayer */
- hdr = 42;
- break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- input_control |= 0x2 << 2; /* Input Format = Bayer */
- hdr = 43;
- break;
- default:
- dev_err(&pcdev->ndev->dev, "Input format %d is not supported\n",
- input_code);
- return -EINVAL;
- }
+ BUG_ON(!tegra_camera_port_is_valid(port));
/*
* Set up low pass filter. Use 0x240 for chromaticity and 0x240
@@ -765,25 +653,98 @@ static int tegra_camera_capture_setup(struct tegra_camera_dev *pcdev)
/* Set up raise-on-edge, so we get an interrupt on end of frame. */
TC_VI_REG_WT(pcdev, TEGRA_VI_VI_RAISE, 0x00000001);
- /* Cleanup registers */
- tegra_camera_capture_clean(pcdev);
-
- /* Setup registers for CSI-A, CSI-B and VIP inputs */
if (port == TEGRA_CAMERA_PORT_CSI_A)
- tegra_camera_capture_setup_csi_a(pcdev, icd, hdr);
+ tegra_camera_capture_setup_csi_a(pcdev, input_format,
+ yuv_input_format);
else if (port == TEGRA_CAMERA_PORT_CSI_B)
- tegra_camera_capture_setup_csi_b(pcdev, icd, hdr);
+ tegra_camera_capture_setup_csi_b(pcdev, input_format,
+ yuv_input_format);
else
- tegra_camera_capture_setup_vip(pcdev, icd, input_control);
+ tegra_camera_capture_setup_vip(pcdev, input_format,
+ yuv_input_format);
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL,
+ (pcdev->pdata->flip_v ? (0x1 << 20) : 0) |
+ (pcdev->pdata->flip_h ? (0x1 << 19) : 0) |
+ (yuv_output_format << 17) |
+ output_format);
+
+ // if the video is interlaced, then take two frames
+ frame_count = IS_INTERLACED ? 2 : 1;
+
+ /*
+ * Set up frame size. Bits 31:16 are the number of lines, and
+ * bits 15:0 are the number of pixels per line.
+ */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE,
+ ((icd->user_height/frame_count) << 16) | icd->user_width);
+
+ /* Set the number of frames in the buffer. */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_FIRST, frame_count);
+
+ /* Set up buffer frame size. */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_FIRST,
+ ((icd->user_height/frame_count) << 16) | icd->user_width);
+ if(output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420) {
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST,
+ ((icd->user_height/frame_count) * icd->user_width) | (2<<30));
+ }
+ else {
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST,
+ ((icd->user_height/frame_count) * bytes_per_line) | (2<<30));
+ }
+
+ /* First output memory enabled */
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000);
- /* Setup registers for output channels */
- return tegra_camera_capture_output_channel_setup(pcdev, icd);
}
-static int tegra_camera_capture_buffer_setup(struct tegra_camera_dev *pcdev,
- struct tegra_buffer *buf)
+//
+// make_interlaced
+//
+// make one interlaced frame out of two frames in the buffer @ addr
+// this function must be issued for Y, U and V buffers.
+//
+static void make_interlaced(uint8_t * addr, int width, int height) {
+ uint8_t starting_line_buf[width];
+ bool lines[height];
+ int i;
+ int destination_line, starting_line;
+ for (i = 1; i < height-1; i++) lines[i] = false;
+ lines[0] = true;
+ lines[height-1] = true;
+
+ #define real_line(i) ( (i % 2) ? ((i / 2) + (height/2)) : (i / 2) )
+ while (1) {
+ starting_line = -1;
+ for (i = 1; i < ((height)-1); i++) if (!lines[i]) {
+ starting_line = i;
+ break;
+ }
+ if (starting_line == -1) break; // all is done
+
+ memcpy(starting_line_buf, (void*) ((unsigned int)(addr) + (width*starting_line)), width);
+ destination_line = starting_line;
+ while (1) {
+ lines[destination_line] = true;
+ if (real_line(destination_line) == starting_line) {
+ memcpy(addr + width * destination_line, starting_line_buf, width);
+ break;
+ }
+ memcpy(addr + (width * destination_line), (void*) ((unsigned int)(addr) + (width * real_line(destination_line))), width);
+ destination_line = real_line(destination_line);
+ }
+ }
+}
+
+static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
+ struct tegra_buffer *buf)
{
- struct soc_camera_device *icd = buf->icd;
+ struct soc_camera_device *icd = pcdev->icd;
+ int port = pcdev->pdata->port;
+ int err;
+ int bytes_per_line;
+ uint8_t *src;
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_YUV420:
@@ -802,90 +763,87 @@ static int tegra_camera_capture_buffer_setup(struct tegra_camera_dev *pcdev,
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SBGGR10:
- /* output 1 */
- if (buf->output_channel == 0) {
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST,
- buf->buffer_addr);
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_FIRST,
- buf->start_addr);
- /* output 2 */
- } else if (buf->output_channel == 1) {
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_SECOND,
- buf->buffer_addr);
- TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_SECOND,
- buf->start_addr);
- } else {
- dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n",
- buf->output_channel);
- return -EINVAL;
- }
- break;
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST,
+ buf->buffer_addr);
+ TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_FIRST,
+ buf->start_addr);
+
+ break;
default:
- dev_err(&pcdev->ndev->dev, "Wrong host format %d\n",
- icd->current_fmt->host_fmt->fourcc);
- return -EINVAL;
+ BUG_ON(1);
}
- return 0;
-}
+ BUG_ON(!tegra_camera_port_is_valid(port));
-static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
- struct tegra_buffer *buf)
-{
- struct soc_camera_device *icd = buf->icd;
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int port = pdata->port;
- int err;
+ if (port == TEGRA_CAMERA_PORT_CSI_A)
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
+ 0x0000f005);
+ else if (port == TEGRA_CAMERA_PORT_CSI_B)
+ TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND,
+ 0x0000f005);
+ else
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
+ 0x00000005); // start & stop
- err = tegra_camera_capture_buffer_setup(pcdev, buf);
- if (err < 0)
- return err;
/*
* Only wait on CSI frame end syncpt if we're using CSI. Otherwise,
* wait on VIP VSYNC syncpt.
*/
- if (port == TEGRA_CAMERA_PORT_CSI_A) {
- pcdev->syncpt_csi_a++;
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
- 0x0000f005);
- err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_A,
- pcdev->syncpt_csi_a,
- TEGRA_SYNCPT_CSI_WAIT_TIMEOUT,
- NULL);
- } else if (port == TEGRA_CAMERA_PORT_CSI_B) {
- pcdev->syncpt_csi_b++;
- TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND,
- 0x0000f005);
+
+ pcdev->syncpt_vi++;
+ pcdev->syncpt_csi++;
+
+ if (tegra_camera_port_is_csi(port))
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_CSI_B,
- pcdev->syncpt_csi_b,
- TEGRA_SYNCPT_CSI_WAIT_TIMEOUT,
- NULL);
- } else {
- pcdev->syncpt_vi++;
- TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
- 0x00000001);
+ TEGRA_VI_SYNCPT_CSI,
+ pcdev->syncpt_csi,
+ TEGRA_SYNCPT_CSI_WAIT_TIMEOUT,
+ NULL);
+ else
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
- TEGRA_VI_SYNCPT_VI,
- pcdev->syncpt_csi_a,
- TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
- NULL);
- }
+ TEGRA_VI_SYNCPT_VI,
+ pcdev->syncpt_vi,
+ TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
+ NULL);
- if (!err)
- return 0;
+ if (IS_INTERLACED) {
+
+ TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
+ 0x00000005); // start & stop
+
+ pcdev->syncpt_vi++;
+ pcdev->syncpt_csi++;
+
+ err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
+ TEGRA_VI_SYNCPT_VI,
+ pcdev->syncpt_vi,
+ TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
+ NULL);
+
+ src = (uint8_t*)(buf->virtual_addr);
+ if(icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YUV420 || icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420 ) {
+ make_interlaced(src, icd->user_width, icd->user_height); // Y
+ make_interlaced(src + icd->user_width * icd->user_height, icd->user_width, icd->user_height/4); // U
+ make_interlaced(src + icd->user_width * icd->user_height + (icd->user_width * icd->user_height)/4, icd->user_width, icd->user_height/4); // V
+ }
+ else {
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ make_interlaced(src, bytes_per_line, icd->user_height); // Y
+ }
+ }
+
+ if (!err)
+ return 0;
if (tegra_camera_port_is_csi(port)) {
u32 ppstatus;
u32 cilstatus;
u32 rostatus;
- dev_warn(&icd->vdev->dev, "Timeout on CSI syncpt\n");
- dev_warn(&icd->vdev->dev, "buffer_addr = 0x%08x\n",
+ dev_err(&pcdev->ndev->dev, "Timeout on CSI syncpt\n");
+ dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n",
buf->buffer_addr);
ppstatus = TC_VI_REG_RD(pcdev,
@@ -895,7 +853,7 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
rostatus = TC_VI_REG_RD(pcdev,
TEGRA_CSI_CSI_READONLY_STATUS);
- dev_warn(&icd->vdev->dev,
+ dev_err(&pcdev->ndev->dev,
"PPSTATUS = 0x%08x, "
"CILSTATUS = 0x%08x, "
"ROSTATUS = 0x%08x\n",
@@ -903,14 +861,14 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
} else {
u32 vip_input_status;
- dev_warn(&pcdev->ndev->dev, "Timeout on VI syncpt\n");
- dev_warn(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n",
+ dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n");
+ dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n",
buf->buffer_addr);
vip_input_status = TC_VI_REG_RD(pcdev,
TEGRA_VI_VIP_INPUT_STATUS);
- dev_warn(&pcdev->ndev->dev,
+ dev_err(&pcdev->ndev->dev,
"VIP_INPUT_STATUS = 0x%08x\n",
vip_input_status);
}
@@ -918,10 +876,12 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev,
return err;
}
-static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port)
+static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev)
{
+ int port = pcdev->pdata->port;
int err;
- struct tegra_buffer *buf = to_tegra_vb(pcdev->active);
+
+ BUG_ON(!tegra_camera_port_is_valid(port));
if (port == TEGRA_CAMERA_PORT_CSI_A)
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
@@ -929,9 +889,9 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port)
else if (port == TEGRA_CAMERA_PORT_CSI_B)
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND,
0x0000f002);
- else
- TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
- 0x00000005);
+// else
+// TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
+// 0x00000005);
if (tegra_camera_port_is_csi(port))
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
@@ -947,28 +907,17 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port)
u32 ppstatus;
u32 cilstatus;
- dev_warn(&pcdev->ndev->dev, "Timeout on VI syncpt\n");
-
- if (buf->output_channel == 0)
- buffer_addr = TC_VI_REG_RD(pcdev,
+ dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n");
+ buffer_addr = TC_VI_REG_RD(pcdev,
TEGRA_VI_VB0_BASE_ADDRESS_FIRST);
- else if (buf->output_channel == 1)
- buffer_addr = TC_VI_REG_RD(pcdev,
- TEGRA_VI_VB0_BASE_ADDRESS_SECOND);
- else {
- dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n",
- buf->output_channel);
- return -EINVAL;
- }
-
- dev_warn(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n",
+ dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n",
buffer_addr);
ppstatus = TC_VI_REG_RD(pcdev,
TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
cilstatus = TC_VI_REG_RD(pcdev,
TEGRA_CSI_CSI_CIL_STATUS);
- dev_warn(&pcdev->ndev->dev,
+ dev_err(&pcdev->ndev->dev,
"PPSTATUS = 0x%08x, CILSTATUS = 0x%08x\n",
ppstatus, cilstatus);
}
@@ -976,132 +925,81 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port)
return err;
}
-static void tegra_camera_activate(struct tegra_camera_dev *pcdev)
-{
- nvhost_module_busy_ext(pcdev->ndev);
-
- /* Enable external power */
- regulator_enable(pcdev->reg);
-
- /* Unpowergate VE */
- tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
-
- /* Turn on relevant clocks. */
- clk_set_rate(pcdev->clk_vi, 150000000);
- clk_enable(pcdev->clk_vi);
- clk_set_rate(pcdev->clk_vi_sensor, 24000000);
- clk_enable(pcdev->clk_vi_sensor);
- clk_enable(pcdev->clk_csi);
- clk_enable(pcdev->clk_isp);
- clk_enable(pcdev->clk_csus);
- clk_set_rate(pcdev->clk_sclk, 80000000);
- clk_enable(pcdev->clk_sclk);
- clk_set_rate(pcdev->clk_sclk, 375000000);
- clk_enable(pcdev->clk_emc);
-
- /* Save current syncpt values. */
- tegra_camera_save_syncpts(pcdev);
-}
-
-static void tegra_camera_deactivate(struct tegra_camera_dev *pcdev)
-{
- /* Turn off relevant clocks. */
- clk_disable(pcdev->clk_vi);
- clk_disable(pcdev->clk_vi_sensor);
- clk_disable(pcdev->clk_csi);
- clk_disable(pcdev->clk_isp);
- clk_disable(pcdev->clk_csus);
- clk_disable(pcdev->clk_sclk);
- clk_disable(pcdev->clk_emc);
-
- /* Powergate VE */
- tegra_powergate_partition(TEGRA_POWERGATE_VENC);
-
- /* Disable external power */
- regulator_disable(pcdev->reg);
-
- nvhost_module_idle_ext(pcdev->ndev);
-
- pcdev->cal_done = 0;
-}
-
static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev)
{
- struct vb2_buffer *vb = pcdev->active;
- struct tegra_buffer *buf = to_tegra_vb(vb);
- struct soc_camera_device *icd = buf->icd;
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int port = pdata->port;
+ struct vb2_buffer *vb;
+ struct tegra_buffer *buf;
int retry = TEGRA_SYNCPT_RETRY_COUNT;
- int err;
+ int port = pcdev->pdata->port;
+ int err = 0;
- while (retry) {
- err = tegra_camera_capture_start(pcdev, buf);
- /* Capturing succeed, stop capturing */
- if (!err)
- err = tegra_camera_capture_stop(pcdev, port);
- /* Capturing failed, stop and retry */
- else {
- retry--;
-
- /* Stop streaming. */
- if (port == TEGRA_CAMERA_PORT_CSI_A) {
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
- 0x0000f002);
- /* Clear status registers. */
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_CSI_PIXEL_PARSER_STATUS,
- 0xffffffff);
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_CSI_CIL_STATUS,
- 0xffffffff);
- } else if (port == TEGRA_CAMERA_PORT_CSI_B) {
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND,
- 0x0000f002);
- /* Clear status registers. */
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_CSI_PIXEL_PARSER_STATUS,
- 0xffffffff);
- TC_VI_REG_WT(pcdev,
- TEGRA_CSI_CSI_CIL_STATUS,
- 0xffffffff);
- } else {
- TC_VI_REG_WT(pcdev,
- TEGRA_VI_CAMERA_CONTROL,
- 0x00000005);
- }
+ if (!pcdev->active)
+ return 0;
- tegra_camera_incr_syncpts(pcdev);
- tegra_camera_save_syncpts(pcdev);
+ vb = pcdev->active;
+ buf = to_tegra_vb(vb);
- continue;
+ while (retry--) {
+ err = tegra_camera_capture_start(pcdev, buf);
+ if (err == 0) {
+ err = tegra_camera_capture_stop(pcdev);
+ if (err == 0) break;
}
- break;
- }
+ /* Stop streaming. */
+ if (port == TEGRA_CAMERA_PORT_CSI_A) {
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
+ 0x0000f002);
+ /* Clear status registers. */
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_CSI_PIXEL_PARSER_STATUS,
+ 0xffffffff);
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_CSI_CIL_STATUS,
+ 0xffffffff);
+ } else if (port == TEGRA_CAMERA_PORT_CSI_B) {
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND,
+ 0x0000f002);
+ /* Clear status registers. */
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_CSI_PIXEL_PARSER_STATUS,
+ 0xffffffff);
+ TC_VI_REG_WT(pcdev,
+ TEGRA_CSI_CSI_CIL_STATUS,
+ 0xffffffff);
+ } else {
+ TC_VI_REG_WT(pcdev,
+ TEGRA_VI_CAMERA_CONTROL,
+ 0x00000005);
+ }
- /* Reset hardware for too many errors */
- if (!retry) {
- tegra_camera_deactivate(pcdev);
- mdelay(5);
- tegra_camera_activate(pcdev);
- if (pcdev->active)
- tegra_camera_capture_setup(pcdev);
+ tegra_camera_incr_syncpts(pcdev);
+ tegra_camera_save_syncpts(pcdev);
+ continue;
}
spin_lock_irq(&pcdev->videobuf_queue_lock);
+ /*
+ * If vb->state is VB2_BUF_STATE_ERROR, then the vb has already been
+ * removed, so we shouldn't remove it again.
+ */
+ if (vb->state != VB2_BUF_STATE_ERROR)
+ list_del_init(&buf->queue);
+
+ if (!list_empty(&pcdev->capture))
+ pcdev->active = &list_entry(pcdev->capture.next,
+ struct tegra_buffer, queue)->vb;
+ else
+ pcdev->active = NULL;
+
do_gettimeofday(&vb->v4l2_buf.timestamp);
vb->v4l2_buf.field = pcdev->field;
- if (port == TEGRA_CAMERA_PORT_CSI_A)
- vb->v4l2_buf.sequence = pcdev->sequence_a++;
- else if (port == TEGRA_CAMERA_PORT_CSI_B)
- vb->v4l2_buf.sequence = pcdev->sequence_b++;
+ vb->v4l2_buf.sequence = pcdev->sequence++;
- vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- list_del_init(&buf->queue);
+ vb2_buffer_done(vb, (err != 0) ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
pcdev->num_frames++;
@@ -1110,145 +1008,101 @@ static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev)
return err;
}
-static int tegra_camera_csi_pad_calibration(struct tegra_camera_dev *pcdev)
+static void tegra_camera_work(struct work_struct *work)
{
- struct vb2_buffer *vb = pcdev->active;
- struct tegra_buffer *buf = to_tegra_vb(vb);
- struct soc_camera_device *icd = buf->icd;
- struct tegra_camera_platform_data *pdata = icd->link->priv;
- int port = pdata->port;
- int retry = 500;
- u32 data;
- int err = -EINVAL;
-
- TC_VI_REG_WT(pcdev, TEGRA_CSI_DSI_MIPI_CAL_CONFIG, 0x40300);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_MIPIBIAS_PAD_CONFIG0, 0x50700);
-
- if (port == TEGRA_CAMERA_PORT_CSI_B || pdata->lanes == 4)
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x200004);
-
- data = 0x2a000004;
- if (port == TEGRA_CAMERA_PORT_CSI_A)
- data |= 0x200000; /* MIPI_CAL_SELA */
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, data);
-
- /* Start calibration */
- data |= 0x80000000;
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, data);
+ struct tegra_camera_dev *pcdev =
+ container_of(work, struct tegra_camera_dev, work);
- while (retry--) {
- data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS);
- if ((data & 0x8000) == 0x8000)
- break;
- udelay(20);
- }
- if (!retry) {
- dev_warn(&pcdev->ndev->dev, "MIPI calibration timeout!\n");
- goto cal_out;
- }
+ mutex_lock(&pcdev->work_mutex);
- /* Clear status */
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, data);
- retry = 500;
- while (retry--) {
- data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS);
- if ((data & 0x8000) == 0x0)
- break;
- udelay(20);
- }
+ while (pcdev->active)
+ tegra_camera_capture_frame(pcdev);
- if (!retry) {
- dev_warn(&pcdev->ndev->dev,
- "Clear MIPI calibration status timeout!\n");
- goto cal_out;
- }
+ mutex_unlock(&pcdev->work_mutex);
+}
- data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
- err = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS);
- if (data | err) {
- dev_warn(&pcdev->ndev->dev,
- "Calibration status not be cleared!\n");
- err = -EINVAL;
- goto cal_out;
- }
+static void tegra_camera_activate(struct tegra_camera_dev *pcdev)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ u32 val;
+ void __iomem *apb_misc;
+#endif
- /* Calibration succeed */
- err = 0;
+ nvhost_module_busy_ext(pcdev->ndev);
-cal_out:
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, data);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if(!tegra_powergate_is_powered(TEGRA_POWERGATE_VENC))
+ tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
+#endif
- /* un-select to avoid interference with DSI */
- if (port == TEGRA_CAMERA_PORT_CSI_B || pdata->lanes == 4)
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x4);
+ /* Turn on relevant clocks. */
+ clk_enable(pcdev->clk_vi);
+ clk_enable(pcdev->clk_vi_sensor);
+ clk_enable(pcdev->clk_csi);
+ clk_enable(pcdev->clk_isp);
+ clk_enable(pcdev->clk_csus);
- TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, 0x2a000004);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+ val = readl(apb_misc + 0x42c);
+ writel(val | 0x1, apb_misc + 0x42c);
+#endif
- return err;
+ /* Save current syncpt values. */
+ tegra_camera_save_syncpts(pcdev);
}
-static void tegra_camera_work(struct work_struct *work)
+static void tegra_camera_deactivate(struct tegra_camera_dev *pcdev)
{
- struct tegra_camera_dev *pcdev =
- container_of(work, struct tegra_camera_dev, work);
- struct tegra_buffer *buf;
+ mutex_lock(&pcdev->work_mutex);
- while (1) {
- mutex_lock(&pcdev->work_mutex);
-
- spin_lock_irq(&pcdev->videobuf_queue_lock);
- if (list_empty(&pcdev->capture)) {
- pcdev->active = NULL;
- spin_unlock_irq(&pcdev->videobuf_queue_lock);
- mutex_unlock(&pcdev->work_mutex);
- return;
- }
+ /* Cancel active buffer. */
+ if (pcdev->active) {
+ list_del_init(&to_tegra_vb(pcdev->active)->queue);
+ vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR);
+ pcdev->active = NULL;
+ }
- buf = list_entry(pcdev->capture.next, struct tegra_buffer,
- queue);
- pcdev->active = &buf->vb;
- spin_unlock_irq(&pcdev->videobuf_queue_lock);
+ mutex_unlock(&pcdev->work_mutex);
- tegra_camera_capture_setup(pcdev);
- if (!pcdev->cal_done) {
- tegra_camera_csi_pad_calibration(pcdev);
- pcdev->cal_done = 1;
- }
- tegra_camera_capture_frame(pcdev);
+ /* Turn off relevant clocks. */
+ clk_disable(pcdev->clk_vi);
+ clk_disable(pcdev->clk_vi_sensor);
+ clk_disable(pcdev->clk_csi);
+ clk_disable(pcdev->clk_isp);
+ clk_disable(pcdev->clk_csus);
+
+ nvhost_module_idle_ext(pcdev->ndev);
- mutex_unlock(&pcdev->work_mutex);
- }
}
-static int tegra_camera_init_buffer(struct tegra_buffer *buf)
+static void tegra_camera_init_buffer(struct tegra_camera_dev *pcdev,
+ struct tegra_buffer *buf)
{
- struct soc_camera_device *icd = buf->icd;
+ struct soc_camera_device *icd = pcdev->icd;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- struct tegra_camera_platform_data *pdata = icd->link->priv;
+ buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); // physical addr
+ buf->virtual_addr = vb2_plane_vaddr(&buf->vb, 0); // save virtual addr for later interlace handling
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SBGGR10:
- buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0);
buf->start_addr = buf->buffer_addr;
- if (pdata->flip_v)
+ if (pcdev->pdata->flip_v)
buf->start_addr += bytes_per_line *
(icd->user_height-1);
- if (pdata->flip_h)
+ if (pcdev->pdata->flip_h)
buf->start_addr += bytes_per_line - 1;
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0);
buf->buffer_addr_u = buf->buffer_addr +
icd->user_width * icd->user_height;
buf->buffer_addr_v = buf->buffer_addr_u +
@@ -1265,7 +1119,7 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf)
buf->start_addr_u = buf->buffer_addr_u;
buf->start_addr_v = buf->buffer_addr_v;
- if (pdata->flip_v) {
+ if (pcdev->pdata->flip_v) {
buf->start_addr += icd->user_width *
(icd->user_height - 1);
@@ -1276,7 +1130,7 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf)
((icd->user_height/2) - 1));
}
- if (pdata->flip_h) {
+ if (pcdev->pdata->flip_h) {
buf->start_addr += icd->user_width - 1;
buf->start_addr_u += (icd->user_width/2) - 1;
@@ -1287,12 +1141,8 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf)
break;
default:
- dev_err(icd->parent, "Wrong host format %d\n",
- icd->current_fmt->host_fmt->fourcc);
- return -EINVAL;
+ BUG_ON(1);
}
-
- return 0;
}
/*
@@ -1307,7 +1157,6 @@ static int tegra_camera_videobuf_setup(struct vb2_queue *vq,
struct soc_camera_device *icd = container_of(vq,
struct soc_camera_device,
vb2_vidq);
- struct tegra_camera_platform_data *pdata = icd->link->priv;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct tegra_camera_dev *pcdev = ici->priv;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
@@ -1320,16 +1169,20 @@ static int tegra_camera_videobuf_setup(struct vb2_queue *vq,
*num_planes = 1;
- if (pdata->port == TEGRA_CAMERA_PORT_CSI_A)
- pcdev->sequence_a = 0;
- else if (pdata->port == TEGRA_CAMERA_PORT_CSI_B)
- pcdev->sequence_b = 0;
+ pcdev->sequence = 0;
sizes[0] = bytes_per_line * icd->user_height;
alloc_ctxs[0] = pcdev->alloc_ctx;
if (!*num_buffers)
*num_buffers = 2;
+ dev_dbg(icd->parent, "num_buffers=%u, size=%lu\n",
+ *num_buffers, sizes[0]);
+
+ tegra_camera_capture_setup(pcdev);
+
+ dev_dbg(icd->parent, "Finished tegra_camera_videobuf_setup()\n");
+
return 0;
}
@@ -1338,8 +1191,9 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb)
struct soc_camera_device *icd = container_of(vb->vb2_queue,
struct soc_camera_device,
vb2_vidq);
- struct tegra_buffer *buf = to_tegra_vb(vb);
- struct tegra_camera_platform_data *pdata = icd->link->priv;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct tegra_camera_dev *pcdev = ici->priv;
+ struct tegra_buffer *buf;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
unsigned long size;
@@ -1349,19 +1203,7 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb)
if (bytes_per_line < 0)
return bytes_per_line;
- buf->icd = icd;
-
- if (!pdata) {
- dev_err(icd->parent, "No platform data for this device!\n");
- return -EINVAL;
- }
-
- if (!tegra_camera_port_is_valid(pdata->port)) {
- dev_err(icd->parent,
- "Invalid camera port %d in platform data\n",
- pdata->port);
- return -EINVAL;
- }
+ buf = to_tegra_vb(vb);
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
vb, vb2_plane_vaddr(vb, 0), vb2_plane_size(vb, 0));
@@ -1375,10 +1217,7 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb)
memset(vb2_plane_vaddr(vb, 0), 0xbd, vb2_plane_size(vb, 0));
#endif
- if (!icd->current_fmt) {
- dev_err(icd->parent, "%s NULL format point\n", __func__);
- return -EINVAL;
- }
+ BUG_ON(NULL == icd->current_fmt);
size = icd->user_height * bytes_per_line;
@@ -1390,7 +1229,11 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(vb, 0, size);
- return tegra_camera_init_buffer(buf);
+ tegra_camera_init_buffer(pcdev, buf);
+
+ dev_dbg(icd->parent, "Finished tegra_camera_videobuf_prepare()\n");
+
+ return 0;
}
static void tegra_camera_videobuf_queue(struct vb2_buffer *vb)
@@ -1402,12 +1245,18 @@ static void tegra_camera_videobuf_queue(struct vb2_buffer *vb)
struct tegra_camera_dev *pcdev = ici->priv;
struct tegra_buffer *buf = to_tegra_vb(vb);
+ dev_dbg(icd->parent, "In tegra_camera_videobuf_queue()\n");
+
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
spin_lock_irq(&pcdev->videobuf_queue_lock);
list_add_tail(&buf->queue, &pcdev->capture);
- schedule_work(&pcdev->work);
+
+ if (!pcdev->active) {
+ pcdev->active = vb;
+ schedule_work(&pcdev->work);
+ }
spin_unlock_irq(&pcdev->videobuf_queue_lock);
dev_dbg(icd->parent, "Finished tegra_camera_videobuf_queue()\n");
@@ -1462,24 +1311,16 @@ static int tegra_camera_stop_streaming(struct vb2_queue *q)
struct tegra_camera_dev *pcdev = ici->priv;
struct list_head *buf_head, *tmp;
-
mutex_lock(&pcdev->work_mutex);
spin_lock_irq(&pcdev->videobuf_queue_lock);
- list_for_each_safe(buf_head, tmp, &pcdev->capture) {
- struct tegra_buffer *buf = container_of(buf_head,
- struct tegra_buffer,
- queue);
- if (buf->icd == icd)
- list_del_init(buf_head);
- }
- spin_unlock_irq(&pcdev->videobuf_queue_lock);
- if (pcdev->active) {
- struct tegra_buffer *buf = to_tegra_vb(pcdev->active);
- if (buf->icd == icd)
- pcdev->active = NULL;
- }
+ pcdev->active = NULL;
+
+ list_for_each_safe(buf_head, tmp, &pcdev->capture)
+ list_del_init(buf_head);
+
+ spin_unlock_irq(&pcdev->videobuf_queue_lock);
mutex_unlock(&pcdev->work_mutex);
@@ -1524,13 +1365,24 @@ static int tegra_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct tegra_camera_dev *pcdev = ici->priv;
+ int err;
- if (!pcdev->enable_refcnt) {
- pm_runtime_get_sync(ici->v4l2_dev.dev);
- tegra_camera_activate(pcdev);
- pcdev->num_frames = 0;
+ if (pcdev->icd)
+ return -EBUSY;
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ if (pcdev->pdata->enable_camera) {
+ err = pcdev->pdata->enable_camera(pcdev->ndev);
+ if (IS_ERR_VALUE(err))
+ return err;
}
- pcdev->enable_refcnt++;
+
+ tegra_camera_activate(pcdev);
+
+ pcdev->icd = icd;
+
+ pcdev->num_frames = 0;
dev_dbg(icd->parent, "TEGRA Camera host attached to camera %d\n",
icd->devnum);
@@ -1544,12 +1396,14 @@ static void tegra_camera_remove_device(struct soc_camera_device *icd)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct tegra_camera_dev *pcdev = ici->priv;
- pcdev->enable_refcnt--;
- if (!pcdev->enable_refcnt) {
- cancel_work_sync(&pcdev->work);
- tegra_camera_deactivate(pcdev);
- pm_runtime_put_sync(ici->v4l2_dev.dev);
- }
+ tegra_camera_deactivate(pcdev);
+
+ pcdev->icd = NULL;
+
+ if (pcdev->pdata->disable_camera)
+ pcdev->pdata->disable_camera(pcdev->ndev);
+
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
dev_dbg(icd->parent, "Frames captured: %d\n", pcdev->num_frames);
@@ -1591,8 +1445,6 @@ static int tegra_camera_get_formats(struct soc_camera_device *icd,
case V4L2_MBUS_FMT_VYUY8_2X8:
case V4L2_MBUS_FMT_YUYV8_2X8:
case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_1X10:
formats += ARRAY_SIZE(tegra_camera_formats);
for (k = 0;
xlate && (k < ARRAY_SIZE(tegra_camera_formats));
@@ -1721,6 +1573,15 @@ static int tegra_camera_try_fmt(struct soc_camera_device *icd,
case V4L2_FIELD_NONE:
pix->field = V4L2_FIELD_NONE;
break;
+ case V4L2_FIELD_INTERLACED_BT:
+ pix->field = V4L2_FIELD_INTERLACED_BT;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ pix->field = V4L2_FIELD_INTERLACED_TB;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ pix->field = V4L2_FIELD_INTERLACED;
+ break;
default:
/* TODO: support interlaced at least in pass-through mode */
dev_err(icd->parent, "Field type %d unsupported.\n",
@@ -1785,7 +1646,8 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
goto exit;
}
- pcdev->ndev = ndev;
+ pcdev->pdata = ndev->dev.platform_data;
+ pcdev->ndev = ndev;
pcdev->ici.priv = pcdev;
pcdev->ici.v4l2_dev.dev = &ndev->dev;
@@ -1798,6 +1660,14 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
spin_lock_init(&pcdev->videobuf_queue_lock);
mutex_init(&pcdev->work_mutex);
+ nvhost_set_drvdata(ndev, pcdev);
+
+ if (!tegra_camera_port_is_valid(pcdev->pdata->port)) {
+ dev_err(&ndev->dev, "Invalid camera port %d in platform data\n",
+ pcdev->pdata->port);
+ goto exit_free_pcdev;
+ }
+
pcdev->clk_vi = clk_get_sys("tegra_camera", "vi");
if (IS_ERR_OR_NULL(pcdev->clk_vi)) {
dev_err(&ndev->dev, "Failed to get vi clock.\n");
@@ -1827,48 +1697,16 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
dev_err(&ndev->dev, "Failed to get csus clock.\n");
goto exit_put_clk_isp;
}
-
- pcdev->clk_sclk = clk_get_sys("tegra_camera", "sclk");
- if (IS_ERR_OR_NULL(pcdev->clk_sclk)) {
- dev_err(&ndev->dev, "Failed to get sclk clock.\n");
- goto exit_put_clk_csus;
- }
-
- pcdev->clk_emc = clk_get_sys("tegra_camera", "emc");
- if (IS_ERR_OR_NULL(pcdev->clk_emc)) {
- dev_err(&ndev->dev, "Failed to get emc clock.\n");
- goto exit_put_clk_sclk;
- }
-
clk_set_rate(pcdev->clk_vi, 150000000);
clk_set_rate(pcdev->clk_vi_sensor, 24000000);
- /* Get regulator pointer */
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- pcdev->reg = regulator_get(&ndev->dev, "vcsi");
-#else
- pcdev->reg = regulator_get(&ndev->dev, "avdd_dsi_csi");
-#endif
- if (IS_ERR_OR_NULL(pcdev->reg)) {
- dev_err(&ndev->dev, "%s: couldn't get regulator\n",
- __func__);
- goto exit_put_clk_emc;
- }
-
- nvhost_set_drvdata(ndev, pcdev);
err = nvhost_client_device_get_resources(ndev);
- if (err) {
- dev_err(&ndev->dev, "%s: nvhost get resources failed %d\n",
- __func__, err);
- goto exit_put_regulator;
- }
+ if (err)
+ goto exit_put_clk_csus;
- err = nvhost_client_device_init(ndev);
- if (err) {
- dev_err(&ndev->dev, "%s: nvhost init failed %d\n",
- __func__, err);
- goto exit_put_regulator;
- }
+ // initialize nvhost client device only the first time
+ if (ndev->power_attrib == NULL)
+ nvhost_client_device_init(ndev);
pcdev->vi_base = ndev->aperture;
@@ -1876,10 +1714,10 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
pm_runtime_enable(&ndev->dev);
pm_runtime_resume(&ndev->dev);
- pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(&ndev->dev);
+ pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(pcdev->ici.v4l2_dev.dev);
if (IS_ERR(pcdev->alloc_ctx)) {
err = PTR_ERR(pcdev->alloc_ctx);
- goto exit_pm_disable;
+ goto exit_put_resources;
}
err = soc_camera_host_register(&pcdev->ici);
@@ -1892,15 +1730,9 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev,
exit_cleanup_alloc_ctx:
vb2_dma_nvmap_cleanup_ctx(pcdev->alloc_ctx);
-exit_pm_disable:
+exit_put_resources:
pm_runtime_disable(&ndev->dev);
nvhost_client_device_put_resources(ndev);
-exit_put_regulator:
- regulator_put(pcdev->reg);
-exit_put_clk_emc:
- clk_put(pcdev->clk_emc);
-exit_put_clk_sclk:
- clk_put(pcdev->clk_sclk);
exit_put_clk_csus:
clk_put(pcdev->clk_csus);
exit_put_clk_isp:
@@ -1963,6 +1795,11 @@ static int tegra_camera_suspend(struct nvhost_device *ndev, pm_message_t state)
/* Suspend the camera sensor. */
WARN_ON(!pcdev->icd->ops->suspend);
pcdev->icd->ops->suspend(pcdev->icd, state);
+
+ /* Power off the camera subsystem. */
+ pcdev->pdata->disable_camera(pcdev->ndev);
+
+ nvhost_module_idle_ext(nvhost_get_parent(ndev));
}
return 0;
@@ -1976,10 +1813,14 @@ static int tegra_camera_resume(struct nvhost_device *ndev)
/* We only need to do something if a camera sensor is attached. */
if (pcdev->icd) {
+ nvhost_module_busy_ext(nvhost_get_parent(ndev));
+
+ /* Power on the camera subsystem. */
+ pcdev->pdata->enable_camera(pcdev->ndev);
+
/* Resume the camera host. */
tegra_camera_save_syncpts(pcdev);
- if (pcdev->active)
- tegra_camera_capture_setup(pcdev);
+ tegra_camera_capture_setup(pcdev);
/* Resume the camera sensor. */
WARN_ON(!pcdev->icd->ops->resume);
@@ -2021,6 +1862,5 @@ module_exit(tegra_camera_exit);
MODULE_DESCRIPTION("TEGRA SoC Camera Host driver");
MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
-MODULE_AUTHOR("Bryan Wu <pengw@nvidia.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("nvhost:" TEGRA_CAM_DRV_NAME);