Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,87 @@
comment "soc_camera sensor drivers"
config SOC_CAMERA_IMX074
tristate "imx074 support"
depends on SOC_CAMERA && I2C
help
This driver supports IMX074 cameras from Sony
config SOC_CAMERA_MT9M001
tristate "mt9m001 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9M001 cameras from Micron, monochrome
and colour models.
config SOC_CAMERA_MT9M111
tristate "mt9m111, mt9m112 and mt9m131 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9M111, MT9M112 and MT9M131 cameras from
Micron/Aptina
config SOC_CAMERA_MT9T031
tristate "mt9t031 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9T031 cameras from Micron.
config SOC_CAMERA_MT9T112
tristate "mt9t112 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9T112 cameras from Aptina.
config SOC_CAMERA_MT9V022
tristate "mt9v022 and mt9v024 support"
depends on SOC_CAMERA && I2C
help
This driver supports MT9V022 cameras from Micron
config SOC_CAMERA_OV2640
tristate "ov2640 camera support"
depends on SOC_CAMERA && I2C
help
This is a ov2640 camera driver
config SOC_CAMERA_OV5642
tristate "ov5642 camera support"
depends on SOC_CAMERA && I2C
help
This is a V4L2 camera driver for the OmniVision OV5642 sensor
config SOC_CAMERA_OV6650
tristate "ov6650 sensor support"
depends on SOC_CAMERA && I2C
---help---
This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
config SOC_CAMERA_OV772X
tristate "ov772x camera support"
depends on SOC_CAMERA && I2C
help
This is a ov772x camera driver
config SOC_CAMERA_OV9640
tristate "ov9640 camera support"
depends on SOC_CAMERA && I2C
help
This is a ov9640 camera driver
config SOC_CAMERA_OV9740
tristate "ov9740 camera support"
depends on SOC_CAMERA && I2C
help
This is a ov9740 camera driver
config SOC_CAMERA_RJ54N1
tristate "rj54n1cb0c support"
depends on SOC_CAMERA && I2C
help
This is a rj54n1cb0c video driver
config SOC_CAMERA_TW9910
tristate "tw9910 support"
depends on SOC_CAMERA && I2C
help
This is a tw9910 video driver

View file

@ -0,0 +1,14 @@
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o

View file

