diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile index b431a67193cc..4b1849d72031 100644 --- a/drivers/media/spi/Makefile +++ b/drivers/media/spi/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_VIDEO_GS1662) += gs1662.o -obj-$(CONFIG_VIDEO_ROCKCHIP_PREISP) += rk1608_dphy.o rk1608.o +obj-$(CONFIG_VIDEO_ROCKCHIP_PREISP) += rk1608_dphy.o rk1608.o rk1608_dev.o obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o ccflags-y += -Idrivers/media/dvb-core diff --git a/drivers/media/spi/rk1608.c b/drivers/media/spi/rk1608.c index 99fc5e9cc254..b0d2d98583b8 100644 --- a/drivers/media/spi/rk1608.c +++ b/drivers/media/spi/rk1608.c @@ -63,7 +63,7 @@ static inline struct rk1608_state *to_state(struct v4l2_subdev *sd) * * It returns zero on success, else a negative error code. */ -static int rk1608_operation_query(struct spi_device *spi, s32 *state) +int rk1608_operation_query(struct spi_device *spi, s32 *state) { s32 query_cmd = RK1608_CMD_QUERY; struct spi_transfer query_cmd_packet = { @@ -84,8 +84,39 @@ static int rk1608_operation_query(struct spi_device *spi, s32 *state) 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) +/** + * rk1608_state_query - RK1608 system state query + * + * @spi: spi device + * @state: system state [out] + * Context: can sleep + * + * It returns zero on success, else a negative error code. + */ +int rk1608_state_query(struct spi_device *spi, int32_t *state) +{ + int ret = 0; + s32 query_cmd = RK1608_CMD_QUERY_REG2; + 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); + ret = spi_sync(spi, &m); + + return ret; +} + +int rk1608_write(struct spi_device *spi, + s32 addr, const s32 *data, size_t data_len) { u8 *local_buf = NULL; int ret = 0; @@ -165,8 +196,8 @@ static int _rk1608_safe_write(struct spi_device *spi, s32 addr, return (state & RK1608_STATE_MASK); } -static int rk1608_safe_write(struct spi_device *spi, - s32 addr, const s32 *data, size_t data_len) +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; @@ -215,8 +246,8 @@ static void rk1608_hw_init(struct spi_device *spi) * * 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) +int rk1608_read(struct spi_device *spi, + s32 addr, s32 *data, size_t data_len) { u8 *local_buf = NULL; int ret; @@ -287,8 +318,8 @@ static int rk1608_read(struct spi_device *spi, * * 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) +int rk1608_safe_read(struct spi_device *spi, + s32 addr, s32 *data, size_t data_len) { s32 state = 0; s32 retry = 0; @@ -408,7 +439,7 @@ static int rk1608_download_section(struct spi_device *spi, const u8 *data, * * It returns zero on success, else a negative error code. */ -static int rk1608_download_fw(struct spi_device *spi, const char *fw_name) +int rk1608_download_fw(struct spi_device *spi, const char *fw_name) { const struct rk1608_header *head; const struct firmware *fw; @@ -593,14 +624,22 @@ static int rk1608_msg_set_stream_out_off(struct rk1608_state *pdata, return rk1608_send_msg_to_dsp(pdata, msg); } -static int rk1608_set_log_level(struct rk1608_state *pdata, - struct msg *msg, int level) +int rk1608_set_log_level(struct rk1608_state *pdata, int level) { + struct msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + int ret = 0; + + if (!msg) + return -ENOMEM; + 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); + ret = rk1608_send_msg_to_dsp(pdata, msg); + kfree(msg); + + return ret; } static int rk1608_send_meta_hdrae(struct rk1608_state *pdata, @@ -742,7 +781,7 @@ static void rk1608_cs_set_value(struct rk1608_state *pdata, int value) spi_sync(pdata->spi, &m); } -static void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz) +void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz) { pdata->spi->max_speed_hz = hz; } @@ -783,7 +822,10 @@ static int rk1608_power_on(struct rk1608_state *pdata) if (pdata->irq > 0) enable_irq(pdata->irq); - return 0; + if (!ret) + ret = rk1608_set_log_level(pdata, pdata->log_level); + + return ret; } static int rk1608_power_off(struct rk1608_state *pdata) @@ -800,10 +842,27 @@ static int rk1608_power_off(struct rk1608_state *pdata) return 0; } +int rk1608_set_power(struct rk1608_state *pdata, int on) +{ + mutex_lock(&pdata->lock); + if (on) { + if (!pdata->power_count) + rk1608_power_on(pdata); + } else { + if (pdata->power_count == 1) + rk1608_power_off(pdata); + } + + pdata->power_count += on ? 1 : -1; + if (pdata->power_count < 0) + pdata->power_count = 0; + mutex_unlock(&pdata->lock); + + 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); @@ -812,15 +871,6 @@ static int rk1608_sensor_power(struct v4l2_subdev *sd, int 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); @@ -831,7 +881,7 @@ static int rk1608_sensor_power(struct v4l2_subdev *sd, int on) WARN_ON(pdata->power_count < 0); mutex_unlock(&pdata->lock); - return ret; + return 0; } static int rk1608_stream_on(struct rk1608_state *pdata) @@ -1117,11 +1167,11 @@ static int rk1608_msq_read_head(struct spi_device *spi, * @q: msg queue * @m: a msg pointer buf [out] * - * need call rk1608_msq_recv_msg_free to free msg after msg use done + * need 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) +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m) { struct rk1608_msg_queue queue; struct rk1608_msg_queue *q = &queue; @@ -1161,14 +1211,18 @@ static int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m) return -EPERM; *m = kmalloc(msg_size, GFP_KERNEL); + if (!*m) + return -ENOMEM; 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) + if (err) { kfree(*m); + *m = NULL; + } return err; } @@ -1197,8 +1251,7 @@ static u32 rk1608_msq_tail_free_size(const struct rk1608_msg_queue *q) * * It returns zero on success, else a negative error code. */ -static int rk1608_interrupt_request(struct spi_device *spi, - s32 interrupt_num) +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 = { @@ -1241,7 +1294,7 @@ static u32 rk1608_msq_head_free_size(const struct rk1608_msg_queue *q) * * It returns zero on success, else a negative error code. */ -static int rk1608_msq_send_msg(struct spi_device *spi, struct msg *m) +int rk1608_msq_send_msg(struct spi_device *spi, struct msg *m) { int err = 0; s32 tmp = 0; @@ -1282,14 +1335,14 @@ static int rk1608_msq_send_msg(struct spi_device *spi, struct msg *m) return err; } -static int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m) +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; + return -EINVAL; } else if (m->mux.sync == 1) { mutex_lock(&pdata->send_msg_lock); msg_num = pdata->msg_num; @@ -1326,7 +1379,7 @@ static void rk1608_print_rk1608_log(struct rk1608_state *pdata, str[log->size * sizeof(s32) - 1] = 0; str += sizeof(struct msg); - dev_info(pdata->dev, "RK1608(%d): %s", log->id.core_id, str); + dev_info(pdata->dev, "DSP(%d): %s", log->id.core_id, str); } static void rk1608_dispatch_received_msg(struct rk1608_state *pdata, @@ -1340,6 +1393,8 @@ static void rk1608_dispatch_received_msg(struct rk1608_state *pdata, if (msg->type == id_msg_rk1608_log_t) rk1608_print_rk1608_log(pdata, msg); + + rk1608_dev_receive_msg(pdata, msg); } static irqreturn_t rk1608_threaded_isr(int irq, void *ctx) @@ -1509,6 +1564,7 @@ static int rk1608_probe(struct spi_device *spi) return -ENOMEM; rk1608->dev = &spi->dev; rk1608->spi = spi; + rk1608->log_level = LOG_INFO; spi_set_drvdata(spi, rk1608); ret = rk1608_parse_dt_property(rk1608); @@ -1550,6 +1606,8 @@ static int rk1608_probe(struct spi_device *spi) disable_irq(rk1608->irq); } + rk1608_dev_register(rk1608); + return 0; } @@ -1561,6 +1619,7 @@ static int rk1608_remove(struct spi_device *spi) mutex_destroy(&rk1608->lock); mutex_destroy(&rk1608->send_msg_lock); mutex_destroy(&rk1608->sensor_lock); + rk1608_dev_unregister(rk1608); return 0; } diff --git a/drivers/media/spi/rk1608.h b/drivers/media/spi/rk1608.h index ef1e0905b746..1a2533911766 100644 --- a/drivers/media/spi/rk1608.h +++ b/drivers/media/spi/rk1608.h @@ -10,8 +10,11 @@ #define __RK1608_H__ #include +#include #include "rk1608_dphy.h" +#define RK1608_VERSION "v1.1" + #define RK1608_OP_TRY_MAX 3 #define RK1608_OP_TRY_DELAY 10 #define RK1608_CMD_WRITE 0x00000011 @@ -80,6 +83,11 @@ (((u32)(x) & 0x01010101) << 7)) #endif +struct rk1608_client_list { + struct mutex mutex; /* protect clients */ + struct list_head list; +}; + struct rk1608_state { struct v4l2_subdev sd; struct rk1608_dphy *dphy[2]; @@ -95,6 +103,9 @@ struct rk1608_state { struct v4l2_subdev *sensor[4]; struct device *dev; struct spi_device *spi; + struct miscdevice misc; + struct rk1608_client_list clients; + int log_level; int power_count; int msg_num; u32 link_nums; @@ -159,6 +170,13 @@ struct rk1608_msg_queue { u32 cur_recv; /* current msg receive position */ }; +enum _log_level { + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG, +}; + struct msg { u32 size; /* unit 4 bytes */ u16 type; @@ -359,7 +377,7 @@ enum { id_msg_xfile_mkdir_t }; -static int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m); +int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m); /** * rk1608_write - RK1608 synchronous write * @@ -371,8 +389,8 @@ static int rk1608_send_msg_to_dsp(struct rk1608_state *pdata, struct msg *m); * * 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); +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 @@ -385,8 +403,8 @@ static int rk1608_write(struct spi_device *spi, s32 addr, * * 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); +int rk1608_safe_write(struct spi_device *spi, + s32 addr, const s32 *data, size_t data_len); /** * rk1608_read - RK1608 synchronous read @@ -399,8 +417,8 @@ static int rk1608_safe_write(struct spi_device *spi, * * 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); +int rk1608_read(struct spi_device *spi, s32 addr, + s32 *data, size_t data_len); /** * rk1608_safe_read - RK1608 synchronous read with state check @@ -413,8 +431,8 @@ static int rk1608_read(struct spi_device *spi, s32 addr, * * 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); +int rk1608_safe_read(struct spi_device *spi, + s32 addr, s32 *data, size_t data_len); /** * rk1608_operation_query - RK1608 last operation state query @@ -425,7 +443,18 @@ static int rk1608_safe_read(struct spi_device *spi, * * It returns zero on success, else a negative error code. */ -static int rk1608_operation_query(struct spi_device *spi, s32 *state); +int rk1608_operation_query(struct spi_device *spi, s32 *state); + +/** + * rk1608_state_query - RK1608 system state query + * + * @spi: spi device + * @state: system state [out] + * Context: can sleep + * + * It returns zero on success, else a negative error code. + */ +int rk1608_state_query(struct spi_device *spi, int32_t *state); /** * rk1608_interrupt_request - RK1608 request a rk1608 interrupt @@ -436,17 +465,8 @@ static int rk1608_operation_query(struct spi_device *spi, s32 *state); * * It returns zero on success, else a negative error code. */ -static int rk1608_interrupt_request(struct spi_device *spi, - s32 interrupt_num); +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 * @@ -456,29 +476,37 @@ static int rk1608_download_section(struct spi_device *spi, const u8 *data, * * 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); +int rk1608_download_fw(struct spi_device *spi, const char *fw_name); /** * rk1608_msq_recv_msg - receive a msg from RK1608 -> AP msg queue * - * @q: msg queue + * @spi: spi device * @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); +int rk1608_msq_recv_msg(struct spi_device *spi, struct msg **m); + +/* + * rk1608_msq_send_msg - send a msg from AP -> RK1608 msg queue + * + * @spi: spi device + * @m: a msg to send + * + * It returns zero on success, else a negative error code. + */ +int rk1608_msq_send_msg(struct spi_device *spi, struct msg *m); + +int rk1608_set_power(struct rk1608_state *pdata, int on); + +void rk1608_set_spi_speed(struct rk1608_state *pdata, u32 hz); + +int rk1608_set_log_level(struct rk1608_state *pdata, int level); + +int rk1608_dev_register(struct rk1608_state *pdata); +void rk1608_dev_unregister(struct rk1608_state *pdata); +void rk1608_dev_receive_msg(struct rk1608_state *pdata, struct msg *msg); #endif diff --git a/drivers/media/spi/rk1608_dev.c b/drivers/media/spi/rk1608_dev.c new file mode 100644 index 000000000000..4c206f435f66 --- /dev/null +++ b/drivers/media/spi/rk1608_dev.c @@ -0,0 +1,1028 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Rockchip rk1608 device driver + * + * Copyright (C) Rockchip Electronics Co., Ltd. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "rk1608.h" + +#define DEBUG_DUMP_ALL_SEND_RECV_MSG 0 + +#define MSG_QUEUE_DEFAULT_SIZE (8 * 1024) + +struct 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 rk1608_client { + s8 id; + struct msg_queue q; + struct list_head list; + wait_queue_head_t wait; + void *private_data; +}; + +enum { + AUTO_ARG_TYPE_STR, + AUTO_ARG_TYPE_INT32, +}; + +struct auto_arg { + int type; + union { + s32 m_int32; + const char *m_str; + }; +}; + +struct auto_args { + int argc; + struct auto_arg *argv; +}; + +/** + * msq_init - Initialize msg queue + * + * @q: the msg queue to initialize + * @size: size of msg queue buf + * + * It returns zero on success, else a negative error code. + */ +static int msq_init(struct msg_queue *q, int size) +{ + u32 *buf = kmalloc(size, GFP_KERNEL); + + q->buf_head = buf; + q->buf_tail = buf + size / sizeof(u32); + q->cur_send = buf; + q->cur_recv = buf; + + return 0; +} + +/** + * msq_release - release msg queue buf + * + * @q: the msg queue to release + */ +static void msq_release(struct msg_queue *q) +{ + kfree(q->buf_head); + q->buf_head = NULL; + q->buf_tail = NULL; + q->cur_send = NULL; + q->cur_recv = NULL; +} + +/** + * msq_is_empty - tests whether a msg queue is empty + * + * @q: the msg queue to test + * + * It returns true on msg queue is empty, else false. + */ +static int msq_is_empty(const struct msg_queue *q) +{ + return q->cur_send == q->cur_recv; +} + +/** + * 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 4 bytes + */ +static u32 msq_tail_free_size(const struct msg_queue *q) +{ + if (q->cur_send >= q->cur_recv) + return (q->buf_tail - q->cur_send); + + return q->cur_recv - q->cur_send; +} + +/** + * 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 4 bytes + */ +static u32 msq_head_free_size(const struct msg_queue *q) +{ + if (q->cur_send >= q->cur_recv) + return (q->cur_recv - q->buf_head); + + return 0; +} + +/** + * msq_send_msg - send a msg to msg queue + * + * @q: msg queue + * @m: a msg to queue + * + * It returns zero on success, else a negative error code. + */ +static int msq_send_msg(struct msg_queue *q, const struct msg *m) +{ + int ret = 0; + + if (msq_tail_free_size(q) > m->size) { + u32 *next_send; + + memcpy(q->cur_send, m, m->size * sizeof(u32)); + next_send = q->cur_send + m->size; + if (next_send == q->buf_tail) + next_send = q->buf_head; + + q->cur_send = next_send; + } else if (msq_head_free_size(q) > m->size) { + *q->cur_send = 0; /* set size to 0 for skip to head mark */ + memcpy(q->buf_head, m, m->size * sizeof(u32)); + q->cur_send = q->buf_head + m->size; + } else { + ret = -1; + } + + return ret; +} + +/** + * msq_recv_msg - receive a msg from msg queue + * + * @q: msg queue + * @m: a msg pointer buf [out] + * + * need call msq_recv_msg_free to free msg after msg use done + * + * It returns zero on success, else a negative error code. + */ +static int msq_recv_msg(struct msg_queue *q, struct msg **m) +{ + *m = NULL; + if (msq_is_empty(q)) + return -1; + + /* skip to head when size is 0 */ + if (*q->cur_recv == 0) + *m = (struct msg *)q->buf_head; + else + *m = (struct msg *)q->cur_recv; + + return 0; +} + +/** + * msq_free_received_msg - free a received msg to msg queue + * + * @q: msg queue + * @m: a msg + * + * It returns zero on success, else a negative error code. + */ +static int msq_free_received_msg(struct msg_queue *q, const struct msg *m) +{ + /* skip to head when size is 0 */ + if (*q->cur_recv == 0) { + q->cur_recv = q->buf_head + m->size; + } else { + u32 *next_recv; + + next_recv = q->cur_recv + m->size; + if (next_recv == q->buf_tail) + next_recv = q->buf_head; + + q->cur_recv = next_recv; + } + + return 0; +} + +static void rk1608_client_list_init(struct rk1608_client_list *s) +{ + mutex_init(&s->mutex); + INIT_LIST_HEAD(&s->list); +} + +static struct rk1608_client *rk1608_client_new(void) +{ + struct rk1608_client *c = kzalloc(sizeof(*c), GFP_KERNEL); + + if (!c) + return NULL; + + c->id = INVALID_ID; + INIT_LIST_HEAD(&c->list); + msq_init(&c->q, MSG_QUEUE_DEFAULT_SIZE); + init_waitqueue_head(&c->wait); + + return c; +} + +static void rk1608_client_release(struct rk1608_client *c) +{ + msq_release(&c->q); + kfree(c); +} + +static struct rk1608_client * +rk1608_client_find(struct rk1608_client_list *s, + struct rk1608_client *c) +{ + struct rk1608_client *client = NULL; + + list_for_each_entry(client, &s->list, list) { + if (c == client) + return c; + } + + return NULL; +} + +static int rk1608_client_connect(struct rk1608_state *pdata, + struct rk1608_client *c) +{ + struct rk1608_client_list *s = &pdata->clients; + + mutex_lock(&s->mutex); + if (rk1608_client_find(s, c)) { + mutex_unlock(&s->mutex); + return -1; + } + + list_add_tail(&c->list, &s->list); + mutex_unlock(&s->mutex); + + return 0; +} + +static void rk1608_client_disconnect(struct rk1608_state *pdata, + struct rk1608_client *c) +{ + struct rk1608_client_list *s = &pdata->clients; + + mutex_lock(&s->mutex); + if (rk1608_client_find(s, c)) + list_del_init(&c->list); + mutex_unlock(&s->mutex); +} + +static int parse_arg(const char *s, struct auto_arg *arg) +{ + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + long v; + + v = simple_strtol(s, NULL, 16); + arg->type = AUTO_ARG_TYPE_INT32; + arg->m_int32 = v; + } else if (isdigit(s[0])) { + long v; + + v = simple_strtol(s, NULL, 10); + arg->type = AUTO_ARG_TYPE_INT32; + arg->m_int32 = v; + } else { + arg->type = AUTO_ARG_TYPE_STR; + arg->m_str = s; + } + + return 0; +} + +static int parse_auto_args(char *s, struct auto_args *args) +{ + int i = 0; + char c = 0; + int last_is_arg_flag = 0; + const char *last_arg; + + args->argc = 0; + + i = -1; + do { + c = s[++i]; + if (c == ' ' || c == ',' || c == '\n' || c == '\r' || c == 0) { + if (last_is_arg_flag) + args->argc++; + + last_is_arg_flag = 0; + } else { + last_is_arg_flag = 1; + } + } while (c != 0 && c != '\n' && c != '\r'); + + args->argv = + kmalloc_array(args->argc, sizeof(struct auto_arg), GFP_KERNEL); + if (!args->argv) + return -ENOMEM; + + i = -1; + last_is_arg_flag = 0; + last_arg = s; + args->argc = 0; + do { + c = s[++i]; + if (c == ' ' || c == ',' || c == '\n' || c == '\r' || c == 0) { + if (last_is_arg_flag) { + parse_arg(last_arg, args->argv + args->argc++); + s[i] = 0; + } + last_is_arg_flag = 0; + } else { + if (last_is_arg_flag == 0) + last_arg = s + i; + + last_is_arg_flag = 1; + } + } while (c != 0 && c != '\n' && c != '\r'); + + return c == 0 ? i : i + 1; +} + +static void free_auto_args(struct auto_args *args) +{ + kfree(args->argv); + args->argv = NULL; + args->argc = 0; +} + +static void int32_hexdump(const char *prefix, s32 *data, int len) +{ + pr_err("%s\n", prefix); + print_hex_dump(KERN_ERR, "offset ", DUMP_PREFIX_OFFSET, + 16, 4, data, len, false); + pr_err("\n"); +} + +static int do_cmd_write(struct rk1608_state *pdata, + const struct auto_args *args) +{ + s32 addr; + s32 len = (args->argc - 2) * sizeof(s32); + s32 *data; + int i; + + if (args->argc < 3 || args->argv[1].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Mis or unknown args!\n"); + return -1; + } + + len = MIN(len, RK1608_MAX_OP_BYTES); + + addr = args->argv[1].m_int32; + data = kmalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + for (i = 0; i < len / 4; i++) { + if (args->argv[i + 2].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Unknown args!\n"); + kfree(data); + return -1; + } + + data[i] = args->argv[i + 2].m_int32; + } + + rk1608_write(pdata->spi, addr, data, len); + + kfree(data); + + dev_info(pdata->dev, "write addr: 0x%x, len: %d bytes\n", addr, len); + return 0; +} + +static int do_cmd_read(struct rk1608_state *pdata, + const struct auto_args *args) +{ + s32 addr; + s32 len; + s32 *data; + + if (args->argc < 3 || args->argv[1].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Mis or unknown args!\n"); + return -1; + } + + addr = args->argv[1].m_int32; + if (args->argc == 2) { + len = 32; + } else { + if (args->argv[2].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Unknown args!\n"); + return -1; + } + len = args->argv[2].m_int32 * sizeof(s32); + len = MIN(len, RK1608_MAX_OP_BYTES); + } + + data = kmalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_info(pdata->dev, "\nread addr: %x, len: %d bytes\n", addr, len); + rk1608_read(pdata->spi, addr, data, len); + int32_hexdump("read data:", data, len); + kfree(data); + + return 0; +} + +static int do_cmd_set_spi_rate(struct rk1608_state *pdata, + const struct auto_args *args) +{ + if (args->argc < 2 || args->argv[1].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Mis or unknown args!\n"); + return -1; + } + + pdata->max_speed_hz = args->argv[1].m_int32; + dev_info(pdata->dev, "set spi max speed to %d!\n", pdata->max_speed_hz); + + if (args->argc == 3 && args->argv[2].type == AUTO_ARG_TYPE_INT32) { + pdata->min_speed_hz = args->argv[2].m_int32; + dev_info(pdata->dev, "set spi min speed to %d!\n", + pdata->min_speed_hz); + } + + return 0; +} + +static int do_cmd_query(struct rk1608_state *pdata, + const struct auto_args *args) +{ + s32 state; + + rk1608_operation_query(pdata->spi, &state); + dev_info(pdata->dev, "state %x\n", state); + return 0; +} + +static int do_cmd_download_fw(struct rk1608_state *pdata, + const struct auto_args *args) +{ + int ret = 0; + const char *fw_name = NULL; + + if (args->argc == 2 && args->argv[1].type == AUTO_ARG_TYPE_STR) + fw_name = args->argv[1].m_str; + + ret = rk1608_download_fw(pdata->spi, fw_name); + if (ret) + dev_err(pdata->dev, "download firmware failed!\n"); + else + dev_info(pdata->dev, "download firmware success!\n"); + + return 0; +} + +static int do_cmd_fast_write(struct rk1608_state *pdata, + const struct auto_args *args) +{ + int ret = 0; + s32 reg; + + if (args->argc != 2 || args->argv[1].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Mis or unknown args!\n"); + return -1; + } + + reg = args->argv[1].m_int32; + + ret = rk1608_interrupt_request(pdata->spi, reg); + dev_info(pdata->dev, "interrupt request reg1:%x ret:%x\n", reg, ret); + + return 0; +} + +static int do_cmd_fast_read(struct rk1608_state *pdata, + const struct auto_args *args) +{ + s32 state; + + rk1608_state_query(pdata->spi, &state); + dev_info(pdata->dev, "dsp state %x\n", state); + + return 0; +} + +static int do_cmd_send_msg(struct rk1608_state *pdata, + const struct auto_args *args) +{ + struct msg *m; + int ret = 0; + int msg_len; + u32 i = 0; + + if (args->argc < 2) { + dev_err(pdata->dev, "need more args\n"); + return -1; + } + + msg_len = args->argc * sizeof(u32); + + m = kmalloc(msg_len, GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->size = msg_len / 4; + for (i = 1; i < m->size; i++) { + if (args->argv[i].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Unknown args!\n"); + kfree(m); + return -1; + } + + *((s32 *)m + i) = args->argv[i].m_int32; + } + + ret = rk1608_send_msg_to_dsp(pdata, m); + + dev_info(pdata->dev, "send msg len: %d, ret: %x\n", m->size, ret); + + kfree(m); + + return 0; +} + +static int do_cmd_recv_msg(struct rk1608_state *pdata, + const struct auto_args *args) +{ + struct msg *m; + char buf[256] = ""; + int ret = 0; + + ret = rk1608_msq_recv_msg(pdata->spi, &m); + if (ret || !m) + return 0; + + dev_info(pdata->dev, "\nrecv msg len: %d, ret: %x\n", m->size, ret); + int32_hexdump("recv msg:", (s32 *)m, m->size * 4); + + dev_info(pdata->dev, buf); + + kfree(m); + + return 0; +} + +static int do_cmd_power_on(struct rk1608_state *pdata, + const struct auto_args *args) +{ + int ret; + + ret = rk1608_set_power(pdata, 1); + dev_info(pdata->dev, "do cmd power on, count++\n"); + + return ret; +} + +static int do_cmd_power_off(struct rk1608_state *pdata, + const struct auto_args *args) +{ + int ret; + + ret = rk1608_set_power(pdata, 0); + dev_info(pdata->dev, "do cmd power off, count--\n"); + + return ret; +} + +static int do_cmd_set_dsp_log_level(struct rk1608_state *pdata, + const struct auto_args *args) +{ + int ret; + + if (args->argc != 2 || args->argv[1].type != AUTO_ARG_TYPE_INT32) { + dev_err(pdata->dev, "Mis or unknown args!\n"); + return -1; + } + + pdata->log_level = args->argv[1].m_int32; + ret = rk1608_set_log_level(pdata, pdata->log_level); + + dev_info(pdata->dev, "set dsp log level %d, ret: %d\n", + pdata->log_level, ret); + + return ret; +} + +static int do_cmd_version(struct rk1608_state *pdata, + const struct auto_args *args) +{ + dev_info(pdata->dev, "driver version: %s\n", RK1608_VERSION); + return 0; +} + +static int do_cmd_help(struct rk1608_state *pdata) +{ + dev_info(pdata->dev, "\n" + "support debug commands:\n" + "v -- rk1608 driver version.\n" + "log level -- set rk1608 log level.\n" + "on -- power count + 1.\n" + "off -- power count - 1.\n" + "f [fw_name] -- download fw.\n" + "q -- query operation status.\n" + "r addr [length=32] -- read addr.\n" + "w addr value,... -- write addr.\n" + "s type,... -- send msg.\n" + "rate max [min] -- set spi speed.\n\n"); + return 0; +} + +static int do_cmd(struct rk1608_state *pdata, const struct auto_args *args) +{ + const char *s; + + if (args->argv->type != AUTO_ARG_TYPE_STR) + return 0; + + s = args->argv->m_str; + /* echo c > /dev/rk_preisp */ + if (!strcmp(s, "c")) + return do_cmd_recv_msg(pdata, args); + /* echo f [fw_name] > /dev/rk_preisp */ + if (!strcmp(s, "f")) + return do_cmd_download_fw(pdata, args); + /* echo fw reg1 > /dev/rk_preisp */ + if (!strcmp(s, "fw")) + return do_cmd_fast_write(pdata, args); + /* echo fr > /dev/rk_preisp */ + if (!strcmp(s, "fr")) + return do_cmd_fast_read(pdata, args); + /* echo log level > /dev/rk_preisp */ + if (!strcmp(s, "log")) + return do_cmd_set_dsp_log_level(pdata, args); + /* echo on > /dev/rk_preisp */ + if (!strcmp(s, "on")) + return do_cmd_power_on(pdata, args); + /* echo off > /dev/rk_preisp */ + if (!strcmp(s, "off")) + return do_cmd_power_off(pdata, args); + /* echo q > /dev/rk_preisp */ + if (!strcmp(s, "q")) + return do_cmd_query(pdata, args); + /* echo r addr [length] > /dev/rk_preisp */ + if (!strcmp(s, "r")) + return do_cmd_read(pdata, args); + /* echo rate > /dev/rk_preisp */ + if (!strcmp(s, "rate")) + return do_cmd_set_spi_rate(pdata, args); + /* echo s type,... > /dev/rk_preisp */ + if (!strcmp(s, "s")) + return do_cmd_send_msg(pdata, args); + /* echo v > /dev/rk_preisp */ + if (!strcmp(s, "v")) + return do_cmd_version(pdata, args); + /* echo w addr value,... > /dev/rk_preisp */ + if (!strcmp(s, "w")) + return do_cmd_write(pdata, args); + + dev_err(pdata->dev, "unknown commands:%s\n", s); + do_cmd_help(pdata); + + return 0; +} + +static int rk1608_dev_open(struct inode *inode, struct file *file) +{ + struct rk1608_state *pdata = + container_of(file->private_data, struct rk1608_state, misc); + struct rk1608_client *client = rk1608_client_new(); + + client->private_data = pdata; + file->private_data = client; + + rk1608_set_power(pdata, 1); + + return 0; +} + +static int rk1608_dev_release(struct inode *inode, struct file *file) +{ + struct rk1608_client *client = file->private_data; + struct rk1608_state *pdata = client->private_data; + + rk1608_client_disconnect(pdata, client); + rk1608_client_release(client); + rk1608_set_power(pdata, 0); + + return 0; +} + +static ssize_t rk1608_dev_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf; + struct auto_args args; + int i; + struct rk1608_client *client = file->private_data; + struct rk1608_state *pdata = client->private_data; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; + + i = 0; + while (buf[i] != 0) { + int ret = parse_auto_args(buf + i, &args); + + if (ret < 0) + break; + + i += ret; + if (args.argc == 0) + continue; + + do_cmd(pdata, &args); + free_auto_args(&args); + } + + kfree(buf); + + return count; +} + +static long rk1608_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + void __user *ubuf = (void __user *)arg; + struct rk1608_client *client = file->private_data; + struct rk1608_state *pdata = client->private_data; + + switch (cmd) { + case PREISP_POWER_ON: + ret = rk1608_set_power(pdata, 1); + break; + case PREISP_POWER_OFF: + ret = rk1608_set_power(pdata, 0); + break; + case PREISP_DOWNLOAD_FW: { + char fw_name[PREISP_FW_NAME_LEN]; + + if (strncpy_from_user(fw_name, ubuf, PREISP_FW_NAME_LEN) <= 0) { + ret = -EFAULT; + break; + } + dev_info(pdata->dev, "download fw:%s\n", fw_name); + ret = rk1608_download_fw(pdata->spi, fw_name); + break; + } + case PREISP_WRITE: { + struct preisp_apb_pkt pkt; + s32 *data; + + if (copy_from_user(&pkt, ubuf, sizeof(pkt))) { + ret = -EFAULT; + break; + } + pkt.data_len = MIN(pkt.data_len, RK1608_MAX_OP_BYTES); + data = memdup_user((void __user *)pkt.data, pkt.data_len); + if (IS_ERR(data)) { + ret = (long)ERR_PTR((long)data); + break; + } + + ret = rk1608_safe_write(pdata->spi, pkt.addr, data, pkt.data_len); + kfree(data); + break; + } + case PREISP_READ: { + struct preisp_apb_pkt pkt; + s32 *data; + + if (copy_from_user(&pkt, ubuf, sizeof(pkt))) { + ret = -EFAULT; + break; + } + pkt.data_len = MIN(pkt.data_len, RK1608_MAX_OP_BYTES); + data = kmalloc(pkt.data_len, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + break; + } + + ret = rk1608_safe_read(pdata->spi, pkt.addr, data, pkt.data_len); + if (ret) { + kfree(data); + break; + } + ret = copy_to_user((void __user *)pkt.data, data, pkt.data_len); + + kfree(data); + break; + } + case PREISP_ST_QUERY: { + s32 state; + + ret = rk1608_state_query(pdata->spi, &state); + if (ret) + break; + + ret = put_user(state, (s32 __user *)ubuf); + break; + } + case PREISP_IRQ_REQUEST: { + int int_num = arg; + + ret = rk1608_interrupt_request(pdata->spi, int_num); + break; + } + case PREISP_SEND_MSG: { + struct msg *msg; + u32 len; + + if (get_user(len, (u32 __user *)ubuf)) { + ret = -EFAULT; + break; + } + len = len * sizeof(s32); + msg = memdup_user(ubuf, len); + if (IS_ERR(msg)) { + ret = (long)ERR_PTR((long)msg); + break; + } + +#if DEBUG_DUMP_ALL_SEND_RECV_MSG == 1 + int32_hexdump("send msg:", (s32 *)msg, len); +#endif + + ret = rk1608_send_msg_to_dsp(pdata, msg); + kfree(msg); + break; + } + case PREISP_QUERY_MSG: { + struct msg *msg; + + ret = msq_recv_msg(&client->q, &msg); + if (ret) + break; + + ret = put_user(msg->size, (u32 __user *)ubuf); + break; + } + case PREISP_RECV_MSG: { + struct msg *msg; + + ret = msq_recv_msg(&client->q, &msg); + if (ret) + break; + + ret = copy_to_user(ubuf, msg, msg->size * sizeof(u32)); + msq_free_received_msg(&client->q, msg); + break; + } + case PREISP_CLIENT_CONNECT: { + client->id = (int)arg; + ret = rk1608_client_connect(pdata, client); + break; + } + case PREISP_CLIENT_DISCONNECT: { + rk1608_client_disconnect(pdata, client); + client->id = INVALID_ID; + break; + } + default: + ret = -EFAULT; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +#define PREISP_WRITE32 _IOW('p', 6, struct preisp_apb_pkt32) +#define PREISP_READ32 _IOR('p', 7, struct preisp_apb_pkt32) + +struct preisp_apb_pkt32 { + s32 data_len; + s32 addr; + compat_uptr_t data; +}; + +static long rk1608_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct preisp_apb_pkt32 pkt32; + struct preisp_apb_pkt *pkt; + + switch (cmd) { + case PREISP_WRITE32: + cmd = PREISP_WRITE; + break; + case PREISP_READ32: + cmd = PREISP_READ; + break; + default: + break; + } + + switch (cmd) { + case PREISP_WRITE: + case PREISP_READ: + if (copy_from_user(&pkt32, (void __user *)arg, sizeof(pkt32))) + return -EFAULT; + + pkt = compat_alloc_user_space(sizeof(*pkt)); + if (!pkt || + put_user(pkt32.data_len, &pkt->data_len) || + put_user(pkt32.addr, &pkt->addr) || + put_user(compat_ptr(pkt32.data), &pkt->data)) + return -EFAULT; + + ret = rk1608_dev_ioctl(file, cmd, (unsigned long)pkt); + break; + default: + ret = rk1608_dev_ioctl(file, cmd, arg); + break; + } + + return ret; +} +#endif + +static unsigned int rk1608_dev_poll(struct file *file, poll_table *wait) +{ + struct rk1608_client *client = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &client->wait, wait); + + if (!msq_is_empty(&client->q)) + mask |= POLLIN; + + return mask; +} + +static const struct file_operations rk1608_fops = { + .owner = THIS_MODULE, + .open = rk1608_dev_open, + .release = rk1608_dev_release, + .write = rk1608_dev_write, + .poll = rk1608_dev_poll, + .unlocked_ioctl = rk1608_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rk1608_compat_ioctl, +#endif +}; + +void rk1608_dev_receive_msg(struct rk1608_state *pdata, struct msg *msg) +{ + struct rk1608_client_list *s = &pdata->clients; + struct rk1608_client *client; + +#if DEBUG_DUMP_ALL_SEND_RECV_MSG == 1 + int32_hexdump("recv msg:", (s32 *)msg, msg->size * 4); +#endif + + mutex_lock(&s->mutex); + list_for_each_entry(client, &s->list, list) { + if (client->id == msg->id.camera_id) { + msq_send_msg(&client->q, msg); + wake_up_interruptible(&client->wait); + } + } + mutex_unlock(&s->mutex); +} + +int rk1608_dev_register(struct rk1608_state *pdata) +{ + int ret; + + rk1608_client_list_init(&pdata->clients); + + pdata->misc.minor = MISC_DYNAMIC_MINOR; + pdata->misc.name = "rk_preisp"; + pdata->misc.fops = &rk1608_fops; + + ret = misc_register(&pdata->misc); + if (ret < 0) + dev_err(pdata->dev, "Error: misc_register returned %d\n", ret); + + return 0; +} + +void rk1608_dev_unregister(struct rk1608_state *pdata) +{ + misc_deregister(&pdata->misc); +} diff --git a/include/uapi/linux/rk-preisp.h b/include/uapi/linux/rk-preisp.h index 3b58c2f7e137..fa07c7cd5644 100644 --- a/include/uapi/linux/rk-preisp.h +++ b/include/uapi/linux/rk-preisp.h @@ -9,6 +9,8 @@ #include +#define PREISP_FW_NAME_LEN 128 + #define PREISP_LSCTBL_SIZE 289 #define PREISP_CMD_SET_HDRAE_EXP \ @@ -17,6 +19,27 @@ #define PREISP_CMD_SAVE_HDRAE_PARAM \ _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct preisp_hdrae_para_s) +#define PREISP_POWER_ON _IO('p', 1) +#define PREISP_POWER_OFF _IO('p', 2) +#define PREISP_REQUEST_SLEEP _IOW('p', 3, s32) +#define PREISP_WAKEUP _IO('p', 4) +#define PREISP_DOWNLOAD_FW _IOW('p', 5, char[PREISP_FW_NAME_LEN]) +#define PREISP_WRITE _IOW('p', 6, struct preisp_apb_pkt) +#define PREISP_READ _IOR('p', 7, struct preisp_apb_pkt) +#define PREISP_ST_QUERY _IOR('p', 8, s32) +#define PREISP_IRQ_REQUEST _IOW('p', 9, s32) +#define PREISP_SEND_MSG _IOW('p', 11, s32) +#define PREISP_QUERY_MSG _IOR('p', 12, s32) +#define PREISP_RECV_MSG _IOR('p', 13, s32) +#define PREISP_CLIENT_CONNECT _IOW('p', 15, s32) +#define PREISP_CLIENT_DISCONNECT _IO('p', 16) + +struct preisp_apb_pkt { + s32 data_len; + s32 addr; + s32 *data; +}; + /** * struct preisp_hdrae_para_s - awb and lsc para for preisp *