diff options
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/adv7180.c | 172 | ||||
-rw-r--r-- | drivers/media/video/max9526.c | 1102 | ||||
-rw-r--r-- | drivers/media/video/tegra_v4l2_camera.c | 1198 |
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); |