media: spi: add driver for rk1608

Change-Id: I8508668fcd1e35c49fe581875fcf9045e004ae9c
Signed-off-by: Hu Kejun <william.hu@rock-chips.com>
This commit is contained in:
Hu Kejun
2018-10-31 19:37:20 +08:00
committed by Tao Huang
parent 642bed250f
commit c0202e0bc5
7 changed files with 2703 additions and 0 deletions

View File

@@ -9,6 +9,15 @@ config VIDEO_GS1662
---help---
Enable the GS1662 driver which serializes video streams.
config VIDEO_ROCKCHIP_PREISP
tristate "Rockchip Image Signal Pre-processing driver"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && SPI_MASTER
depends on ARCH_ROCKCHIP || COMPILE_TEST
select V4L2_FWNODE
default n
help
Support for Pre-isp on the rockchip SoC.
endmenu
endif

View File

@@ -1 +1,2 @@
obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
obj-$(CONFIG_VIDEO_ROCKCHIP_PREISP) += rk1608_dphy.o rk1608.o

1596
drivers/media/spi/rk1608.c Normal file

File diff suppressed because it is too large Load Diff

484
drivers/media/spi/rk1608.h Normal file
View File

@@ -0,0 +1,484 @@
/* SPDX-License-Identifier: GPL-2.0 */
/**
* Rockchip rk1608 driver
*
* Copyright (C) 2017-2018 Rockchip Electronics Co., Ltd.
*
*/
#ifndef __RK1608_H__
#define __RK1608_H__
#include <linux/spi/spi.h>
#include "rk1608_dphy.h"
#define RK1608_OP_TRY_MAX 3
#define RK1608_OP_TRY_DELAY 10
#define RK1608_CMD_WRITE 0x00000011
#define RK1608_CMD_WRITE_REG0 0X00010011
#define RK1608_CMD_WRITE_REG1 0X00020011
#define RK1608_CMD_READ 0x00000077
#define RK1608_CMD_READ_BEGIN 0x000000aa
#define RK1608_CMD_QUERY 0x000000ff
#define RK1608_CMD_QUERY_REG2 0x000001ff
#define RK1608_STATE_ID_MASK 0xffff0000
#define RK1608_STATE_ID 0X16080000
#define RK1608_STATE_MASK 0x0000ffff
#define APB_CMD_WRITE_REG1 0X00020011
#define RK1608_R_MSG_QUEUE_ADDR 0x60050000
#define RK1608_IRQ_TYPE_MSG 0x12345678
#define BOOT_REQUEST_ADDR 0x18000010
#define RK1608_HEAD_ADDR 0x60000000
#define RK1608_FW_NAME "rk1608.rkl"
#define RK1608_S_MSG_QUEUE_ADDR 0x60050010
#define RK1608_PMU_SYS_REG0 0x120000f0
#define RK1608_MSG_QUEUE_OK_MASK 0xffff0001
#define RK1608_MSG_QUEUE_OK_TAG 0x16080001
#define RK1608_MAX_OP_BYTES 60000
#define MSG_SYNC_TIMEOUT 50
#define BOOT_FLAG_CRC (0x01 << 0)
#define BOOT_FLAG_EXE (0x01 << 1)
#define BOOT_FLAG_LOAD_PMEM (0x01 << 2)
#define BOOT_FLAG_ACK (0x01 << 3)
#define BOOT_FLAG_READ_WAIT (0x01 << 4)
#define BOOT_FLAG_BOOT_REQUEST (0x01 << 5)
#define DEBUG_DUMP_ALL_SEND_RECV_MSG 0
#define RK1608_MCLK_RATE (24 * 1000 * 1000ul)
#define SENSOR_TIMEOUT 1000
#define OPM_SLAVE_MODE 0x100000
#define RSD_SEL_2CYC 0x008000
#define DFS_SEL_16BIT 0x000002
#define SPI_CTRL0 0x11060000
#define SPI_ENR 0x11060008
#define CRUPMU_CLKSEL14_CON 0x12008098
#define PMUGRF_GPIO1A_E 0x12030040
#define PMUGRF_GPIO1B_E 0x12030044
#define BIT7_6_SEL_8MA 0xf000a000
#define BIT1_0_SEL_8MA 0x000f000a
#define SPI0_PLL_SEL_APLL 0xff004000
#define INVALID_ID -1
#define RK1608_MAX_SEC_NUM 10
#define ISP_DSP_HDRAE_MAXGRIDITEMS 225
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MSB2LSB32
#define MSB2LSB32(x) ((((u32)(x) & 0x80808080) >> 7) | \
(((u32)(x) & 0x40404040) >> 5) | \
(((u32)(x) & 0x20202020) >> 3) | \
(((u32)(x) & 0x10101010) >> 1) | \
(((u32)(x) & 0x08080808) << 1) | \
(((u32)(x) & 0x04040404) << 3) | \
(((u32)(x) & 0x02020202) << 5) | \
(((u32)(x) & 0x01010101) << 7))
#endif
struct rk1608_state {
struct v4l2_subdev sd;
struct rk1608_dphy *dphy[2];
struct mutex lock; /* protect resource */
struct mutex sensor_lock; /* protect sensor */
struct mutex send_msg_lock; /* protect msg */
spinlock_t hdrae_lock; /* protect hdrae parameter */
struct gpio_desc *reset_gpio;
struct gpio_desc *irq_gpio;
int irq;
struct gpio_desc *wakeup_gpio;
struct gpio_desc *aesync_gpio;
struct v4l2_subdev *sensor[4];
struct device *dev;
struct spi_device *spi;
int power_count;
int msg_num;
u32 link_nums;
u32 sensor_cnt;
u32 sensor_nums[2];
u32 msg_done[8];
wait_queue_head_t msg_wait;
struct media_pad pad;
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *exposure;
struct v4l2_ctrl *gain;
struct v4l2_ctrl_handler ctrl_handler;
s64 link_freqs;
u32 max_speed_hz;
u32 min_speed_hz;
struct preisp_hdrae_para_s hdrae_para;
struct preisp_hdrae_exp_s hdrae_exp;
u32 set_exp_cnt;
const char *firm_name;
};
struct rk1608_section {
union {
u32 offset;
u32 wait_value;
};
u32 size;
union {
u32 load_addr;
u32 wait_addr;
};
u16 wait_time;
u16 timeout;
u16 crc_16;
u8 flag;
u8 type;
};
struct rk1608_header {
char version[32];
u32 header_size;
u32 section_count;
struct rk1608_section sections[RK1608_MAX_SEC_NUM];
};
struct rk1608_boot_req {
u32 flag;
u32 load_addr;
u32 boot_len;
u8 status;
u8 dummy[2];
u8 cmd;
};
struct rk1608_msg_queue {
u32 buf_head; /* msg buffer head */
u32 buf_tail; /* msg buffer tail */
u32 cur_send; /* current msg send position */
u32 cur_recv; /* current msg receive position */
};
struct msg {
u32 size; /* unit 4 bytes */
u16 type;
union {
u8 camera_id;
u8 core_id;
} id;
union {
u8 sync;
u8 log_level;
s8 err;
} mux;
};
struct msg_init {
struct msg msg_head;
u32 i2c_bus;
u32 i2c_clk;
s8 in_mipi_phy;
s8 out_mipi_phy;
s8 mipi_lane;
s8 bayer;
u8 sensor_name[32];
u8 i2c_slave_addr;
};
struct preisp_vc_cfg {
s8 data_id;
s8 decode_format;
s8 flag;
s8 unused;
u16 width;
u16 height;
};
struct msg_in_size {
struct msg msg_head;
struct preisp_vc_cfg channel[2];
};
struct msg_out_size_head {
struct msg msg_head;
u16 width;
u16 height;
u32 mipi_clk;
u16 line_length_pclk;
u16 frame_length_lines;
u8 mipi_lane;
};
struct msg_set_output_size {
struct msg_out_size_head head;
struct preisp_vc_cfg channel[2];
};
enum ISP_AE_Bayer_Mode_e {
BAYER_MODE_MIN = 0,
BAYER_MODE_BGGR = 1,
BAYER_MODE_GRBG = 2,
BAYER_MODE_GBRG = 3,
BAYER_MODE_RGGB = 4,
BAYER_MODE_MAX = 5
};
struct ISP_AE_YCOEFF_s {
int rcoef;
int gcoef;
int bcoef;
int offset;
};
enum ISP_AE_Hist_Mode_e {
AE_HISTSTATICMODE_INVALID = 0,
AE_HISTSTATICMODE_Y,
AE_HISTSTATICMODE_R,
AE_HISTSTATICMODE_G,
AE_HISTSTATICMODE_B,
AE_HISTSTATICMODE_MAX
};
enum ISP_AE_Grid_Mode_e {
AE_MEASURE_GRID_INVALID = 0,
AE_MEASURE_GRID_1X1,
AE_MEASURE_GRID_5X5,
AE_MEASURE_GRID_9X9,
AE_MEASURE_GRID_15X15,
AE_MEASURE_GRID_MAX
};
struct ISP_DSP_hdrae_cfg_s {
enum ISP_AE_Bayer_Mode_e bayer_mode;
enum ISP_AE_Grid_Mode_e grid_mode;
u8 weight[ISP_DSP_HDRAE_MAXGRIDITEMS];
enum ISP_AE_Hist_Mode_e hist_mode;
struct ISP_AE_YCOEFF_s ycoeff;
u8 imgbits;
u16 width;
u16 height;
u16 frames;
};
struct msg_set_sensor_info_s {
struct msg msg_head;
u32 set_exp_cnt;
u16 r_gain;
u16 b_gain;
u16 gr_gain;
u16 gb_gain;
u32 exp_time[3];
u32 exp_gain[3];
u32 reg_exp_time[3];
u32 reg_exp_gain[3];
s32 lsc_table[17 * 17];
struct ISP_DSP_hdrae_cfg_s dsp_hdrae;
};
enum {
/* AP -> RK1608
* 1 msg of sensor
*/
id_msg_init_sensor_t = 0x0001,
id_msg_set_input_size_t,
id_msg_set_output_size_t,
id_msg_set_stream_in_on_t,
id_msg_set_stream_in_off_t,
id_msg_set_stream_out_on_t,
id_msg_set_stream_out_off_t,
/* AP -> RK1608
* 2 msg of take picture
*/
id_msg_take_picture_t = 0x0021,
id_msg_take_picture_done_t,
/* AP -> RK1608
* 3 msg of realtime parameter
*/
id_msg_rt_args_t = 0x0031,
id_msg_set_sensor_info_t,
/* AP -> RK1608
* 4 msg of power manager
*/
id_msg_set_sys_mode_bypass_t = 0x0200,
id_msg_set_sys_mode_standby_t,
id_msg_set_sys_mode_idle_enable_t,
id_msg_set_sys_mode_idle_disable_t,
id_msg_set_sys_mode_slave_dsp_on_t,
id_msg_set_sys_mode_slave_dsp_off_t,
/* AP -> RK1608
* 5 msg of debug config
*/
id_msg_set_log_level_t = 0x0250,
/* RK1608 -> AP
* 6 response of sensor msg
*/
id_msg_init_sensor_ret_t = 0x0301,
id_msg_set_input_size_ret_t,
id_msg_set_output_size_ret_t,
id_msg_set_stream_in_on_ret_t,
id_msg_set_stream_in_off_ret_t,
id_msg_set_stream_out_on_ret_t,
id_msg_set_stream_out_off_ret_t,
/* RK1608 -> AP
* 7 response of take picture msg
*/
id_msg_take_picture_ret_t = 0x0320,
id_msg_take_picture_done_ret_t,
/* RK1608 -> AP
* 8 response of realtime parameter msg
*/
id_msg_rt_args_ret_t = 0x0330,
/* rk1608 -> AP */
id_msg_do_i2c_t = 0x0390,
/* AP -> rk1608 */
id_msg_do_i2c_ret_t,
/* RK1608 -> AP
* 9 msg of print log
*/
id_msg_rk1608_log_t = 0x0400,
/* dsi2csi dump */
id_msg_dsi2sci_rgb_dump_t = 0x6000,
id_msg_dsi2sci_nv12_dump_t = 0x6001,
/* RK1608 -> AP
* 10 msg of xfile
*/
id_msg_xfile_import_t = 0x8000 + 0x0600,
id_msg_xfile_export_t,
id_msg_xfile_mkdir_t
};
static int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m);
/**
* rk1608_write - RK1608 synchronous write
*
* @spi: spi device
* @addr: resource address
* @data: data buffer
* @data_len: data buffer size, in bytes
* Context: can sleep
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_write(struct spi_device *spi, s32 addr,
const s32 *data, size_t data_len);
/**
* rk1608_safe_write - RK1608 synchronous write with state check
*
* @spi: spi device
* @addr: resource address
* @data: data buffer
* @data_len: data buffer size, in bytes
* Context: can sleep
*
* It returns zero on success, else operation state code.
*/
static int rk1608_safe_write(struct spi_device *spi,
s32 addr, const s32 *data, size_t data_len);
/**
* rk1608_read - RK1608 synchronous read
*
* @spi: spi device
* @addr: resource address
* @data: data buffer [out]
* @data_len: data buffer size, in bytes
* Context: can sleep
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_read(struct spi_device *spi, s32 addr,
s32 *data, size_t data_len);
/**
* rk1608_safe_read - RK1608 synchronous read with state check
*
* @spi: spi device
* @addr: resource address
* @data: data buffer [out]
* @data_len: data buffer size, in bytes
* Context: can sleep
*
* It returns zero on success, else operation state code.
*/
static int rk1608_safe_read(struct spi_device *spi,
s32 addr, s32 *data, size_t data_len);
/**
* rk1608_operation_query - RK1608 last operation state query
*
* @spi: spi device
* @state: last operation state [out]
* Context: can sleep
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_operation_query(struct spi_device *spi, s32 *state);
/**
* rk1608_interrupt_request - RK1608 request a rk1608 interrupt
*
* @spi: spi device
* @interrupt_num: interrupt identification
* Context: can sleep
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_interrupt_request(struct spi_device *spi,
s32 interrupt_num);
static int rk1608_read_wait(struct spi_device *spi,
const struct rk1608_section *sec);
static int rk1608_boot_request(struct spi_device *spi,
const struct rk1608_section *sec);
static int rk1608_download_section(struct spi_device *spi, const u8 *data,
const struct rk1608_section *sec);
/**
* rk1608_download_fw: - rk1608 firmware download through spi
*
* @spi: spi device
* @fw_name: name of firmware file, NULL for default firmware name
* Context: can sleep
*
* It returns zero on success, else a negative error code.
**/
static int rk1608_download_fw(struct spi_device *spi, const char *fw_name);
/**
* rk1608_msq_read_head - read rk1608 msg queue head
*
* @spi: spi device
* @addr: msg queue head addr
* @m: msg queue pointer
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_msq_read_head(struct spi_device *spi,
u32 addr, struct rk1608_msg_queue *q);
/**
* rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue
*
* @q: msg queue
* @m: a msg pointer buf [out]
*
* need call rk1608_msq_free_received_msg to free msg after msg use done
*
* It returns zero on success, else a negative error code.
*/
static int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m);
#endif

