/*
* References (c = chapter, p = page):
* REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60
* REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <linux/v4l2-dv-timings.h>
#include <linux/hdmi.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-of.h>
#include <media/camera_common.h>
#include <dt-bindings/gpio/tegra-gpio.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/i2c-dev.h>
#include <linux/fs.h>
// #include <media/v4l2-chip-ident.h>
#include <media/tegra-v4l2-camera.h>
#include <media/camera_common.h>
#include <media/soc_camera.h>
#include "tc358743.h"
#include "tc358743_regs.h"
/* RGB ouput selection */
// #define TC358743_VOUT_RGB
static int debug = 3;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-3)");
MODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver");
MODULE_AUTHOR("Ramakrishnan Muthukrishnan <ram@rkrishnan.org>");
MODULE_AUTHOR("Mikhail Khelik <mkhelik@cisco.com>");
MODULE_AUTHOR("Mats Randgaard <matrandg@cisco.com>");
MODULE_LICENSE("GPL");
/* mode */
enum {
tc358743_MODE_1280X720,
tc358743_MODE_1920X1080,
};
/* frame rate */
static const int tc358743_30fps[] = {
30,
};
static const int tc358743_30_60fps[] = {
30,
60,
};
static const int tc358743_50fps[] = {
50,
};
/* frame format */
static const struct camera_common_frmfmt tc358743_frmfmt[] = {
{{1280, 720}, tc358743_30_60fps, 2, 1, tc358743_MODE_1280X720},
{{1920, 1080}, tc358743_30_60fps, 2, 1, tc358743_MODE_1920X1080},
{{1280, 720}, tc358743_50fps, 1, 1, tc358743_MODE_1280X720},
{{1920, 1080}, tc358743_50fps, 1, 1, tc358743_MODE_1920X1080},
};
// static const struct camera_common_colorfmt tc358743_color_fmts[] = {
// {
// MEDIA_BUS_FMT_SRGGB12_1X12,
// V4L2_COLORSPACE_SRGB,
// V4L2_PIX_FMT_SRGGB12,
// },
// {
// MEDIA_BUS_FMT_UYVY8_1X16,
// V4L2_COLORSPACE_SRGB,
// V4L2_PIX_FMT_UYVY,
// },
// };
#define EDID_NUM_BLOCKS_MAX 8
#define EDID_BLOCK_SIZE 128
static u8 edid[] = {
#ifdef TC358743_VOUT_RGB
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
0x50,0x21,0x9C,0x27,0x00,0x00,0x00,0x00,
0x19,0x12,0x01,0x03,0x80,0x00,0x00,0x78,
0x0E,0x00,0xB2,0xA0,0x57,0x49,0x9B,0x26,
0x10,0x48,0x4F,0x2F,0xCF,0x00,0x31,0x59,
0x45,0x59,0x61,0x59,0x81,0x99,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A,
0x80,0x18,0x71,0x38,0x2D,0x40,0x58,0x2C,
0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,
0x00,0x00,0x00,0xFD,0x00,0x31,0x55,0x18,
0x5E,0x11,0x00,0x0A,0x20,0x20,0x20,0x20,
0x20,0x20,0x00,0x00,0x00,0xFC,0x00,0x54,
0x6F,0x73,0x68,0x69,0x62,0x61,0x2D,0x48,
0x32,0x43,0x0A,0x20,0x00,0x00,0x00,0xFD,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xc3,
0x02,0x03,0x1a,0xc0,0x48,0xa2,0x10,0x04,
0x02,0x01,0x21,0x14,0x13,0x23,0x09,0x07,
0x07,0x65,0x03,0x0c,0x00,0x10,0x00,0xe2,
0x00,0x2a,0x01,0x1d,0x00,0x80,0x51,0xd0,
0x1c,0x20,0x40,0x80,0x35,0x00,0x00,0x00,
0x00,0x00,0x00,0x1e,0x8c,0x0a,0xd0,0x8a,
0x20,0xe0,0x2d,0x10,0x10,0x3e,0x96,0x00,
0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd7
#else
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
0x52,0x62,0x88,0x88,0x00,0x88,0x88,0x88,
0x1C,0x15,0x01,0x03,0x80,0x00,0x00,0x78,
0x0A,0x0D,0xC9,0xA0,0x57,0x47,0x98,0x27,
0x12,0x48,0x4C,0x00,0x00,0x00,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A,
0x80,0xD0,0x72,0x38,0x2D,0x40,0x10,0x2C,
0x45,0x80,0x66,0x4C,0x00,0x00,0x00,0x1E,
0x01,0x1D,0x00,0xBC,0x52,0xD0,0x1E,0x20,
0xB8,0x28,0x55,0x40,0x66,0x4C,0x00,0x00,
0x00,0x1E,0x00,0x00,0x00,0xFC,0x00,0x54,
0x6F,0x73,0x68,0x69,0x62,0x61,0x2D,0x48,
0x32,0x43,0x0A,0x20,0x00,0x00,0x00,0xFD,
0x00,0x14,0x78,0x01,0xFF,0x10,0x00,0x0A,
0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xBA,
0x02,0x03,0x1A,0x71,0x47,0x9F,0x13,0x22,
0x1F,0x02,0x11,0x1F,0x23,0x09,0x07,0x01,
0x83,0x01,0x00,0x00,0x65,0x03,0x0C,0x00,
0x10,0x00,0x01,0x1D,0x80,0x18,0x71,0x38,
0x2D,0x40,0x58,0x2C,0x45,0x00,0x66,0x4C,
0x00,0x00,0x00,0x1E,0x02,0x3A,0x80,0xD0,
0x72,0x38,0x2D,0x40,0x10,0x2C,0x45,0x80,
0x66,0x4C,0x00,0x00,0x00,0x1E,0x8C,0x0A,
0xD0,0x8A,0x20,0xE0,0x2D,0x10,0x10,0x3E,
0x96,0x00,0x66,0x4C,0x00,0x00,0x00,0x18,
0x8C,0x0A,0xD0,0x90,0x20,0x40,0x31,0x20,
0x0C,0x40,0x55,0x00,0x66,0x4C,0x00,0x00,
0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,
#endif
};
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE (EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE + 2)
static const struct v4l2_dv_timings_cap tc358743_timings_cap = {
.type = V4L2_DV_BT_656_1120,
/* keep this initialization for compatibility with GCC < 4.4.6 */
.reserved = { 0 },
/* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */
V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000,
V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
V4L2_DV_BT_CAP_PROGRESSIVE |
V4L2_DV_BT_CAP_REDUCED_BLANKING |
V4L2_DV_BT_CAP_CUSTOM)
};
struct tc358743_state {
struct tc358743_platform_data pdata;
// struct v4l2_of_bus_mipi_csi2 bus;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler hdl;
struct i2c_client *i2c_client;
struct regmap *regmap;
/* CONFCTL is modified in ops and tc358743_hdmi_sys_int_handler */
struct mutex confctl_mutex;
/* controls */
struct v4l2_ctrl *detect_tx_5v_ctrl;
struct v4l2_ctrl *audio_sampling_rate_ctrl;
struct v4l2_ctrl *audio_present_ctrl;
/* work queues */
struct workqueue_struct *work_queues;
struct delayed_work delayed_work_enable_hotplug;
/* edid */
u8 edid_blocks_written;
/* used by i2c_wr() */
u8 wr_data[MAX_XFER_SIZE];
struct v4l2_dv_timings timings;
u32 mbus_fmt_code;
struct gpio_desc *reset_gpio;
};
static inline struct tc358743_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct tc358743_state, sd);
}
/*
static char * sdo_bit_len [] = {
[0b000] = "16bit (lower 8bit discarded)",
[0b001] = "16bit (lower 8bit + 1 discarded)",
[0b010] = "18bit (lower 6bit discarded)",
[0b011] = "18bit (lower 6bit + 1 discarded)",
[0b100] = "20bit (lower 4bit discarded)",
[0b101] = "20bit (lower 4bit + 1 discarded)",
[0b110] = "24bit no rounding",
[0b111] = "Output OFF (Mute)",
};
static char * sdo_fmt [] = {
[MASK_SDO_FMT_RIGHT] = "Right justified",
[MASK_SDO_FMT_LEFT] = "Left justified",
[MASK_SDO_FMT_I2S] = "I2S",
[0b011] = "I2S",
};
static char * no_yes [] = {
[0] = "No",
[1] = "Yes",
};
static char * no_with [] = {
[0] = "No",
[1] = "With",
};
static char * off_on [] = {
[0b000] = "Off",
[0b001] = "On",
};
static char * audout_sel [] = {
[0b00] = "CSI2-TX",
[0b01] = "Reseved",
[0b10] = "I2S",
[0b11] = "TDM",
};
*/
/* --------------- I2C --------------- */
static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
{
struct tc358743_state *state = to_state(sd);
struct i2c_client *client = state->i2c_client;
int err;
u8 buf[2] = { reg >> 8, reg &0xff };
struct i2c_msg msgs[] = {
{
.addr = client-