From c0202e0bc539e89d2253ddf5321eaee0c4ceeded Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Wed, 31 Oct 2018 19:37:20 +0800 Subject: [PATCH] media: spi: add driver for rk1608 Change-Id: I8508668fcd1e35c49fe581875fcf9045e004ae9c Signed-off-by: Hu Kejun --- drivers/media/spi/Kconfig | 9 + drivers/media/spi/Makefile | 1 + drivers/media/spi/rk1608.c | 1596 +++++++++++++++++++++++++++++++ drivers/media/spi/rk1608.h | 484 ++++++++++ drivers/media/spi/rk1608_dphy.c | 532 +++++++++++ drivers/media/spi/rk1608_dphy.h | 25 + include/uapi/linux/rk-preisp.h | 56 ++ 7 files changed, 2703 insertions(+) create mode 100644 drivers/media/spi/rk1608.c create mode 100644 drivers/media/spi/rk1608.h create mode 100644 drivers/media/spi/rk1608_dphy.c create mode 100644 drivers/media/spi/rk1608_dphy.h create mode 100644 include/uapi/linux/rk-preisp.h diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index a21f5a39a440..367f53fa9d7e 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -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 diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile index ea64013d16cc..5dc9fb180404 100644 --- a/drivers/media/spi/Makefile +++ b/drivers/media/spi/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_GS1662) += gs1662.o +obj-$(CONFIG_VIDEO_ROCKCHIP_PREISP) += rk1608_dphy.o rk1608.o diff --git a/drivers/media/spi/rk1608.c b/drivers/media/spi/rk1608.c new file mode 100644 index 000000000000..4c0909a783ac --- /dev/null +++ b/drivers/media/spi/rk1608.c @@ -0,0 +1,1596 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Rockchip rk1608 driver + * + * Copyright (C) 2017-2018 Rockchip Electronics Co., Ltd. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk1608.h" + +#define ENABLE_DMA_BUFFER 1 +#define SPI_BUFSIZ max(32, SMP_CACHE_BYTES) + +/** + * 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_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rk1608_state, sd); +} + +/** + * rk1608_operation_query - RK1608 last operation state query + * + * @spi: device from which data will be read + * @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) +{ + s32 query_cmd = RK1608_CMD_QUERY; + struct spi_transfer query_cmd_packet = { + .tx_buf = &query_cmd, + .len = sizeof(query_cmd), + }; + struct spi_transfer state_packet = { + .rx_buf = state, + .len = sizeof(*state), + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&query_cmd_packet, &m); + spi_message_add_tail(&state_packet, &m); + spi_sync(spi, &m); + + return ((*state & RK1608_STATE_ID_MASK) == RK1608_STATE_ID) ? 0 : -1; +} + +static int rk1608_write(struct spi_device *spi, + s32 addr, const s32 *data, size_t data_len) +{ + u8 *local_buf = NULL; + int ret = 0; + s32 write_cmd = RK1608_CMD_WRITE; + + struct spi_transfer write_cmd_packet = { + .tx_buf = &write_cmd, + .len = sizeof(write_cmd), + }; + struct spi_transfer addr_packet = { + .tx_buf = &addr, + .len = sizeof(addr), + }; + struct spi_transfer data_packet = { + .tx_buf = data, + .len = data_len, + }; + struct spi_message m; + u32 trans_len; + +#if ENABLE_DMA_BUFFER + trans_len = data_len + sizeof(write_cmd) + sizeof(addr); + if (trans_len > (size_t)SPI_BUFSIZ) { + local_buf = kmalloc(max_t(size_t, SPI_BUFSIZ, data_len), + GFP_KERNEL | GFP_DMA); + if (!local_buf) + return -ENOMEM; + memcpy(local_buf, data, data_len); + data_packet.tx_buf = local_buf; + } +#endif + + spi_message_init(&m); + spi_message_add_tail(&write_cmd_packet, &m); + spi_message_add_tail(&addr_packet, &m); + spi_message_add_tail(&data_packet, &m); + ret = spi_sync(spi, &m); + + kfree(local_buf); + + return ret; +} + +/** + * 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 a negative error code. + */ +static int _rk1608_safe_write(struct spi_device *spi, s32 addr, + const s32 *data, size_t data_len) +{ + int ret = 0; + s32 state = 0; + s32 try = 0; + + do { + ret = rk1608_write(spi, addr, data, data_len); + if (ret == 0) + ret = rk1608_operation_query(spi, &state); + + if (ret != 0) + return ret; + else if ((state & RK1608_STATE_MASK) == 0) + break; + + if (try++ == RK1608_OP_TRY_MAX) + break; + udelay(RK1608_OP_TRY_DELAY); + } while (1); + + return (state & RK1608_STATE_MASK); +} + +static int rk1608_safe_write(struct spi_device *spi, + s32 addr, const s32 *data, size_t data_len) +{ + int ret = 0; + size_t max_op_size = (size_t)RK1608_MAX_OP_BYTES; + + while (data_len > 0) { + size_t slen = ALIGN(MIN(data_len, max_op_size), 4); + + ret = _rk1608_safe_write(spi, addr, data, slen); + if (ret == -ENOMEM) { + max_op_size = slen / 2; + continue; + } + + if (ret) + break; + data_len = data_len - slen; + data = (s32 *)((s8 *)data + slen); + addr += slen; + } + + return ret; +} + +static void rk1608_hw_init(struct spi_device *spi) +{ + s32 write_data = SPI0_PLL_SEL_APLL; + + /* modify rk1608 spi slave clk to 300M */ + rk1608_safe_write(spi, CRUPMU_CLKSEL14_CON, &write_data, 4); + + /* modify rk1608 spi io driver strength to 8mA */ + write_data = BIT7_6_SEL_8MA; + rk1608_safe_write(spi, PMUGRF_GPIO1A_E, &write_data, 4); + write_data = BIT1_0_SEL_8MA; + rk1608_safe_write(spi, PMUGRF_GPIO1B_E, &write_data, 4); +} + +/** + * 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) +{ + u8 *local_buf = NULL; + int ret; + s32 real_len = MIN(data_len, RK1608_MAX_OP_BYTES); + s32 read_cmd = RK1608_CMD_READ | (real_len << 14 & + RK1608_STATE_ID_MASK); + s32 read_begin_cmd = RK1608_CMD_READ_BEGIN; + s32 dummy = 0; + struct spi_transfer read_cmd_packet = { + .tx_buf = &read_cmd, + .len = sizeof(read_cmd), + }; + struct spi_transfer addr_packet = { + .tx_buf = &addr, + .len = sizeof(addr), + }; + struct spi_transfer read_dummy_packet = { + .tx_buf = &dummy, + .len = sizeof(dummy), + }; + struct spi_transfer read_begin_cmd_packet = { + .tx_buf = &read_begin_cmd, + .len = sizeof(read_begin_cmd), + }; + struct spi_transfer data_packet = { + .rx_buf = data, + .len = data_len, + }; + struct spi_message m; + u32 trans_len; + +#if ENABLE_DMA_BUFFER + trans_len = data_len + sizeof(read_cmd) + sizeof(addr) + + sizeof(dummy) + sizeof(read_begin_cmd); + if (trans_len > (size_t)SPI_BUFSIZ) { + local_buf = kmalloc(max_t(size_t, SPI_BUFSIZ, data_len), + GFP_KERNEL | GFP_DMA); + if (!local_buf) + return -ENOMEM; + data_packet.rx_buf = local_buf; + } +#endif + + spi_message_init(&m); + spi_message_add_tail(&read_cmd_packet, &m); + spi_message_add_tail(&addr_packet, &m); + spi_message_add_tail(&read_dummy_packet, &m); + spi_message_add_tail(&read_begin_cmd_packet, &m); + spi_message_add_tail(&data_packet, &m); + ret = spi_sync(spi, &m); + + if (local_buf) { + memcpy(data, local_buf, data_len); + kfree(local_buf); + } + + return ret; +} + +/** + * 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 a negative error code. + */ +static int rk1608_safe_read(struct spi_device *spi, + s32 addr, s32 *data, size_t data_len) +{ + s32 state = 0; + s32 retry = 0; + + do { + rk1608_read(spi, addr, data, data_len); + if (rk1608_operation_query(spi, &state) != 0) + return -EPERM; + if ((state & RK1608_STATE_MASK) == 0) + break; + udelay(RK1608_OP_TRY_DELAY); + } while (retry++ != RK1608_OP_TRY_MAX); + + return -(state & RK1608_STATE_MASK); +} + +static int rk1608_read_wait(struct spi_device *spi, + const struct rk1608_section *sec) +{ + s32 value = 0; + int retry = 0; + int ret = 0; + + do { + ret = rk1608_safe_read(spi, sec->wait_addr, &value, 4); + if (!ret && value == sec->wait_value) + break; + + if (retry++ == sec->timeout) { + ret = -EPERM; + dev_err(&spi->dev, "Read 0x%x is %x != %x timeout\n", + sec->wait_addr, value, sec->wait_value); + break; + } + msleep(sec->wait_time); + } while (1); + + return ret; +} + +static int rk1608_boot_request(struct spi_device *spi, + const struct rk1608_section *sec) +{ + struct rk1608_boot_req boot_req; + int retry = 0; + int ret = 0; + + /* Send boot request to rk1608 for ddr init */ + boot_req.flag = sec->flag; + boot_req.load_addr = sec->load_addr; + boot_req.boot_len = sec->size; + boot_req.status = 1; + boot_req.cmd = 2; + + ret = rk1608_safe_write(spi, BOOT_REQUEST_ADDR, + (s32 *)&boot_req, sizeof(boot_req)); + if (ret) + return ret; + + if (sec->flag & BOOT_FLAG_READ_WAIT) { + /* Waitting for rk1608 init ddr done */ + do { + ret = rk1608_safe_read(spi, BOOT_REQUEST_ADDR, + (s32 *)&boot_req, + sizeof(boot_req)); + + if (!ret && boot_req.status == 0) + break; + + if (retry++ == sec->timeout) { + ret = -EPERM; + dev_err(&spi->dev, "Boot request timeout\n"); + break; + } + msleep(sec->wait_time); + } while (1); + } + + return ret; +} + +static int rk1608_download_section(struct spi_device *spi, const u8 *data, + const struct rk1608_section *sec) +{ + int ret = 0; + + dev_info(&spi->dev, "offset:%x,size:%x,addr:%x,wait_time:%x", + sec->offset, sec->size, sec->load_addr, sec->wait_time); + + dev_info(&spi->dev, "timeout:%x,crc:%x,flag:%x,type:%x", + sec->timeout, sec->crc_16, sec->flag, sec->type); + + if (sec->size > 0) { + ret = rk1608_safe_write(spi, sec->load_addr, + (s32 *)(data + sec->offset), + sec->size); + if (ret) { + dev_err(&spi->dev, "RK1608 safe write err =%d\n", ret); + return ret; + } + } + + if (sec->flag & BOOT_FLAG_BOOT_REQUEST) + ret = rk1608_boot_request(spi, sec); + else if (sec->flag & BOOT_FLAG_READ_WAIT) + ret = rk1608_read_wait(spi, sec); + + return ret; +} + +/** + * 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) +{ + const struct rk1608_header *head; + const struct firmware *fw; + u32 i = 0; + int ret = 0; + + if (!fw_name) + fw_name = RK1608_FW_NAME; + + dev_info(&spi->dev, "Before request firmware"); + ret = request_firmware(&fw, fw_name, &spi->dev); + if (ret) { + dev_err(&spi->dev, "Request firmware %s failed!", fw_name); + return ret; + } + + head = (const struct rk1608_header *)fw->data; + + dev_info(&spi->dev, "Request firmware %s (version:%s) success!", + fw_name, head->version); + + for (i = 0; i < head->section_count; i++) { + ret = rk1608_download_section(spi, fw->data, + &head->sections[i]); + if (ret) + break; + } + + release_firmware(fw); + return ret; +} + +static int rk1608_lsb_w32(struct spi_device *spi, s32 addr, s32 data) +{ + s32 write_cmd = RK1608_CMD_WRITE; + struct spi_transfer write_cmd_packet = { + .tx_buf = &write_cmd, + .len = sizeof(write_cmd), + }; + struct spi_transfer addr_packet = { + .tx_buf = &addr, + .len = sizeof(addr), + }; + struct spi_transfer data_packet = { + .tx_buf = &data, + .len = sizeof(data), + }; + struct spi_message m; + + write_cmd = MSB2LSB32(write_cmd); + addr = MSB2LSB32(addr); + data = MSB2LSB32(data); + spi_message_init(&m); + spi_message_add_tail(&write_cmd_packet, &m); + spi_message_add_tail(&addr_packet, &m); + spi_message_add_tail(&data_packet, &m); + + return spi_sync(spi, &m); +} + +static int rk1608_msg_init_sensor(struct rk1608_state *pdata, + struct msg_init *msg, int id) +{ + struct i2c_client *client; + + msg->msg_head.size = sizeof(struct msg_init); + msg->msg_head.type = id_msg_init_sensor_t; + msg->msg_head.id.camera_id = id; + msg->msg_head.mux.sync = 1; + msg->in_mipi_phy = pdata->dphy[id]->in_mipi; + msg->out_mipi_phy = pdata->dphy[id]->out_mipi; + msg->mipi_lane = pdata->dphy[id]->mipi_lane; + msg->bayer = 0; + memcpy(msg->sensor_name, pdata->sensor[id]->name, + sizeof(msg->sensor_name)); + + client = v4l2_get_subdevdata(pdata->sensor[id]); + msg->i2c_slave_addr = (client->addr) << 1; + msg->i2c_bus = 1; + + return rk1608_send_msg_to_dsp(pdata, &msg->msg_head); +} + +static int rk1608_msg_set_input_size(struct rk1608_state *pdata, + struct msg_in_size *msg, int id) +{ + msg->msg_head.size = sizeof(struct msg_in_size) / sizeof(int); + msg->msg_head.type = id_msg_set_input_size_t; + msg->msg_head.id.camera_id = id; + msg->msg_head.mux.sync = 1; + + msg->channel[0].data_id = 0x6b; + msg->channel[0].decode_format = 0x2b; + msg->channel[0].flag = 1; + msg->channel[0].width = pdata->dphy[id]->mf.width; + msg->channel[0].height = pdata->dphy[id]->mf.height * 2; + + msg->channel[1].data_id = 0x2b; + msg->channel[1].decode_format = 0x2b; + msg->channel[1].flag = 2; + msg->channel[1].width = pdata->dphy[id]->mf.width; + msg->channel[1].height = pdata->dphy[id]->mf.height; + + return rk1608_send_msg_to_dsp(pdata, &msg->msg_head); +} + +static int rk1608_msg_set_output_size(struct rk1608_state *pdata, + struct msg_set_output_size *msg, int id) +{ + msg->head.msg_head.size = sizeof(struct msg_set_output_size) / sizeof(int); + msg->head.msg_head.type = id_msg_set_output_size_t; + msg->head.msg_head.id.camera_id = id; + msg->head.msg_head.mux.sync = 1; + msg->head.width = pdata->dphy[id]->mf.width; + msg->head.height = pdata->dphy[id]->mf.height; + msg->head.mipi_clk = pdata->dphy[id]->link_freqs; + msg->head.line_length_pclk = pdata->dphy[id]->htotal; + msg->head.frame_length_lines = pdata->dphy[id]->vtotal; + msg->head.mipi_lane = pdata->dphy[id]->mipi_lane; + + msg->channel[0].data_id = 0x2b; + msg->channel[0].decode_format = 0x2b; + msg->channel[0].flag = 1; + msg->channel[0].width = pdata->dphy[id]->mf.width; + msg->channel[0].height = pdata->dphy[id]->mf.height; + + msg->channel[1].data_id = 0x30; + msg->channel[1].decode_format = 0x2b; + msg->channel[1].flag = 0; + msg->channel[1].width = pdata->dphy[id]->mf.width; + msg->channel[1].height = 2; + + return rk1608_send_msg_to_dsp(pdata, &msg->head.msg_head); +} + +static int rk1608_msg_set_stream_in_on(struct rk1608_state *pdata, + struct msg *msg, int id) +{ + msg->size = sizeof(struct msg); + msg->type = id_msg_set_stream_in_on_t; + msg->id.camera_id = id; + msg->mux.sync = 1; + + return rk1608_send_msg_to_dsp(pdata, msg); +} + +static int rk1608_msg_set_stream_in_off(struct rk1608_state *pdata, + struct msg *msg, int id) +{ + msg->size = sizeof(struct msg); + msg->type = id_msg_set_stream_in_off_t; + msg->id.camera_id = id; + msg->mux.sync = 1; + + return rk1608_send_msg_to_dsp(pdata, msg); +} + +static int rk1608_msg_set_stream_out_on(struct rk1608_state *pdata, + struct msg *msg, int id) +{ + msg->size = sizeof(struct msg); + msg->type = id_msg_set_stream_out_on_t; + msg->id.camera_id = id; + msg->mux.sync = 1; + + return rk1608_send_msg_to_dsp(pdata, msg); +} + +static int rk1608_msg_set_stream_out_off(struct rk1608_state *pdata, + struct msg *msg, int id) +{ + msg->size = sizeof(struct msg); + msg->type = id_msg_set_stream_out_off_t; + msg->id.camera_id = id; + msg->mux.sync = 1; + + return rk1608_send_msg_to_dsp(pdata, msg); +} + +static int rk1608_set_log_level(struct rk1608_state *pdata, + struct msg *msg, int level) +{ + msg->size = sizeof(struct msg); + msg->type = id_msg_set_log_level_t; + msg->mux.log_level = level; + + return rk1608_send_msg_to_dsp(pdata, msg); +} + +static int rk1608_send_meta_hdrae(struct rk1608_state *pdata, + struct preisp_hdrae_exp_s *hdrae_exp) +{ + int ret = 0; + unsigned long flags; + struct msg_set_sensor_info_s *msg; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->msg_head.size = sizeof(*msg) / 4; + msg->msg_head.type = id_msg_set_sensor_info_t; + msg->msg_head.id.camera_id = 0; + msg->set_exp_cnt = pdata->set_exp_cnt++; + + spin_lock_irqsave(&pdata->hdrae_lock, flags); + msg->r_gain = pdata->hdrae_para.r_gain; + msg->b_gain = pdata->hdrae_para.b_gain; + msg->gr_gain = pdata->hdrae_para.gr_gain; + msg->gb_gain = pdata->hdrae_para.gb_gain; + memcpy(msg->lsc_table, pdata->hdrae_para.lsc_table, + sizeof(msg->lsc_table)); + spin_unlock_irqrestore(&pdata->hdrae_lock, flags); + + /* dsp hdrae */ + msg->dsp_hdrae.bayer_mode = BAYER_MODE_BGGR; + msg->dsp_hdrae.grid_mode = AE_MEASURE_GRID_15X15; + memset(&msg->dsp_hdrae.weight[0], 3, ISP_DSP_HDRAE_MAXGRIDITEMS); + msg->dsp_hdrae.hist_mode = AE_HISTSTATICMODE_Y; + msg->dsp_hdrae.ycoeff.rcoef = 1; + msg->dsp_hdrae.ycoeff.gcoef = 1; + msg->dsp_hdrae.ycoeff.bcoef = 1; + msg->dsp_hdrae.ycoeff.offset = 0; + msg->dsp_hdrae.imgbits = 0; + msg->dsp_hdrae.width = 1920; + msg->dsp_hdrae.height = 1080; + msg->dsp_hdrae.frames = 2; + + msg->reg_exp_time[0] = hdrae_exp->long_exp_reg; + msg->reg_exp_gain[0] = hdrae_exp->long_gain_reg; + msg->reg_exp_time[1] = hdrae_exp->middle_exp_reg; + msg->reg_exp_gain[1] = hdrae_exp->middle_gain_reg; + msg->reg_exp_time[2] = hdrae_exp->short_exp_reg; + msg->reg_exp_gain[2] = hdrae_exp->short_gain_reg; + + msg->exp_time[0] = hdrae_exp->long_exp_val; + msg->exp_gain[0] = hdrae_exp->long_gain_val; + msg->exp_time[1] = hdrae_exp->middle_exp_val; + msg->exp_gain[1] = hdrae_exp->middle_gain_val; + msg->exp_time[2] = hdrae_exp->short_exp_val; + msg->exp_gain[2] = hdrae_exp->short_gain_val; + + ret = rk1608_send_msg_to_dsp(pdata, &msg->msg_head); + kfree(msg); + + return ret; +} + +static int rk1608_init_sensor(struct rk1608_state *pdata, int id) +{ + struct msg *msg = NULL; + struct msg_init *msg_init = NULL; + struct msg_in_size *msg_in_size = NULL; + struct msg_set_output_size *msg_out_size = NULL; + int ret = 0; + + if (!pdata->sensor[id]) { + dev_err(pdata->dev, "Did not find a sensor[%d]!\n", id); + return -EINVAL; + } + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto err; + } + msg_init = kzalloc(sizeof(*msg_init), GFP_KERNEL); + if (!msg_init) { + ret = -ENOMEM; + goto err; + } + msg_in_size = kzalloc(sizeof(*msg_in_size), GFP_KERNEL); + if (!msg_in_size) { + ret = -ENOMEM; + goto err; + } + msg_out_size = kzalloc(sizeof(*msg_out_size), GFP_KERNEL); + if (!msg_out_size) { + ret = -ENOMEM; + goto err; + } + + ret = rk1608_msg_init_sensor(pdata, msg_init, id); + ret |= rk1608_msg_set_input_size(pdata, msg_in_size, id); + ret |= rk1608_msg_set_output_size(pdata, msg_out_size, id); + ret |= rk1608_msg_set_stream_in_on(pdata, msg, id); + ret |= rk1608_msg_set_stream_out_on(pdata, msg, id); + +err: + kfree(msg_init); + kfree(msg_in_size); + kfree(msg_out_size); + kfree(msg); + + return ret; +} + +static int rk1608_deinit(struct rk1608_state *pdata, int id) +{ + struct msg *msg; + int ret = 0; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + ret = rk1608_msg_set_stream_out_off(pdata, msg, id); + ret |= rk1608_msg_set_stream_in_off(pdata, msg, id); + kfree(msg); + + return ret; +} + +static void rk1608_cs_set_value(struct rk1608_state *pdata, int value) +{ + s8 null_cmd = 0; + + struct spi_transfer null_cmd_packet = { + .tx_buf = &null_cmd, + .len = sizeof(null_cmd), + .cs_change = !value, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&null_cmd_packet, &m); + spi_sync(pdata->spi, &m); +} + +static void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz) +{ + pdata->spi->max_speed_hz = hz; +} + +static int rk1608_power_on(struct rk1608_state *pdata) +{ + struct spi_device *spi = pdata->spi; + int ret = 0; + + /* Request rk1608 enter slave mode */ + rk1608_cs_set_value(pdata, 0); + if (pdata->wakeup_gpio) + gpiod_direction_output(pdata->wakeup_gpio, 1); + + usleep_range(3000, 3500); + if (pdata->reset_gpio) { + gpiod_direction_output(pdata->reset_gpio, 1); + gpiod_direction_output(pdata->reset_gpio, 0); + gpiod_direction_output(pdata->reset_gpio, 1); + } + + /* After Reset pull-up, CSn should keep low for 2ms+ */ + usleep_range(3000, 3500); + rk1608_cs_set_value(pdata, 1); + rk1608_lsb_w32(spi, SPI_ENR, 0); + rk1608_lsb_w32(spi, SPI_CTRL0, + OPM_SLAVE_MODE | RSD_SEL_2CYC | DFS_SEL_16BIT); + rk1608_hw_init(pdata->spi); + rk1608_set_spi_speed(pdata, pdata->max_speed_hz); + + /* Download system firmware */ + ret = rk1608_download_fw(pdata->spi, pdata->firm_name); + if (ret) + dev_err(pdata->dev, "Download firmware failed!"); + else + dev_info(pdata->dev, "Download firmware success!"); + + if (pdata->irq > 0) + enable_irq(pdata->irq); + + return 0; +} + +static int rk1608_power_off(struct rk1608_state *pdata) +{ + /* Request rk1608 enter slave mode */ + if (pdata->irq > 0) + disable_irq(pdata->irq); + if (pdata->wakeup_gpio) + gpiod_direction_output(pdata->wakeup_gpio, 0); + if (pdata->reset_gpio) + gpiod_direction_output(pdata->reset_gpio, 0); + rk1608_cs_set_value(pdata, 0); + + return 0; +} + +static int rk1608_sensor_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct msg *msg = NULL; + struct rk1608_state *pdata = to_state(sd); + + mutex_lock(&pdata->lock); + if (on) { + v4l2_subdev_call(pdata->sensor[sd->grp_id], core, s_power, on); + if (!pdata->power_count) + rk1608_power_on(pdata); + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + mutex_unlock(&pdata->lock); + return -ENOMEM; + } + + ret = rk1608_set_log_level(pdata, msg, 2); + if (!msg) + kfree(msg); + } else if (!on && pdata->power_count == 1) { + v4l2_subdev_call(pdata->sensor[sd->grp_id], core, s_power, on); + rk1608_power_off(pdata); + } + + /* Update the power count. */ + pdata->power_count += on ? 1 : -1; + WARN_ON(pdata->power_count < 0); + mutex_unlock(&pdata->lock); + + return ret; +} + +static int rk1608_stream_on(struct rk1608_state *pdata) +{ + int id = 0, cnt = 0, ret = 0; + + mutex_lock(&pdata->lock); + id = pdata->sd.grp_id; + pdata->sensor_cnt = 0; + pdata->set_exp_cnt = 1; + ret = rk1608_init_sensor(pdata, id); + if (ret) { + dev_err(pdata->dev, "Init rk1608[%d] is failed!", + pdata->sd.grp_id); + mutex_unlock(&pdata->lock); + return ret; + } + + /* Waiting for the sensor to be ready */ + while (pdata->sensor_cnt < pdata->sensor_nums[id]) { + /* TIMEOUT 10s break */ + if (cnt++ > SENSOR_TIMEOUT) { + dev_err(pdata->dev, + "Sensor%d is ready to timeout!", + pdata->sensor_cnt); + break; + } + usleep_range(10000, 11000); + } + + if (pdata->sensor_nums[id]) { + if (pdata->sensor_cnt == pdata->sensor_nums[id]) + dev_info(pdata->dev, "Sensor(num %d) is ready!", + pdata->sensor_cnt); + } else { + dev_warn(pdata->dev, "No sensor is found!"); + } + mutex_unlock(&pdata->lock); + + pdata->hdrae_para.r_gain = 0x0100; + pdata->hdrae_para.b_gain = 0x0100; + pdata->hdrae_para.gr_gain = 0x0100; + pdata->hdrae_para.gb_gain = 0x0100; + for (cnt = 0; cnt < PREISP_LSCTBL_SIZE; cnt++) + pdata->hdrae_para.lsc_table[cnt] = 0x0400; + memset(&pdata->hdrae_exp, 0, sizeof(pdata->hdrae_exp)); + return 0; +} + +static int rk1608_stream_off(struct rk1608_state *pdata) +{ + mutex_lock(&pdata->sensor_lock); + pdata->sensor_cnt = 0; + mutex_unlock(&pdata->sensor_lock); + rk1608_deinit(pdata, pdata->sd.grp_id); + + return 0; +} + +static int rk1608_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret; + struct rk1608_state *pdata = to_state(sd); + + if (enable) + ret = rk1608_stream_on(pdata); + else + ret = rk1608_stream_off(pdata); + + v4l2_subdev_call(pdata->sensor[sd->grp_id], video, s_stream, enable); + + return ret; +} + +static int rk1608_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct rk1608_state *pdata = to_state(sd); + + v4l2_subdev_call(pdata->sensor[sd->grp_id], + video, + g_frame_interval, + fi); + + return 0; +} + +static long rk1608_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct rk1608_state *pdata = to_state(sd); + struct preisp_hdrae_para_s *hdrae_para; + struct preisp_hdrae_exp_s *hdrae_exp; + + switch (cmd) { + case PREISP_CMD_SAVE_HDRAE_PARAM: + hdrae_para = arg; + spin_lock(&pdata->hdrae_lock); + pdata->hdrae_para = *hdrae_para; + spin_unlock(&pdata->hdrae_lock); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae_exp = arg; + if (pdata->hdrae_exp.long_exp_reg == hdrae_exp->long_exp_reg && + pdata->hdrae_exp.long_gain_reg == hdrae_exp->long_gain_reg && + pdata->hdrae_exp.short_exp_reg == hdrae_exp->short_exp_reg && + pdata->hdrae_exp.short_gain_reg == hdrae_exp->short_gain_reg) + break; + + pdata->hdrae_exp = *hdrae_exp; + + /* hdr exposure start */ + if (pdata->aesync_gpio) + gpiod_direction_output(pdata->aesync_gpio, 1); + + v4l2_subdev_call(pdata->sensor[sd->grp_id], core, ioctl, + cmd, hdrae_exp); + + if (pdata->aesync_gpio) + gpiod_direction_output(pdata->aesync_gpio, 0); + + rk1608_send_meta_hdrae(pdata, hdrae_exp); + break; + default: + return -ENOTTY; + } + + return 0; +} + +static int rk1608_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *remote_ctrl; + struct rk1608_state *pdata = + container_of(ctrl->handler, + struct rk1608_state, ctrl_handler); + int id = pdata->sd.grp_id; + + remote_ctrl = v4l2_ctrl_find(pdata->sensor[id]->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_state *pdata = + container_of(ctrl->handler, + struct rk1608_state, ctrl_handler); + int id = pdata->sd.grp_id; + + remote_ctrl = v4l2_ctrl_find(pdata->sensor[id]->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 int rk1608_initialize_controls(struct rk1608_state *rk1608) +{ + int ret; + struct v4l2_ctrl_handler *handler; + unsigned long flags = V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + + handler = &rk1608->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + + rk1608->hblank = v4l2_ctrl_new_std(handler, + &rk1608_ctrl_ops, + V4L2_CID_HBLANK, + 0, 0x7FFFFFFF, 1, 0); + if (rk1608->hblank) + rk1608->hblank->flags |= flags; + + rk1608->vblank = v4l2_ctrl_new_std(handler, + &rk1608_ctrl_ops, + V4L2_CID_VBLANK, + 0, 0x7FFFFFFF, 1, 0); + if (rk1608->vblank) + rk1608->vblank->flags |= flags; + + rk1608->exposure = v4l2_ctrl_new_std(handler, + &rk1608_ctrl_ops, + V4L2_CID_EXPOSURE, + 0, 0x7FFFFFFF, 1, 0); + if (rk1608->exposure) + rk1608->exposure->flags |= flags; + + rk1608->gain = v4l2_ctrl_new_std(handler, + &rk1608_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + 0, 0x7FFFFFFF, 1, 0); + if (rk1608->gain) + rk1608->gain->flags |= flags; + + if (handler->error) { + ret = handler->error; + dev_err(rk1608->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + rk1608->sd.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +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_core_ops rk1608_core_ops = { + .s_power = rk1608_sensor_power, + .ioctl = rk1608_ioctl, +}; + +static const struct v4l2_subdev_ops rk1608_subdev_ops = { + .core = &rk1608_core_ops, + .video = &rk1608_subdev_video_ops, +}; + +/** + * 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) +{ + int err = 0; + s32 reg; + + err = rk1608_safe_read(spi, RK1608_PMU_SYS_REG0, ®, 4); + + if (err || ((reg & RK1608_MSG_QUEUE_OK_MASK) != + RK1608_MSG_QUEUE_OK_TAG)) + return -EINVAL; + + err = rk1608_safe_read(spi, addr, (s32 *)q, sizeof(*q)); + + return err; +} + +/** + * 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_recv_msg_free 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) +{ + struct rk1608_msg_queue queue; + struct rk1608_msg_queue *q = &queue; + u32 size = 0, msg_size = 0; + u32 recv_addr = 0; + u32 next_recv_addr = 0; + int err = 0; + + *m = NULL; + err = rk1608_msq_read_head(spi, RK1608_S_MSG_QUEUE_ADDR, q); + if (err) + return err; + + if (q->cur_send == q->cur_recv) + return -EINVAL; + /* Skip to head when size is 0 */ + err = rk1608_safe_read(spi, (s32)q->cur_recv, (s32 *)&size, 4); + if (err) + return err; + if (size == 0) { + err = rk1608_safe_read(spi, (s32)q->buf_head, (s32 *)&size, 4); + if (err) + return err; + + msg_size = size * sizeof(u32); + recv_addr = q->buf_head; + next_recv_addr = q->buf_head + msg_size; + } else { + msg_size = size * sizeof(u32); + recv_addr = q->cur_recv; + next_recv_addr = q->cur_recv + msg_size; + if (next_recv_addr == q->buf_tail) + next_recv_addr = q->buf_head; + } + + if (msg_size > (q->buf_tail - q->buf_head)) + return -EPERM; + + *m = kmalloc(msg_size, GFP_KERNEL); + err = rk1608_safe_read(spi, recv_addr, (s32 *)*m, msg_size); + if (err == 0) { + err = rk1608_safe_write(spi, RK1608_S_MSG_QUEUE_ADDR + + (u8 *)&q->cur_recv - (u8 *)q, + &next_recv_addr, 4); + } + if (err) + kfree(*m); + + return err; +} + +/** + * rk1608_msq_tail_free_size - get msg queue tail unused buf size + * + * @q: msg queue + * + * It returns size of msg queue tail unused buf size, unit byte + */ +static u32 rk1608_msq_tail_free_size(const struct rk1608_msg_queue *q) +{ + if (q->cur_send >= q->cur_recv) + return (q->buf_tail - q->cur_send); + + return q->cur_recv - q->cur_send; +} + +/** + * rk1608_interrupt_request - RK1608 request a dsp 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) +{ + s32 write_reg1_cmd = APB_CMD_WRITE_REG1; + struct spi_transfer write_reg1_cmd_packet = { + .tx_buf = &write_reg1_cmd, + .len = sizeof(write_reg1_cmd), + }; + struct spi_transfer reg1_packet = { + .tx_buf = &interrupt_num, + .len = sizeof(interrupt_num), + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&write_reg1_cmd_packet, &m); + spi_message_add_tail(®1_packet, &m); + + return spi_sync(spi, &m); +} + +/** + * dsp_msq_head_free_size - get msg queue head unused buf size + * + * @q: msg queue + * + * It returns size of msg queue head unused buf size, unit byte + */ +static u32 rk1608_msq_head_free_size(const struct rk1608_msg_queue *q) +{ + if (q->cur_send >= q->cur_recv) + return (q->cur_recv - q->buf_head); + + return 0; +} + +/* + * rk1608_msq_send_msg - send a msg to Soc -> DSP msg queue + * + * @spi: spi device + * @m: a msg to send + * + * It returns zero on success, else a negative error code. + */ +static int rk1608_msq_send_msg(struct spi_device *spi, struct msg *m) +{ + int err = 0; + s32 tmp = 0; + struct rk1608_msg_queue queue; + struct rk1608_msg_queue *q = &queue; + u32 msg_size = m->size * sizeof(u32); + + err = rk1608_msq_read_head(spi, RK1608_R_MSG_QUEUE_ADDR, q); + if (err) + return err; + + if (rk1608_msq_tail_free_size(q) > msg_size) { + u32 next_send; + + err = rk1608_safe_write(spi, q->cur_send, (s32 *)m, msg_size); + next_send = q->cur_send + msg_size; + if (next_send == q->buf_tail) + next_send = q->buf_head; + q->cur_send = next_send; + } else if (rk1608_msq_head_free_size(q) > msg_size) { + /* Set size to 0 for skip to head mark */ + err = rk1608_safe_write(spi, q->cur_send, &tmp, 4); + if (err) + return err; + err = rk1608_safe_write(spi, q->buf_head, (s32 *)m, msg_size); + q->cur_send = q->buf_head + msg_size; + } else { + return -EPERM; + } + + if (err) + return err; + + err = rk1608_safe_write(spi, RK1608_R_MSG_QUEUE_ADDR + + (u8 *)&q->cur_send - (u8 *)q, &q->cur_send, 4); + rk1608_interrupt_request(spi, RK1608_IRQ_TYPE_MSG); + + return err; +} + +static int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m) +{ + int ret, msg_num = 0, timeout = 0; + + /* For msg sync */ + if (pdata->msg_num >= 8) { + dev_err(pdata->dev, "MSG sync queue full\n!"); + ret = -EINVAL; + } else if (m->mux.sync == 1) { + mutex_lock(&pdata->send_msg_lock); + msg_num = pdata->msg_num; + pdata->msg_done[pdata->msg_num++] = 0; + mutex_unlock(&pdata->send_msg_lock); + } + + mutex_lock(&pdata->send_msg_lock); + ret = rk1608_msq_send_msg(pdata->spi, m); + mutex_unlock(&pdata->send_msg_lock); + + /* For msg sync */ + if (m->mux.sync == 1) { + timeout = wait_event_timeout(pdata->msg_wait, + pdata->msg_done[msg_num], + MSG_SYNC_TIMEOUT); + if (unlikely(timeout <= 0)) { + dev_info(pdata->dev, + "MSG wait timeout %d msg_num:%d\n", + timeout, pdata->msg_num); + mutex_lock(&pdata->send_msg_lock); + pdata->msg_done[msg_num] = 0; + mutex_unlock(&pdata->send_msg_lock); + } + } + + return ret; +} + +static void rk1608_print_rk1608_log(struct rk1608_state *pdata, + struct msg *log) +{ + char *str = (char *)(log); + + str[log->size * sizeof(s32) - 1] = 0; + str += sizeof(struct msg); + dev_info(pdata->dev, "RK1608(%d): %s", log->id.core_id, str); +} + +static void rk1608_dispatch_received_msg(struct rk1608_state *pdata, + struct msg *msg) +{ + if (msg->type == id_msg_set_stream_out_on_ret_t) { + mutex_lock(&pdata->sensor_lock); + pdata->sensor_cnt++; + mutex_unlock(&pdata->sensor_lock); + } + + if (msg->type == id_msg_rk1608_log_t) + rk1608_print_rk1608_log(pdata, msg); +} + +static irqreturn_t rk1608_threaded_isr(int irq, void *ctx) +{ + struct rk1608_state *pdata = ctx; + struct msg *msg; + + while (!rk1608_msq_recv_msg(pdata->spi, &msg) && NULL != msg) { + rk1608_dispatch_received_msg(pdata, msg); + /* For kernel msg sync */ + if (msg->type >= id_msg_init_sensor_ret_t && + msg->type <= id_msg_set_stream_out_off_ret_t) { + dev_info(pdata->dev, "RK1608 kernel sync\n"); + mutex_lock(&pdata->send_msg_lock); + pdata->msg_num--; + pdata->msg_done[pdata->msg_num] = 1; + mutex_unlock(&pdata->send_msg_lock); + wake_up(&pdata->msg_wait); + } + kfree(msg); + } + + return IRQ_HANDLED; +} + +static int rk1608_parse_dt_property(struct rk1608_state *pdata) +{ + int i, ret = 0; + struct device *dev = pdata->dev; + struct device_node *node = dev->of_node; + + if (!node) + return -ENODEV; + + of_property_read_u32(node, "spi-max-frequency", + &pdata->max_speed_hz); + if (ret) { + dev_err(dev, "can not get spi-max-frequency!"); + return -ENOENT; + } + + ret = of_property_read_u32(node, "spi-min-frequency", + &pdata->min_speed_hz); + if (ret) { + dev_warn(dev, "can not get spi-min-frequency!"); + pdata->min_speed_hz = pdata->max_speed_hz / 2; + } + + ret = of_property_read_string(node, "firmware-names", + &pdata->firm_name); + if (ret) { + dev_warn(dev, "can not get firmware-names!"); + pdata->firm_name = NULL; + } + + pdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->reset_gpio)) { + dev_err(dev, "can not find reset-gpio\n"); + return PTR_ERR(pdata->reset_gpio); + } + + pdata->irq = -1; + pdata->irq_gpio = devm_gpiod_get(dev, "irq", GPIOD_IN); + if (IS_ERR(pdata->irq_gpio)) { + dev_err(dev, "can not find irq-gpio\n"); + return -ENOENT; + } + + pdata->wakeup_gpio = devm_gpiod_get_optional(dev, "wakeup", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->wakeup_gpio)) { + dev_err(dev, "can not find wakeup_gpio\n"); + return PTR_ERR(pdata->wakeup_gpio); + } + + pdata->aesync_gpio = devm_gpiod_get_optional(dev, "aesync", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->aesync_gpio)) { + dev_err(dev, "can not find aesync_gpio\n"); + return PTR_ERR(pdata->aesync_gpio); + } + + pdata->msg_num = 0; + init_waitqueue_head(&pdata->msg_wait); + for (i = 0; i < 8; i++) + pdata->msg_done[i] = 0; + + return ret; +} + +static int rk1608_get_remote_node_dev(struct rk1608_state *pdev) +{ + struct i2c_client *sensor_pdev = NULL; + struct platform_device *dphydev = NULL; + struct device *dev = pdev->dev; + struct device_node *parent = dev->of_node; + struct device_node *remote = NULL; + int ret = 0, dphys = 0, sensor_nums = 0; + int i; + + for (i = 0; i < 2; i++) { + remote = of_graph_get_remote_node(parent, 0, i); + if (!remote) + continue; + + dphydev = of_find_device_by_node(remote); + of_node_put(remote); + if (!dphydev) { + dev_err(dev, "Failed to get dhpy device(%s)\n", + of_node_full_name(remote)); + continue; + } else { + pdev->dphy[dphys] = platform_get_drvdata(dphydev); + if (pdev->dphy[dphys]) { + dphydev = NULL; + pdev->dphy[dphys]->rk1608_sd = &pdev->sd; + pdev->sensor_nums[dphys] = + pdev->dphy[dphys]->cam_nums; + dphys++; + } else { + dev_err(dev, "Failed to get dhpy drvdata\n"); + } + } + } + + for (i = 0; i < 4; i++) { + remote = of_graph_get_remote_node(parent, 1, i); + if (!remote) + continue; + + sensor_pdev = of_find_i2c_device_by_node(remote); + of_node_put(remote); + if (!sensor_pdev) { + dev_err(dev, "Failed to get sensor device(%s)\n", + of_node_full_name(remote)); + continue; + } else { + pdev->sensor[sensor_nums] = + i2c_get_clientdata(sensor_pdev); + if (pdev->sensor[sensor_nums]) { + sensor_pdev = NULL; + sensor_nums++; + } else { + dev_err(dev, "Failed to get sensor drvdata\n"); + } + } + } + + if (dphys && sensor_nums) + dev_info(dev, "Get %d dphys, %d sensors!\n", + dphys, sensor_nums); + else + ret = -EINVAL; + + return ret; +} + +static int rk1608_probe(struct spi_device *spi) +{ + struct rk1608_state *rk1608; + struct v4l2_subdev *sd; + int ret = 0; + + rk1608 = devm_kzalloc(&spi->dev, sizeof(*rk1608), GFP_KERNEL); + if (!rk1608) + return -ENOMEM; + rk1608->dev = &spi->dev; + rk1608->spi = spi; + spi_set_drvdata(spi, rk1608); + + ret = rk1608_parse_dt_property(rk1608); + if (ret) { + dev_err(rk1608->dev, "RK1608 parse dt property err %x\n", ret); + return ret; + } + + ret = rk1608_get_remote_node_dev(rk1608); + if (ret) { + dev_err(rk1608->dev, "Get remote node dev err %x\n", ret); + return ret; + } + + rk1608->sensor_cnt = 0; + mutex_init(&rk1608->sensor_lock); + mutex_init(&rk1608->send_msg_lock); + mutex_init(&rk1608->lock); + spin_lock_init(&rk1608->hdrae_lock); + sd = &rk1608->sd; + + rk1608_initialize_controls(rk1608); + v4l2_spi_subdev_init(sd, spi, &rk1608_subdev_ops); + + if (!IS_ERR(rk1608->irq_gpio)) { + rk1608->irq = gpiod_to_irq(rk1608->irq_gpio); + ret = devm_request_threaded_irq( + rk1608->dev, + rk1608->irq, + NULL, + rk1608_threaded_isr, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "msg-irq", + rk1608); + if (ret) { + dev_err(rk1608->dev, + "cannot request thread irq: %d\n", ret); + v4l2_ctrl_handler_free(&rk1608->ctrl_handler); + return ret; + } + disable_irq(rk1608->irq); + } + + return 0; +} + +static int rk1608_remove(struct spi_device *spi) +{ + struct rk1608_state *rk1608 = spi_get_drvdata(spi); + + v4l2_ctrl_handler_free(&rk1608->ctrl_handler); + mutex_destroy(&rk1608->lock); + mutex_destroy(&rk1608->send_msg_lock); + mutex_destroy(&rk1608->sensor_lock); + + return 0; +} + +static const struct spi_device_id rk1608_id[] = { + { "rk1608", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rk1608_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id rk1608_of_match[] = { + { .compatible = "rockchip,rk1608" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rk1608_of_match); +#endif + +static struct spi_driver rk1608_driver = { + .driver = { + .of_match_table = of_match_ptr(rk1608_of_match), + .name = "rk1608", + }, + .probe = rk1608_probe, + .remove = rk1608_remove, + .id_table = rk1608_id, +}; + +static int __init preisp_mod_init(void) +{ + return spi_register_driver(&rk1608_driver); +} + +static void __exit preisp_mod_exit(void) +{ + spi_unregister_driver(&rk1608_driver); +} + +late_initcall(preisp_mod_init); +module_exit(preisp_mod_exit); + +MODULE_AUTHOR("Rockchip Camera/ISP team"); +MODULE_DESCRIPTION("A DSP driver for rk1608 chip"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/spi/rk1608.h b/drivers/media/spi/rk1608.h new file mode 100644 index 000000000000..f011ba791a94 --- /dev/null +++ b/drivers/media/spi/rk1608.h @@ -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 +#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 diff --git a/drivers/media/spi/rk1608_dphy.c b/drivers/media/spi/rk1608_dphy.c new file mode 100644 index 000000000000..d40ee4687e04 --- /dev/null +++ b/drivers/media/spi/rk1608_dphy.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Rockchip rk1608 driver + * + * Copyright (C) 2017-2018 Rockchip Electronics Co., Ltd. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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"); diff --git a/drivers/media/spi/rk1608_dphy.h b/drivers/media/spi/rk1608_dphy.h new file mode 100644 index 000000000000..12fe828dcace --- /dev/null +++ b/drivers/media/spi/rk1608_dphy.h @@ -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; +}; diff --git a/include/uapi/linux/rk-preisp.h b/include/uapi/linux/rk-preisp.h new file mode 100644 index 000000000000..3b58c2f7e137 --- /dev/null +++ b/include/uapi/linux/rk-preisp.h @@ -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 + +#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 */