From 7373fe8cbbdc6197d64a5da096b9055c6eb18c3d Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 3 Nov 2015 22:34:03 +0100 Subject: [PATCH] bcwc_pcie: Wait for command completion based on events --- bcwc_drv.c | 13 +++- bcwc_drv.h | 7 ++ bcwc_isp.c | 188 +++++++++++++++++++++++-------------------------- bcwc_isp.h | 17 ++++- bcwc_ringbuf.c | 7 +- bcwc_ringbuf.h | 2 +- 6 files changed, 127 insertions(+), 107 deletions(-) diff --git a/bcwc_drv.c b/bcwc_drv.c index fda9566..562b78a 100644 --- a/bcwc_drv.c +++ b/bcwc_drv.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "bcwc_drv.h" #include "bcwc_hw.h" #include "bcwc_isp.h" @@ -100,6 +102,11 @@ static void bcwc_handle_irq(struct bcwc_private *dev_priv, struct fw_channel *ch pr_debug("Interrupt from channel source %d, type %d [%s]\n", chan->source, chan->type, chan->name); bcwc_channel_ringbuf_dump(dev_priv, chan); + if (chan == dev_priv->channel_io) { + pr_debug("command ready\n"); + wake_up_interruptible(&dev_priv->wq); + } + while(bcwc_channel_ringbuf_entry_available(dev_priv, chan)) { entry = bcwc_channel_ringbuf_get_entry(dev_priv, chan); if (!entry) @@ -180,8 +187,12 @@ static irqreturn_t bcwc_irq_handler(int irq, void *arg) { struct bcwc_private *dev_priv = arg; u32 pending; - + unsigned long flags; + + spin_lock_irqsave(&dev_priv->io_lock, flags); pending = BCWC_ISP_REG_READ(ISP_REG_41000); + spin_unlock_irqrestore(&dev_priv->io_lock, flags); + if (!(pending & 0xf0)) return IRQ_NONE; diff --git a/bcwc_drv.h b/bcwc_drv.h index 516afa0..d8c65bf 100644 --- a/bcwc_drv.h +++ b/bcwc_drv.h @@ -21,6 +21,8 @@ #define _PCWC_PCIE_H #include +#include + #include "bcwc_reg.h" #include "bcwc_ringbuf.h" @@ -28,6 +30,8 @@ #define BCWC_PCI_S2_MEM 2 #define BCWC_PCI_ISP_IO 4 +#define MAX(a, b) ((a)>(b)?(a):(b)) + struct bcwc_reg { u32 offset; u32 value; @@ -46,6 +50,9 @@ struct bcwc_private { struct pci_dev *pdev; unsigned int dma_mask; + /* waitqueue for signaling command completion */ + wait_queue_head_t wq; + /* Mapped PCI resources */ void *s2_io; u32 s2_io_len; diff --git a/bcwc_isp.c b/bcwc_isp.c index 5fbbec5..d2a3d8c 100644 --- a/bcwc_isp.c +++ b/bcwc_isp.c @@ -258,27 +258,83 @@ out: return -ENOMEM; } -static void bcwc_isp_powerdown(struct bcwc_private *dev_priv) +int bcwc_isp_cmd(struct bcwc_private *dev_priv, enum bcwc_isp_cmds command, void *in, + int request_len, void *out, int *response_len) { struct isp_mem_obj *request; struct isp_cmd_hdr *cmd; + struct bcwc_ringbuf_entry *entry; + int len, ret; - pr_debug("going to power down isp\n"); + if (response_len) { + len = MAX(request_len, *response_len); + /* XXX: not needed, for debugging */ + memset(out, 0, *response_len); + } else { + len = request_len; + } + pr_debug("sending cmd %d to firmware\n", command); - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); + request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd) + len); if (!request) { dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); - return; + return -ENOMEM; } - pr_debug("allocated request cmd buffer at offset %08lx\n", request->offset); cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->opcode = CISP_CMD_POWER_DOWN; + memset(cmd, 0, len); + cmd->opcode = command; - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 8, 8); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); + if (request_len) + memcpy(cmd + sizeof(struct isp_cmd_hdr), in, request_len); + + entry = bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, + request->offset, request_len + 8, (response_len ? *response_len : 0) + 8); + + if (command == CISP_CMD_STOP) { + /* stop doesn't seem to generate a response */ + ret = 0; + goto out; + } + + if (!wait_event_interruptible_timeout(dev_priv->wq, (entry->address_flags & 1), 5 * HZ)) { + dev_err(&dev_priv->pdev->dev, "timeout wait for command %d\n", cmd->opcode); + bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); + if (response_len) + *response_len = 0; + ret = -ETIMEDOUT; + goto out; + } + /* XXX: response size in the ringbuf is zero after command completion, how is buffer size + verification done? */ + if (response_len && *response_len) + memcpy(out, (entry->address_flags & ~3) + dev_priv->s2_mem, *response_len); + + pr_debug("status %04x, request_len %d response len %d address_flags %x", cmd->status, + entry->request_size, entry->response_size, entry->address_flags); + + ret = 0; +out: + isp_mem_destroy(request); + return ret; +} + + + +int bcwc_isp_cmd_start(struct bcwc_private *dev_priv) +{ + pr_debug("sending start cmd to firmware\n"); + return bcwc_isp_cmd(dev_priv, CISP_CMD_START, NULL, 0, NULL, NULL); +} + +int bcwc_isp_cmd_stop(struct bcwc_private *dev_priv) +{ + return bcwc_isp_cmd(dev_priv, CISP_CMD_STOP, NULL, 0, NULL, NULL); +} + +int bcwc_isp_cmd_powerdown(struct bcwc_private *dev_priv) +{ + return bcwc_isp_cmd(dev_priv, CISP_CMD_POWER_DOWN, NULL, 0, NULL, NULL); } int isp_uninit(struct bcwc_private *dev_priv) @@ -286,7 +342,7 @@ int isp_uninit(struct bcwc_private *dev_priv) int retries; u32 reg; BCWC_ISP_REG_WRITE(0xf7fbdff9, 0xc3000); - bcwc_isp_powerdown(dev_priv); + bcwc_isp_cmd_powerdown(dev_priv); for (retries = 0; retries < 1000; retries++) { reg = BCWC_ISP_REG_READ(0xc3000); if (reg == 0x8042006) @@ -325,118 +381,50 @@ int isp_uninit(struct bcwc_private *dev_priv) return 0; } -int bcwc_isp_cmd_start(struct bcwc_private *dev_priv) -{ - struct isp_mem_obj *request; - struct isp_cmd_hdr *cmd; - - pr_debug("sending start cmd to firmware\n"); - - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); - if (!request) { - dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); - return -ENOMEM; - } - - cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->opcode = CISP_CMD_START; - - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 8, 8); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); - return 0; -} - -int bcwc_isp_cmd_stop(struct bcwc_private *dev_priv) -{ - struct isp_mem_obj *request; - struct isp_cmd_hdr *cmd; - - pr_debug("sending start command to firmware\n"); - - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); - if (!request) { - dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); - return -ENOMEM; - } - - cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->opcode = 1; - - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 8, 8); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); - return 0; -} int bcwc_isp_cmd_print_enable(struct bcwc_private *dev_priv, int enable) { - struct isp_mem_obj *request; - struct isp_cmd_print_enable *cmd; + struct isp_cmd_print_enable cmd; - pr_debug("sending print enable %d\n", enable); + cmd.enable = enable; - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); - if (!request) { - dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); - return -ENOMEM; - } - - cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->hdr.opcode = CISP_CMD_PRINT_ENABLE; - cmd->enable = enable; - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 12, 12); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); + return bcwc_isp_cmd(dev_priv, CISP_CMD_PRINT_ENABLE, &cmd, sizeof(cmd), NULL, NULL); + print_hex_dump_bytes("PE RES", DUMP_PREFIX_OFFSET, &cmd, sizeof(cmd)); return 0; } int bcwc_isp_cmd_set_loadfile(struct bcwc_private *dev_priv) { - struct isp_mem_obj *request; - struct isp_cmd_set_loadfile *cmd; + struct isp_cmd_set_loadfile cmd; + struct isp_mem_obj *file; pr_debug("set loadfile\n"); - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); - if (!request) { + memset(&cmd, 0, sizeof(cmd)); + + file = isp_mem_create(dev_priv, FTHD_MEM_CMD, 1024*1024*16); + if (!file) { dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); return -ENOMEM; } - cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->hdr.opcode = CISP_CMD_CH_SET_FILE_LOAD; - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 20, 20); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); - return 0; + cmd.addr = file->offset; + cmd.length = 16 * 1024 * 1024; + return bcwc_isp_cmd(dev_priv, CISP_CMD_CH_SET_FILE_LOAD, &cmd, sizeof(cmd), NULL, NULL); } -int bcwc_isp_cmd_sensor_detect(struct bcwc_private *dev_priv) +int bcwc_isp_cmd_channel_info(struct bcwc_private *dev_priv) { - struct isp_mem_obj *request; - struct isp_cmd_sensor_detect *cmd; + struct isp_cmd_channel_info cmd, info; + int ret, len; pr_debug("sending ch info\n"); - request = isp_mem_create(dev_priv, FTHD_MEM_CMD, sizeof(cmd)); - if (!request) { - dev_err(&dev_priv->pdev->dev, "failed to allocate cmd memory object\n"); - return -ENOMEM; - } - - cmd = dev_priv->s2_mem + request->offset; - memset(cmd, 0, sizeof(*cmd)); - cmd->hdr.opcode = CISP_CMD_CH_INFO_GET; - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_io, request->offset, 168, 168); - mdelay(100); - bcwc_channel_ringbuf_dump(dev_priv, dev_priv->channel_io); - print_hex_dump_bytes("CHINFO ", DUMP_PREFIX_OFFSET, &cmd, sizeof(*cmd)); - return 0; + memset(&cmd, 0, sizeof(cmd)); + len = sizeof(info); + ret = bcwc_isp_cmd(dev_priv, CISP_CMD_CH_INFO_GET, &cmd, 0, &info, &len); + print_hex_dump_bytes("CHINFO ", DUMP_PREFIX_OFFSET, &info, sizeof(info)); + return ret; } int isp_init(struct bcwc_private *dev_priv) diff --git a/bcwc_isp.h b/bcwc_isp.h index 1fd6728..1a79e86 100644 --- a/bcwc_isp.h +++ b/bcwc_isp.h @@ -469,14 +469,24 @@ struct isp_channel_info { struct isp_cmd_hdr { u32 unknown0; - enum bcwc_isp_cmds opcode; + u16 opcode; + u16 status; } __attribute__((packed)); struct isp_cmd_print_enable { - struct isp_cmd_hdr hdr; u32 enable; } __attribute__((packed)); +struct isp_cmd_set_loadfile { + u32 unknown; + u32 addr; + u32 length; +} __attribute__((packed)); + +struct isp_cmd_channel_info { + u32 data[160]; +} __attribute__((packed)); + #define to_isp_mem_obj(x) container_of((x), struct isp_mem_obj, base) extern int isp_init(struct bcwc_private *dev_priv); @@ -490,4 +500,7 @@ extern int isp_mem_destroy(struct isp_mem_obj *obj); extern int bcwc_isp_cmd_start(struct bcwc_private *dev_priv); extern int bcwc_isp_cmd_stop(struct bcwc_private *dev_priv); extern int bcwc_isp_cmd_print_enable(struct bcwc_private *dev_priv, int enable); +extern int bcwc_isp_cmd_set_loadfile(struct bcwc_private *dev_priv); +extern int bcwc_isp_cmd_channel_info(struct bcwc_private *dev_priv); +extern int bcwc_isp_cmd_channel_start(struct bcwc_private *dev_priv); #endif diff --git a/bcwc_ringbuf.c b/bcwc_ringbuf.c index 2ade146..39c8a4c 100644 --- a/bcwc_ringbuf.c +++ b/bcwc_ringbuf.c @@ -81,21 +81,22 @@ void bcwc_channel_ringbuf_init(struct bcwc_private *dev_priv, struct fw_channel } } -int bcwc_channel_ringbuf_send(struct bcwc_private *dev_priv, struct fw_channel *chan, +struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_send(struct bcwc_private *dev_priv, struct fw_channel *chan, u32 data_offset, u32 request_size, u32 response_size) { struct bcwc_ringbuf_entry *entry; entry = get_entry_addr(dev_priv, chan, chan->ringbuf.send_idx++); pr_debug("send entry %p offset %08x\n", entry, data_offset); - entry->address_flags = data_offset | (chan->type == 0 ? 0 : 1); entry->request_size = request_size; entry->response_size = response_size; + entry->address_flags = data_offset | (chan->type == 0 ? 0 : 1); + pr_debug("address_flags %x, request size %x response size %x\n", entry->address_flags, entry->request_size, entry->response_size); wmb(); BCWC_ISP_REG_WRITE(0x10 << chan->source, ISP_REG_41020); - return 0; + return entry; } struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_get_entry(struct bcwc_private *dev_priv, diff --git a/bcwc_ringbuf.h b/bcwc_ringbuf.h index c7372cf..8e7eedf 100644 --- a/bcwc_ringbuf.h +++ b/bcwc_ringbuf.h @@ -48,7 +48,7 @@ struct bcwc_private; extern void bcwc_channel_ringbuf_dump(struct bcwc_private *dev_priv, struct fw_channel *chan); extern void bcwc_channel_ringbuf_init(struct bcwc_private *dev_priv, struct fw_channel *chan); extern struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_get_entry(struct bcwc_private *, struct fw_channel *); -extern int bcwc_channel_ringbuf_send(struct bcwc_private *dev_priv, struct fw_channel *chan, +extern struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_send(struct bcwc_private *dev_priv, struct fw_channel *chan, u32 data_offset, u32 request_size, u32 response_size); extern void bcwc_channel_ringbuf_mark_entry_available(struct bcwc_private *dev_priv,