#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/log2.h>
+#include <linux/gpio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
-#include <asm/gpio.h>
-
/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
* The platform has to define i2c_board_info
* and call i2c_register_board_info() */
/* Progressive scan, master, defaults */
#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
-static const struct soc_camera_data_format mt9v022_formats[] = {
+static const struct soc_camera_data_format mt9v022_colour_formats[] = {
+ /* Order important: first natively supported,
+ * second supported with a GPIO extender */
{
- .name = "RGB Bayer (sRGB)",
- .depth = 8,
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .name = "RGB Bayer (sRGB)",
+ .name = "Bayer (sRGB) 10 bit",
.depth = 10,
.fourcc = V4L2_PIX_FMT_SBGGR16,
.colorspace = V4L2_COLORSPACE_SRGB,
}, {
+ .name = "Bayer (sRGB) 8 bit",
+ .depth = 8,
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }
+};
+
+static const struct soc_camera_data_format mt9v022_monochrome_formats[] = {
+ /* Order important - see above */
+ {
.name = "Monochrome 10 bit",
.depth = 10,
.fourcc = V4L2_PIX_FMT_Y16,
struct mt9v022 {
struct i2c_client *client;
struct soc_camera_device icd;
- int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
+ int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
int switch_gpio;
u16 chip_control;
unsigned char datawidth;
#endif
}
-static int mt9v022_set_capture_format(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect, unsigned int flags)
+static int bus_switch_possible(struct mt9v022 *mt9v022)
+{
+#ifdef CONFIG_MT9V022_PCA9536_SWITCH
+ return gpio_is_valid(mt9v022->switch_gpio);
+#else
+ return 0;
+#endif
+}
+
+static int mt9v022_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- unsigned int width_flag = flags & (IS_DATAWIDTH_10 | IS_DATAWIDTH_9 |
- IS_DATAWIDTH_8);
- u16 pixclk = 0;
+ unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
int ret;
+ u16 pixclk = 0;
/* Only one width bit may be set */
if (!is_power_of_2(width_flag))
return -EINVAL;
+ if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) ||
+ (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) ||
+ (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) {
+ /* Well, we actually only can do 10 or 8 bits... */
+ if (width_flag == SOCAM_DATAWIDTH_9)
+ return -EINVAL;
+
+ ret = bus_switch_act(mt9v022,
+ width_flag == SOCAM_DATAWIDTH_8);
+ if (ret < 0)
+ return ret;
+
+ mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
+ }
+
+ if (flags & SOCAM_PCLK_SAMPLE_RISING)
+ pixclk |= 0x10;
+
+ if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH))
+ pixclk |= 0x1;
+
+ if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH))
+ pixclk |= 0x2;
+
+ ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk);
+ if (ret < 0)
+ return ret;
+
+ if (!(flags & SOCAM_MASTER))
+ mt9v022->chip_control &= ~0x8;
+
+ ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ pixclk, mt9v022->chip_control);
+
+ return 0;
+}
+
+static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
+{
+ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ unsigned int width_flag = SOCAM_DATAWIDTH_10;
+
+ if (bus_switch_possible(mt9v022))
+ width_flag |= SOCAM_DATAWIDTH_8;
+
+ return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
+ SOCAM_MASTER | SOCAM_SLAVE |
+ width_flag;
+}
+
+static int mt9v022_set_fmt_cap(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ int ret;
+
/* The caller provides a supported format, as verified per call to
* icd->try_fmt_cap(), datawidth is from our supported format list */
switch (pixfmt) {
dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height);
- if ((mt9v022->datawidth != 10 && (width_flag == IS_DATAWIDTH_10)) ||
- (mt9v022->datawidth != 9 && (width_flag == IS_DATAWIDTH_9)) ||
- (mt9v022->datawidth != 8 && (width_flag == IS_DATAWIDTH_8))) {
- /* data width switch requested */
- if (!gpio_is_valid(mt9v022->switch_gpio))
- return -EINVAL;
-
- /* Well, we actually only can do 10 or 8 bits... */
- if (width_flag == IS_DATAWIDTH_9)
- return -EINVAL;
-
- ret = bus_switch_act(mt9v022,
- width_flag == IS_DATAWIDTH_8);
- if (ret < 0)
- return ret;
-
- mt9v022->datawidth = width_flag == IS_DATAWIDTH_8 ? 8 : 10;
- }
-
- if (flags & IS_PCLK_SAMPLE_RISING)
- pixclk |= 0x10;
-
- if (!(flags & IS_HSYNC_ACTIVE_HIGH))
- pixclk |= 0x1;
-
- if (!(flags & IS_VSYNC_ACTIVE_HIGH))
- pixclk |= 0x2;
-
- ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk);
- if (ret < 0)
- return ret;
-
- if (!(flags & IS_MASTER))
- mt9v022->chip_control &= ~0x8;
-
- ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (ret < 0)
- return ret;
-
- dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
- pixclk, mt9v022->chip_control);
-
return 0;
}
}
#endif
-static unsigned int mt9v022_get_datawidth(struct soc_camera_device *icd)
-{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- return mt9v022->datawidth;
-}
-
-const struct v4l2_queryctrl mt9v022_controls[] = {
+static const struct v4l2_queryctrl mt9v022_controls[] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
}
};
-static int mt9v022_get_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl);
-static int mt9v022_set_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl);
+static int mt9v022_video_probe(struct soc_camera_device *);
+static void mt9v022_video_remove(struct soc_camera_device *);
+static int mt9v022_get_control(struct soc_camera_device *, struct v4l2_control *);
+static int mt9v022_set_control(struct soc_camera_device *, struct v4l2_control *);
static struct soc_camera_ops mt9v022_ops = {
.owner = THIS_MODULE,
+ .probe = mt9v022_video_probe,
+ .remove = mt9v022_video_remove,
.init = mt9v022_init,
.release = mt9v022_release,
.start_capture = mt9v022_start_capture,
.stop_capture = mt9v022_stop_capture,
- .set_capture_format = mt9v022_set_capture_format,
+ .set_fmt_cap = mt9v022_set_fmt_cap,
.try_fmt_cap = mt9v022_try_fmt_cap,
- .formats = mt9v022_formats,
- .num_formats = ARRAY_SIZE(mt9v022_formats),
- .get_datawidth = mt9v022_get_datawidth,
+ .set_bus_param = mt9v022_set_bus_param,
+ .query_bus_param = mt9v022_query_bus_param,
.controls = mt9v022_controls,
.num_controls = ARRAY_SIZE(mt9v022_controls),
.get_control = mt9v022_get_control,
!strcmp("color", sensor_type))) {
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
+ icd->formats = mt9v022_colour_formats;
+ if (mt9v022->client->dev.platform_data)
+ icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats);
+ else
+ icd->num_formats = 1;
} else {
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
+ icd->formats = mt9v022_monochrome_formats;
+ if (mt9v022->client->dev.platform_data)
+ icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats);
+ else
+ icd->num_formats = 1;
}
if (ret >= 0)
soc_camera_video_stop(&mt9v022->icd);
}
-static int mt9v022_probe(struct i2c_client *client)
+static int mt9v022_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
{
struct mt9v022 *mt9v022;
struct soc_camera_device *icd;
i2c_set_clientdata(client, mt9v022);
icd = &mt9v022->icd;
- icd->probe = mt9v022_video_probe;
- icd->remove = mt9v022_video_remove;
icd->ops = &mt9v022_ops;
icd->control = &client->dev;
icd->x_min = 1;
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,
};
static int __init mt9v022_mod_init(void)