@ -0,0 +1,497 @@
/*
* Driver for IMX074 CMOS Image Sensor from Sony
*
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* Partially inspired by the IMX074 driver from the Android / MSM tree
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/v4l2-mediabus.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/soc_camera.h>
#include <media/v4l2-async.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
/* IMX074 registers */
#define MODE_SELECT 0x0100
#define IMAGE_ORIENTATION 0x0101
#define GROUPED_PARAMETER_HOLD 0x0104
/* Integration Time */
#define COARSE_INTEGRATION_TIME_HI 0x0202
#define COARSE_INTEGRATION_TIME_LO 0x0203
/* Gain */
#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
/* PLL registers */
#define PRE_PLL_CLK_DIV 0x0305
#define PLL_MULTIPLIER 0x0307
#define PLSTATIM 0x302b
#define VNDMY_ABLMGSHLMT 0x300a
#define Y_OPBADDR_START_DI 0x3014
/* mode setting */
#define FRAME_LENGTH_LINES_HI 0x0340
#define FRAME_LENGTH_LINES_LO 0x0341
#define LINE_LENGTH_PCK_HI 0x0342
#define LINE_LENGTH_PCK_LO 0x0343
#define YADDR_START 0x0347
#define YADDR_END 0x034b
#define X_OUTPUT_SIZE_MSB 0x034c
#define X_OUTPUT_SIZE_LSB 0x034d
#define Y_OUTPUT_SIZE_MSB 0x034e
#define Y_OUTPUT_SIZE_LSB 0x034f
#define X_EVEN_INC 0x0381
#define X_ODD_INC 0x0383
#define Y_EVEN_INC 0x0385
#define Y_ODD_INC 0x0387
#define HMODEADD 0x3001
#define VMODEADD 0x3016
#define VAPPLINE_START 0x3069
#define VAPPLINE_END 0x306b
#define SHUTTER 0x3086
#define HADDAVE 0x30e8
#define LANESEL 0x3301
/* IMX074 supported geometry */
#define IMX074_WIDTH 1052
#define IMX074_HEIGHT 780
/* IMX074 has only one fixed colorspace per pixelcode */
struct imx074_datafmt {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
};
struct imx074 {
struct v4l2_subdev subdev;
const struct imx074_datafmt *fmt;
struct v4l2_clk *clk;
};
static const struct imx074_datafmt imx074_colour_fmts[] = {
{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static struct imx074 *to_imx074(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct imx074, subdev);
}
/* Find a data format by a pixel code in an array */
static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
{
int i;
for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
if (imx074_colour_fmts[i].code == code)
return imx074_colour_fmts + i;
return NULL;
}
static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
{
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
unsigned char tx[3];
int ret;
msg.addr = client->addr;
msg.buf = tx;
msg.len = 3;
msg.flags = 0;
tx[0] = addr >> 8;
tx[1] = addr & 0xff;
tx[2] = data;
ret = i2c_transfer(adap, &msg, 1);
mdelay(2);
return ret == 1 ? 0 : -EIO;
}
static int reg_read(struct i2c_client *client, const u16 addr)
{
u8 buf[2] = {addr >> 8, addr & 0xff};
int ret;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = buf,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = 2,
.buf = buf,
},
};
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0) {
dev_warn(&client->dev, "Reading register %x from %x failed\n",
addr, client->addr);
return ret;
}
return buf[0] & 0xff; /* no sign-extension */
}
static int imx074_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
if (!fmt) {
mf->code = imx074_colour_fmts[0].code;
mf->colorspace = imx074_colour_fmts[0].colorspace;
}
mf->width = IMX074_WIDTH;
mf->height = IMX074_HEIGHT;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int imx074_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct imx074 *priv = to_imx074(client);
dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
/* MIPI CSI could have changed the format, double-check */
if (!imx074_find_datafmt(mf->code))
return -EINVAL;
imx074_try_fmt(sd, mf);
priv->fmt = imx074_find_datafmt(mf->code);
return 0;
}
static int imx074_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct imx074 *priv = to_imx074(client);
const struct imx074_datafmt *fmt = priv->fmt;
mf->code = fmt->code;
mf->colorspace = fmt->colorspace;
mf->width = IMX074_WIDTH;
mf->height = IMX074_HEIGHT;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct v4l2_rect *rect = &a->c;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
rect->top = 0;
rect->left = 0;
rect->width = IMX074_WIDTH;
rect->height = IMX074_HEIGHT;
return 0;
}
static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = IMX074_WIDTH;
a->bounds.height = IMX074_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
return -EINVAL;
*code = imx074_colour_fmts[index].code;
return 0;
}
static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
/* MODE_SELECT: stream or standby */
return reg_write(client, MODE_SELECT, !!enable);
}
static int imx074_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct imx074 *priv = to_imx074(client);
return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static int imx074_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->type = V4L2_MBUS_CSI2;
cfg->flags = V4L2_MBUS_CSI2_2_LANE |
V4L2_MBUS_CSI2_CHANNEL_0 |
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
return 0;
}
static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
.s_stream = imx074_s_stream,
.s_mbus_fmt = imx074_s_fmt,
.g_mbus_fmt = imx074_g_fmt,
.try_mbus_fmt = imx074_try_fmt,
.enum_mbus_fmt = imx074_enum_fmt,
.g_crop = imx074_g_crop,
.cropcap = imx074_cropcap,
.g_mbus_config = imx074_g_mbus_config,
};
static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
.s_power = imx074_s_power,
};
static struct v4l2_subdev_ops imx074_subdev_ops = {
.core = &imx074_subdev_core_ops,
.video = &imx074_subdev_video_ops,
};
static int imx074_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
int ret;
u16 id;
ret = imx074_s_power(subdev, 1);
if (ret < 0)
return ret;
/* Read sensor Model ID */
ret = reg_read(client, 0);
if (ret < 0)
goto done;
id = ret << 8;
ret = reg_read(client, 1);
if (ret < 0)
goto done;
id |= ret;
dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
if (id != 0x74) {
ret = -ENODEV;
goto done;
}
/* PLL Setting EXTCLK=24MHz, 22.5times */
reg_write(client, PLL_MULTIPLIER, 0x2D);
reg_write(client, PRE_PLL_CLK_DIV, 0x02);
reg_write(client, PLSTATIM, 0x4B);
/* 2-lane mode */
reg_write(client, 0x3024, 0x00);
reg_write(client, IMAGE_ORIENTATION, 0x00);
/* select RAW mode:
* 0x08+0x08 = top 8 bits
* 0x0a+0x08 = compressed 8-bits
* 0x0a+0x0a = 10 bits
*/
reg_write(client, 0x0112, 0x08);
reg_write(client, 0x0113, 0x08);
/* Base setting for High frame mode */
reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
reg_write(client, Y_OPBADDR_START_DI, 0x08);
reg_write(client, 0x3015, 0x37);
reg_write(client, 0x301C, 0x01);
reg_write(client, 0x302C, 0x05);
reg_write(client, 0x3031, 0x26);
reg_write(client, 0x3041, 0x60);
reg_write(client, 0x3051, 0x24);
reg_write(client, 0x3053, 0x34);
reg_write(client, 0x3057, 0xC0);
reg_write(client, 0x305C, 0x09);
reg_write(client, 0x305D, 0x07);
reg_write(client, 0x3060, 0x30);
reg_write(client, 0x3065, 0x00);
reg_write(client, 0x30AA, 0x08);
reg_write(client, 0x30AB, 0x1C);
reg_write(client, 0x30B0, 0x32);
reg_write(client, 0x30B2, 0x83);
reg_write(client, 0x30D3, 0x04);
reg_write(client, 0x3106, 0x78);
reg_write(client, 0x310C, 0x82);
reg_write(client, 0x3304, 0x05);
reg_write(client, 0x3305, 0x04);
reg_write(client, 0x3306, 0x11);
reg_write(client, 0x3307, 0x02);
reg_write(client, 0x3308, 0x0C);
reg_write(client, 0x3309, 0x06);
reg_write(client, 0x330A, 0x08);
reg_write(client, 0x330B, 0x04);
reg_write(client, 0x330C, 0x08);
reg_write(client, 0x330D, 0x06);
reg_write(client, 0x330E, 0x01);
reg_write(client, 0x3381, 0x00);
/* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
/* 1608 = 1560 + 48 (black lines) */
reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
reg_write(client, YADDR_START, 0x00);
reg_write(client, YADDR_END, 0x2F);
/* 0x838 == 2104 */
reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
/* 0x618 == 1560 */
reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
reg_write(client, X_EVEN_INC, 0x01);
reg_write(client, X_ODD_INC, 0x03);
reg_write(client, Y_EVEN_INC, 0x01);
reg_write(client, Y_ODD_INC, 0x03);
reg_write(client, HMODEADD, 0x00);
reg_write(client, VMODEADD, 0x16);
reg_write(client, VAPPLINE_START, 0x24);
reg_write(client, VAPPLINE_END, 0x53);
reg_write(client, SHUTTER, 0x00);
reg_write(client, HADDAVE, 0x80);
reg_write(client, LANESEL, 0x00);
reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */
ret = 0;
done:
imx074_s_power(subdev, 0);
return ret;
}
static int imx074_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct imx074 *priv;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
if (!ssdd) {
dev_err(&client->dev, "IMX074: missing platform data!\n");
return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
return -EIO;
}
priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL);
if (!priv)
return -ENOMEM;
v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
priv->fmt = &imx074_colour_fmts[0];
priv->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(priv->clk)) {
dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk));
return -EPROBE_DEFER;
}
ret = soc_camera_power_init(&client->dev, ssdd);
if (ret < 0)
goto epwrinit;
ret = imx074_video_probe(client);
if (ret < 0)
goto eprobe;
ret = v4l2_async_register_subdev(&priv->subdev);
if (!ret)
return 0;
epwrinit:
eprobe:
v4l2_clk_put(priv->clk);
return ret;
}
static int imx074_remove(struct i2c_client *client)
{
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct imx074 *priv = to_imx074(client);
v4l2_async_unregister_subdev(&priv->subdev);
v4l2_clk_put(priv->clk);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
return 0;
}
static const struct i2c_device_id imx074_id[] = {
{ "imx074", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, imx074_id);
static struct i2c_driver imx074_i2c_driver = {
.driver = {
.name = "imx074",
},
.probe = imx074_probe,
.remove = imx074_remove,
.id_table = imx074_id,
};
module_i2c_driver(imx074_i2c_driver);
MODULE_DESCRIPTION("Sony IMX074 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,735 @@
/*
* Driver for MT9M001 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
/*
* mt9m001 i2c address 0x5d
* The platform has to define struct i2c_board_info objects and link to them
* from struct soc_camera_host_desc
*/
/* mt9m001 selected register addresses */
#define MT9M001_CHIP_VERSION 0x00
#define MT9M001_ROW_START 0x01
#define MT9M001_COLUMN_START 0x02
#define MT9M001_WINDOW_HEIGHT 0x03
#define MT9M001_WINDOW_WIDTH 0x04
#define MT9M001_HORIZONTAL_BLANKING 0x05
#define MT9M001_VERTICAL_BLANKING 0x06
#define MT9M001_OUTPUT_CONTROL 0x07
#define MT9M001_SHUTTER_WIDTH 0x09
#define MT9M001_FRAME_RESTART 0x0b
#define MT9M001_SHUTTER_DELAY 0x0c
#define MT9M001_RESET 0x0d
#define MT9M001_READ_OPTIONS1 0x1e
#define MT9M001_READ_OPTIONS2 0x20
#define MT9M001_GLOBAL_GAIN 0x35
#define MT9M001_CHIP_ENABLE 0xF1
#define MT9M001_MAX_WIDTH 1280
#define MT9M001_MAX_HEIGHT 1024
#define MT9M001_MIN_WIDTH 48
#define MT9M001_MIN_HEIGHT 32
#define MT9M001_COLUMN_SKIP 20
#define MT9M001_ROW_SKIP 12
/* MT9M001 has only one fixed colorspace per pixelcode */
struct mt9m001_datafmt {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */
static const struct mt9m001_datafmt *mt9m001_find_datafmt(
enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
int n)
{
int i;
for (i = 0; i < n; i++)
if (fmt[i].code == code)
return fmt + i;
return NULL;
}
static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
/*
* Order important: first natively supported,
* second supported with a GPIO extender
*/
{V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
/* Order important - see above */
{V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
{V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
};
struct mt9m001 {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct {
/* exposure/auto-exposure cluster */
struct v4l2_ctrl *autoexposure;
struct v4l2_ctrl *exposure;
};
struct v4l2_rect rect; /* Sensor window */
struct v4l2_clk *clk;
const struct mt9m001_datafmt *fmt;
const struct mt9m001_datafmt *fmts;
int num_fmts;
unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
};
static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
}
static int reg_read(struct i2c_client *client, const u8 reg)
{
return i2c_smbus_read_word_swapped(client, reg);
}
static int reg_write(struct i2c_client *client, const u8 reg,
const u16 data)
{
return i2c_smbus_write_word_swapped(client, reg, data);
}
static int reg_set(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret | data);
}
static int reg_clear(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret & ~data);
}
static int mt9m001_init(struct i2c_client *client)
{
int ret;
dev_dbg(&client->dev, "%s\n", __func__);
/*
* We don't know, whether platform provides reset, issue a soft reset
* too. This returns all registers to their default values.
*/
ret = reg_write(client, MT9M001_RESET, 1);
if (!ret)
ret = reg_write(client, MT9M001_RESET, 0);
/* Disable chip, synchronous option update */
if (!ret)
ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
return ret;
}
static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
/* Switch to master "normal" mode or stop sensor readout */
if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
return -EIO;
return 0;
}
static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct v4l2_rect rect = a->c;
int ret;
const u16 hblank = 9, vblank = 25;
if (mt9m001->fmts == mt9m001_colour_fmts)
/*
* Bayer format - even number of rows for simplicity,
* but let the user play with the top row.
*/
rect.height = ALIGN(rect.height, 2);
/* Datasheet requirement: see register description */
rect.width = ALIGN(rect.width, 2);
rect.left = ALIGN(rect.left, 2);
soc_camera_limit_side(&rect.left, &rect.width,
MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
soc_camera_limit_side(&rect.top, &rect.height,
MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
/* Blanking and start values - default... */
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
if (!ret)
ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
/*
* The caller provides a supported format, as verified per
* call to .try_mbus_fmt()
*/
if (!ret)
ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
if (!ret)
ret = reg_write(client, MT9M001_ROW_START, rect.top);
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
rect.height + mt9m001->y_skip_top - 1);
if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
if (!ret)
mt9m001->rect = rect;
return ret;
}
static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
a->c = mt9m001->rect;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = MT9M001_COLUMN_SKIP;
a->bounds.top = MT9M001_ROW_SKIP;
a->bounds.width = MT9M001_MAX_WIDTH;
a->bounds.height = MT9M001_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int mt9m001_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
mf->width = mt9m001->rect.width;
mf->height = mt9m001->rect.height;
mf->code = mt9m001->fmt->code;
mf->colorspace = mt9m001->fmt->colorspace;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int mt9m001_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct v4l2_crop a = {
.c = {
.left = mt9m001->rect.left,
.top = mt9m001->rect.top,
.width = mf->width,
.height = mf->height,
},
};
int ret;
/* No support for scaling so far, just crop. TODO: use skipping */
ret = mt9m001_s_crop(sd, &a);
if (!ret) {
mf->width = mt9m001->rect.width;
mf->height = mt9m001->rect.height;
mt9m001->fmt = mt9m001_find_datafmt(mf->code,
mt9m001->fmts, mt9m001->num_fmts);
mf->colorspace = mt9m001->fmt->colorspace;
}
return ret;
}
static int mt9m001_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
const struct mt9m001_datafmt *fmt;
v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
MT9M001_MAX_WIDTH, 1,
&mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
if (mt9m001->fmts == mt9m001_colour_fmts)
mf->height = ALIGN(mf->height - 1, 2);
fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
mt9m001->num_fmts);
if (!fmt) {
fmt = mt9m001->fmt;
mf->code = fmt->code;
}
mf->colorspace = fmt->colorspace;
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9m001_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
reg->size = 2;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
return -EIO;
return 0;
}
static int mt9m001_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
return 0;
}
#endif
static int mt9m001_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct mt9m001 *mt9m001 = to_mt9m001(client);
return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on);
}
static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9m001 *mt9m001 = container_of(ctrl->handler,
struct mt9m001, hdl);
s32 min, max;
switch (ctrl->id) {
case V4L2_CID_EXPOSURE_AUTO:
min = mt9m001->exposure->minimum;
max = mt9m001->exposure->maximum;
mt9m001->exposure->val =
(524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
break;
}
return 0;
}
static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9m001 *mt9m001 = container_of(ctrl->handler,
struct mt9m001, hdl);
struct v4l2_subdev *sd = &mt9m001->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_ctrl *exp = mt9m001->exposure;
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
else
data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
if (data < 0)
return -EIO;
return 0;
case V4L2_CID_GAIN:
/* See Datasheet Table 7, Gain settings. */
if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
unsigned long range = ctrl->default_value - ctrl->minimum;
data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
} else {
/* Pack it into 1.125..15 variable step, register values 9..67 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
unsigned long range = ctrl->maximum - ctrl->default_value - 1;
unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) *
111 + range / 2) / range + 9;
if (gain <= 32)
data = gain;
else if (gain <= 64)
data = ((gain - 32) * 16 + 16) / 32 + 80;
else
data = ((gain - 64) * 7 + 28) / 56 + 96;
dev_dbg(&client->dev, "Setting gain from %d to %d\n",
reg_read(client, MT9M001_GLOBAL_GAIN), data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
}
return 0;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
unsigned long range = exp->maximum - exp->minimum;
unsigned long shutter = ((exp->val - (s32)exp->minimum) * 1048 +
range / 2) / range + 1;
dev_dbg(&client->dev,
"Setting shutter width from %d to %lu\n",
reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
return -EIO;
} else {
const u16 vblank = 25;
mt9m001->total_h = mt9m001->rect.height +
mt9m001->y_skip_top + vblank;
if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
return -EIO;
}
return 0;
}
return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd,
struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
s32 data;
unsigned long flags;
int ret;
ret = mt9m001_s_power(&mt9m001->subdev, 1);
if (ret < 0)
return ret;
/* Enable the chip */
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&client->dev, "write: %d\n", data);
/* Read out the chip version register */
data = reg_read(client, MT9M001_CHIP_VERSION);
/* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
switch (data) {
case 0x8411:
case 0x8421:
mt9m001->fmts = mt9m001_colour_fmts;
break;
case 0x8431:
mt9m001->fmts = mt9m001_monochrome_fmts;
break;
default:
dev_err(&client->dev,
"No MT9M001 chip detected, register read %x\n", data);
ret = -ENODEV;
goto done;
}
mt9m001->num_fmts = 0;
/*
* This is a 10bit sensor, so by default we only allow 10bit.
* The platform may support different bus widths due to
* different routing of the data lines.
*/
if (ssdd->query_bus_param)
flags = ssdd->query_bus_param(ssdd);
else
flags = SOCAM_DATAWIDTH_10;
if (flags & SOCAM_DATAWIDTH_10)
mt9m001->num_fmts++;
else
mt9m001->fmts++;
if (flags & SOCAM_DATAWIDTH_8)
mt9m001->num_fmts++;
mt9m001->fmt = &mt9m001->fmts[0];
dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
data == 0x8431 ? "C12STM" : "C12ST");
ret = mt9m001_init(client);
if (ret < 0) {
dev_err(&client->dev, "Failed to initialise the camera\n");
goto done;
}
/* mt9m001_init() has reset the chip, returning registers to defaults */
ret = v4l2_ctrl_handler_setup(&mt9m001->hdl);
done:
mt9m001_s_power(&mt9m001->subdev, 0);
return ret;
}
static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd)
{
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
}
static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
*lines = mt9m001->y_skip_top;
return 0;
}
static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
.g_volatile_ctrl = mt9m001_g_volatile_ctrl,
.s_ctrl = mt9m001_s_ctrl,
};
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m001_g_register,
.s_register = mt9m001_s_register,
#endif
.s_power = mt9m001_s_power,
};
static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
if (index >= mt9m001->num_fmts)
return -EINVAL;
*code = mt9m001->fmts[index].code;
return 0;
}
static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
/* MT9M001 has all capture_format parameters fixed */
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
const struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct mt9m001 *mt9m001 = to_mt9m001(client);
unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
if (ssdd->set_bus_param)
return ssdd->set_bus_param(ssdd, 1 << (bps - 1));
/*
* Without board specific bus width settings we only support the
* sensors native bus width
*/
return bps == 10 ? 0 : -EINVAL;
}
static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
.s_stream = mt9m001_s_stream,
.s_mbus_fmt = mt9m001_s_fmt,
.g_mbus_fmt = mt9m001_g_fmt,
.try_mbus_fmt = mt9m001_try_fmt,
.s_crop = mt9m001_s_crop,
.g_crop = mt9m001_g_crop,
.cropcap = mt9m001_cropcap,
.enum_mbus_fmt = mt9m001_enum_fmt,
.g_mbus_config = mt9m001_g_mbus_config,
.s_mbus_config = mt9m001_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
.g_skip_top_lines = mt9m001_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9m001_subdev_ops = {
.core = &mt9m001_subdev_core_ops,
.video = &mt9m001_subdev_video_ops,
.sensor = &mt9m001_subdev_sensor_ops,
};
static int mt9m001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9m001 *mt9m001;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
if (!ssdd) {
dev_err(&client->dev, "MT9M001 driver needs platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;
}
mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL);
if (!mt9m001)
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
V4L2_CID_GAIN, 0, 127, 1, 64);
mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
/*
* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width
*/
mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
&mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
V4L2_EXPOSURE_AUTO);
mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
if (mt9m001->hdl.error)
return mt9m001->hdl.error;
v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
V4L2_EXPOSURE_MANUAL, true);
/* Second stage probe - when a capture adapter is there */
mt9m001->y_skip_top = 0;
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
mt9m001->rect.top = MT9M001_ROW_SKIP;
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
mt9m001->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(mt9m001->clk)) {
ret = PTR_ERR(mt9m001->clk);
goto eclkget;
}
ret = mt9m001_video_probe(ssdd, client);
if (ret) {
v4l2_clk_put(mt9m001->clk);
eclkget:
v4l2_ctrl_handler_free(&mt9m001->hdl);
}
return ret;
}
static int mt9m001_remove(struct i2c_client *client)
{
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
v4l2_clk_put(mt9m001->clk);
v4l2_device_unregister_subdev(&mt9m001->subdev);
v4l2_ctrl_handler_free(&mt9m001->hdl);
mt9m001_video_remove(ssdd);
return 0;
}
static const struct i2c_device_id mt9m001_id[] = {
{ "mt9m001", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9m001_id);
static struct i2c_driver mt9m001_i2c_driver = {
.driver = {
.name = "mt9m001",
},
.probe = mt9m001_probe,
.remove = mt9m001_remove,
.id_table = mt9m001_id,
};
module_i2c_driver(mt9m001_i2c_driver);
MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,840 @@
/*
* Driver for MT9T031 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
/*
* ATTENTION: this driver still cannot be used outside of the soc-camera
* framework because of its PM implementation, using the video_device node.
* If hardware becomes available for testing, alternative PM approaches shall
* be considered and tested.
*/
/*
* mt9t031 i2c address 0x5d
* The platform has to define struct i2c_board_info objects and link to them
* from struct soc_camera_host_desc
*/
/* mt9t031 selected register addresses */
#define MT9T031_CHIP_VERSION 0x00
#define MT9T031_ROW_START 0x01
#define MT9T031_COLUMN_START 0x02
#define MT9T031_WINDOW_HEIGHT 0x03
#define MT9T031_WINDOW_WIDTH 0x04
#define MT9T031_HORIZONTAL_BLANKING 0x05
#define MT9T031_VERTICAL_BLANKING 0x06
#define MT9T031_OUTPUT_CONTROL 0x07
#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
#define MT9T031_SHUTTER_WIDTH 0x09
#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
#define MT9T031_FRAME_RESTART 0x0b
#define MT9T031_SHUTTER_DELAY 0x0c
#define MT9T031_RESET 0x0d
#define MT9T031_READ_MODE_1 0x1e
#define MT9T031_READ_MODE_2 0x20
#define MT9T031_READ_MODE_3 0x21
#define MT9T031_ROW_ADDRESS_MODE 0x22
#define MT9T031_COLUMN_ADDRESS_MODE 0x23
#define MT9T031_GLOBAL_GAIN 0x35
#define MT9T031_CHIP_ENABLE 0xF8
#define MT9T031_MAX_HEIGHT 1536
#define MT9T031_MAX_WIDTH 2048
#define MT9T031_MIN_HEIGHT 2
#define MT9T031_MIN_WIDTH 18
#define MT9T031_HORIZONTAL_BLANK 142
#define MT9T031_VERTICAL_BLANK 25
#define MT9T031_COLUMN_SKIP 32
#define MT9T031_ROW_SKIP 20
struct mt9t031 {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct {
/* exposure/auto-exposure cluster */
struct v4l2_ctrl *autoexposure;
struct v4l2_ctrl *exposure;
};
struct v4l2_rect rect; /* Sensor window */
struct v4l2_clk *clk;
u16 xskip;
u16 yskip;
unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
};
static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
}
static int reg_read(struct i2c_client *client, const u8 reg)
{
return i2c_smbus_read_word_swapped(client, reg);
}
static int reg_write(struct i2c_client *client, const u8 reg,
const u16 data)
{
return i2c_smbus_write_word_swapped(client, reg, data);
}
static int reg_set(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret | data);
}
static int reg_clear(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret & ~data);
}
static int set_shutter(struct i2c_client *client, const u32 data)
{
int ret;
ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
if (ret >= 0)
ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);
return ret;
}
static int get_shutter(struct i2c_client *client, u32 *data)
{
int ret;
ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
*data = ret << 16;
if (ret >= 0)
ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
*data |= ret & 0xffff;
return ret < 0 ? ret : 0;
}
static int mt9t031_idle(struct i2c_client *client)
{
int ret;
/* Disable chip output, synchronous option update */
ret = reg_write(client, MT9T031_RESET, 1);
if (ret >= 0)
ret = reg_write(client, MT9T031_RESET, 0);
if (ret >= 0)
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
return ret >= 0 ? 0 : -EIO;
}
static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
if (enable)
/* Switch to master "normal" mode */
ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2);
else
/* Stop sensor readout */
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
if (ret < 0)
return -EIO;
return 0;
}
/* target must be _even_ */
static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
{
unsigned int skip;
if (*source < target + target / 2) {
*source = target;
return 1;
}
skip = min(max, *source + target / 2) / target;
if (skip > 8)
skip = 8;
*source = target * skip;
return skip;
}
/* rect is the sensor rectangle, the caller guarantees parameter validity */
static int mt9t031_set_params(struct i2c_client *client,
struct v4l2_rect *rect, u16 xskip, u16 yskip)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
u16 xbin, ybin;
const u16 hblank = MT9T031_HORIZONTAL_BLANK,
vblank = MT9T031_VERTICAL_BLANK;
xbin = min(xskip, (u16)3);
ybin = min(yskip, (u16)3);
/*
* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper.
* There is always a valid suitably aligned value. The worst case is
* xbin = 3, width = 2048. Then we will start at 36, the last read out
* pixel will be 2083, which is < 2085 - first black pixel.
*
* MT9T031 datasheet imposes window left border alignment, depending on
* the selected xskip. Failing to conform to this requirement produces
* dark horizontal stripes in the image. However, even obeying to this
* requirement doesn't eliminate the stripes in all configurations. They
* appear "locally reproducibly," but can differ between tests under
* different lighting conditions.
*/
switch (xbin) {
case 1:
rect->left &= ~1;
break;
case 2:
rect->left &= ~3;
break;
case 3:
rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ?
(rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6);
}
rect->top &= ~1;
dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
xskip, yskip, rect->width, rect->height, rect->left, rect->top);
/* Disable register update, reconfigure atomically */
ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
if (ret < 0)
return ret;
/* Blanking and start values - default... */
ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank);
if (ret >= 0)
ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank);
if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
/* Binning, skipping */
if (ret >= 0)
ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
((xbin - 1) << 4) | (xskip - 1));
if (ret >= 0)
ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
((ybin - 1) << 4) | (yskip - 1));
}
dev_dbg(&client->dev, "new physical left %u, top %u\n",
rect->left, rect->top);
/*
* The caller provides a supported format, as guaranteed by
* .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
*/
if (ret >= 0)
ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
if (ret >= 0)
ret = reg_write(client, MT9T031_ROW_START, rect->top);
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
rect->height + mt9t031->y_skip_top - 1);
if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
ret = set_shutter(client, mt9t031->total_h);
}
/* Re-enable register update, commit all changes */
if (ret >= 0)
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
if (ret >= 0) {
mt9t031->rect = *rect;
mt9t031->xskip = xskip;
mt9t031->yskip = yskip;
}
return ret < 0 ? ret : 0;
}
static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
{
struct v4l2_rect rect = a->c;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
rect.width = ALIGN(rect.width, 2);
rect.height = ALIGN(rect.height, 2);
soc_camera_limit_side(&rect.left, &rect.width,
MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH);
soc_camera_limit_side(&rect.top, &rect.height,
MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
}
static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
a->c = mt9t031->rect;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = MT9T031_COLUMN_SKIP;
a->bounds.top = MT9T031_ROW_SKIP;
a->bounds.width = MT9T031_MAX_WIDTH;
a->bounds.height = MT9T031_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int mt9t031_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
mf->width = mt9t031->rect.width / mt9t031->xskip;
mf->height = mt9t031->rect.height / mt9t031->yskip;
mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int mt9t031_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
u16 xskip, yskip;
struct v4l2_rect rect = mt9t031->rect;
/*
* try_fmt has put width and height within limits.
* S_FMT: use binning and skipping for scaling
*/
xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
/* mt9t031_set_params() doesn't change width and height */
return mt9t031_set_params(client, &rect, xskip, yskip);
}
/*
* If a user window larger than sensor window is requested, we'll increase the
* sensor window.
*/
static int mt9t031_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
v4l_bound_align_image(
&mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
&mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9t031_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
reg->size = 1;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
return -EIO;
return 0;
}
static int mt9t031_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
return 0;
}
#endif
static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9t031 *mt9t031 = container_of(ctrl->handler,
struct mt9t031, hdl);
const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
s32 min, max;
switch (ctrl->id) {
case V4L2_CID_EXPOSURE_AUTO:
min = mt9t031->exposure->minimum;
max = mt9t031->exposure->maximum;
mt9t031->exposure->val =
(shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
/ shutter_max + min;
break;
}
return 0;
}
static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9t031 *mt9t031 = container_of(ctrl->handler,
struct mt9t031, hdl);
struct v4l2_subdev *sd = &mt9t031->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_ctrl *exp = mt9t031->exposure;
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
if (data < 0)
return -EIO;
return 0;
case V4L2_CID_HFLIP:
if (ctrl->val)
data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
else
data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
if (data < 0)
return -EIO;
return 0;
case V4L2_CID_GAIN:
/* See Datasheet Table 7, Gain settings. */
if (ctrl->val <= ctrl->default_value) {
/* Pack it into 0..1 step 0.125, register values 0..8 */
unsigned long range = ctrl->default_value - ctrl->minimum;
data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range;
dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
} else {
/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
unsigned long range = ctrl->maximum - ctrl->default_value - 1;
/* calculated gain: map 65..127 to 9..1024 step 0.125 */
unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) *
1015 + range / 2) / range + 9;
if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
data = gain;
else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */
data = ((gain - 32) * 16 + 16) / 32 + 80;
else
/* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n",
reg_read(client, MT9T031_GLOBAL_GAIN), data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
}
return 0;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
unsigned int range = exp->maximum - exp->minimum;
unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 +
range / 2) / range + 1;
u32 old;
get_shutter(client, &old);
dev_dbg(&client->dev, "Set shutter from %u to %u\n",
old, shutter);
if (set_shutter(client, shutter) < 0)
return -EIO;
} else {
const u16 vblank = MT9T031_VERTICAL_BLANK;
mt9t031->total_h = mt9t031->rect.height +
mt9t031->y_skip_top + vblank;
if (set_shutter(client, mt9t031->total_h) < 0)
return -EIO;
}
return 0;
default:
return -EINVAL;
}
return 0;
}
/*
* Power Management:
* This function does nothing for now but must be present for pm to work
*/
static int mt9t031_runtime_suspend(struct device *dev)
{
return 0;
}
/*
* Power Management:
* COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged
* they are however changed at reset if the platform hook is present
* thus we rewrite them with the values stored by the driver
*/
static int mt9t031_runtime_resume(struct device *dev)
{
struct video_device *vdev = to_video_device(dev);
struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
u16 xbin, ybin;
xbin = min(mt9t031->xskip, (u16)3);
ybin = min(mt9t031->yskip, (u16)3);
ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
((xbin - 1) << 4) | (mt9t031->xskip - 1));
if (ret < 0)
return ret;
ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
((ybin - 1) << 4) | (mt9t031->yskip - 1));
if (ret < 0)
return ret;
return 0;
}
static const struct dev_pm_ops mt9t031_dev_pm_ops = {
.runtime_suspend = mt9t031_runtime_suspend,
.runtime_resume = mt9t031_runtime_resume,
};
static struct device_type mt9t031_dev_type = {
.name = "MT9T031",
.pm = &mt9t031_dev_pm_ops,
};
static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct video_device *vdev = soc_camera_i2c_to_vdev(client);
struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
if (on) {
ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk);
if (ret < 0)
return ret;
if (vdev)
/* Not needed during probing, when vdev isn't available yet */
vdev->dev.type = &mt9t031_dev_type;
} else {
if (vdev)
vdev->dev.type = NULL;
soc_camera_power_off(&client->dev, ssdd, mt9t031->clk);
}
return 0;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
static int mt9t031_video_probe(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
s32 data;
int ret;
ret = mt9t031_s_power(&mt9t031->subdev, 1);
if (ret < 0)
return ret;
ret = mt9t031_idle(client);
if (ret < 0) {
dev_err(&client->dev, "Failed to initialise the camera\n");
goto done;
}
/* Read out the chip version register */
data = reg_read(client, MT9T031_CHIP_VERSION);
switch (data) {
case 0x1621:
break;
default:
dev_err(&client->dev,
"No MT9T031 chip detected, register read %x\n", data);
ret = -ENODEV;
goto done;
}
dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
ret = v4l2_ctrl_handler_setup(&mt9t031->hdl);
done:
mt9t031_s_power(&mt9t031->subdev, 0);
return ret;
}
static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t031 *mt9t031 = to_mt9t031(client);
*lines = mt9t031->y_skip_top;
return 0;
}
static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
.g_volatile_ctrl = mt9t031_g_volatile_ctrl,
.s_ctrl = mt9t031_s_ctrl,
};
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
.s_power = mt9t031_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9t031_g_register,
.s_register = mt9t031_s_register,
#endif
};
static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index)
return -EINVAL;
*code = V4L2_MBUS_FMT_SBGGR10_1X10;
return 0;
}
static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
if (soc_camera_apply_board_flags(ssdd, cfg) &
V4L2_MBUS_PCLK_SAMPLE_FALLING)
return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
else
return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
}
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
.s_stream = mt9t031_s_stream,
.s_mbus_fmt = mt9t031_s_fmt,
.g_mbus_fmt = mt9t031_g_fmt,
.try_mbus_fmt = mt9t031_try_fmt,
.s_crop = mt9t031_s_crop,
.g_crop = mt9t031_g_crop,
.cropcap = mt9t031_cropcap,
.enum_mbus_fmt = mt9t031_enum_fmt,
.g_mbus_config = mt9t031_g_mbus_config,
.s_mbus_config = mt9t031_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
.g_skip_top_lines = mt9t031_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9t031_subdev_ops = {
.core = &mt9t031_subdev_core_ops,
.video = &mt9t031_subdev_video_ops,
.sensor = &mt9t031_subdev_sensor_ops,
};
static int mt9t031_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9t031 *mt9t031;
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
int ret;
if (!ssdd) {
dev_err(&client->dev, "MT9T031 driver needs platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;
}
mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL);
if (!mt9t031)
return -ENOMEM;
v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
V4L2_CID_GAIN, 0, 127, 1, 64);
/*
* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width
*/
mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
&mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
V4L2_EXPOSURE_AUTO);
mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
if (mt9t031->hdl.error)
return mt9t031->hdl.error;
v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
V4L2_EXPOSURE_MANUAL, true);
mt9t031->y_skip_top = 0;
mt9t031->rect.left = MT9T031_COLUMN_SKIP;
mt9t031->rect.top = MT9T031_ROW_SKIP;
mt9t031->rect.width = MT9T031_MAX_WIDTH;
mt9t031->rect.height = MT9T031_MAX_HEIGHT;
mt9t031->xskip = 1;
mt9t031->yskip = 1;
mt9t031->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(mt9t031->clk)) {
ret = PTR_ERR(mt9t031->clk);
goto eclkget;
}
ret = mt9t031_video_probe(client);
if (ret) {
v4l2_clk_put(mt9t031->clk);
eclkget:
v4l2_ctrl_handler_free(&mt9t031->hdl);
}
return ret;
}
static int mt9t031_remove(struct i2c_client *client)
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
v4l2_clk_put(mt9t031->clk);
v4l2_device_unregister_subdev(&mt9t031->subdev);
v4l2_ctrl_handler_free(&mt9t031->hdl);
return 0;
}
static const struct i2c_device_id mt9t031_id[] = {
{ "mt9t031", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9t031_id);
static struct i2c_driver mt9t031_i2c_driver = {
.driver = {
.name = "mt9t031",
},
.probe = mt9t031_probe,
.remove = mt9t031_remove,
.id_table = mt9t031_id,
};
module_i2c_driver(mt9t031_i2c_driver);
MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,990 @@
/*
* Driver for MT9V022 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <media/mt9v022.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
/*
* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
* The platform has to define struct i2c_board_info objects and link to them
* from struct soc_camera_host_desc
*/
static char *sensor_type;
module_param(sensor_type, charp, S_IRUGO);
MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
/* mt9v022 selected register addresses */
#define MT9V022_CHIP_VERSION 0x00
#define MT9V022_COLUMN_START 0x01
#define MT9V022_ROW_START 0x02
#define MT9V022_WINDOW_HEIGHT 0x03
#define MT9V022_WINDOW_WIDTH 0x04
#define MT9V022_HORIZONTAL_BLANKING 0x05
#define MT9V022_VERTICAL_BLANKING 0x06
#define MT9V022_CHIP_CONTROL 0x07
#define MT9V022_SHUTTER_WIDTH1 0x08
#define MT9V022_SHUTTER_WIDTH2 0x09
#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a
#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b
#define MT9V022_RESET 0x0c
#define MT9V022_READ_MODE 0x0d
#define MT9V022_MONITOR_MODE 0x0e
#define MT9V022_PIXEL_OPERATION_MODE 0x0f
#define MT9V022_LED_OUT_CONTROL 0x1b
#define MT9V022_ADC_MODE_CONTROL 0x1c
#define MT9V022_REG32 0x20
#define MT9V022_ANALOG_GAIN 0x35
#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
#define MT9V022_PIXCLK_FV_LV 0x74
#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
#define MT9V022_AEC_AGC_ENABLE 0xAF
#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD
/* mt9v024 partial list register addresses changes with respect to mt9v022 */
#define MT9V024_PIXCLK_FV_LV 0x72
#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD
/* Progressive scan, master, defaults */
#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
#define MT9V022_MAX_WIDTH 752
#define MT9V022_MAX_HEIGHT 480
#define MT9V022_MIN_WIDTH 48
#define MT9V022_MIN_HEIGHT 32
#define MT9V022_COLUMN_SKIP 1
#define MT9V022_ROW_SKIP 4
#define MT9V022_HORIZONTAL_BLANKING_MIN 43
#define MT9V022_HORIZONTAL_BLANKING_MAX 1023
#define MT9V022_HORIZONTAL_BLANKING_DEF 94
#define MT9V022_VERTICAL_BLANKING_MIN 2
#define MT9V022_VERTICAL_BLANKING_MAX 3000
#define MT9V022_VERTICAL_BLANKING_DEF 45
#define is_mt9v022_rev3(id) (id == 0x1313)
#define is_mt9v024(id) (id == 0x1324)
/* MT9V022 has only one fixed colorspace per pixelcode */
struct mt9v022_datafmt {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */
static const struct mt9v022_datafmt *mt9v022_find_datafmt(
enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
int n)
{
int i;
for (i = 0; i < n; i++)
if (fmt[i].code == code)
return fmt + i;
return NULL;
}
static const struct mt9v022_datafmt mt9v022_colour_fmts[] = {
/*
* Order important: first natively supported,
* second supported with a GPIO extender
*/
{V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
/* Order important - see above */
{V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
{V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
};
/* only registers with different addresses on different mt9v02x sensors */
struct mt9v02x_register {
u8 max_total_shutter_width;
u8 pixclk_fv_lv;
};
static const struct mt9v02x_register mt9v022_register = {
.max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
.pixclk_fv_lv = MT9V022_PIXCLK_FV_LV,
};
static const struct mt9v02x_register mt9v024_register = {
.max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH,
.pixclk_fv_lv = MT9V024_PIXCLK_FV_LV,
};
enum mt9v022_model {
MT9V022IX7ATM,
MT9V022IX7ATC,
};
struct mt9v022 {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct {
/* exposure/auto-exposure cluster */
struct v4l2_ctrl *autoexposure;
struct v4l2_ctrl *exposure;
};
struct {
/* gain/auto-gain cluster */
struct v4l2_ctrl *autogain;
struct v4l2_ctrl *gain;
};
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_rect rect; /* Sensor window */
struct v4l2_clk *clk;
const struct mt9v022_datafmt *fmt;
const struct mt9v022_datafmt *fmts;
const struct mt9v02x_register *reg;
int num_fmts;
enum mt9v022_model model;
u16 chip_control;
u16 chip_version;
unsigned short y_skip_top; /* Lines to skip at the top */
};
static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct mt9v022, subdev);
}
static int reg_read(struct i2c_client *client, const u8 reg)
{
return i2c_smbus_read_word_swapped(client, reg);
}
static int reg_write(struct i2c_client *client, const u8 reg,
const u16 data)
{
return i2c_smbus_write_word_swapped(client, reg, data);
}
static int reg_set(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret | data);
}
static int reg_clear(struct i2c_client *client, const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, ret & ~data);
}
static int mt9v022_init(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
int ret;
/*
* Almost the default mode: master, parallel, simultaneous, and an
* undocumented bit 0x200, which is present in table 7, but not in 8,
* plus snapshot mode to disable scan for now
*/
mt9v022->chip_control |= 0x10;
ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
if (!ret)
ret = reg_write(client, MT9V022_READ_MODE, 0x300);
/* All defaults */
if (!ret)
/* AEC, AGC on */
ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3);
if (!ret)
ret = reg_write(client, MT9V022_ANALOG_GAIN, 16);
if (!ret)
ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480);
if (!ret)
ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480);
if (!ret)
/* default - auto */
ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
if (!ret)
ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
if (!ret)
return v4l2_ctrl_handler_setup(&mt9v022->hdl);
return ret;
}
static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
if (enable) {
/* Switch to master "normal" mode */
mt9v022->chip_control &= ~0x10;
if (is_mt9v022_rev3(mt9v022->chip_version) ||
is_mt9v024(mt9v022->chip_version)) {
/*
* Unset snapshot mode specific settings: clear bit 9
* and bit 2 in reg. 0x20 when in normal mode.
*/
if (reg_clear(client, MT9V022_REG32, 0x204))
return -EIO;
}
} else {
/* Switch to snapshot mode */
mt9v022->chip_control |= 0x10;
if (is_mt9v022_rev3(mt9v022->chip_version) ||
is_mt9v024(mt9v022->chip_version)) {
/*
* Required settings for snapshot mode: set bit 9
* (RST enable) and bit 2 (CR enable) in reg. 0x20
* See TechNote TN0960 or TN-09-225.
*/
if (reg_set(client, MT9V022_REG32, 0x204))
return -EIO;
}
}
if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
return -EIO;
return 0;
}
static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct v4l2_rect rect = a->c;
int min_row, min_blank;
int ret;
/* Bayer format - even size lengths */
if (mt9v022->fmts == mt9v022_colour_fmts) {
rect.width = ALIGN(rect.width, 2);
rect.height = ALIGN(rect.height, 2);
/* Let the user play with the starting pixel */
}
soc_camera_limit_side(&rect.left, &rect.width,
MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH);
soc_camera_limit_side(&rect.top, &rect.height,
MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT);
/* Like in example app. Contradicts the datasheet though */
ret = reg_read(client, MT9V022_AEC_AGC_ENABLE);
if (ret >= 0) {
if (ret & 1) /* Autoexposure */
ret = reg_write(client, mt9v022->reg->max_total_shutter_width,
rect.height + mt9v022->y_skip_top + 43);
/*
* If autoexposure is off, there is no need to set
* MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off
* only if the user has set exposure manually, using the
* V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL.
* In this case the register MT9V022_TOTAL_SHUTTER_WIDTH
* already contains the correct value.
*/
}
/* Setup frame format: defaults apart from width and height */
if (!ret)
ret = reg_write(client, MT9V022_COLUMN_START, rect.left);
if (!ret)
ret = reg_write(client, MT9V022_ROW_START, rect.top);
/*
* mt9v022: min total row time is 660 columns, min blanking is 43
* mt9v024: min total row time is 690 columns, min blanking is 61
*/
if (is_mt9v024(mt9v022->chip_version)) {
min_row = 690;
min_blank = 61;
} else {
min_row = 660;
min_blank = 43;
}
if (!ret)
ret = v4l2_ctrl_s_ctrl(mt9v022->hblank,
rect.width > min_row - min_blank ?
min_blank : min_row - rect.width);
if (!ret)
ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45);
if (!ret)
ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
if (!ret)
ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
rect.height + mt9v022->y_skip_top);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height);
mt9v022->rect = rect;
return 0;
}
static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
a->c = mt9v022->rect;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = MT9V022_COLUMN_SKIP;
a->bounds.top = MT9V022_ROW_SKIP;
a->bounds.width = MT9V022_MAX_WIDTH;
a->bounds.height = MT9V022_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int mt9v022_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
mf->width = mt9v022->rect.width;
mf->height = mt9v022->rect.height;
mf->code = mt9v022->fmt->code;
mf->colorspace = mt9v022->fmt->colorspace;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int mt9v022_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct v4l2_crop a = {
.c = {
.left = mt9v022->rect.left,
.top = mt9v022->rect.top,
.width = mf->width,
.height = mf->height,
},
};
int ret;
/*
* The caller provides a supported format, as verified per call to
* .try_mbus_fmt(), datawidth is from our supported format list
*/
switch (mf->code) {
case V4L2_MBUS_FMT_Y8_1X8:
case V4L2_MBUS_FMT_Y10_1X10:
if (mt9v022->model != MT9V022IX7ATM)
return -EINVAL;
break;
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SBGGR10_1X10:
if (mt9v022->model != MT9V022IX7ATC)
return -EINVAL;
break;
default:
return -EINVAL;
}
/* No support for scaling on this camera, just crop. */
ret = mt9v022_s_crop(sd, &a);
if (!ret) {
mf->width = mt9v022->rect.width;
mf->height = mt9v022->rect.height;
mt9v022->fmt = mt9v022_find_datafmt(mf->code,
mt9v022->fmts, mt9v022->num_fmts);
mf->colorspace = mt9v022->fmt->colorspace;
}
return ret;
}
static int mt9v022_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
const struct mt9v022_datafmt *fmt;
int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
MT9V022_MAX_WIDTH, align,
&mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top,
MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0);
fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts,
mt9v022->num_fmts);
if (!fmt) {
fmt = mt9v022->fmt;
mf->code = fmt->code;
}
mf->colorspace = fmt->colorspace;
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9v022_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
reg->size = 2;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
return -EIO;
return 0;
}
static int mt9v022_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff)
return -EINVAL;
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
return 0;
}
#endif
static int mt9v022_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct mt9v022 *mt9v022 = to_mt9v022(client);
return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on);
}
static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9v022 *mt9v022 = container_of(ctrl->handler,
struct mt9v022, hdl);
struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_ctrl *gain = mt9v022->gain;
struct v4l2_ctrl *exp = mt9v022->exposure;
unsigned long range;
int data;
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
data = reg_read(client, MT9V022_ANALOG_GAIN);
if (data < 0)
return -EIO;
range = gain->maximum - gain->minimum;
gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
return 0;
case V4L2_CID_EXPOSURE_AUTO:
data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
if (data < 0)
return -EIO;
range = exp->maximum - exp->minimum;
exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
return 0;
case V4L2_CID_HBLANK:
data = reg_read(client, MT9V022_HORIZONTAL_BLANKING);
if (data < 0)
return -EIO;
ctrl->val = data;
return 0;
case V4L2_CID_VBLANK:
data = reg_read(client, MT9V022_VERTICAL_BLANKING);
if (data < 0)
return -EIO;
ctrl->val = data;
return 0;
}
return -EINVAL;
}
static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9v022 *mt9v022 = container_of(ctrl->handler,
struct mt9v022, hdl);
struct v4l2_subdev *sd = &mt9v022->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
int data;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x10);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x10);
if (data < 0)
return -EIO;
return 0;
case V4L2_CID_HFLIP:
if (ctrl->val)
data = reg_set(client, MT9V022_READ_MODE, 0x20);
else
data = reg_clear(client, MT9V022_READ_MODE, 0x20);
if (data < 0)
return -EIO;
return 0;
case V4L2_CID_AUTOGAIN:
if (ctrl->val) {
if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
} else {
struct v4l2_ctrl *gain = mt9v022->gain;
/* mt9v022 has minimum == default */
unsigned long range = gain->maximum - gain->minimum;
/* Valid values 16 to 64, 32 to 64 must be even. */
unsigned long gain_val = ((gain->val - (s32)gain->minimum) *
48 + range / 2) / range + 16;
if (gain_val >= 32)
gain_val &= ~1;
/*
* The user wants to set gain manually, hope, she
* knows, what she's doing... Switch AGC off.
*/
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
return -EIO;
}
return 0;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->val == V4L2_EXPOSURE_AUTO) {
data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
} else {
struct v4l2_ctrl *exp = mt9v022->exposure;
unsigned long range = exp->maximum - exp->minimum;
unsigned long shutter = ((exp->val - (s32)exp->minimum) *
479 + range / 2) / range + 1;
/*
* The user wants to set shutter width manually, hope,
* she knows, what she's doing... Switch AEC off.
*/
data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
if (data < 0)
return -EIO;
dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
shutter);
if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
shutter) < 0)
return -EIO;
}
return 0;
case V4L2_CID_HBLANK:
if (reg_write(client, MT9V022_HORIZONTAL_BLANKING,
ctrl->val) < 0)
return -EIO;
return 0;
case V4L2_CID_VBLANK:
if (reg_write(client, MT9V022_VERTICAL_BLANKING,
ctrl->val) < 0)
return -EIO;
return 0;
}
return -EINVAL;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
static int mt9v022_video_probe(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
s32 data;
int ret;
unsigned long flags;
ret = mt9v022_s_power(&mt9v022->subdev, 1);
if (ret < 0)
return ret;
/* Read out the chip version register */
data = reg_read(client, MT9V022_CHIP_VERSION);
/* must be 0x1311, 0x1313 or 0x1324 */
if (data != 0x1311 && data != 0x1313 && data != 0x1324) {
ret = -ENODEV;
dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n",
data);
goto ei2c;
}
mt9v022->chip_version = data;
mt9v022->reg = is_mt9v024(data) ? &mt9v024_register :
&mt9v022_register;
/* Soft reset */
ret = reg_write(client, MT9V022_RESET, 1);
if (ret < 0)
goto ei2c;
/* 15 clock cycles */
udelay(200);
if (reg_read(client, MT9V022_RESET)) {
dev_err(&client->dev, "Resetting MT9V022 failed!\n");
if (ret > 0)
ret = -EIO;
goto ei2c;
}
/* Set monochrome or colour sensor type */
if (sensor_type && (!strcmp("colour", sensor_type) ||
!strcmp("color", sensor_type))) {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
mt9v022->model = MT9V022IX7ATC;
mt9v022->fmts = mt9v022_colour_fmts;
} else {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
mt9v022->model = MT9V022IX7ATM;
mt9v022->fmts = mt9v022_monochrome_fmts;
}
if (ret < 0)
goto ei2c;
mt9v022->num_fmts = 0;
/*
* This is a 10bit sensor, so by default we only allow 10bit.
* The platform may support different bus widths due to
* different routing of the data lines.
*/
if (ssdd->query_bus_param)
flags = ssdd->query_bus_param(ssdd);
else
flags = SOCAM_DATAWIDTH_10;
if (flags & SOCAM_DATAWIDTH_10)
mt9v022->num_fmts++;
else
mt9v022->fmts++;
if (flags & SOCAM_DATAWIDTH_8)
mt9v022->num_fmts++;
mt9v022->fmt = &mt9v022->fmts[0];
dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
data, mt9v022->model == MT9V022IX7ATM ?
"monochrome" : "colour");
ret = mt9v022_init(client);
if (ret < 0)
dev_err(&client->dev, "Failed to initialise the camera\n");
ei2c:
mt9v022_s_power(&mt9v022->subdev, 0);
return ret;
}
static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
*lines = mt9v022->y_skip_top;
return 0;
}
static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
.g_volatile_ctrl = mt9v022_g_volatile_ctrl,
.s_ctrl = mt9v022_s_ctrl,
};
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v022_g_register,
.s_register = mt9v022_s_register,
#endif
.s_power = mt9v022_s_power,
};
static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
if (index >= mt9v022->num_fmts)
return -EINVAL;
*code = mt9v022->fmts[index].code;
return 0;
}
static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct mt9v022 *mt9v022 = to_mt9v022(client);
unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg);
unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
int ret;
u16 pixclk = 0;
if (ssdd->set_bus_param) {
ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1));
if (ret)
return ret;
} else if (bps != 10) {
/*
* Without board specific bus width settings we only support the
* sensors native bus width
*/
return -EINVAL;
}
if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
pixclk |= 0x10;
if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
pixclk |= 0x1;
if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
pixclk |= 0x2;
ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk);
if (ret < 0)
return ret;
if (!(flags & V4L2_MBUS_MASTER))
mt9v022->chip_control &= ~0x8;
ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
pixclk, mt9v022->chip_control);
return 0;
}
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
.s_stream = mt9v022_s_stream,
.s_mbus_fmt = mt9v022_s_fmt,
.g_mbus_fmt = mt9v022_g_fmt,
.try_mbus_fmt = mt9v022_try_fmt,
.s_crop = mt9v022_s_crop,
.g_crop = mt9v022_g_crop,
.cropcap = mt9v022_cropcap,
.enum_mbus_fmt = mt9v022_enum_fmt,
.g_mbus_config = mt9v022_g_mbus_config,
.s_mbus_config = mt9v022_s_mbus_config,
};
static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
.g_skip_top_lines = mt9v022_g_skip_top_lines,
};
static struct v4l2_subdev_ops mt9v022_subdev_ops = {
.core = &mt9v022_subdev_core_ops,
.video = &mt9v022_subdev_video_ops,
.sensor = &mt9v022_subdev_sensor_ops,
};
static int mt9v022_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct mt9v022 *mt9v022;
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct mt9v022_platform_data *pdata;
int ret;
if (!ssdd) {
dev_err(&client->dev, "MT9V022 driver needs platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;
}
mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL);
if (!mt9v022)
return -ENOMEM;
pdata = ssdd->drv_priv;
v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_GAIN, 0, 127, 1, 64);
/*
* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width
*/
mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
&mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
V4L2_EXPOSURE_AUTO);
mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN,
MT9V022_HORIZONTAL_BLANKING_MAX, 1,
MT9V022_HORIZONTAL_BLANKING_DEF);
mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN,
MT9V022_VERTICAL_BLANKING_MAX, 1,
MT9V022_VERTICAL_BLANKING_DEF);
mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
if (mt9v022->hdl.error) {
int err = mt9v022->hdl.error;
dev_err(&client->dev, "control initialisation err %d\n", err);
return err;
}
v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
V4L2_EXPOSURE_MANUAL, true);
v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
/*
* On some platforms the first read out line is corrupted.
* Workaround it by skipping if indicated by platform data.
*/
mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0;
mt9v022->rect.left = MT9V022_COLUMN_SKIP;
mt9v022->rect.top = MT9V022_ROW_SKIP;
mt9v022->rect.width = MT9V022_MAX_WIDTH;
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
mt9v022->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(mt9v022->clk)) {
ret = PTR_ERR(mt9v022->clk);
goto eclkget;
}
ret = mt9v022_video_probe(client);
if (ret) {
v4l2_clk_put(mt9v022->clk);
eclkget:
v4l2_ctrl_handler_free(&mt9v022->hdl);
}
return ret;
}
static int mt9v022_remove(struct i2c_client *client)
{
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
v4l2_clk_put(mt9v022->clk);
v4l2_device_unregister_subdev(&mt9v022->subdev);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
v4l2_ctrl_handler_free(&mt9v022->hdl);
return 0;
}
static const struct i2c_device_id mt9v022_id[] = {
{ "mt9v022", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9v022_id);
static struct i2c_driver mt9v022_i2c_driver = {
.driver = {
.name = "mt9v022",
},
.probe = mt9v022_probe,
.remove = mt9v022_remove,
.id_table = mt9v022_id,
};
module_i2c_driver(mt9v022_i2c_driver);
MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,751 @@
/*
* OmniVision OV96xx Camera Driver
*
* Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
*
* Based on ov772x camera driver:
*
* Copyright (C) 2008 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ov7670 and soc_camera_platform driver,
*
* Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include "ov9640.h"
#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev)
/* default register setup */
static const struct ov9640_reg ov9640_regs_dflt[] = {
{ OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
{ OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
{ OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
{ OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
{ OV9640_TSLB, OV9640_TSLB_YUYV_UYVY },
{ OV9640_COM16, OV9640_COM16_RB_AVG },
/* Gamma curve P */
{ 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
{ 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
{ 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
{ 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
/* Gamma curve T */
{ 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
{ 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
{ 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
{ 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
};
/* Configurations
* NOTE: for YUV, alter the following registers:
* COM12 |= OV9640_COM12_YUV_AVG
*
* for RGB, alter the following registers:
* COM7 |= OV9640_COM7_RGB
* COM13 |= OV9640_COM13_RGB_AVG
* COM15 |= proper RGB color encoding mode
*/
static const struct ov9640_reg ov9640_regs_qqcif[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
{ OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
{ OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
{ OV9640_COM7, OV9640_COM7_QCIF },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_qqvga[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
{ OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
{ OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
{ OV9640_COM7, OV9640_COM7_QVGA },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_qcif[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
{ OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
{ OV9640_COM7, OV9640_COM7_QCIF },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_qvga[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
{ OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
{ OV9640_COM7, OV9640_COM7_QVGA },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_cif[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
{ OV9640_COM3, OV9640_COM3_VP },
{ OV9640_COM7, OV9640_COM7_CIF },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_vga[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
{ OV9640_COM3, OV9640_COM3_VP },
{ OV9640_COM7, OV9640_COM7_VGA },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_sxga[] = {
{ OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
{ OV9640_COM3, OV9640_COM3_VP },
{ OV9640_COM7, 0 },
{ OV9640_COM12, OV9640_COM12_RSVD },
{ OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
{ OV9640_COM15, OV9640_COM15_OR_10F0 },
};
static const struct ov9640_reg ov9640_regs_yuv[] = {
{ OV9640_MTX1, 0x58 },
{ OV9640_MTX2, 0x48 },
{ OV9640_MTX3, 0x10 },
{ OV9640_MTX4, 0x28 },
{ OV9640_MTX5, 0x48 },
{ OV9640_MTX6, 0x70 },
{ OV9640_MTX7, 0x40 },
{ OV9640_MTX8, 0x40 },
{ OV9640_MTX9, 0x40 },
{ OV9640_MTXS, 0x0f },
};
static const struct ov9640_reg ov9640_regs_rgb[] = {
{ OV9640_MTX1, 0x71 },
{ OV9640_MTX2, 0x3e },
{ OV9640_MTX3, 0x0c },
{ OV9640_MTX4, 0x33 },
{ OV9640_MTX5, 0x72 },
{ OV9640_MTX6, 0x00 },
{ OV9640_MTX7, 0x2b },
{ OV9640_MTX8, 0x66 },
{ OV9640_MTX9, 0xd2 },
{ OV9640_MTXS, 0x65 },
};
static enum v4l2_mbus_pixelcode ov9640_codes[] = {
V4L2_MBUS_FMT_UYVY8_2X8,
V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
V4L2_MBUS_FMT_RGB565_2X8_LE,
};
/* read a register */
static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
{
int ret;
u8 data = reg;
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
msg.flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
*val = data;
return 0;
err:
dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
return ret;
}
/* write a register */
static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
{
int ret;
u8 _val;
unsigned char data[2] = { reg, val };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
return ret;
}
/* we have to read the register back ... no idea why, maybe HW bug */
ret = ov9640_reg_read(client, reg, &_val);
if (ret)
dev_err(&client->dev,
"Failed reading back register 0x%02x!\n", reg);
return 0;
}
/* Read a register, alter its bits, write it back */
static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
{
u8 val;
int ret;
ret = ov9640_reg_read(client, reg, &val);
if (ret) {
dev_err(&client->dev,
"[Read]-Modify-Write of register %02x failed!\n", reg);
return val;
}
val |= set;
val &= ~unset;
ret = ov9640_reg_write(client, reg, val);
if (ret)
dev_err(&client->dev,
"Read-Modify-[Write] of register %02x failed!\n", reg);
return ret;
}
/* Soft reset the camera. This has nothing to do with the RESET pin! */
static int ov9640_reset(struct i2c_client *client)
{
int ret;
ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
if (ret)
dev_err(&client->dev,
"An error occurred while entering soft reset!\n");
return ret;
}
/* Start/Stop streaming from the device */
static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
{
return 0;
}
/* Set status of additional camera capabilities */
static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_V, 0);
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
case V4L2_CID_HFLIP:
if (ctrl->val)
return ov9640_reg_rmw(client, OV9640_MVFP,
OV9640_MVFP_H, 0);
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
}
return -EINVAL;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov9640_get_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
u8 val;
if (reg->reg & ~0xff)
return -EINVAL;
reg->size = 1;
ret = ov9640_reg_read(client, reg->reg, &val);
if (ret)
return ret;
reg->val = (__u64)val;
return 0;
}
static int ov9640_set_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg & ~0xff || reg->val & ~0xff)
return -EINVAL;
return ov9640_reg_write(client, reg->reg, reg->val);
}
#endif
static int ov9640_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
/* select nearest higher resolution for capture */
static void ov9640_res_roundup(u32 *width, u32 *height)
{
int i;
enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
for (i = 0; i < ARRAY_SIZE(res_x); i++) {
if (res_x[i] >= *width && res_y[i] >= *height) {
*width = res_x[i];
*height = res_y[i];
return;
}
}
*width = res_x[SXGA];
*height = res_y[SXGA];
}
/* Prepare necessary register changes depending on color encoding */
static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
struct ov9640_reg_alt *alt)
{
switch (code) {
default:
case V4L2_MBUS_FMT_UYVY8_2X8:
alt->com12 = OV9640_COM12_YUV_AVG;
alt->com13 = OV9640_COM13_Y_DELAY_EN |
OV9640_COM13_YUV_DLY(0x01);
break;
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_555;
break;
case V4L2_MBUS_FMT_RGB565_2X8_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_565;
break;
}
}
/* Setup registers according to resolution and color encoding */
static int ov9640_write_regs(struct i2c_client *client, u32 width,
enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
{
const struct ov9640_reg *ov9640_regs, *matrix_regs;
int ov9640_regs_len, matrix_regs_len;
int i, ret;
u8 val;
/* select register configuration for given resolution */
switch (width) {
case W_QQCIF:
ov9640_regs = ov9640_regs_qqcif;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
break;
case W_QQVGA:
ov9640_regs = ov9640_regs_qqvga;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
break;
case W_QCIF:
ov9640_regs = ov9640_regs_qcif;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
break;
case W_QVGA:
ov9640_regs = ov9640_regs_qvga;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
break;
case W_CIF:
ov9640_regs = ov9640_regs_cif;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
break;
case W_VGA:
ov9640_regs = ov9640_regs_vga;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
break;
case W_SXGA:
ov9640_regs = ov9640_regs_sxga;
ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
break;
default:
dev_err(&client->dev, "Failed to select resolution!\n");
return -EINVAL;
}
/* select color matrix configuration for given color encoding */
if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
matrix_regs = ov9640_regs_yuv;
matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
} else {
matrix_regs = ov9640_regs_rgb;
matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
}
/* write register settings into the module */
for (i = 0; i < ov9640_regs_len; i++) {
val = ov9640_regs[i].val;
switch (ov9640_regs[i].reg) {
case OV9640_COM7:
val |= alts->com7;
break;
case OV9640_COM12:
val |= alts->com12;
break;
case OV9640_COM13:
val |= alts->com13;
break;
case OV9640_COM15:
val |= alts->com15;
break;
}
ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
if (ret)
return ret;
}
/* write color matrix configuration into the module */
for (i = 0; i < matrix_regs_len; i++) {
ret = ov9640_reg_write(client, matrix_regs[i].reg,
matrix_regs[i].val);
if (ret)
return ret;
}
return 0;
}
/* program default register values */
static int ov9640_prog_dflt(struct i2c_client *client)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
ov9640_regs_dflt[i].val);
if (ret)
return ret;
}
/* wait for the changes to actually happen, 140ms are not enough yet */
mdelay(150);
return 0;
}
/* set the format we will capture in */
static int ov9640_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov9640_reg_alt alts = {0};
enum v4l2_colorspace cspace;
enum v4l2_mbus_pixelcode code = mf->code;
int ret;
ov9640_res_roundup(&mf->width, &mf->height);
ov9640_alter_regs(mf->code, &alts);
ov9640_reset(client);
ret = ov9640_prog_dflt(client);
if (ret)
return ret;
switch (code) {
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
cspace = V4L2_COLORSPACE_SRGB;
break;
default:
code = V4L2_MBUS_FMT_UYVY8_2X8;
case V4L2_MBUS_FMT_UYVY8_2X8:
cspace = V4L2_COLORSPACE_JPEG;
}
ret = ov9640_write_regs(client, mf->width, code, &alts);
if (!ret) {
mf->code = code;
mf->colorspace = cspace;
}
return ret;
}
static int ov9640_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
ov9640_res_roundup(&mf->width, &mf->height);
mf->field = V4L2_FIELD_NONE;
switch (mf->code) {
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
case V4L2_MBUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
return 0;
}
static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(ov9640_codes))
return -EINVAL;
*code = ov9640_codes[index];
return 0;
}
static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
a->c.left = 0;
a->c.top = 0;
a->c.width = W_SXGA;
a->c.height = H_SXGA;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = W_SXGA;
a->bounds.height = H_SXGA;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int ov9640_video_probe(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
u8 pid, ver, midh, midl;
const char *devname;
int ret;
ret = ov9640_s_power(&priv->subdev, 1);
if (ret < 0)
return ret;
/*
* check and show product ID and manufacturer ID
*/
ret = ov9640_reg_read(client, OV9640_PID, &pid);
if (!ret)
ret = ov9640_reg_read(client, OV9640_VER, &ver);
if (!ret)
ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
if (!ret)
ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
if (ret)
goto done;
switch (VERSION(pid, ver)) {
case OV9640_V2:
devname = "ov9640";
priv->revision = 2;
break;
case OV9640_V3:
devname = "ov9640";
priv->revision = 3;
break;
default:
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
ret = -ENODEV;
goto done;
}
dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
ret = v4l2_ctrl_handler_setup(&priv->hdl);
done:
ov9640_s_power(&priv->subdev, 0);
return ret;
}
static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
.s_ctrl = ov9640_s_ctrl,
};
static struct v4l2_subdev_core_ops ov9640_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9640_get_register,
.s_register = ov9640_set_register,
#endif
.s_power = ov9640_s_power,
};
/* Request bus settings on camera side */
static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
static struct v4l2_subdev_video_ops ov9640_video_ops = {
.s_stream = ov9640_s_stream,
.s_mbus_fmt = ov9640_s_fmt,
.try_mbus_fmt = ov9640_try_fmt,
.enum_mbus_fmt = ov9640_enum_fmt,
.cropcap = ov9640_cropcap,
.g_crop = ov9640_g_crop,
.g_mbus_config = ov9640_g_mbus_config,
};
static struct v4l2_subdev_ops ov9640_subdev_ops = {
.core = &ov9640_core_ops,
.video = &ov9640_video_ops,
};
/*
* i2c_driver function
*/
static int ov9640_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov9640_priv *priv;
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
if (!ssdd) {
dev_err(&client->dev, "Missing platform_data for driver\n");
return -EINVAL;
}
priv = devm_kzalloc(&client->dev, sizeof(struct ov9640_priv), GFP_KERNEL);
if (!priv) {
dev_err(&client->dev,
"Failed to allocate memory for private data!\n");
return -ENOMEM;
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
v4l2_ctrl_handler_init(&priv->hdl, 2);
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
priv->subdev.ctrl_handler = &priv->hdl;
if (priv->hdl.error)
return priv->hdl.error;
priv->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto eclkget;
}
ret = ov9640_video_probe(client);
if (ret) {
v4l2_clk_put(priv->clk);
eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
}
return ret;
}
static int ov9640_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
}
static const struct i2c_device_id ov9640_id[] = {
{ "ov9640", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov9640_id);
static struct i2c_driver ov9640_i2c_driver = {
.driver = {
.name = "ov9640",
},
.probe = ov9640_probe,
.remove = ov9640_remove,
.id_table = ov9640_id,
};
module_i2c_driver(ov9640_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,208 @@
/*
* OmniVision OV96xx Camera Header File
*
* Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__
#define __DRIVERS_MEDIA_VIDEO_OV9640_H__
/* Register definitions */
#define OV9640_GAIN 0x00
#define OV9640_BLUE 0x01
#define OV9640_RED 0x02
#define OV9640_VFER 0x03
#define OV9640_COM1 0x04
#define OV9640_BAVE 0x05
#define OV9640_GEAVE 0x06
#define OV9640_RSID 0x07
#define OV9640_RAVE 0x08
#define OV9640_COM2 0x09
#define OV9640_PID 0x0a
#define OV9640_VER 0x0b
#define OV9640_COM3 0x0c
#define OV9640_COM4 0x0d
#define OV9640_COM5 0x0e
#define OV9640_COM6 0x0f
#define OV9640_AECH 0x10
#define OV9640_CLKRC 0x11
#define OV9640_COM7 0x12
#define OV9640_COM8 0x13
#define OV9640_COM9 0x14
#define OV9640_COM10 0x15
/* 0x16 - RESERVED */
#define OV9640_HSTART 0x17
#define OV9640_HSTOP 0x18
#define OV9640_VSTART 0x19
#define OV9640_VSTOP 0x1a
#define OV9640_PSHFT 0x1b
#define OV9640_MIDH 0x1c
#define OV9640_MIDL 0x1d
#define OV9640_MVFP 0x1e
#define OV9640_LAEC 0x1f
#define OV9640_BOS 0x20
#define OV9640_GBOS 0x21
#define OV9640_GROS 0x22
#define OV9640_ROS 0x23
#define OV9640_AEW 0x24
#define OV9640_AEB 0x25
#define OV9640_VPT 0x26
#define OV9640_BBIAS 0x27
#define OV9640_GBBIAS 0x28
/* 0x29 - RESERVED */
#define OV9640_EXHCH 0x2a
#define OV9640_EXHCL 0x2b
#define OV9640_RBIAS 0x2c
#define OV9640_ADVFL 0x2d
#define OV9640_ADVFH 0x2e
#define OV9640_YAVE 0x2f
#define OV9640_HSYST 0x30
#define OV9640_HSYEN 0x31
#define OV9640_HREF 0x32
#define OV9640_CHLF 0x33
#define OV9640_ARBLM 0x34
/* 0x35..0x36 - RESERVED */
#define OV9640_ADC 0x37
#define OV9640_ACOM 0x38
#define OV9640_OFON 0x39
#define OV9640_TSLB 0x3a
#define OV9640_COM11 0x3b
#define OV9640_COM12 0x3c
#define OV9640_COM13 0x3d
#define OV9640_COM14 0x3e
#define OV9640_EDGE 0x3f
#define OV9640_COM15 0x40
#define OV9640_COM16 0x41
#define OV9640_COM17 0x42
/* 0x43..0x4e - RESERVED */
#define OV9640_MTX1 0x4f
#define OV9640_MTX2 0x50
#define OV9640_MTX3 0x51
#define OV9640_MTX4 0x52
#define OV9640_MTX5 0x53
#define OV9640_MTX6 0x54
#define OV9640_MTX7 0x55
#define OV9640_MTX8 0x56
#define OV9640_MTX9 0x57
#define OV9640_MTXS 0x58
/* 0x59..0x61 - RESERVED */
#define OV9640_LCC1 0x62
#define OV9640_LCC2 0x63
#define OV9640_LCC3 0x64
#define OV9640_LCC4 0x65
#define OV9640_LCC5 0x66
#define OV9640_MANU 0x67
#define OV9640_MANV 0x68
#define OV9640_HV 0x69
#define OV9640_MBD 0x6a
#define OV9640_DBLV 0x6b
#define OV9640_GSP 0x6c /* ... till 0x7b */
#define OV9640_GST 0x7c /* ... till 0x8a */
#define OV9640_CLKRC_DPLL_EN 0x80
#define OV9640_CLKRC_DIRECT 0x40
#define OV9640_CLKRC_DIV(x) ((x) & 0x3f)
#define OV9640_PSHFT_VAL(x) ((x) & 0xff)
#define OV9640_ACOM_2X_ANALOG 0x80
#define OV9640_ACOM_RSVD 0x12
#define OV9640_MVFP_V 0x10
#define OV9640_MVFP_H 0x20
#define OV9640_COM1_HREF_NOSKIP 0x00
#define OV9640_COM1_HREF_2SKIP 0x04
#define OV9640_COM1_HREF_3SKIP 0x08
#define OV9640_COM1_QQFMT 0x20
#define OV9640_COM2_SSM 0x10
#define OV9640_COM3_VP 0x04
#define OV9640_COM4_QQ_VP 0x80
#define OV9640_COM4_RSVD 0x40
#define OV9640_COM5_SYSCLK 0x80
#define OV9640_COM5_LONGEXP 0x01
#define OV9640_COM6_OPT_BLC 0x40
#define OV9640_COM6_ADBLC_BIAS 0x08
#define OV9640_COM6_FMT_RST 0x82
#define OV9640_COM6_ADBLC_OPTEN 0x01
#define OV9640_COM7_RAW_RGB 0x01
#define OV9640_COM7_RGB 0x04
#define OV9640_COM7_QCIF 0x08
#define OV9640_COM7_QVGA 0x10
#define OV9640_COM7_CIF 0x20
#define OV9640_COM7_VGA 0x40
#define OV9640_COM7_SCCB_RESET 0x80
#define OV9640_TSLB_YVYU_YUYV 0x04
#define OV9640_TSLB_YUYV_UYVY 0x08
#define OV9640_COM12_YUV_AVG 0x04
#define OV9640_COM12_RSVD 0x40
#define OV9640_COM13_GAMMA_NONE 0x00
#define OV9640_COM13_GAMMA_Y 0x40
#define OV9640_COM13_GAMMA_RAW 0x80
#define OV9640_COM13_RGB_AVG 0x20
#define OV9640_COM13_MATRIX_EN 0x10
#define OV9640_COM13_Y_DELAY_EN 0x08
#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07)
#define OV9640_COM15_OR_00FF 0x00
#define OV9640_COM15_OR_01FE 0x40
#define OV9640_COM15_OR_10F0 0xc0
#define OV9640_COM15_RGB_NORM 0x00
#define OV9640_COM15_RGB_565 0x10
#define OV9640_COM15_RGB_555 0x30
#define OV9640_COM16_RB_AVG 0x01
/* IDs */
#define OV9640_V2 0x9648
#define OV9640_V3 0x9649
#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF))
/* supported resolutions */
enum {
W_QQCIF = 88,
W_QQVGA = 160,
W_QCIF = 176,
W_QVGA = 320,
W_CIF = 352,
W_VGA = 640,
W_SXGA = 1280
};
#define H_SXGA 960
/* Misc. structures */
struct ov9640_reg_alt {
u8 com7;
u8 com12;
u8 com13;
u8 com15;
};
struct ov9640_reg {
u8 reg;
u8 val;
};
struct ov9640_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct v4l2_clk *clk;
int model;
int revision;
};
#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,973 @@
/*
* tw9910 Video Driver
*
* Copyright (C) 2008 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ov772x driver,
*
* Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
* Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
#include <media/tw9910.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#define GET_ID(val) ((val & 0xF8) >> 3)
#define GET_REV(val) (val & 0x07)
/*
* register offset
*/
#define ID 0x00 /* Product ID Code Register */
#define STATUS1 0x01 /* Chip Status Register I */
#define INFORM 0x02 /* Input Format */
#define OPFORM 0x03 /* Output Format Control Register */
#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
#define OUTCTR1 0x05 /* Output Control I */
#define ACNTL1 0x06 /* Analog Control Register 1 */
#define CROP_HI 0x07 /* Cropping Register, High */
#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
#define CNTRL1 0x0C /* Control Register I */
#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
#define SCALE_HI 0x0E /* Scaling Register, High */
#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
#define CONTRAST 0x11 /* CONTRAST Control Register */
#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
#define SAT_U 0x13 /* Chroma (U) Gain Register */
#define SAT_V 0x14 /* Chroma (V) Gain Register */
#define HUE 0x15 /* Hue Control Register */
#define CORING1 0x17
#define CORING2 0x18 /* Coring and IF compensation */
#define VBICNTL 0x19 /* VBI Control Register */
#define ACNTL2 0x1A /* Analog Control 2 */
#define OUTCTR2 0x1B /* Output Control 2 */
#define SDT 0x1C /* Standard Selection */
#define SDTR 0x1D /* Standard Recognition */
#define TEST 0x1F /* Test Control Register */
#define CLMPG 0x20 /* Clamping Gain */
#define IAGC 0x21 /* Individual AGC Gain */
#define AGCGAIN 0x22 /* AGC Gain */
#define PEAKWT 0x23 /* White Peak Threshold */
#define CLMPL 0x24 /* Clamp level */
#define SYNCT 0x25 /* Sync Amplitude */
#define MISSCNT 0x26 /* Sync Miss Count Register */
#define PCLAMP 0x27 /* Clamp Position Register */
#define VCNTL1 0x28 /* Vertical Control I */
#define VCNTL2 0x29 /* Vertical Control II */
#define CKILL 0x2A /* Color Killer Level Control */
#define COMB 0x2B /* Comb Filter Control */
#define LDLY 0x2C /* Luma Delay and H Filter Control */
#define MISC1 0x2D /* Miscellaneous Control I */
#define LOOP 0x2E /* LOOP Control Register */
#define MISC2 0x2F /* Miscellaneous Control II */
#define MVSN 0x30 /* Macrovision Detection */
#define STATUS2 0x31 /* Chip STATUS II */
#define HFREF 0x32 /* H monitor */
#define CLMD 0x33 /* CLAMP MODE */
#define IDCNTL 0x34 /* ID Detection Control */
#define CLCNTL1 0x35 /* Clamp Control I */
#define ANAPLLCTL 0x4C
#define VBIMIN 0x4D
#define HSLOWCTL 0x4E
#define WSS3 0x4F
#define FILLDATA 0x50
#define SDID 0x51
#define DID 0x52
#define WSS1 0x53
#define WSS2 0x54
#define VVBI 0x55
#define LCTL6 0x56
#define LCTL7 0x57
#define LCTL8 0x58
#define LCTL9 0x59
#define LCTL10 0x5A
#define LCTL11 0x5B
#define LCTL12 0x5C
#define LCTL13 0x5D
#define LCTL14 0x5E
#define LCTL15 0x5F
#define LCTL16 0x60
#define LCTL17 0x61
#define LCTL18 0x62
#define LCTL19 0x63
#define LCTL20 0x64
#define LCTL21 0x65
#define LCTL22 0x66
#define LCTL23 0x67
#define LCTL24 0x68
#define LCTL25 0x69
#define LCTL26 0x6A
#define HSBEGIN 0x6B
#define HSEND 0x6C
#define OVSDLY 0x6D
#define OVSEND 0x6E
#define VBIDELAY 0x6F
/*
* register detail
*/
/* INFORM */
#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
#define FC27_FF 0x00 /* 0 : Square pixel mode. */
/* Must use 24.54MHz for 60Hz field rate */
/* source or 29.5MHz for 50Hz field rate */
#define IFSEL_S 0x10 /* 01 : S-video decoding */
#define IFSEL_C 0x00 /* 00 : Composite video decoding */
/* Y input video selection */
#define YSEL_M0 0x00 /* 00 : Mux0 selected */
#define YSEL_M1 0x04 /* 01 : Mux1 selected */
#define YSEL_M2 0x08 /* 10 : Mux2 selected */
#define YSEL_M3 0x10 /* 11 : Mux3 selected */
/* OPFORM */
#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
/* 1 : ITU-R-656 compatible data sequence format */
#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
/* 1 : 16-bit YCrCb 4:2:2 output format.*/
#define LLCMODE 0x20 /* 1 : LLC output mode. */
/* 0 : free-run output mode */
#define AINC 0x10 /* Serial interface auto-indexing control */
/* 0 : auto-increment */
/* 1 : non-auto */
#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
/* 0 : Vertical out ctrl by HACTIVE and DVALID */
#define OEN_TRI_SEL_MASK 0x07
#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
/* OUTCTR1 */
#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
/* VS pin output control */
#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
#define VSSL_VACT 0x10 /* 1 : VACT */
#define VSSL_FIELD 0x20 /* 2 : FIELD */
#define VSSL_VVALID 0x30 /* 3 : VVALID */
#define VSSL_ZERO 0x70 /* 7 : 0 */
#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
/* HS pin output control */
#define HSSL_HACT 0x00 /* 0 : HACT */
#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
#define HSSL_DVALID 0x02 /* 2 : DVALID */
#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
#define HSSL_ZERO 0x07 /* 7 : 0 */
/* ACNTL1 */
#define SRESET 0x80 /* resets the device to its default state
* but all register content remain unchanged.
* This bit is self-resetting.
*/
#define ACNTL1_PDN_MASK 0x0e
#define CLK_PDN 0x08 /* system clock power down */
#define Y_PDN 0x04 /* Luma ADC power down */
#define C_PDN 0x02 /* Chroma ADC power down */
/* ACNTL2 */
#define ACNTL2_PDN_MASK 0x40
#define PLL_PDN 0x40 /* PLL power down */
/* VBICNTL */
/* RTSEL : control the real time signal output from the MPOUT pin */
#define RTSEL_MASK 0x07
#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
#define RTSEL_MONO 0x04 /* 0100 = MONO */
#define RTSEL_DET50 0x05 /* 0101 = DET50 */
#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
/* HSYNC start and end are constant for now */
#define HSYNC_START 0x0260
#define HSYNC_END 0x0300
/*
* structure
*/
struct regval_list {
unsigned char reg_num;
unsigned char value;
};
struct tw9910_scale_ctrl {
char *name;
unsigned short width;
unsigned short height;
u16 hscale;
u16 vscale;
};
struct tw9910_priv {
struct v4l2_subdev subdev;
struct v4l2_clk *clk;
struct tw9910_video_info *info;
const struct tw9910_scale_ctrl *scale;
v4l2_std_id norm;
u32 revision;
};
static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
{
.name = "NTSC SQ",
.width = 640,
.height = 480,
.hscale = 0x0100,
.vscale = 0x0100,
},
{
.name = "NTSC CCIR601",
.width = 720,
.height = 480,
.hscale = 0x0100,
.vscale = 0x0100,
},
{
.name = "NTSC SQ (CIF)",
.width = 320,
.height = 240,
.hscale = 0x0200,
.vscale = 0x0200,
},
{
.name = "NTSC CCIR601 (CIF)",
.width = 360,
.height = 240,
.hscale = 0x0200,
.vscale = 0x0200,
},
{
.name = "NTSC SQ (QCIF)",
.width = 160,
.height = 120,
.hscale = 0x0400,
.vscale = 0x0400,
},
{
.name = "NTSC CCIR601 (QCIF)",
.width = 180,
.height = 120,
.hscale = 0x0400,
.vscale = 0x0400,
},
};
static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
{
.name = "PAL SQ",
.width = 768,
.height = 576,
.hscale = 0x0100,
.vscale = 0x0100,
},
{
.name = "PAL CCIR601",
.width = 720,
.height = 576,
.hscale = 0x0100,
.vscale = 0x0100,
},
{
.name = "PAL SQ (CIF)",
.width = 384,
.height = 288,
.hscale = 0x0200,
.vscale = 0x0200,
},
{
.name = "PAL CCIR601 (CIF)",
.width = 360,
.height = 288,
.hscale = 0x0200,
.vscale = 0x0200,
},
{
.name = "PAL SQ (QCIF)",
.width = 192,
.height = 144,
.hscale = 0x0400,
.vscale = 0x0400,
},
{
.name = "PAL CCIR601 (QCIF)",
.width = 180,
.height = 144,
.hscale = 0x0400,
.vscale = 0x0400,
},
};
/*
* general function
*/
static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct tw9910_priv,
subdev);
}
static int tw9910_mask_set(struct i2c_client *client, u8 command,
u8 mask, u8 set)
{
s32 val = i2c_smbus_read_byte_data(client, command);
if (val < 0)
return val;
val &= ~mask;
val |= set & mask;
return i2c_smbus_write_byte_data(client, command, val);
}
static int tw9910_set_scale(struct i2c_client *client,
const struct tw9910_scale_ctrl *scale)
{
int ret;
ret = i2c_smbus_write_byte_data(client, SCALE_HI,
(scale->vscale & 0x0F00) >> 4 |
(scale->hscale & 0x0F00) >> 8);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
scale->hscale & 0x00FF);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
scale->vscale & 0x00FF);
return ret;
}
static int tw9910_set_hsync(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
int ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSBEGIN,
(HSYNC_START & 0x07F8) >> 3);
if (ret < 0)
return ret;
/* bit 10 - 3 */
ret = i2c_smbus_write_byte_data(client, HSEND,
(HSYNC_END & 0x07F8) >> 3);
if (ret < 0)
return ret;
/* So far only revisions 0 and 1 have been seen */
/* bit 2 - 0 */
if (1 == priv->revision)
ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
(HSYNC_START & 0x0007) << 4 |
(HSYNC_END & 0x0007));
return ret;
}
static void tw9910_reset(struct i2c_client *client)
{
tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
msleep(1);
}
static int tw9910_power(struct i2c_client *client, int enable)
{
int ret;
u8 acntl1;
u8 acntl2;
if (enable) {
acntl1 = 0;
acntl2 = 0;
} else {
acntl1 = CLK_PDN | Y_PDN | C_PDN;
acntl2 = PLL_PDN;
}
ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
if (ret < 0)
return ret;
return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
}
static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
u32 width, u32 height)
{
const struct tw9910_scale_ctrl *scale;
const struct tw9910_scale_ctrl *ret = NULL;
__u32 diff = 0xffffffff, tmp;
int size, i;
if (norm & V4L2_STD_NTSC) {
scale = tw9910_ntsc_scales;
size = ARRAY_SIZE(tw9910_ntsc_scales);
} else if (norm & V4L2_STD_PAL) {
scale = tw9910_pal_scales;
size = ARRAY_SIZE(tw9910_pal_scales);
} else {
return NULL;
}
for (i = 0; i < size; i++) {
tmp = abs(width - scale[i].width) +
abs(height - scale[i].height);
if (tmp < diff) {
diff = tmp;
ret = scale + i;
}
}
return ret;
}
/*
* subdevice operations
*/
static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
u8 val;
int ret;
if (!enable) {
switch (priv->revision) {
case 0:
val = OEN_TRI_SEL_ALL_OFF_r0;
break;
case 1:
val = OEN_TRI_SEL_ALL_OFF_r1;
break;
default:
dev_err(&client->dev, "un-supported revision\n");
return -EINVAL;
}
} else {
val = OEN_TRI_SEL_ALL_ON;
if (!priv->scale) {
dev_err(&client->dev, "norm select error\n");
return -EPERM;
}
dev_dbg(&client->dev, "%s %dx%d\n",
priv->scale->name,
priv->scale->width,
priv->scale->height);
}
ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
if (ret < 0)
return ret;
return tw9910_power(client, enable);
}
static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
*norm = priv->norm;
return 0;
}
static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
return -EINVAL;
priv->norm = norm;
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int tw9910_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
if (reg->reg > 0xff)
return -EINVAL;
reg->size = 1;
ret = i2c_smbus_read_byte_data(client, reg->reg);
if (ret < 0)
return ret;
/*
* ret = int
* reg->val = __u64
*/
reg->val = (__u64)ret;
return 0;
}
static int tw9910_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->reg > 0xff ||
reg->val > 0xff)
return -EINVAL;
return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
}
#endif
static int tw9910_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct tw9910_priv *priv = to_tw9910(client);
return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
int ret = -EINVAL;
u8 val;
/*
* select suitable norm
*/
priv->scale = tw9910_select_norm(priv->norm, *width, *height);
if (!priv->scale)
goto tw9910_set_fmt_error;
/*
* reset hardware
*/
tw9910_reset(client);
/*
* set bus width
*/
val = 0x00;
if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
val = LEN;
ret = tw9910_mask_set(client, OPFORM, LEN, val);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* select MPOUT behavior
*/
switch (priv->info->mpout) {
case TW9910_MPO_VLOSS:
val = RTSEL_VLOSS; break;
case TW9910_MPO_HLOCK:
val = RTSEL_HLOCK; break;
case TW9910_MPO_SLOCK:
val = RTSEL_SLOCK; break;
case TW9910_MPO_VLOCK:
val = RTSEL_VLOCK; break;
case TW9910_MPO_MONO:
val = RTSEL_MONO; break;
case TW9910_MPO_DET50:
val = RTSEL_DET50; break;
case TW9910_MPO_FIELD:
val = RTSEL_FIELD; break;
case TW9910_MPO_RTCO:
val = RTSEL_RTCO; break;
default:
val = 0;
}
ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* set scale
*/
ret = tw9910_set_scale(client, priv->scale);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* set hsync
*/
ret = tw9910_set_hsync(client);
if (ret < 0)
goto tw9910_set_fmt_error;
*width = priv->scale->width;
*height = priv->scale->height;
return ret;
tw9910_set_fmt_error:
tw9910_reset(client);
priv->scale = NULL;
return ret;
}
static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
a->c.left = 0;
a->c.top = 0;
if (priv->norm & V4L2_STD_NTSC) {
a->c.width = 640;
a->c.height = 480;
} else {
a->c.width = 768;
a->c.height = 576;
}
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
a->bounds.left = 0;
a->bounds.top = 0;
if (priv->norm & V4L2_STD_NTSC) {
a->bounds.width = 640;
a->bounds.height = 480;
} else {
a->bounds.width = 768;
a->bounds.height = 576;
}
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int tw9910_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
if (!priv->scale) {
priv->scale = tw9910_select_norm(priv->norm, 640, 480);
if (!priv->scale)
return -EINVAL;
}
mf->width = priv->scale->width;
mf->height = priv->scale->height;
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_JPEG;
mf->field = V4L2_FIELD_INTERLACED_BT;
return 0;
}
static int tw9910_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
u32 width = mf->width, height = mf->height;
int ret;
WARN_ON(mf->field != V4L2_FIELD_ANY &&
mf->field != V4L2_FIELD_INTERLACED_BT);
/*
* check color format
*/
if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
return -EINVAL;
mf->colorspace = V4L2_COLORSPACE_JPEG;
ret = tw9910_set_frame(sd, &width, &height);
if (!ret) {
mf->width = width;
mf->height = height;
}
return ret;
}
static int tw9910_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tw9910_priv *priv = to_tw9910(client);
const struct tw9910_scale_ctrl *scale;
if (V4L2_FIELD_ANY == mf->field) {
mf->field = V4L2_FIELD_INTERLACED_BT;
} else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
return -EINVAL;
}
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_JPEG;
/*
* select suitable norm
*/
scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
if (!scale)
return -EINVAL;
mf->width = scale->width;
mf->height = scale->height;
return 0;
}
static int tw9910_video_probe(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
s32 id;
int ret;
/*
* tw9910 only use 8 or 16 bit bus width
*/
if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
dev_err(&client->dev, "bus width error\n");
return -ENODEV;
}
ret = tw9910_s_power(&priv->subdev, 1);
if (ret < 0)
return ret;
/*
* check and show Product ID
* So far only revisions 0 and 1 have been seen
*/
id = i2c_smbus_read_byte_data(client, ID);
priv->revision = GET_REV(id);
id = GET_ID(id);
if (0x0B != id ||
0x01 < priv->revision) {
dev_err(&client->dev,
"Product ID error %x:%x\n",
id, priv->revision);
ret = -ENODEV;
goto done;
}
dev_info(&client->dev,
"tw9910 Product ID %0x:%0x\n", id, priv->revision);
priv->norm = V4L2_STD_NTSC;
done:
tw9910_s_power(&priv->subdev, 0);
return ret;
}
static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tw9910_g_register,
.s_register = tw9910_s_register,
#endif
.s_power = tw9910_s_power,
};
static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index)
return -EINVAL;
*code = V4L2_MBUS_FMT_UYVY8_2X8;
return 0;
}
static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = soc_camera_apply_board_flags(ssdd, cfg);
return 0;
}
static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
u8 val = VSSL_VVALID | HSSL_DVALID;
unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg);
/*
* set OUTCTR1
*
* We use VVALID and DVALID signals to control VSYNC and HSYNC
* outputs, in this mode their polarity is inverted.
*/
if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
val |= HSP_HI;
if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
val |= VSP_HI;
return i2c_smbus_write_byte_data(client, OUTCTR1, val);
}
static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
*norm = V4L2_STD_NTSC | V4L2_STD_PAL;
return 0;
}
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.s_std = tw9910_s_std,
.g_std = tw9910_g_std,
.s_stream = tw9910_s_stream,
.g_mbus_fmt = tw9910_g_fmt,
.s_mbus_fmt = tw9910_s_fmt,
.try_mbus_fmt = tw9910_try_fmt,
.cropcap = tw9910_cropcap,
.g_crop = tw9910_g_crop,
.enum_mbus_fmt = tw9910_enum_fmt,
.g_mbus_config = tw9910_g_mbus_config,
.s_mbus_config = tw9910_s_mbus_config,
.g_tvnorms = tw9910_g_tvnorms,
};
static struct v4l2_subdev_ops tw9910_subdev_ops = {
.core = &tw9910_subdev_core_ops,
.video = &tw9910_subdev_video_ops,
};
/*
* i2c_driver function
*/
static int tw9910_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct tw9910_priv *priv;
struct tw9910_video_info *info;
struct i2c_adapter *adapter =
to_i2c_adapter(client->dev.parent);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
if (!ssdd || !ssdd->drv_priv) {
dev_err(&client->dev, "TW9910: missing platform data!\n");
return -EINVAL;
}
info = ssdd->drv_priv;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev,
"I2C-Adapter doesn't support "
"I2C_FUNC_SMBUS_BYTE_DATA\n");
return -EIO;
}
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->info = info;
v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
priv->clk = v4l2_clk_get(&client->dev, "mclk");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
ret = tw9910_video_probe(client);
if (ret < 0)
v4l2_clk_put(priv->clk);
return ret;
}
static int tw9910_remove(struct i2c_client *client)
{
struct tw9910_priv *priv = to_tw9910(client);
v4l2_clk_put(priv->clk);
return 0;
}
static const struct i2c_device_id tw9910_id[] = {
{ "tw9910", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw9910_id);
static struct i2c_driver tw9910_i2c_driver = {
.driver = {
.name = "tw9910",
},
.probe = tw9910_probe,
.remove = tw9910_remove,
.id_table = tw9910_id,
};
module_i2c_driver(tw9910_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for tw9910");
MODULE_AUTHOR("Kuninori Morimoto");
MODULE_LICENSE("GPL v2");