// SPDX-License-Identifier: GPL-2.0
/*
* mt9t112 Camera Driver
*
* Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org>
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ov772x driver, mt9m111 driver,
*
* Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
* Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
*
* TODO: This driver lacks support for frame rate control due to missing
* register level documentation and suitable hardware for testing.
* v4l-utils compliance tools will report errors.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
#include <media/i2c/mt9t112.h>
#include <media/v4l2-common.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-subdev.h>
/* you can check PLL/clock info */
/* #define EXT_CLOCK 24000000 */
/************************************************************************
* macro
***********************************************************************/
/*
* frame size
*/
#define MAX_WIDTH 2048
#define MAX_HEIGHT 1536
/*
* macro of read/write
*/
#define ECHECKER(ret, x) \
do { \
(ret) = (x); \
if ((ret) < 0) \
return (ret); \
} while (0)
#define mt9t112_reg_write(ret, client, a, b) \
ECHECKER(ret, __mt9t112_reg_write(client, a, b))
#define mt9t112_mcu_write(ret, client, a, b) \
ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
#define mt9t112_reg_mask_set(ret, client, a, b, c) \
ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
#define mt9t112_reg_read(ret, client, a) \
ECHECKER(ret, __mt9t112_reg_read(client, a))
/*
* Logical address
*/
#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
#define VAR(id, offset) _VAR(id, offset, 0x0000)
#define VAR8(id, offset) _VAR(id, offset, 0x8000)
/************************************************************************
* struct
***********************************************************************/
struct mt9t112_format {
u32 code;
enum v4l2_colorspace colorspace;
u16 fmt;
u16 order;
};
struct mt9t112_priv {
struct v4l2_subdev subdev;
struct mt9t112_platform_data *info;
struct i2c_client *client;
struct v4l2_rect frame;
struct clk *clk;
struct gpio_desc *standby_gpio;
const struct mt9t112_format *format;
int num_formats;
bool init_done;
};
/************************************************************************
* supported format
***********************************************************************/
static const struct mt9t112_format mt9t112_cfmts[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 1,
.order = 0,
}, {
.code = MEDIA_BUS_FMT_VYUY8_2X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 1,
.order = 1,
}, {
.code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 1,
.order = 2,
}, {
.code = MEDIA_BUS_FMT_YVYU8_2X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 1,
.order = 3,
}, {
.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 8,
.order = 2,
}, {
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 4,
.order = 2,
},
};
/************************************************************************
* general function
***********************************************************************/
static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client),
struct mt9t112_priv,
subdev);
}
static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
{
struct i2c_msg msg[2];
u8 buf[2];
int ret;
command = swab16(command);
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = (u8 *)