// SPDX-License-Identifier: GPL-2.0
/*
* Driver for ST MIPID02 CSI-2 to PARALLEL bridge
*
* Copyright (C) STMicroelectronics SA 2019
* Authors: Mickael Guene <mickael.guene@st.com>
* for STMicroelectronics.
*
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#define MIPID02_CLK_LANE_WR_REG1 0x01
#define MIPID02_CLK_LANE_REG1 0x02
#define MIPID02_CLK_LANE_REG3 0x04
#define MIPID02_DATA_LANE0_REG1 0x05
#define MIPID02_DATA_LANE0_REG2 0x06
#define MIPID02_DATA_LANE1_REG1 0x09
#define MIPID02_DATA_LANE1_REG2 0x0a
#define MIPID02_MODE_REG1 0x14
#define MIPID02_MODE_REG2 0x15
#define MIPID02_DATA_ID_RREG 0x17
#define MIPID02_DATA_SELECTION_CTRL 0x19
#define MIPID02_PIX_WIDTH_CTRL 0x1e
#define MIPID02_PIX_WIDTH_CTRL_EMB 0x1f
/* Bits definition for MIPID02_CLK_LANE_REG1 */
#define CLK_ENABLE BIT(0)
/* Bits definition for MIPID02_CLK_LANE_REG3 */
#define CLK_MIPI_CSI BIT(1)
/* Bits definition for MIPID02_DATA_LANE0_REG1 */
#define DATA_ENABLE BIT(0)
/* Bits definition for MIPID02_DATA_LANEx_REG2 */
#define DATA_MIPI_CSI BIT(0)
/* Bits definition for MIPID02_MODE_REG1 */
#define MODE_DATA_SWAP BIT(2)
#define MODE_NO_BYPASS BIT(6)
/* Bits definition for MIPID02_MODE_REG2 */
#define MODE_HSYNC_ACTIVE_HIGH BIT(1)
#define MODE_VSYNC_ACTIVE_HIGH BIT(2)
/* Bits definition for MIPID02_DATA_SELECTION_CTRL */
#define SELECTION_MANUAL_DATA BIT(2)
#define SELECTION_MANUAL_WIDTH BIT(3)
static const u32 mipid02_supported_fmt_codes[] = {
MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12,
MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12,
MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_BGR888_1X24,
MEDIA_BUS_FMT_RGB565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE,
MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_UYVY8_2X8,
MEDIA_BUS_FMT_JPEG_1X8
};
/* regulator supplies */
static const char * const mipid02_supply_name[] = {
"VDDE", /* 1.8V digital I/O supply */
"VDDIN", /* 1V8 voltage regulator supply */
};
#define MIPID02_NUM_SUPPLIES ARRAY_SIZE(mipid02_supply_name)
#define MIPID02_SINK_0 0
#define MIPID02_SINK_1 1
#define MIPID02_SOURCE 2
#define MIPID02_PAD_NB 3
struct mipid02_dev {
struct i2c_client *i2c_client;
struct regulator_bulk_data supplies[MIPID02_NUM_SUPPLIES];
struct v4l2_subdev sd;
struct media_pad pad[MIPID02_PAD_NB];
struct clk *xclk;
struct gpio_desc *reset_gpio;
/* endpoints info */
struct v4l2_fwnode_endpoint rx;
u64 link_frequency;
struct v4l2_fwnode_endpoint tx;
/* remote source */
struct v4l2_async_subdev asd;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *s_subdev;
/* registers */
struct {
u8 clk_lane_reg1;
u8 data_lane0_reg1;
u8 data_lane1_reg1;
u8 mode_reg1;
u8 mode_reg2;
u8 data_selection_ctrl;
u8 data_id_rreg;
u8 pix_width_ctrl;
u8 pix_width_ctrl_emb;
} r;
/* lock to protect all members below */
struct mutex lock;
bool streaming;
struct v4l2_mbus_framefmt fmt;
};
static int bpp_from_code(__u32 code)
{
switch (code) {
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SRGGB8_1X8:
return 8;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
return 10;
case MEDIA_BUS_FMT_SBGGR12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
return 12;
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_RGB565_2X8_LE:
case MEDIA_BUS_FMT_RGB565_2X8_BE:
return 16;
case MEDIA_BUS_FMT_BGR888_1X24:
return 24;
default:
return 0;
}
}
static u8 data_type_from_code(__u32 code)
{
switch (code) {
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SRGGB8_1X8:
return 0x2a;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
return 0x2b;
case MEDIA_BUS_FMT_SBGGR12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
return 0x2c;
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_UYVY8_2X8:
return 0x1e;
case MEDIA_BUS_FMT_BGR888_1X24:
return 0x24;
case MEDIA_BUS_FMT_RGB565_2X8_LE:
case MEDIA_BUS_FMT_RGB565_2X8_BE:
return 0x22;
default:
return 0