View File

@@ -0,0 +1,532 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Rockchip rk1608 driver
*
* Copyright (C) 2017-2018 Rockchip Electronics Co., Ltd.
*
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/mfd/syscon.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/types.h>
#include <linux/rk-preisp.h>
#include <linux/rkisp1-config.h>
#include "rk1608_dphy.h"
/**
* Rk1608 is used as the Pre-ISP to link on Soc, which mainly has two
* functions. One is to download the firmware of RK1608, and the other
* is to match the extra sensor such as camera and enable sensor by
* calling sensor's s_power.
* |-----------------------|
* | Sensor Camera |
* |-----------------------|
* |-----------||----------|
* |-----------||----------|
* |-----------\/----------|
* | Pre-ISP RK1608 |
* |-----------------------|
* |-----------||----------|
* |-----------||----------|
* |-----------\/----------|
* | Rockchip Soc |
* |-----------------------|
* Data Transfer As shown above. In RK1608, the data received from the
* extra sensor,and it is passed to the Soc through ISP.
*/
static inline struct rk1608_dphy *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct rk1608_dphy, sd);
}
static int rk1608_s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct v4l2_ctrl *remote_ctrl;
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = pdata->sd.grp_id;
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
V4L2_CID_HBLANK);
if (remote_ctrl) {
v4l2_ctrl_g_ctrl(remote_ctrl);
__v4l2_ctrl_modify_range(pdata->hblank,
remote_ctrl->minimum,
remote_ctrl->maximum,
remote_ctrl->step,
remote_ctrl->default_value);
}
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
V4L2_CID_VBLANK);
if (remote_ctrl) {
v4l2_ctrl_g_ctrl(remote_ctrl);
__v4l2_ctrl_modify_range(pdata->vblank,
remote_ctrl->minimum,
remote_ctrl->maximum,
remote_ctrl->step,
remote_ctrl->default_value);
}
return 0;
}
static int rk1608_s_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = sd->grp_id;
return 0;
}
static int rk1608_sensor_power(struct v4l2_subdev *sd, int on)
{
int ret = 0;
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = sd->grp_id;
ret = v4l2_subdev_call(pdata->rk1608_sd, core, s_power, on);
return ret;
}
static int rk1608_s_stream(struct v4l2_subdev *sd, int enable)
{
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = sd->grp_id;
v4l2_subdev_call(pdata->rk1608_sd, video, s_stream, enable);
return 0;
}
static int rk1608_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
struct rk1608_dphy *pdata = to_state(sd);
if (code->index > 0)
return -EINVAL;
code->code = pdata->mf.code;
return 0;
}
static int rk1608_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct v4l2_mbus_framefmt *mf = &fmt->format;
struct rk1608_dphy *pdata = to_state(sd);
mf->code = pdata->mf.code;
mf->width = pdata->mf.width;
mf->height = pdata->mf.height;
mf->field = pdata->mf.field;
mf->colorspace = pdata->mf.colorspace;
return 0;
}
static int rk1608_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct v4l2_ctrl *remote_ctrl;
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = pdata->sd.grp_id;
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
V4L2_CID_HBLANK);
if (remote_ctrl) {
v4l2_ctrl_g_ctrl(remote_ctrl);
__v4l2_ctrl_modify_range(pdata->hblank,
remote_ctrl->minimum,
remote_ctrl->maximum,
remote_ctrl->step,
remote_ctrl->default_value);
}
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
V4L2_CID_VBLANK);
if (remote_ctrl) {
v4l2_ctrl_g_ctrl(remote_ctrl);
__v4l2_ctrl_modify_range(pdata->vblank,
remote_ctrl->minimum,
remote_ctrl->maximum,
remote_ctrl->step,
remote_ctrl->default_value);
}
return 0;
}
static int rk1608_g_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *fi)
{
struct rk1608_dphy *pdata = to_state(sd);
pdata->rk1608_sd->grp_id = sd->grp_id;
v4l2_subdev_call(pdata->rk1608_sd,
video,
g_frame_interval,
fi);
return 0;
}
static long rk1608_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct rk1608_dphy *pdata = to_state(sd);
long ret;
switch (cmd) {
case PREISP_CMD_SAVE_HDRAE_PARAM:
case PREISP_CMD_SET_HDRAE_EXP:
pdata->rk1608_sd->grp_id = pdata->sd.grp_id;
ret = v4l2_subdev_call(pdata->rk1608_sd, core, ioctl,
cmd, arg);
return ret;
}
return -ENOTTY;
}
#ifdef CONFIG_COMPAT
static long rk1608_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
void __user *up = compat_ptr(arg);
struct preisp_hdrae_exp_s hdrae_exp;
switch (cmd) {
case PREISP_CMD_SET_HDRAE_EXP:
if (copy_from_user(&hdrae_exp, up, sizeof(hdrae_exp)))
return -EFAULT;
return rk1608_ioctl(sd, cmd, &hdrae_exp);
}
return -ENOTTY;
}
#endif
static int rk1608_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_ctrl *remote_ctrl;
struct rk1608_dphy *pdata =
container_of(ctrl->handler,
struct rk1608_dphy, ctrl_handler);
pdata->rk1608_sd->grp_id = pdata->sd.grp_id;
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
ctrl->id);
if (remote_ctrl) {
ctrl->val = v4l2_ctrl_g_ctrl(remote_ctrl);
__v4l2_ctrl_modify_range(ctrl,
remote_ctrl->minimum,
remote_ctrl->maximum,
remote_ctrl->step,
remote_ctrl->default_value);
}
return 0;
}
static int rk1608_set_ctrl(struct v4l2_ctrl *ctrl)
{
int ret = 0;
struct v4l2_ctrl *remote_ctrl;
struct rk1608_dphy *pdata =
container_of(ctrl->handler,
struct rk1608_dphy, ctrl_handler);
pdata->rk1608_sd->grp_id = pdata->sd.grp_id;
remote_ctrl = v4l2_ctrl_find(pdata->rk1608_sd->ctrl_handler,
ctrl->id);
if (remote_ctrl)
ret = v4l2_ctrl_s_ctrl(remote_ctrl, ctrl->val);
return ret;
}
static const struct v4l2_ctrl_ops rk1608_ctrl_ops = {
.g_volatile_ctrl = rk1608_g_volatile_ctrl,
.s_ctrl = rk1608_set_ctrl,
};
static const struct v4l2_ctrl_config rk1608_priv_ctrls[] = {
{
.ops = NULL,
.id = CIFISP_CID_EMB_VC,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Embedded visual channel",
.min = 0,
.max = 3,
.def = 0,
.step = 1,
}, {
.ops = NULL,
.id = CIFISP_CID_EMB_DT,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Embedded data type",
.min = 0,
.max = 0xff,
.def = 0x30,
.step = 1,
}
};
static int rk1608_initialize_controls(struct rk1608_dphy *dphy)
{
u32 i;
int ret;
s64 pixel_rate, pixel_bit;
struct v4l2_ctrl_handler *handler;
unsigned long flags = V4L2_CTRL_FLAG_VOLATILE |
V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
handler = &dphy->ctrl_handler;
ret = v4l2_ctrl_handler_init(handler, 8);
if (ret)
return ret;
dphy->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
V4L2_CID_LINK_FREQ, 0,
0, &dphy->link_freqs);
if (dphy->link_freq)
dphy->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
switch (dphy->data_type) {
case 0x2b:
pixel_bit = 10;
break;
case 0x2c:
pixel_bit = 12;
break;
default:
pixel_bit = 8;
break;
}
pixel_rate = V4L2_CID_LINK_FREQ * dphy->mipi_lane * 2 / pixel_bit;
dphy->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
V4L2_CID_PIXEL_RATE,
0, pixel_rate, 1, pixel_rate);
dphy->hblank = v4l2_ctrl_new_std(handler,
&rk1608_ctrl_ops,
V4L2_CID_HBLANK,
0, 0x7FFFFFFF, 1, 0);
if (dphy->hblank)
dphy->hblank->flags |= flags;
dphy->vblank = v4l2_ctrl_new_std(handler,
&rk1608_ctrl_ops,
V4L2_CID_VBLANK,
0, 0x7FFFFFFF, 1, 0);
if (dphy->vblank)
dphy->vblank->flags |= flags;
dphy->exposure = v4l2_ctrl_new_std(handler,
&rk1608_ctrl_ops,
V4L2_CID_EXPOSURE,
0, 0x7FFFFFFF, 1, 0);
if (dphy->exposure)
dphy->exposure->flags |= flags;
dphy->gain = v4l2_ctrl_new_std(handler,
&rk1608_ctrl_ops,
V4L2_CID_ANALOGUE_GAIN,
0, 0x7FFFFFFF, 1, 0);
if (dphy->gain)
dphy->gain->flags |= flags;
for (i = 0; i < ARRAY_SIZE(rk1608_priv_ctrls); i++)
v4l2_ctrl_new_custom(handler, &rk1608_priv_ctrls[i], NULL);
if (handler->error) {
ret = handler->error;
dev_err(dphy->dev,
"Failed to init controls(%d)\n", ret);
goto err_free_handler;
}
dphy->sd.ctrl_handler = handler;
return 0;
err_free_handler:
v4l2_ctrl_handler_free(handler);
return ret;
}
static const struct v4l2_subdev_internal_ops dphy_subdev_internal_ops = {
.open = rk1608_s_open,
.close = rk1608_s_close,
};
static const struct v4l2_subdev_video_ops rk1608_subdev_video_ops = {
.s_stream = rk1608_s_stream,
.g_frame_interval = rk1608_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops rk1608_subdev_pad_ops = {
.enum_mbus_code = rk1608_enum_mbus_code,
.get_fmt = rk1608_get_fmt,
.set_fmt = rk1608_set_fmt,
};
static const struct v4l2_subdev_core_ops rk1608_core_ops = {
.s_power = rk1608_sensor_power,
.ioctl = rk1608_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = rk1608_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_ops dphy_subdev_ops = {
.core = &rk1608_core_ops,
.video = &rk1608_subdev_video_ops,
.pad = &rk1608_subdev_pad_ops,
};
static int rk1608_dphy_dt_property(struct rk1608_dphy *dphy)
{
int ret = 0;
struct device_node *node = dphy->dev->of_node;
ret = of_property_read_u32(node, "id", &dphy->sd.grp_id);
if (ret)
dev_warn(dphy->dev, "Can not get id!");
ret = of_property_read_u32(node, "cam_nums", &dphy->cam_nums);
if (ret)
dev_warn(dphy->dev, "Can not get cam_nums!");
ret = of_property_read_u32(node, "data_type", &dphy->data_type);
if (ret)
dev_warn(dphy->dev, "Can not get data_type!");
ret = of_property_read_u32(node, "in_mipi", &dphy->in_mipi);
if (ret)
dev_warn(dphy->dev, "Can not get in_mipi!");
ret = of_property_read_u32(node, "out_mipi", &dphy->out_mipi);
if (ret)
dev_warn(dphy->dev, "Can not get out_mipi!");
ret = of_property_read_u32(node, "mipi_lane", &dphy->mipi_lane);
if (ret)
dev_warn(dphy->dev, "Can not get mipi_lane!");
ret = of_property_read_u32(node, "field", &dphy->mf.field);
if (ret)
dev_warn(dphy->dev, "Can not get field!");
ret = of_property_read_u32(node, "colorspace", &dphy->mf.colorspace);
if (ret)
dev_warn(dphy->dev, "Can not get colorspace!");
ret = of_property_read_u32(node, "code", &dphy->mf.code);
if (ret)
dev_warn(dphy->dev, "Can not get code!");
ret = of_property_read_u32(node, "width", &dphy->mf.width);
if (ret)
dev_warn(dphy->dev, "Can not get width!");
ret = of_property_read_u32(node, "height", &dphy->mf.height);
if (ret)
dev_warn(dphy->dev, "Can not get height!");
ret = of_property_read_u32(node, "htotal", &dphy->htotal);
if (ret)
dev_warn(dphy->dev, "Can not get htotal!");
ret = of_property_read_u32(node, "vtotal", &dphy->vtotal);
if (ret)
dev_warn(dphy->dev, "Can not get vtotal!");
ret = of_property_read_u64(node, "link-freqs", &dphy->link_freqs);
if (ret)
dev_warn(dphy->dev, "Can not get link_freqs!");
return ret;
}
static int rk1608_dphy_probe(struct platform_device *pdev)
{
struct rk1608_dphy *dphy;
struct v4l2_subdev *sd;
int ret = 0;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;
dphy->dev = &pdev->dev;
platform_set_drvdata(pdev, dphy);
sd = &dphy->sd;
sd->dev = &pdev->dev;
v4l2_subdev_init(sd, &dphy_subdev_ops);
rk1608_dphy_dt_property(dphy);
snprintf(sd->name, sizeof(sd->name), "RK1608-dphy%d", sd->grp_id);
rk1608_initialize_controls(dphy);
sd->internal_ops = &dphy_subdev_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
dphy->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
ret = media_entity_init(&sd->entity, 1, &dphy->pad, 0);
if (ret < 0)
goto handler_err;
ret = v4l2_async_register_subdev(sd);
if (ret < 0)
goto register_err;
dev_info(dphy->dev, "RK1608-dphy(%d) probe success!\n", sd->grp_id);
return 0;
register_err:
media_entity_cleanup(&sd->entity);
handler_err:
v4l2_ctrl_handler_free(dphy->sd.ctrl_handler);
devm_kfree(&pdev->dev, dphy);
return ret;
}
static int rk1608_dphy_remove(struct platform_device *pdev)
{
struct rk1608_dphy *dphy = platform_get_drvdata(pdev);
v4l2_async_unregister_subdev(&dphy->sd);
media_entity_cleanup(&dphy->sd.entity);
v4l2_ctrl_handler_free(&dphy->ctrl_handler);
return 0;
}
static const struct of_device_id dphy_of_match[] = {
{ .compatible = "rockchip,rk1608-dphy" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rk1608_of_match);
static struct platform_driver rk1608_dphy_drv = {
.driver = {
.of_match_table = of_match_ptr(dphy_of_match),
.name = "RK1608-dphy",
},
.probe = rk1608_dphy_probe,
.remove = rk1608_dphy_remove,
};
module_platform_driver(rk1608_dphy_drv);
MODULE_AUTHOR("Rockchip Camera/ISP team");
MODULE_DESCRIPTION("A DSP driver for rk1608 chip");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
struct rk1608_dphy {
struct v4l2_subdev sd;
struct v4l2_subdev *rk1608_sd;
struct platform_device *pdev;
struct device *dev;
struct media_pad pad;
struct v4l2_mbus_framefmt mf;
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *exposure;
struct v4l2_ctrl *gain;
struct v4l2_ctrl_handler ctrl_handler;
u32 cam_nums;
u32 in_mipi;
u32 out_mipi;
u32 mipi_lane;
u32 data_type;
u32 htotal;
u32 vtotal;
s64 link_freqs;
};

View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */
/*
* Rockchip preisp driver
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*/
#ifndef _UAPI_RKPREISP_H
#define _UAPI_RKPREISP_H
#include <linux/types.h>
#define PREISP_LSCTBL_SIZE 289
#define PREISP_CMD_SET_HDRAE_EXP \
_IOW('V', BASE_VIDIOC_PRIVATE + 0, struct preisp_hdrae_exp_s)
#define PREISP_CMD_SAVE_HDRAE_PARAM \
_IOW('V', BASE_VIDIOC_PRIVATE + 1, struct preisp_hdrae_para_s)
/**
* struct preisp_hdrae_para_s - awb and lsc para for preisp
*
* @r_gain: awb r gain
* @b_gain: awb b gain
* @gr_gain: awb gr gain
* @gb_gain: awb gb gain
* @lsc_table: lsc data of gr
*/
struct preisp_hdrae_para_s {
unsigned short r_gain;
unsigned short b_gain;
unsigned short gr_gain;
unsigned short gb_gain;
int lsc_table[PREISP_LSCTBL_SIZE];
};
/**
* struct preisp_hdrae_exp_s - hdrae exposure
*
*/
struct preisp_hdrae_exp_s {
unsigned int long_exp_reg;
unsigned int long_gain_reg;
unsigned int middle_exp_reg;
unsigned int middle_gain_reg;
unsigned int short_exp_reg;
unsigned int short_gain_reg;
unsigned int long_exp_val;
unsigned int long_gain_val;
unsigned int middle_exp_val;
unsigned int middle_gain_val;
unsigned int short_exp_val;
unsigned int short_gain_val;
};
#endif /* _UAPI_RKPREISP_H */