diff options
Diffstat (limited to 'drivers/media/platform/allegro-dvt/allegro-core.c')
| -rw-r--r-- | drivers/media/platform/allegro-dvt/allegro-core.c | 118 |
1 files changed, 92 insertions, 26 deletions
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 510c3c9661d9..eec0b8b30b7f 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -177,6 +177,7 @@ struct allegro_dev { */ unsigned long channel_user_ids; struct list_head channels; + struct mutex channels_lock; }; static const struct regmap_config allegro_regmap_config = { @@ -198,6 +199,7 @@ static const struct regmap_config allegro_sram_config = { }; struct allegro_channel { + struct kref ref; struct allegro_dev *dev; struct v4l2_fh fh; struct v4l2_ctrl_handler ctrl_handler; @@ -430,33 +432,55 @@ static unsigned long allegro_next_user_id(struct allegro_dev *dev) } static struct allegro_channel * -allegro_find_channel_by_user_id(struct allegro_dev *dev, - unsigned int user_id) +allegro_ref_get_channel_by_user_id(struct allegro_dev *dev, + unsigned int user_id) { struct allegro_channel *channel; + guard(mutex)(&dev->channels_lock); + list_for_each_entry(channel, &dev->channels, list) { - if (channel->user_id == user_id) - return channel; + if (channel->user_id == user_id) { + if (kref_get_unless_zero(&channel->ref)) + return channel; + break; + } } return ERR_PTR(-EINVAL); } static struct allegro_channel * -allegro_find_channel_by_channel_id(struct allegro_dev *dev, - unsigned int channel_id) +allegro_ref_get_channel_by_channel_id(struct allegro_dev *dev, + unsigned int channel_id) { struct allegro_channel *channel; + guard(mutex)(&dev->channels_lock); + list_for_each_entry(channel, &dev->channels, list) { - if (channel->mcu_channel_id == channel_id) - return channel; + if (channel->mcu_channel_id == channel_id) { + if (kref_get_unless_zero(&channel->ref)) + return channel; + break; + } } return ERR_PTR(-EINVAL); } +static void allegro_free_channel(struct kref *ref) +{ + struct allegro_channel *channel = container_of(ref, struct allegro_channel, ref); + + kfree(channel); +} + +static int allegro_ref_put_channel(struct allegro_channel *channel) +{ + return kref_put(&channel->ref, allegro_free_channel); +} + static inline bool channel_exists(struct allegro_channel *channel) { return channel->mcu_channel_id != -1; @@ -831,6 +855,20 @@ out: return err; } +static unsigned int allegro_mbox_get_available(struct allegro_mbox *mbox) +{ + struct regmap *sram = mbox->dev->sram; + unsigned int head, tail; + + regmap_read(sram, mbox->head, &head); + regmap_read(sram, mbox->tail, &tail); + + if (tail >= head) + return tail - head; + else + return mbox->size - (head - tail); +} + static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, u32 *dst, size_t nbyte) { @@ -839,11 +877,15 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, u16 type; } __attribute__ ((__packed__)) *header; struct regmap *sram = mbox->dev->sram; - unsigned int head; + unsigned int available, head; ssize_t size; size_t body_no_wrap; int stride = regmap_get_reg_stride(sram); + available = allegro_mbox_get_available(mbox); + if (available < sizeof(*header)) + return -EAGAIN; + regmap_read(sram, mbox->head, &head); if (head > mbox->size) return -EIO; @@ -857,6 +899,8 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, return -EIO; if (size > nbyte) return -EINVAL; + if (size > available) + return -EAGAIN; /* * The message might wrap within the mailbox. If the message does not @@ -916,26 +960,27 @@ out: * allegro_mbox_notify() - Notify the mailbox about a new message * @mbox: The allegro_mbox to notify */ -static void allegro_mbox_notify(struct allegro_mbox *mbox) +static int allegro_mbox_notify(struct allegro_mbox *mbox) { struct allegro_dev *dev = mbox->dev; union mcu_msg_response *msg; - ssize_t size; u32 *tmp; int err; msg = kmalloc(sizeof(*msg), GFP_KERNEL); if (!msg) - return; + return -ENOMEM; msg->header.version = dev->fw_info->mailbox_version; tmp = kmalloc(mbox->size, GFP_KERNEL); - if (!tmp) + if (!tmp) { + err = -ENOMEM; goto out; + } - size = allegro_mbox_read(mbox, tmp, mbox->size); - if (size < 0) + err = allegro_mbox_read(mbox, tmp, mbox->size); + if (err < 0) goto out; err = allegro_decode_mail(msg, tmp); @@ -947,6 +992,8 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) out: kfree(tmp); kfree(msg); + + return err; } static int allegro_encoder_buffer_init(struct allegro_dev *dev, @@ -2124,7 +2171,7 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, state = VB2_BUF_STATE_DONE; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); if (msg->is_idr) dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; else @@ -2163,7 +2210,7 @@ allegro_handle_create_channel(struct allegro_dev *dev, int err = 0; struct create_channel_param param; - channel = allegro_find_channel_by_user_id(dev, msg->user_id); + channel = allegro_ref_get_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { v4l2_warn(&dev->v4l2_dev, "received %s for unknown user %d\n", @@ -2230,6 +2277,7 @@ allegro_handle_create_channel(struct allegro_dev *dev, out: channel->error = err; complete(&channel->completion); + allegro_ref_put_channel(channel); /* Handled successfully, error is passed via channel->error */ return 0; @@ -2241,7 +2289,7 @@ allegro_handle_destroy_channel(struct allegro_dev *dev, { struct allegro_channel *channel; - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + channel = allegro_ref_get_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, "received %s for unknown channel %d\n", @@ -2254,6 +2302,7 @@ allegro_handle_destroy_channel(struct allegro_dev *dev, "user %d: vcu destroyed channel %d\n", channel->user_id, channel->mcu_channel_id); complete(&channel->completion); + allegro_ref_put_channel(channel); return 0; } @@ -2264,7 +2313,7 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + channel = allegro_ref_get_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, "received %s for unknown channel %d\n", @@ -2274,6 +2323,7 @@ allegro_handle_encode_frame(struct allegro_dev *dev, } allegro_channel_finish_frame(channel, msg); + allegro_ref_put_channel(channel); return 0; } @@ -2329,7 +2379,10 @@ static irqreturn_t allegro_irq_thread(int irq, void *data) if (!dev->mbox_status) return IRQ_NONE; - allegro_mbox_notify(dev->mbox_status); + while (allegro_mbox_get_available(dev->mbox_status) > 0) { + if (allegro_mbox_notify(dev->mbox_status)) + break; + } return IRQ_HANDLED; } @@ -2602,8 +2655,14 @@ static int allegro_create_channel(struct allegro_channel *channel) allegro_mcu_send_create_channel(dev, channel); time_left = wait_for_completion_timeout(&channel->completion, msecs_to_jiffies(5000)); - if (time_left == 0) + if (time_left == 0) { + v4l2_warn(&dev->v4l2_dev, + "user %d: timeout while creating channel\n", + channel->user_id); + channel->error = -ETIMEDOUT; + } + if (channel->error) goto err; @@ -3050,6 +3109,8 @@ static int allegro_open(struct file *file) if (!channel) return -ENOMEM; + kref_init(&channel->ref); + v4l2_fh_init(&channel->fh, vdev); init_completion(&channel->completion); @@ -3216,7 +3277,10 @@ static int allegro_open(struct file *file) goto error; } - list_add(&channel->list, &dev->channels); + scoped_guard(mutex, &dev->channels_lock) { + list_add(&channel->list, &dev->channels); + } + v4l2_fh_add(&channel->fh, file); allegro_channel_adjust(channel); @@ -3232,17 +3296,20 @@ error: static int allegro_release(struct file *file) { struct allegro_channel *channel = file_to_channel(file); + struct allegro_dev *dev = channel->dev; v4l2_m2m_ctx_release(channel->fh.m2m_ctx); - list_del(&channel->list); + scoped_guard(mutex, &dev->channels_lock) { + list_del(&channel->list); + } v4l2_ctrl_handler_free(&channel->ctrl_handler); v4l2_fh_del(&channel->fh, file); v4l2_fh_exit(&channel->fh); - kfree(channel); + allegro_ref_put_channel(channel); return 0; } @@ -3333,8 +3400,6 @@ static int allegro_s_fmt_vid_cap(struct file *file, void *fh, return err; vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) return -EBUSY; @@ -3836,6 +3901,7 @@ static int allegro_probe(struct platform_device *pdev) dev->plat_dev = pdev; init_completion(&dev->init_complete); INIT_LIST_HEAD(&dev->channels); + mutex_init(&dev->channels_lock); mutex_init(&dev->lock); |
