diff options
Diffstat (limited to 'drivers/media/platform')
177 files changed, 15339 insertions, 3650 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 9287faafdce5..3f0b7bb68cc9 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -65,6 +65,7 @@ config VIDEO_MUX source "drivers/media/platform/allegro-dvt/Kconfig" source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" +source "drivers/media/platform/arm/Kconfig" source "drivers/media/platform/aspeed/Kconfig" source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/broadcom/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6fd7db0541c7..6d5f79ddfcc3 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -8,6 +8,7 @@ obj-y += allegro-dvt/ obj-y += amlogic/ obj-y += amphion/ +obj-y += arm/ obj-y += aspeed/ obj-y += atmel/ obj-y += broadcom/ 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); diff --git a/drivers/media/platform/amlogic/c3/isp/Kconfig b/drivers/media/platform/amlogic/c3/isp/Kconfig index 02c62a50a5e8..809208cd7e3a 100644 --- a/drivers/media/platform/amlogic/c3/isp/Kconfig +++ b/drivers/media/platform/amlogic/c3/isp/Kconfig @@ -10,6 +10,7 @@ config VIDEO_C3_ISP select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC + select V4L2_ISP help Video4Linux2 driver for Amlogic C3 ISP pipeline. The C3 ISP is used for processing raw images and diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index c80667dd7662..6f9ca7a7dd88 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -3,11 +3,13 @@ * Copyright (C) 2024 Amlogic, Inc. All rights reserved */ +#include <linux/build_bug.h> #include <linux/cleanup.h> #include <linux/media/amlogic/c3-isp-config.h> #include <linux/pm_runtime.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-isp.h> #include <media/v4l2-mc.h> #include <media/videobuf2-vmalloc.h> @@ -51,11 +53,6 @@ union c3_isp_params_block { typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp, const union c3_isp_params_block *block); -struct c3_isp_params_handler { - size_t size; - c3_isp_block_handler handler; -}; - #define to_c3_isp_params_buffer(vbuf) \ container_of(vbuf, struct c3_isp_params_buffer, vb) @@ -523,41 +520,37 @@ static void c3_isp_params_cfg_blc(struct c3_isp_device *isp, ISP_TOP_BEO_CTRL_BLC_EN); } -static const struct c3_isp_params_handler c3_isp_params_handlers[] = { - [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = { - .size = sizeof(struct c3_isp_params_awb_gains), - .handler = c3_isp_params_cfg_awb_gains, - }, - [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = { - .size = sizeof(struct c3_isp_params_awb_config), - .handler = c3_isp_params_cfg_awb_config, - }, - [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = { - .size = sizeof(struct c3_isp_params_ae_config), - .handler = c3_isp_params_cfg_ae_config, - }, - [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = { - .size = sizeof(struct c3_isp_params_af_config), - .handler = c3_isp_params_cfg_af_config, - }, - [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = { - .size = sizeof(struct c3_isp_params_pst_gamma), - .handler = c3_isp_params_cfg_pst_gamma, - }, - [C3_ISP_PARAMS_BLOCK_CCM] = { - .size = sizeof(struct c3_isp_params_ccm), - .handler = c3_isp_params_cfg_ccm, - }, - [C3_ISP_PARAMS_BLOCK_CSC] = { - .size = sizeof(struct c3_isp_params_csc), - .handler = c3_isp_params_cfg_csc, - }, - [C3_ISP_PARAMS_BLOCK_BLC] = { - .size = sizeof(struct c3_isp_params_blc), - .handler = c3_isp_params_cfg_blc, - }, +static const c3_isp_block_handler c3_isp_params_handlers[] = { + [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = c3_isp_params_cfg_awb_gains, + [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = c3_isp_params_cfg_awb_config, + [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = c3_isp_params_cfg_ae_config, + [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = c3_isp_params_cfg_af_config, + [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = c3_isp_params_cfg_pst_gamma, + [C3_ISP_PARAMS_BLOCK_CCM] = c3_isp_params_cfg_ccm, + [C3_ISP_PARAMS_BLOCK_CSC] = c3_isp_params_cfg_csc, + [C3_ISP_PARAMS_BLOCK_BLC] = c3_isp_params_cfg_blc, +}; + +#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \ + [C3_ISP_PARAMS_BLOCK_ ## block] = { \ + .size = sizeof(struct c3_isp_params_ ## data), \ + } + +static const struct v4l2_isp_params_block_type_info +c3_isp_params_block_types_info[] = { + C3_ISP_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains), + C3_ISP_PARAMS_BLOCK_INFO(AWB_CONFIG, awb_config), + C3_ISP_PARAMS_BLOCK_INFO(AE_CONFIG, ae_config), + C3_ISP_PARAMS_BLOCK_INFO(AF_CONFIG, af_config), + C3_ISP_PARAMS_BLOCK_INFO(PST_GAMMA, pst_gamma), + C3_ISP_PARAMS_BLOCK_INFO(CCM, ccm), + C3_ISP_PARAMS_BLOCK_INFO(CSC, csc), + C3_ISP_PARAMS_BLOCK_INFO(BLC, blc), }; +static_assert(ARRAY_SIZE(c3_isp_params_handlers) == + ARRAY_SIZE(c3_isp_params_block_types_info)); + static void c3_isp_params_cfg_blocks(struct c3_isp_params *params) { struct c3_isp_params_cfg *config = params->buff->cfg; @@ -568,14 +561,14 @@ static void c3_isp_params_cfg_blocks(struct c3_isp_params *params) /* Walk the list of parameter blocks and process them */ while (block_offset < config->data_size) { - const struct c3_isp_params_handler *block_handler; const union c3_isp_params_block *block; + c3_isp_block_handler block_handler; block = (const union c3_isp_params_block *) &config->data[block_offset]; - block_handler = &c3_isp_params_handlers[block->header.type]; - block_handler->handler(params->isp, block); + block_handler = c3_isp_params_handlers[block->header.type]; + block_handler(params->isp, block); block_offset += block->header.size; } @@ -771,26 +764,15 @@ static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf); struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue); - struct c3_isp_params_cfg *cfg = buf->cfg; struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0); size_t payload_size = vb2_get_plane_payload(vb, 0); - size_t header_size = offsetof(struct c3_isp_params_cfg, data); - size_t block_offset = 0; - size_t cfg_size; - - /* Payload size can't be greater than the destination buffer size */ - if (payload_size > params->vfmt.fmt.meta.buffersize) { - dev_dbg(params->isp->dev, - "Payload size is too large: %zu\n", payload_size); - return -EINVAL; - } + struct c3_isp_params_cfg *cfg = buf->cfg; + int ret; - /* Payload size can't be smaller than the header size */ - if (payload_size < header_size) { - dev_dbg(params->isp->dev, - "Payload size is too small: %zu\n", payload_size); - return -EINVAL; - } + ret = v4l2_isp_params_validate_buffer_size(params->isp->dev, vb, + params->vfmt.fmt.meta.buffersize); + if (ret) + return ret; /* * Use the internal scratch buffer to avoid userspace modifying @@ -798,70 +780,10 @@ static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb) */ memcpy(cfg, usr_cfg, payload_size); - /* Only v0 is supported at the moment */ - if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) { - dev_dbg(params->isp->dev, - "Invalid params buffer version: %u\n", cfg->version); - return -EINVAL; - } - - /* Validate the size reported in the parameter buffer header */ - cfg_size = header_size + cfg->data_size; - if (cfg_size != payload_size) { - dev_dbg(params->isp->dev, - "Data size %zu and payload size %zu are different\n", - cfg_size, payload_size); - return -EINVAL; - } - - /* Walk the list of parameter blocks and validate them */ - cfg_size = cfg->data_size; - while (cfg_size >= sizeof(struct c3_isp_params_block_header)) { - const struct c3_isp_params_block_header *block; - const struct c3_isp_params_handler *handler; - - block = (struct c3_isp_params_block_header *) - &cfg->data[block_offset]; - - if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) { - dev_dbg(params->isp->dev, - "Invalid params block type\n"); - return -EINVAL; - } - - if (block->size > cfg_size) { - dev_dbg(params->isp->dev, - "Block size is greater than cfg size\n"); - return -EINVAL; - } - - if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE | - C3_ISP_PARAMS_BLOCK_FL_DISABLE)) == - (C3_ISP_PARAMS_BLOCK_FL_ENABLE | - C3_ISP_PARAMS_BLOCK_FL_DISABLE)) { - dev_dbg(params->isp->dev, - "Invalid parameters block flags\n"); - return -EINVAL; - } - - handler = &c3_isp_params_handlers[block->type]; - if (block->size != handler->size) { - dev_dbg(params->isp->dev, - "Invalid params block size\n"); - return -EINVAL; - } - - block_offset += block->size; - cfg_size -= block->size; - } - - if (cfg_size) { - dev_dbg(params->isp->dev, - "Unexpected data after the params buffer end\n"); - return -EINVAL; - } - - return 0; + return v4l2_isp_params_validate_buffer(params->isp->dev, vb, + (struct v4l2_isp_params_buffer *)cfg, + c3_isp_params_block_types_info, + ARRAY_SIZE(c3_isp_params_block_types_info)); } static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb) diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c index 5744853a4003..c51c6f4e41dc 100644 --- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c +++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c @@ -632,13 +632,8 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct ge2d_ctx *ctx = file_to_ge2d_ctx(file); - struct vb2_queue *vq; struct ge2d_frame *frm; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - frm = get_frame(ctx, f->type); f->fmt.pix = frm->pix_fmt; diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 32eef2fd1f2a..c0d2aabb9e0e 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -532,8 +532,6 @@ static int vdec_s_fmt_common(struct vpu_inst *inst, struct v4l2_format *f) return -EINVAL; q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -823,7 +821,7 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg) vbuf = &vpu_buf->m2m_buf.vb; src_buf = vdec_get_src_buffer(inst, info->consumed_count); if (src_buf) { - v4l2_m2m_buf_copy_metadata(src_buf, vbuf, true); + v4l2_m2m_buf_copy_metadata(src_buf, vbuf); if (info->consumed_count) { v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx); vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE); diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index c5c1f1fbaa80..aced76401b69 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -223,8 +223,6 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -790,7 +788,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst, src_buf = vpu_find_buf_by_sequence(inst, inst->out_format.type, frame->info.frame_id); if (src_buf) { - v4l2_m2m_buf_copy_metadata(src_buf, vbuf, true); + v4l2_m2m_buf_copy_metadata(src_buf, vbuf); vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE); v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, src_buf); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index da00f5fc0e5d..168f0514851e 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -10,7 +10,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> @@ -542,47 +542,30 @@ const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst) static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np) { - struct device_node *node; struct resource res; int ret; - if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) { - dev_err(core->dev, "need 2 memory-region for boot and rpc\n"); - return -ENODEV; + ret = of_reserved_mem_region_to_resource(np, 0, &res); + if (ret) { + dev_err(core->dev, "Cannot get boot-region\n"); + return ret; } - node = of_parse_phandle(np, "memory-region", 0); - if (!node) { - dev_err(core->dev, "boot-region of_parse_phandle error\n"); - return -ENODEV; - } - if (of_address_to_resource(node, 0, &res)) { - dev_err(core->dev, "boot-region of_address_to_resource error\n"); - of_node_put(node); - return -EINVAL; - } core->fw.phys = res.start; core->fw.length = resource_size(&res); - of_node_put(node); - - node = of_parse_phandle(np, "memory-region", 1); - if (!node) { - dev_err(core->dev, "rpc-region of_parse_phandle error\n"); - return -ENODEV; - } - if (of_address_to_resource(node, 0, &res)) { - dev_err(core->dev, "rpc-region of_address_to_resource error\n"); - of_node_put(node); - return -EINVAL; + ret = of_reserved_mem_region_to_resource(np, 1, &res); + if (ret) { + dev_err(core->dev, "Cannot get rpc-region\n"); + return ret; } + core->rpc.phys = res.start; core->rpc.length = resource_size(&res); if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) { dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n", &core->rpc.phys, core->rpc.length); - of_node_put(node); return -EINVAL; } @@ -594,7 +577,6 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np) if (ret != VPU_CORE_MEMORY_UNCACHED) { dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n", &core->rpc.phys, core->rpc.length); - of_node_put(node); return -EINVAL; } @@ -606,8 +588,6 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np) core->act.length = core->rpc.length - core->res->rpc_size - core->log.length; core->rpc.length = core->res->rpc_size; - of_node_put(node); - return 0; } diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c index efbfd2652721..2cca61f41bea 100644 --- a/drivers/media/platform/amphion/vpu_drv.c +++ b/drivers/media/platform/amphion/vpu_drv.c @@ -175,31 +175,6 @@ static void vpu_remove(struct platform_device *pdev) mutex_destroy(&vpu->lock); } -static int __maybe_unused vpu_runtime_resume(struct device *dev) -{ - return 0; -} - -static int __maybe_unused vpu_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int __maybe_unused vpu_resume(struct device *dev) -{ - return 0; -} - -static int __maybe_unused vpu_suspend(struct device *dev) -{ - return 0; -} - -static const struct dev_pm_ops vpu_pm_ops = { - SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume) -}; - static struct vpu_resources imx8qxp_res = { .plat_type = IMX8QXP, .mreg_base = 0x40000000, @@ -231,7 +206,6 @@ static struct platform_driver amphion_vpu_driver = { .driver = { .name = "amphion-vpu", .of_match_table = vpu_dt_match, - .pm = &vpu_pm_ops, }, }; diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index ba688566dffd..80802975c4f1 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -1337,22 +1337,18 @@ static int vpu_malone_insert_scode_vc1_g_seq(struct malone_scode_t *scode) { if (!scode->inst->total_input_count) return 0; - if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb))) - scode->need_data = 0; return 0; } static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode) { - struct vb2_v4l2_buffer *vbuf; u8 nal_hdr[MALONE_VC1_NAL_HEADER_LEN]; u32 *data = NULL; int ret; - vbuf = to_vb2_v4l2_buffer(scode->vb); data = vb2_plane_vaddr(scode->vb, 0); - if (scode->inst->total_input_count == 0 || vpu_vb_is_codecconfig(vbuf)) + if (scode->inst->total_input_count == 0) return 0; if (MALONE_VC1_CONTAIN_NAL(*data)) return 0; @@ -1373,8 +1369,6 @@ static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode) int size = 0; u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN]; - if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb))) - scode->need_data = 0; if (scode->inst->total_input_count) return 0; scode->need_data = 0; @@ -1560,7 +1554,7 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str scode.vb = vb; scode.wptr = wptr; scode.need_data = 1; - if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf)) + if (vbuf->sequence == 0) ret = vpu_malone_insert_scode(&scode, SCODE_SEQUENCE); if (ret < 0) @@ -1596,7 +1590,7 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str * This module is currently only supported for the H264 and HEVC formats, * for other formats, vpu_malone_add_scode() will return 0. */ - if ((disp_imm || low_latency) && !vpu_vb_is_codecconfig(vbuf)) { + if (disp_imm || low_latency) { ret = vpu_malone_add_scode(inst->core->iface, inst->id, &inst->stream_buffer, @@ -1643,7 +1637,6 @@ int vpu_malone_input_frame(struct vpu_shared_addr *shared, struct vpu_inst *inst, struct vb2_buffer *vb) { struct vpu_dec_ctrl *hc = shared->priv; - struct vb2_v4l2_buffer *vbuf; struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[inst->id]; u32 disp_imm = hc->codec_param[inst->id].disp_imm; u32 size; @@ -1657,16 +1650,6 @@ int vpu_malone_input_frame(struct vpu_shared_addr *shared, return ret; size = ret; - /* - * if buffer only contain codec data, and the timestamp is invalid, - * don't put the invalid timestamp to resync - * merge the data to next frame - */ - vbuf = to_vb2_v4l2_buffer(vb); - if (vpu_vb_is_codecconfig(vbuf)) { - inst->extra_size += size; - return 0; - } if (inst->extra_size) { size += inst->extra_size; inst->extra_size = 0; diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index fcb2eff813ac..47dff9a35bb4 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -349,16 +349,6 @@ struct vb2_v4l2_buffer *vpu_next_src_buf(struct vpu_inst *inst) if (!src_buf || vpu_get_buffer_state(src_buf) == VPU_BUF_STATE_IDLE) return NULL; - while (vpu_vb_is_codecconfig(src_buf)) { - v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx); - vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - src_buf = v4l2_m2m_next_src_buf(inst->fh.m2m_ctx); - if (!src_buf || vpu_get_buffer_state(src_buf) == VPU_BUF_STATE_IDLE) - return NULL; - } - return src_buf; } @@ -713,15 +703,15 @@ static int vpu_v4l2_release(struct vpu_inst *inst) { vpu_trace(inst->vpu->dev, "%p\n", inst); - vpu_release_core(inst->core); - put_device(inst->dev); - if (inst->workqueue) { cancel_work_sync(&inst->msg_work); destroy_workqueue(inst->workqueue); inst->workqueue = NULL; } + vpu_release_core(inst->core); + put_device(inst->dev); + v4l2_ctrl_handler_free(&inst->ctrl_handler); mutex_destroy(&inst->lock); diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h index 4a87b06ae520..da9945f25e32 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.h +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -39,14 +39,4 @@ static inline struct vpu_format *vpu_get_format(struct vpu_inst *inst, u32 type) else return &inst->cap_format; } - -static inline int vpu_vb_is_codecconfig(struct vb2_v4l2_buffer *vbuf) -{ -#ifdef V4L2_BUF_FLAG_CODECCONFIG - return (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG) ? 1 : 0; -#else - return 0; -#endif -} - #endif diff --git a/drivers/media/platform/arm/Kconfig b/drivers/media/platform/arm/Kconfig new file mode 100644 index 000000000000..4f0764c329c7 --- /dev/null +++ b/drivers/media/platform/arm/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "ARM media platform drivers" + +source "drivers/media/platform/arm/mali-c55/Kconfig" diff --git a/drivers/media/platform/arm/Makefile b/drivers/media/platform/arm/Makefile new file mode 100644 index 000000000000..8cc4918725ef --- /dev/null +++ b/drivers/media/platform/arm/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += mali-c55/ diff --git a/drivers/media/platform/arm/mali-c55/Kconfig b/drivers/media/platform/arm/mali-c55/Kconfig new file mode 100644 index 000000000000..5b084b3c3340 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MALI_C55 + tristate "ARM Mali-C55 Image Signal Processor driver" + depends on ARCH_VEXPRESS || ARCH_RENESAS || COMPILE_TEST + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + select GENERIC_PHY_MIPI_DPHY + select MEDIA_CONTROLLER + select V4L2_FWNODE + select V4L2_ISP + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + help + Enable this to support Arm's Mali-C55 Image Signal Processor. + + To compile this driver as a module, choose M here: the module + will be called mali-c55. diff --git a/drivers/media/platform/arm/mali-c55/Makefile b/drivers/media/platform/arm/mali-c55/Makefile new file mode 100644 index 000000000000..d5718b0b23e0 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +mali-c55-y := mali-c55-capture.o \ + mali-c55-core.o \ + mali-c55-isp.o \ + mali-c55-params.o \ + mali-c55-resizer.o \ + mali-c55-stats.o \ + mali-c55-tpg.o + +obj-$(CONFIG_VIDEO_MALI_C55) += mali-c55.o diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c new file mode 100644 index 000000000000..7aaa5c3f7354 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Video capture devices + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/cleanup.h> +#include <linux/minmax.h> +#include <linux/lockdep.h> +#include <linux/pm_runtime.h> +#include <linux/string.h> +#include <linux/videodev2.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +static const struct mali_c55_format_info mali_c55_fmts[] = { + /* + * This table is missing some entries which need further work or + * investigation: + * + * Base mode 5 is "Generic Data" + * Base mode 8 is a backwards V4L2_PIX_FMT_XYUV32 - no V4L2 equivalent + * Base mode 9 seems to have no V4L2 equivalent + * Base mode 17, 19 and 20 describe formats which seem to have no V4L2 + * equivalent + */ + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .mbus_codes = { + MEDIA_BUS_FMT_RGB121212_1X36, + MEDIA_BUS_FMT_RGB202020_1X60, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_RGB32, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_ARGB2101010, + .mbus_codes = { + MEDIA_BUS_FMT_RGB121212_1X36, + MEDIA_BUS_FMT_RGB202020_1X60, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_A2R10G10B10, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .mbus_codes = { + MEDIA_BUS_FMT_RGB121212_1X36, + MEDIA_BUS_FMT_RGB202020_1X60, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_RGB565, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .mbus_codes = { + MEDIA_BUS_FMT_RGB121212_1X36, + MEDIA_BUS_FMT_RGB202020_1X60, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_RGB24, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_YUY2, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_UYVY, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_Y210, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_Y210, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + /* + * This is something of a hack, the ISP thinks it's running NV12M but + * by setting uv_plane = 0 we simply discard that planes and only output + * the Y-plane. + */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_NV12_21, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_NV12_21, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT1 + } + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .mbus_codes = { + MEDIA_BUS_FMT_YUV10_1X30, + }, + .is_raw = false, + .registers = { + .base_mode = MALI_C55_OUTPUT_NV12_21, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT2 + } + }, + /* + * RAW uncompressed formats are all packed in 16 bpp. + * TODO: Expand this list to encompass all possible RAW formats. + */ + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .mbus_codes = { + MEDIA_BUS_FMT_SRGGB16_1X16, + }, + .is_raw = true, + .registers = { + .base_mode = MALI_C55_OUTPUT_RAW16, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR16_1X16, + }, + .is_raw = true, + .registers = { + .base_mode = MALI_C55_OUTPUT_RAW16, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .mbus_codes = { + MEDIA_BUS_FMT_SGBRG16_1X16, + }, + .is_raw = true, + .registers = { + .base_mode = MALI_C55_OUTPUT_RAW16, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .mbus_codes = { + MEDIA_BUS_FMT_SGRBG16_1X16, + }, + .is_raw = true, + .registers = { + .base_mode = MALI_C55_OUTPUT_RAW16, + .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0 + } + }, +}; + +void mali_c55_cap_dev_write(struct mali_c55_cap_dev *cap_dev, unsigned int addr, + u32 val) +{ + mali_c55_ctx_write(cap_dev->mali_c55, addr + cap_dev->reg_offset, val); +} + +static u32 mali_c55_cap_dev_read(struct mali_c55_cap_dev *cap_dev, unsigned int addr) +{ + return mali_c55_ctx_read(cap_dev->mali_c55, addr + cap_dev->reg_offset); +} + +static void mali_c55_cap_dev_update_bits(struct mali_c55_cap_dev *cap_dev, + unsigned int addr, u32 mask, u32 val) +{ + u32 orig, tmp; + + orig = mali_c55_cap_dev_read(cap_dev, addr); + + tmp = orig & ~mask; + tmp |= val & mask; + + if (tmp != orig) + mali_c55_cap_dev_write(cap_dev, addr, tmp); +} + +static bool +mali_c55_mbus_code_can_produce_fmt(const struct mali_c55_format_info *fmt, + u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fmt->mbus_codes); i++) { + if (fmt->mbus_codes[i] == code) + return true; + } + + return false; +} + +bool mali_c55_format_is_raw(unsigned int mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) { + if (mali_c55_fmts[i].mbus_codes[0] == mbus_code) + return mali_c55_fmts[i].is_raw; + } + + return false; +} + +static const struct mali_c55_format_info * +mali_c55_format_from_pix(const u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) { + if (mali_c55_fmts[i].fourcc == pixelformat) + return &mali_c55_fmts[i]; + } + + /* + * If we find no matching pixelformat, we'll just default to the first + * one for now. + */ + + return &mali_c55_fmts[0]; +} + +static const char * const capture_device_names[] = { + "mali-c55 fr", + "mali-c55 ds", +}; + +static int mali_c55_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct mali_c55_cap_dev *cap_dev = video_get_drvdata(vdev); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); + const struct v4l2_pix_format_mplane *pix_mp; + const struct mali_c55_format_info *cap_fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = link->source->index, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + pix_mp = &cap_dev->format.format; + cap_fmt = cap_dev->format.info; + + if (sd_fmt.format.width != pix_mp->width || + sd_fmt.format.height != pix_mp->height) { + dev_dbg(cap_dev->mali_c55->dev, + "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + sd_fmt.format.width, sd_fmt.format.height, + pix_mp->width, pix_mp->height); + return -EPIPE; + } + + if (!mali_c55_mbus_code_can_produce_fmt(cap_fmt, sd_fmt.format.code)) { + dev_dbg(cap_dev->mali_c55->dev, + "link '%s':%u -> '%s':%u not valid: mbus_code 0x%04x cannot produce pixel format %p4cc\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + sd_fmt.format.code, &pix_mp->pixelformat); + return -EPIPE; + } + + return 0; +} + +static const struct media_entity_operations mali_c55_media_ops = { + .link_validate = mali_c55_link_validate, +}; + +static int mali_c55_vb2_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mali_c55_cap_dev *cap_dev = q->drv_priv; + unsigned int i; + + if (*num_planes) { + if (*num_planes != cap_dev->format.format.num_planes) + return -EINVAL; + + for (i = 0; i < cap_dev->format.format.num_planes; i++) + if (sizes[i] < cap_dev->format.format.plane_fmt[i].sizeimage) + return -EINVAL; + } else { + *num_planes = cap_dev->format.format.num_planes; + for (i = 0; i < cap_dev->format.format.num_planes; i++) + sizes[i] = cap_dev->format.format.plane_fmt[i].sizeimage; + } + + return 0; +} + +static void mali_c55_buf_queue(struct vb2_buffer *vb) +{ + struct mali_c55_cap_dev *cap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_buffer *buf = container_of(vbuf, + struct mali_c55_buffer, vb); + unsigned int i; + + buf->planes_pending = cap_dev->format.format.num_planes; + + for (i = 0; i < cap_dev->format.format.num_planes; i++) { + unsigned long size = cap_dev->format.format.plane_fmt[i].sizeimage; + + vb2_set_plane_payload(vb, i, size); + } + + buf->vb.field = V4L2_FIELD_NONE; + + guard(spinlock)(&cap_dev->buffers.lock); + list_add_tail(&buf->queue, &cap_dev->buffers.input); +} + +static int mali_c55_buf_init(struct vb2_buffer *vb) +{ + struct mali_c55_cap_dev *cap_dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_buffer *buf = container_of(vbuf, + struct mali_c55_buffer, vb); + unsigned int i; + + for (i = 0; i < cap_dev->format.format.num_planes; i++) + buf->addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i); + + return 0; +} + +void mali_c55_set_next_buffer(struct mali_c55_cap_dev *cap_dev) +{ + struct v4l2_pix_format_mplane *pix_mp; + struct mali_c55_buffer *buf; + dma_addr_t *addrs; + + scoped_guard(spinlock, &cap_dev->buffers.lock) { + buf = list_first_entry_or_null(&cap_dev->buffers.input, + struct mali_c55_buffer, queue); + if (buf) + list_del(&buf->queue); + } + + if (!buf) { + /* + * If we underflow then we can tell the ISP that we don't want + * to write out the next frame. + */ + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_Y_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, + 0x00); + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_UV_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, + 0x00); + return; + } + + pix_mp = &cap_dev->format.format; + + mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_Y_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, + MALI_C55_WRITER_FRAME_WRITE_ENABLE); + if (cap_dev->format.info->registers.uv_plane) + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_UV_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, + MALI_C55_WRITER_FRAME_WRITE_ENABLE); + + addrs = buf->addrs; + mali_c55_cap_dev_write(cap_dev, + MALI_C55_REG_Y_WRITER_BANKS_BASE, + addrs[MALI_C55_PLANE_Y]); + mali_c55_cap_dev_write(cap_dev, + MALI_C55_REG_UV_WRITER_BANKS_BASE, + addrs[MALI_C55_PLANE_UV]); + + mali_c55_cap_dev_write(cap_dev, + MALI_C55_REG_Y_WRITER_OFFSET, + pix_mp->plane_fmt[MALI_C55_PLANE_Y].bytesperline); + mali_c55_cap_dev_write(cap_dev, + MALI_C55_REG_UV_WRITER_OFFSET, + pix_mp->plane_fmt[MALI_C55_PLANE_UV].bytesperline); + + guard(spinlock)(&cap_dev->buffers.processing_lock); + list_add_tail(&buf->queue, &cap_dev->buffers.processing); +} + +/** + * mali_c55_set_plane_done - mark the plane as written and process the buffer if + * both planes are finished. + * @cap_dev: pointer to the fr or ds pipe output + * @plane: the plane to mark as completed + * + * The Mali C55 ISP has muliplanar outputs for some formats that come with two + * separate "buffer write completed" interrupts - we need to flag each plane's + * completion and check whether both planes are done - if so, complete the buf + * in vb2. + */ +void mali_c55_set_plane_done(struct mali_c55_cap_dev *cap_dev, + enum mali_c55_planes plane) +{ + struct mali_c55_isp *isp = &cap_dev->mali_c55->isp; + struct mali_c55_buffer *buf; + + scoped_guard(spinlock, &cap_dev->buffers.processing_lock) { + buf = list_first_entry_or_null(&cap_dev->buffers.processing, + struct mali_c55_buffer, queue); + + /* + * If the stream was stopped, the buffer might have been sent + * back to userspace already. + */ + if (!buf || --buf->planes_pending) + return; + + list_del(&buf->queue); + } + + /* If the other plane is also done... */ + buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns(); + buf->vb.sequence = isp->frame_sequence++; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void mali_c55_cap_dev_stream_disable(struct mali_c55_cap_dev *cap_dev) +{ + mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_Y_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, 0x00); + mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_UV_WRITER_MODE, + MALI_C55_WRITER_FRAME_WRITE_MASK, 0x00); +} + +static void mali_c55_cap_dev_stream_enable(struct mali_c55_cap_dev *cap_dev) +{ + /* + * The Mali ISP can hold up to 5 buffer addresses and simply cycle + * through them, but it's not clear to me that the vb2 queue _guarantees_ + * it will queue buffers to the driver in a fixed order, and ensuring + * we call vb2_buffer_done() for the right buffer seems to me to add + * pointless complexity given in multi-context mode we'd need to + * re-write those registers every frame anyway...so we tell the ISP to + * use a single register and update it for each frame. + */ + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_Y_WRITER_BANKS_CONFIG, + MALI_C55_REG_Y_WRITER_MAX_BANKS_MASK, 0); + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_UV_WRITER_BANKS_CONFIG, + MALI_C55_REG_UV_WRITER_MAX_BANKS_MASK, 0); + + mali_c55_set_next_buffer(cap_dev); +} + +static void mali_c55_cap_dev_return_buffers(struct mali_c55_cap_dev *cap_dev, + enum vb2_buffer_state state) +{ + struct mali_c55_buffer *buf, *tmp; + + scoped_guard(spinlock, &cap_dev->buffers.lock) { + list_for_each_entry_safe(buf, tmp, &cap_dev->buffers.input, + queue) { + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + } + + guard(spinlock)(&cap_dev->buffers.processing_lock); + list_for_each_entry_safe(buf, tmp, &cap_dev->buffers.processing, queue) { + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static void mali_c55_cap_dev_format_configure(struct mali_c55_cap_dev *cap_dev) +{ + const struct mali_c55_format_info *capture_format = cap_dev->format.info; + const struct v4l2_pix_format_mplane *pix_mp = &cap_dev->format.format; + const struct v4l2_format_info *info; + + info = v4l2_format_info(pix_mp->pixelformat); + if (WARN_ON(!info)) + return; + + mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_Y_WRITER_MODE, + capture_format->registers.base_mode); + mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_ACTIVE_OUT_Y_SIZE, + MALI_C55_REG_ACTIVE_OUT_SIZE_W(pix_mp->width) | + MALI_C55_REG_ACTIVE_OUT_SIZE_H(pix_mp->height)); + + if (info->mem_planes > 1) { + mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_UV_WRITER_MODE, + capture_format->registers.base_mode); + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_UV_WRITER_MODE, + MALI_C55_WRITER_SUBMODE_MASK, + MALI_C55_WRITER_SUBMODE(capture_format->registers.uv_plane)); + + mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_ACTIVE_OUT_UV_SIZE, + MALI_C55_REG_ACTIVE_OUT_SIZE_W(pix_mp->width) | + MALI_C55_REG_ACTIVE_OUT_SIZE_H(pix_mp->height)); + } + + if (info->pixel_enc == V4L2_PIXEL_ENC_YUV) { + mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_CS_CONV_CONFIG, + MALI_C55_CS_CONV_MATRIX_MASK); + + if (info->hdiv > 1) + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_CS_CONV_CONFIG, + MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_MASK, + MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_ENABLE); + if (info->vdiv > 1) + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_CS_CONV_CONFIG, + MALI_C55_CS_CONV_VERT_DOWNSAMPLE_MASK, + MALI_C55_CS_CONV_VERT_DOWNSAMPLE_ENABLE); + if (info->hdiv > 1 || info->vdiv > 1) + mali_c55_cap_dev_update_bits(cap_dev, + MALI_C55_REG_CS_CONV_CONFIG, + MALI_C55_CS_CONV_FILTER_MASK, + MALI_C55_CS_CONV_FILTER_ENABLE); + } +} + +static int mali_c55_vb2_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mali_c55_cap_dev *cap_dev = q->drv_priv; + struct mali_c55 *mali_c55 = cap_dev->mali_c55; + struct mali_c55_resizer *rsz = cap_dev->rsz; + struct mali_c55_isp *isp = &mali_c55->isp; + int ret; + + guard(mutex)(&isp->capture_lock); + + ret = pm_runtime_resume_and_get(mali_c55->dev); + if (ret) + goto err_return_buffers; + + ret = video_device_pipeline_alloc_start(&cap_dev->vdev); + if (ret) { + dev_dbg(mali_c55->dev, "%s failed to start media pipeline\n", + cap_dev->vdev.name); + goto err_pm_put; + } + + mali_c55_cap_dev_format_configure(cap_dev); + mali_c55_cap_dev_stream_enable(cap_dev); + + ret = v4l2_subdev_enable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD, + BIT(0)); + if (ret) + goto err_disable_cap_dev; + + if (mali_c55_pipeline_ready(mali_c55)) { + ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + if (ret < 0) + goto err_disable_rsz; + } + + return 0; + +err_disable_rsz: + v4l2_subdev_disable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD, BIT(0)); +err_disable_cap_dev: + mali_c55_cap_dev_stream_disable(cap_dev); + video_device_pipeline_stop(&cap_dev->vdev); +err_pm_put: + pm_runtime_put_autosuspend(mali_c55->dev); +err_return_buffers: + mali_c55_cap_dev_return_buffers(cap_dev, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void mali_c55_vb2_stop_streaming(struct vb2_queue *q) +{ + struct mali_c55_cap_dev *cap_dev = q->drv_priv; + struct mali_c55 *mali_c55 = cap_dev->mali_c55; + struct mali_c55_resizer *rsz = cap_dev->rsz; + struct mali_c55_isp *isp = &mali_c55->isp; + + guard(mutex)(&isp->capture_lock); + + if (mali_c55_pipeline_ready(mali_c55)) { + if (v4l2_subdev_is_streaming(&isp->sd)) + v4l2_subdev_disable_streams(&isp->sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + } + + v4l2_subdev_disable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD, BIT(0)); + mali_c55_cap_dev_stream_disable(cap_dev); + mali_c55_cap_dev_return_buffers(cap_dev, VB2_BUF_STATE_ERROR); + video_device_pipeline_stop(&cap_dev->vdev); + pm_runtime_put_autosuspend(mali_c55->dev); +} + +static const struct vb2_ops mali_c55_vb2_ops = { + .queue_setup = &mali_c55_vb2_queue_setup, + .buf_queue = &mali_c55_buf_queue, + .buf_init = &mali_c55_buf_init, + .start_streaming = &mali_c55_vb2_start_streaming, + .stop_streaming = &mali_c55_vb2_stop_streaming, +}; + +static const struct v4l2_file_operations mali_c55_v4l2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static void mali_c55_try_fmt(struct v4l2_pix_format_mplane *pix_mp) +{ + const struct mali_c55_format_info *capture_format; + const struct v4l2_format_info *info; + struct v4l2_plane_pix_format *plane, *y_plane; + unsigned int padding; + unsigned int i; + + capture_format = mali_c55_format_from_pix(pix_mp->pixelformat); + pix_mp->pixelformat = capture_format->fourcc; + + pix_mp->width = clamp(pix_mp->width, MALI_C55_MIN_WIDTH, + MALI_C55_MAX_WIDTH); + pix_mp->height = clamp(pix_mp->height, MALI_C55_MIN_HEIGHT, + MALI_C55_MAX_HEIGHT); + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->colorspace = V4L2_COLORSPACE_DEFAULT; + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + + info = v4l2_format_info(pix_mp->pixelformat); + pix_mp->num_planes = info->mem_planes; + memset(pix_mp->plane_fmt, 0, sizeof(pix_mp->plane_fmt)); + + y_plane = &pix_mp->plane_fmt[0]; + y_plane->bytesperline = clamp(y_plane->bytesperline, + info->bpp[0] * pix_mp->width, 65535U); + + /* + * The ISP requires that the stride be aligned to 16-bytes. This is not + * detailed in the documentation but has been verified experimentally. + */ + y_plane->bytesperline = ALIGN(y_plane->bytesperline, 16); + y_plane->sizeimage = y_plane->bytesperline * pix_mp->height; + + padding = y_plane->bytesperline - (pix_mp->width * info->bpp[0]); + + for (i = 1; i < info->comp_planes; i++) { + plane = &pix_mp->plane_fmt[i]; + + plane->bytesperline = DIV_ROUND_UP(info->bpp[i] * pix_mp->width, + info->hdiv) + padding; + plane->sizeimage = DIV_ROUND_UP(plane->bytesperline * + pix_mp->height, info->vdiv); + } + + if (info->mem_planes == 1) { + for (i = 1; i < info->comp_planes; i++) { + plane = &pix_mp->plane_fmt[i]; + y_plane->sizeimage += plane->sizeimage; + } + } +} + +static int mali_c55_try_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + mali_c55_try_fmt(&f->fmt.pix_mp); + + return 0; +} + +static void mali_c55_set_format(struct mali_c55_cap_dev *cap_dev, + struct v4l2_pix_format_mplane *pix_mp) +{ + mali_c55_try_fmt(pix_mp); + + cap_dev->format.format = *pix_mp; + cap_dev->format.info = mali_c55_format_from_pix(pix_mp->pixelformat); +} + +static int mali_c55_s_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mali_c55_cap_dev *cap_dev = video_drvdata(file); + + if (vb2_is_busy(&cap_dev->queue)) + return -EBUSY; + + mali_c55_set_format(cap_dev, &f->fmt.pix_mp); + + return 0; +} + +static int mali_c55_g_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mali_c55_cap_dev *cap_dev = video_drvdata(file); + + f->fmt.pix_mp = cap_dev->format.format; + + return 0; +} + +static int mali_c55_enum_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct mali_c55_cap_dev *cap_dev = video_drvdata(file); + unsigned int j = 0; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) { + if (f->mbus_code && + !mali_c55_mbus_code_can_produce_fmt(&mali_c55_fmts[i], + f->mbus_code)) + continue; + + /* Downscale pipe can't output RAW formats */ + if (mali_c55_fmts[i].is_raw && + cap_dev->reg_offset == MALI_C55_CAP_DEV_DS_REG_OFFSET) + continue; + + if (j++ == f->index) { + f->pixelformat = mali_c55_fmts[i].fourcc; + return 0; + } + } + + return -EINVAL; +} + +static int mali_c55_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops mali_c55_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_try_fmt_vid_cap_mplane = mali_c55_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = mali_c55_s_fmt_vid_cap_mplane, + .vidioc_g_fmt_vid_cap_mplane = mali_c55_g_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_cap = mali_c55_enum_fmt_vid_cap_mplane, + .vidioc_querycap = mali_c55_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int mali_c55_register_cap_dev(struct mali_c55 *mali_c55, + enum mali_c55_cap_devs cap_dev_id) +{ + struct mali_c55_cap_dev *cap_dev = &mali_c55->cap_devs[cap_dev_id]; + struct v4l2_pix_format_mplane pix_mp; + struct video_device *vdev; + struct vb2_queue *vb2q; + int ret; + + vdev = &cap_dev->vdev; + vb2q = &cap_dev->queue; + + cap_dev->mali_c55 = mali_c55; + mutex_init(&cap_dev->lock); + INIT_LIST_HEAD(&cap_dev->buffers.input); + INIT_LIST_HEAD(&cap_dev->buffers.processing); + spin_lock_init(&cap_dev->buffers.lock); + spin_lock_init(&cap_dev->buffers.processing_lock); + + switch (cap_dev_id) { + case MALI_C55_CAP_DEV_FR: + cap_dev->rsz = &mali_c55->resizers[MALI_C55_RSZ_FR]; + cap_dev->reg_offset = MALI_C55_CAP_DEV_FR_REG_OFFSET; + break; + case MALI_C55_CAP_DEV_DS: + cap_dev->rsz = &mali_c55->resizers[MALI_C55_RSZ_DS]; + cap_dev->reg_offset = MALI_C55_CAP_DEV_DS_REG_OFFSET; + break; + default: + ret = -EINVAL; + goto err_destroy_mutex; + } + + cap_dev->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&cap_dev->vdev.entity, 1, &cap_dev->pad); + if (ret) { + mutex_destroy(&cap_dev->lock); + goto err_destroy_mutex; + } + + vb2q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vb2q->io_modes = VB2_MMAP | VB2_DMABUF; + vb2q->drv_priv = cap_dev; + vb2q->mem_ops = &vb2_dma_contig_memops; + vb2q->ops = &mali_c55_vb2_ops; + vb2q->buf_struct_size = sizeof(struct mali_c55_buffer); + vb2q->min_queued_buffers = 1; + vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2q->lock = &cap_dev->lock; + vb2q->dev = mali_c55->dev; + + ret = vb2_queue_init(vb2q); + if (ret) { + dev_err(mali_c55->dev, "%s vb2 queue init failed\n", + cap_dev->vdev.name); + goto err_cleanup_media_entity; + } + + strscpy(cap_dev->vdev.name, capture_device_names[cap_dev_id], + sizeof(cap_dev->vdev.name)); + vdev->release = video_device_release_empty; + vdev->fops = &mali_c55_v4l2_fops; + vdev->ioctl_ops = &mali_c55_v4l2_ioctl_ops; + vdev->lock = &cap_dev->lock; + vdev->v4l2_dev = &mali_c55->v4l2_dev; + vdev->queue = &cap_dev->queue; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &mali_c55_media_ops; + video_set_drvdata(vdev, cap_dev); + + memset(&pix_mp, 0, sizeof(pix_mp)); + pix_mp.pixelformat = V4L2_PIX_FMT_RGB565; + pix_mp.width = MALI_C55_DEFAULT_WIDTH; + pix_mp.height = MALI_C55_DEFAULT_HEIGHT; + mali_c55_set_format(cap_dev, &pix_mp); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(mali_c55->dev, + "%s failed to register video device\n", + cap_dev->vdev.name); + goto err_release_vb2q; + } + + return 0; + +err_release_vb2q: + vb2_queue_release(vb2q); +err_cleanup_media_entity: + media_entity_cleanup(&cap_dev->vdev.entity); +err_destroy_mutex: + mutex_destroy(&cap_dev->lock); + + return ret; +} + +int mali_c55_register_capture_devs(struct mali_c55 *mali_c55) +{ + int ret; + + ret = mali_c55_register_cap_dev(mali_c55, MALI_C55_CAP_DEV_FR); + if (ret) + return ret; + + if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) { + ret = mali_c55_register_cap_dev(mali_c55, MALI_C55_CAP_DEV_DS); + if (ret) { + mali_c55_unregister_capture_devs(mali_c55); + return ret; + } + } + + return 0; +} + +static void mali_c55_unregister_cap_dev(struct mali_c55 *mali_c55, + enum mali_c55_cap_devs cap_dev_id) +{ + struct mali_c55_cap_dev *cap_dev = &mali_c55->cap_devs[cap_dev_id]; + + if (!video_is_registered(&cap_dev->vdev)) + return; + + vb2_video_unregister_device(&cap_dev->vdev); + media_entity_cleanup(&cap_dev->vdev.entity); + mutex_destroy(&cap_dev->lock); +} + +void mali_c55_unregister_capture_devs(struct mali_c55 *mali_c55) +{ + mali_c55_unregister_cap_dev(mali_c55, MALI_C55_CAP_DEV_FR); + if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) + mali_c55_unregister_cap_dev(mali_c55, MALI_C55_CAP_DEV_DS); +} diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-common.h b/drivers/media/platform/arm/mali-c55/mali-c55-common.h new file mode 100644 index 000000000000..31c1deaca146 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-common.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ARM Mali-C55 ISP Driver - Common definitions + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#ifndef _MALI_C55_COMMON_H +#define _MALI_C55_COMMON_H + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/reset.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-isp.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + +#define MALI_C55_DRIVER_NAME "mali-c55" + +/* min and max values for the image sizes */ +#define MALI_C55_MIN_WIDTH 640U +#define MALI_C55_MIN_HEIGHT 480U +#define MALI_C55_MAX_WIDTH 8192U +#define MALI_C55_MAX_HEIGHT 8192U +#define MALI_C55_DEFAULT_WIDTH 1920U +#define MALI_C55_DEFAULT_HEIGHT 1080U + +#define MALI_C55_DEFAULT_MEDIA_BUS_FMT MEDIA_BUS_FMT_RGB121212_1X36 + +#define MALI_C55_NUM_CLKS 3 +#define MALI_C55_NUM_RESETS 3 + +struct device; +struct mali_c55; +struct mali_c55_cap_dev; +struct media_pipeline; +struct mali_c55_params_buffer; +struct platform_device; +struct resource; + +enum mali_c55_isp_pads { + MALI_C55_ISP_PAD_SINK_VIDEO, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + MALI_C55_ISP_PAD_SOURCE_BYPASS, + MALI_C55_ISP_PAD_SOURCE_STATS, + MALI_C55_ISP_PAD_SINK_PARAMS, + MALI_C55_ISP_NUM_PADS, +}; + +struct mali_c55_tpg { + struct mali_c55 *mali_c55; + struct v4l2_subdev sd; + struct media_pad pad; + struct mali_c55_tpg_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *vblank; + } ctrls; +}; + +struct mali_c55_isp { + struct mali_c55 *mali_c55; + struct v4l2_subdev sd; + struct media_pad pads[MALI_C55_ISP_NUM_PADS]; + struct v4l2_ctrl_handler handler; + struct media_pad *remote_src; + /* Mutex to guard vb2 start/stop streaming */ + struct mutex capture_lock; + unsigned int frame_sequence; +}; + +enum mali_c55_resizer_ids { + MALI_C55_RSZ_FR, + MALI_C55_RSZ_DS, + MALI_C55_NUM_RSZS, +}; + +enum mali_c55_rsz_pads { + MALI_C55_RSZ_SINK_PAD, + MALI_C55_RSZ_SOURCE_PAD, + MALI_C55_RSZ_SINK_BYPASS_PAD, + MALI_C55_RSZ_NUM_PADS +}; + +struct mali_c55_resizer { + struct mali_c55 *mali_c55; + struct mali_c55_cap_dev *cap_dev; + enum mali_c55_resizer_ids id; + struct v4l2_subdev sd; + struct media_pad pads[MALI_C55_RSZ_NUM_PADS]; + unsigned int num_routes; +}; + +enum mali_c55_cap_devs { + MALI_C55_CAP_DEV_FR, + MALI_C55_CAP_DEV_DS, + MALI_C55_NUM_CAP_DEVS +}; + +struct mali_c55_format_info { + u32 fourcc; + /* + * The output formats can be produced by a couple of different media bus + * formats, depending on how the ISP is configured. + */ + unsigned int mbus_codes[2]; + bool is_raw; + struct { + u32 base_mode; + u32 uv_plane; + } registers; +}; + +struct mali_c55_isp_format_info { + u32 code; + u32 shifted_code; + bool bypass; + u32 order; +}; + +enum mali_c55_planes { + MALI_C55_PLANE_Y, + MALI_C55_PLANE_UV, + MALI_C55_NUM_PLANES +}; + +struct mali_c55_buffer { + struct vb2_v4l2_buffer vb; + unsigned int planes_pending; + struct list_head queue; + dma_addr_t addrs[MALI_C55_NUM_PLANES]; +}; + +struct mali_c55_cap_dev { + struct mali_c55 *mali_c55; + struct mali_c55_resizer *rsz; + struct video_device vdev; + struct media_pad pad; + struct vb2_queue queue; + /* Mutex to provide to vb2 */ + struct mutex lock; + unsigned int reg_offset; + + struct { + const struct mali_c55_format_info *info; + struct v4l2_pix_format_mplane format; + } format; + + struct { + /* Spinlock to guard buffer queue */ + spinlock_t lock; + /* Spinlock to guard the queue of buffers being processed */ + spinlock_t processing_lock; + struct list_head input; + struct list_head processing; + } buffers; +}; + +struct mali_c55_stats_buf { + struct vb2_v4l2_buffer vb; + unsigned int segments_remaining; + struct list_head queue; + bool failed; +}; + +struct mali_c55_params_buf { + struct vb2_v4l2_buffer vb; + struct list_head queue; + struct v4l2_isp_params_buffer *config; +}; + +struct mali_c55_stats { + struct mali_c55 *mali_c55; + struct video_device vdev; + struct vb2_queue queue; + struct media_pad pad; + /* Mutex to provide to vb2 */ + struct mutex lock; + + struct { + /* Spinlock to guard buffer queue */ + spinlock_t lock; + struct list_head queue; + } buffers; +}; + +struct mali_c55_params { + struct mali_c55 *mali_c55; + struct video_device vdev; + struct vb2_queue queue; + struct media_pad pad; + /* Mutex to provide to vb2 */ + struct mutex lock; + + struct { + /* Spinlock to guard buffer queue */ + spinlock_t lock; + struct list_head queue; + } buffers; +}; + +enum mali_c55_config_spaces { + MALI_C55_CONFIG_PONG, + MALI_C55_CONFIG_PING, +}; + +/** + * struct mali_c55_context - Fields relating to a single camera context + * + * @mali_c55: Pointer to the main struct mali_c55 + * @registers: A pointer to some allocated memory holding register + * values to be written to the hardware at frame interrupt + * @base: Base address of the config space in the hardware + * @lock: A spinlock to protect against writes to @registers whilst that + * space is being copied to the hardware + * @list: A list head to facilitate a context queue + */ +struct mali_c55_context { + struct mali_c55 *mali_c55; + u32 *registers; + phys_addr_t base; + /* Spinlock to prevent simultaneous access of register space */ + spinlock_t lock; + struct list_head list; +}; + +struct mali_c55 { + struct device *dev; + void __iomem *base; + struct clk_bulk_data clks[MALI_C55_NUM_CLKS]; + struct reset_control_bulk_data resets[MALI_C55_NUM_RESETS]; + int irqnum; + + u16 capabilities; + bool inline_mode; + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + struct media_pipeline pipe; + + struct mali_c55_tpg tpg; + struct mali_c55_isp isp; + struct mali_c55_resizer resizers[MALI_C55_NUM_RSZS]; + struct mali_c55_cap_dev cap_devs[MALI_C55_NUM_CAP_DEVS]; + struct mali_c55_params params; + struct mali_c55_stats stats; + + struct mali_c55_context context; + u32 next_config; +}; + +void mali_c55_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val); +void mali_c55_cap_dev_write(struct mali_c55_cap_dev *cap_dev, unsigned int addr, + u32 val); +void mali_c55_update_bits(struct mali_c55 *mali_c55, unsigned int addr, + u32 mask, u32 val); +u32 mali_c55_read(struct mali_c55 *mali_c55, unsigned int addr); +void mali_c55_ctx_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val); +u32 mali_c55_ctx_read(struct mali_c55 *mali_c55, unsigned int addr); +void mali_c55_ctx_update_bits(struct mali_c55 *mali_c55, unsigned int addr, + u32 mask, u32 val); + +int mali_c55_config_write(struct mali_c55_context *ctx, + enum mali_c55_config_spaces cfg_space, + bool force_synchronous); + +int mali_c55_register_isp(struct mali_c55 *mali_c55); +int mali_c55_register_tpg(struct mali_c55 *mali_c55); +void mali_c55_unregister_tpg(struct mali_c55 *mali_c55); +void mali_c55_unregister_isp(struct mali_c55 *mali_c55); +int mali_c55_register_resizers(struct mali_c55 *mali_c55); +void mali_c55_unregister_resizers(struct mali_c55 *mali_c55); +int mali_c55_register_capture_devs(struct mali_c55 *mali_c55); +void mali_c55_unregister_capture_devs(struct mali_c55 *mali_c55); +int mali_c55_register_stats(struct mali_c55 *mali_c55); +void mali_c55_unregister_stats(struct mali_c55 *mali_c55); +int mali_c55_register_params(struct mali_c55 *mali_c55); +void mali_c55_unregister_params(struct mali_c55 *mali_c55); +struct mali_c55_context *mali_c55_get_active_context(struct mali_c55 *mali_c55); +void mali_c55_set_plane_done(struct mali_c55_cap_dev *cap_dev, + enum mali_c55_planes plane); +void mali_c55_set_next_buffer(struct mali_c55_cap_dev *cap_dev); +void mali_c55_isp_queue_event_sof(struct mali_c55 *mali_c55); + +bool mali_c55_format_is_raw(unsigned int mbus_code); + +const struct mali_c55_isp_format_info * +mali_c55_isp_fmt_next(const struct mali_c55_isp_format_info *fmt); +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_code(u32 code); +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_shifted_code(u32 code); +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_index(u32 index); +bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55); +void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55, + enum mali_c55_config_spaces cfg_space); +void mali_c55_params_write_config(struct mali_c55 *mali_c55); + +#endif /* _MALI_C55_COMMON_H */ diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c new file mode 100644 index 000000000000..43b834459ccf --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -0,0 +1,917 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Core driver code + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +static const char * const mali_c55_interrupt_names[] = { + [MALI_C55_IRQ_ISP_START] = "ISP start", + [MALI_C55_IRQ_ISP_DONE] = "ISP done", + [MALI_C55_IRQ_MCM_ERROR] = "Multi-context management error", + [MALI_C55_IRQ_BROKEN_FRAME_ERROR] = "Broken frame error", + [MALI_C55_IRQ_MET_AF_DONE] = "AF metering done", + [MALI_C55_IRQ_MET_AEXP_DONE] = "AEXP metering done", + [MALI_C55_IRQ_MET_AWB_DONE] = "AWB metering done", + [MALI_C55_IRQ_AEXP_1024_DONE] = "AEXP 1024-bit histogram done", + [MALI_C55_IRQ_IRIDIX_MET_DONE] = "Iridix metering done", + [MALI_C55_IRQ_LUT_INIT_DONE] = "LUT memory init done", + [MALI_C55_IRQ_FR_Y_DONE] = "Full resolution Y plane DMA done", + [MALI_C55_IRQ_FR_UV_DONE] = "Full resolution U/V plane DMA done", + [MALI_C55_IRQ_DS_Y_DONE] = "Downscale Y plane DMA done", + [MALI_C55_IRQ_DS_UV_DONE] = "Downscale U/V plane DMA done", + [MALI_C55_IRQ_LINEARIZATION_DONE] = "Linearisation done", + [MALI_C55_IRQ_RAW_FRONTEND_DONE] = "Raw frontend processing done", + [MALI_C55_IRQ_NOISE_REDUCTION_DONE] = "Noise reduction done", + [MALI_C55_IRQ_IRIDIX_DONE] = "Iridix done", + [MALI_C55_IRQ_BAYER2RGB_DONE] = "Bayer to RGB conversion done", + [MALI_C55_IRQ_WATCHDOG_TIMER] = "Watchdog timer timed out", + [MALI_C55_IRQ_FRAME_COLLISION] = "Frame collision error", + [MALI_C55_IRQ_UNUSED] = "IRQ bit unused", + [MALI_C55_IRQ_DMA_ERROR] = "DMA error", + [MALI_C55_IRQ_INPUT_STOPPED] = "Input port safely stopped", + [MALI_C55_IRQ_MET_AWB_TARGET1_HIT] = "AWB metering target 1 address hit", + [MALI_C55_IRQ_MET_AWB_TARGET2_HIT] = "AWB metering target 2 address hit" +}; + +static const unsigned int config_space_addrs[] = { + [MALI_C55_CONFIG_PING] = 0x0ab6c, + [MALI_C55_CONFIG_PONG] = 0x22b2c, +}; + +static const char * const mali_c55_clk_names[MALI_C55_NUM_CLKS] = { + "vclk", + "aclk", + "hclk", +}; + +static const char * const mali_c55_reset_names[MALI_C55_NUM_RESETS] = { + "vresetn", + "aresetn", + "hresetn", +}; + +/* + * System IO + * + * The Mali-C55 ISP has up to two configuration register spaces (called 'ping' + * and 'pong'), with the expectation that the 'active' space will be left + * untouched whilst a frame is being processed and the 'inactive' space + * configured ready to be switched to during the blanking period before the next + * frame processing starts. These spaces should ideally be set via DMA transfer + * from a buffer rather than through individual register set operations. There + * is also a shared global register space which should be set normally. For now + * though we will simply use a CPU write and target DMA transfers of the config + * space in the future. + * + * As groundwork for that path any read/write call that is made to an address + * within those config spaces should infact be directed to a buffer that was + * allocated to hold them rather than the IO memory itself. The actual copy of + * that buffer to IO mem will happen on interrupt. + */ + +void mali_c55_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val) +{ + WARN_ON(addr >= MALI_C55_REG_CONFIG_SPACES_OFFSET); + + writel(val, mali_c55->base + addr); +} + +u32 mali_c55_read(struct mali_c55 *mali_c55, unsigned int addr) +{ + WARN_ON(addr >= MALI_C55_REG_CONFIG_SPACES_OFFSET); + + return readl(mali_c55->base + addr); +} + +void mali_c55_update_bits(struct mali_c55 *mali_c55, unsigned int addr, + u32 mask, u32 val) +{ + u32 orig, new; + + orig = mali_c55_read(mali_c55, addr); + + new = orig & ~mask; + new |= val & mask; + + if (new != orig) + mali_c55_write(mali_c55, addr, new); +} + +static void __mali_c55_ctx_write(struct mali_c55_context *ctx, + unsigned int addr, u32 val) +{ + addr = (addr - MALI_C55_REG_CONFIG_SPACES_OFFSET) / 4; + ctx->registers[addr] = val; +} + +void mali_c55_ctx_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val) +{ + struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); + + WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET); + + spin_lock(&ctx->lock); + __mali_c55_ctx_write(ctx, addr, val); + spin_unlock(&ctx->lock); +} + +static u32 __mali_c55_ctx_read(struct mali_c55_context *ctx, unsigned int addr) +{ + addr = (addr - MALI_C55_REG_CONFIG_SPACES_OFFSET) / 4; + return ctx->registers[addr]; +} + +u32 mali_c55_ctx_read(struct mali_c55 *mali_c55, unsigned int addr) +{ + struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); + u32 val; + + WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET); + + spin_lock(&ctx->lock); + val = __mali_c55_ctx_read(ctx, addr); + spin_unlock(&ctx->lock); + + return val; +} + +void mali_c55_ctx_update_bits(struct mali_c55 *mali_c55, unsigned int addr, + u32 mask, u32 val) +{ + struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); + u32 orig, tmp; + + WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET); + + spin_lock(&ctx->lock); + + orig = __mali_c55_ctx_read(ctx, addr); + + tmp = orig & ~mask; + tmp |= val & mask; + + if (tmp != orig) + __mali_c55_ctx_write(ctx, addr, tmp); + + spin_unlock(&ctx->lock); +} + +int mali_c55_config_write(struct mali_c55_context *ctx, + enum mali_c55_config_spaces cfg_space, + bool force_synchronous) +{ + struct mali_c55 *mali_c55 = ctx->mali_c55; + + memcpy_toio(mali_c55->base + config_space_addrs[cfg_space], + ctx->registers, MALI_C55_CONFIG_SPACE_SIZE); + + return 0; +} + +struct mali_c55_context *mali_c55_get_active_context(struct mali_c55 *mali_c55) +{ + return &mali_c55->context; +} + +static void mali_c55_remove_links(struct mali_c55 *mali_c55) +{ + unsigned int i; + + media_entity_remove_links(&mali_c55->tpg.sd.entity); + media_entity_remove_links(&mali_c55->isp.sd.entity); + + for (i = 0; i < MALI_C55_NUM_RSZS; i++) + media_entity_remove_links(&mali_c55->resizers[i].sd.entity); + + for (i = 0; i < MALI_C55_NUM_CAP_DEVS; i++) + media_entity_remove_links(&mali_c55->cap_devs[i].vdev.entity); +} + +static int mali_c55_create_links(struct mali_c55 *mali_c55) +{ + int ret; + + /* Test pattern generator to ISP */ + ret = media_create_pad_link(&mali_c55->tpg.sd.entity, 0, + &mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SINK_VIDEO, 0); + if (ret) { + dev_err(mali_c55->dev, "failed to link TPG and ISP\n"); + goto err_remove_links; + } + + /* Full resolution resizer pipe. */ + ret = media_create_pad_link(&mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + &mali_c55->resizers[MALI_C55_RSZ_FR].sd.entity, + MALI_C55_RSZ_SINK_PAD, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(mali_c55->dev, "failed to link ISP and FR resizer\n"); + goto err_remove_links; + } + + /* Full resolution bypass. */ + ret = media_create_pad_link(&mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SOURCE_BYPASS, + &mali_c55->resizers[MALI_C55_RSZ_FR].sd.entity, + MALI_C55_RSZ_SINK_BYPASS_PAD, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(mali_c55->dev, "failed to link ISP and FR resizer\n"); + goto err_remove_links; + } + + /* Resizer pipe to video capture nodes. */ + ret = media_create_pad_link(&mali_c55->resizers[0].sd.entity, + MALI_C55_RSZ_SOURCE_PAD, + &mali_c55->cap_devs[MALI_C55_CAP_DEV_FR].vdev.entity, + 0, MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(mali_c55->dev, + "failed to link FR resizer and video device\n"); + goto err_remove_links; + } + + /* The downscale pipe is an optional hardware block */ + if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) { + ret = media_create_pad_link(&mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + &mali_c55->resizers[MALI_C55_RSZ_DS].sd.entity, + MALI_C55_RSZ_SINK_PAD, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(mali_c55->dev, + "failed to link ISP and DS resizer\n"); + goto err_remove_links; + } + + ret = media_create_pad_link(&mali_c55->resizers[1].sd.entity, + MALI_C55_RSZ_SOURCE_PAD, + &mali_c55->cap_devs[MALI_C55_CAP_DEV_DS].vdev.entity, + 0, MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(mali_c55->dev, + "failed to link DS resizer and video device\n"); + goto err_remove_links; + } + } + + ret = media_create_pad_link(&mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SOURCE_STATS, + &mali_c55->stats.vdev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(mali_c55->dev, + "failed to link ISP and 3a stats node\n"); + goto err_remove_links; + } + + ret = media_create_pad_link(&mali_c55->params.vdev.entity, 0, + &mali_c55->isp.sd.entity, + MALI_C55_ISP_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(mali_c55->dev, + "failed to link ISP and parameters video node\n"); + goto err_remove_links; + } + + return 0; + +err_remove_links: + mali_c55_remove_links(mali_c55); + return ret; +} + +static void mali_c55_unregister_entities(struct mali_c55 *mali_c55) +{ + mali_c55_remove_links(mali_c55); + mali_c55_unregister_tpg(mali_c55); + mali_c55_unregister_isp(mali_c55); + mali_c55_unregister_resizers(mali_c55); + mali_c55_unregister_capture_devs(mali_c55); + mali_c55_unregister_params(mali_c55); + mali_c55_unregister_stats(mali_c55); +} + +static void mali_c55_swap_next_config(struct mali_c55 *mali_c55) +{ + struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); + + mali_c55_config_write(ctx, mali_c55->next_config ? + MALI_C55_CONFIG_PING : MALI_C55_CONFIG_PONG, + false); + + mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG, + MALI_C55_REG_MCU_CONFIG_WRITE_MASK, + MALI_C55_MCU_CONFIG_WRITE(mali_c55->next_config)); +} + +static int mali_c55_register_entities(struct mali_c55 *mali_c55) +{ + int ret; + + ret = mali_c55_register_tpg(mali_c55); + if (ret) + return ret; + + ret = mali_c55_register_isp(mali_c55); + if (ret) + goto err_unregister_entities; + + ret = mali_c55_register_resizers(mali_c55); + if (ret) + goto err_unregister_entities; + + ret = mali_c55_register_capture_devs(mali_c55); + if (ret) + goto err_unregister_entities; + + ret = mali_c55_register_params(mali_c55); + if (ret) + goto err_unregister_entities; + + ret = mali_c55_register_stats(mali_c55); + if (ret) + goto err_unregister_entities; + + ret = mali_c55_create_links(mali_c55); + if (ret) + goto err_unregister_entities; + + return 0; + +err_unregister_entities: + mali_c55_unregister_entities(mali_c55); + + return ret; +} + +static int mali_c55_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct mali_c55 *mali_c55 = container_of(notifier, + struct mali_c55, notifier); + struct media_pad *pad = &mali_c55->isp.pads[MALI_C55_ISP_PAD_SINK_VIDEO]; + int ret; + + /* + * By default we'll flag this link enabled and the TPG disabled, but + * no immutable flag because we need to be able to switch between the + * two. + */ + ret = v4l2_create_fwnode_links_to_pad(subdev, pad, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(mali_c55->dev, "failed to create link for %s\n", + subdev->name); + + return ret; +} + +static int mali_c55_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct mali_c55 *mali_c55 = container_of(notifier, + struct mali_c55, notifier); + + return v4l2_device_register_subdev_nodes(&mali_c55->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations mali_c55_notifier_ops = { + .bound = mali_c55_notifier_bound, + .complete = mali_c55_notifier_complete, +}; + +static int mali_c55_parse_endpoint(struct mali_c55 *mali_c55) +{ + struct v4l2_async_connection *asc; + struct fwnode_handle *ep; + + /* + * The ISP should have a single endpoint pointing to some flavour of + * CSI-2 receiver...but for now at least we do want everything to work + * normally even with no sensors connected, as we have the TPG. If we + * don't find a sensor just warn and return success. + */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(mali_c55->dev), + 0, 0, 0); + if (!ep) { + dev_warn(mali_c55->dev, "no local endpoint found\n"); + return 0; + } + + asc = v4l2_async_nf_add_fwnode_remote(&mali_c55->notifier, ep, + struct v4l2_async_connection); + fwnode_handle_put(ep); + if (IS_ERR(asc)) { + dev_err(mali_c55->dev, "failed to add remote fwnode\n"); + return PTR_ERR(asc); + } + + return 0; +} + +static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55) +{ + int ret; + + strscpy(mali_c55->media_dev.model, "ARM Mali-C55 ISP", + sizeof(mali_c55->media_dev.model)); + + media_device_init(&mali_c55->media_dev); + + ret = media_device_register(&mali_c55->media_dev); + if (ret) + goto err_cleanup_media_device; + + mali_c55->v4l2_dev.mdev = &mali_c55->media_dev; + ret = v4l2_device_register(mali_c55->dev, &mali_c55->v4l2_dev); + if (ret) { + dev_err(mali_c55->dev, "failed to register V4L2 device\n"); + goto err_unregister_media_device; + }; + + mali_c55->notifier.ops = &mali_c55_notifier_ops; + v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev); + + ret = mali_c55_register_entities(mali_c55); + if (ret) { + dev_err(mali_c55->dev, "failed to register entities\n"); + goto err_cleanup_nf; + } + + ret = mali_c55_parse_endpoint(mali_c55); + if (ret) + goto err_cleanup_nf; + + ret = v4l2_async_nf_register(&mali_c55->notifier); + if (ret) { + dev_err(mali_c55->dev, "failed to register notifier\n"); + goto err_unregister_entities; + } + + return 0; + +err_unregister_entities: + mali_c55_unregister_entities(mali_c55); +err_cleanup_nf: + v4l2_async_nf_cleanup(&mali_c55->notifier); + v4l2_device_unregister(&mali_c55->v4l2_dev); +err_unregister_media_device: + media_device_unregister(&mali_c55->media_dev); +err_cleanup_media_device: + media_device_cleanup(&mali_c55->media_dev); + + return ret; +} + +static void mali_c55_media_frameworks_deinit(struct mali_c55 *mali_c55) +{ + v4l2_async_nf_unregister(&mali_c55->notifier); + mali_c55_unregister_entities(mali_c55); + v4l2_async_nf_cleanup(&mali_c55->notifier); + v4l2_device_unregister(&mali_c55->v4l2_dev); + media_device_unregister(&mali_c55->media_dev); + media_device_cleanup(&mali_c55->media_dev); +} + +bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55) +{ + struct mali_c55_cap_dev *fr = &mali_c55->cap_devs[MALI_C55_CAP_DEV_FR]; + struct mali_c55_cap_dev *ds = &mali_c55->cap_devs[MALI_C55_CAP_DEV_DS]; + struct mali_c55_params *params = &mali_c55->params; + struct mali_c55_stats *stats = &mali_c55->stats; + + return vb2_start_streaming_called(&fr->queue) && + (!(mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) || + vb2_start_streaming_called(&ds->queue)) && + vb2_start_streaming_called(¶ms->queue) && + vb2_start_streaming_called(&stats->queue); +} + +static int mali_c55_check_hwcfg(struct mali_c55 *mali_c55) +{ + u32 product, version, revision, capabilities; + + product = mali_c55_read(mali_c55, MALI_C55_REG_PRODUCT); + version = mali_c55_read(mali_c55, MALI_C55_REG_VERSION); + revision = mali_c55_read(mali_c55, MALI_C55_REG_REVISION); + + mali_c55->media_dev.hw_revision = version; + + dev_info(mali_c55->dev, "Detected Mali-C55 ISP %u.%u.%u\n", + product, version, revision); + + capabilities = mali_c55_read(mali_c55, + MALI_C55_REG_GLOBAL_PARAMETER_STATUS); + + /* + * In its current iteration, the driver only supports inline mode. Given + * we cannot control input data timing in this mode, we cannot guarantee + * that the vertical blanking periods between frames will be long enough + * for us to write configuration data to the ISP during them. For that + * reason we can't really support single config space configuration + * until memory input mode is implemented. + */ + if (!(capabilities & MALI_C55_GPS_PONG_FITTED)) { + dev_err(mali_c55->dev, "Pong config space not fitted.\n"); + return -EINVAL; + } + + mali_c55->capabilities = capabilities & 0xffff; + + return 0; +} + +static irqreturn_t mali_c55_isr(int irq, void *context) +{ + struct device *dev = context; + struct mali_c55 *mali_c55 = dev_get_drvdata(dev); + unsigned long interrupt_status; + u32 curr_config; + unsigned int i; + + interrupt_status = mali_c55_read(mali_c55, + MALI_C55_REG_INTERRUPT_STATUS_VECTOR); + if (!interrupt_status) + return IRQ_NONE; + + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR_VECTOR, + interrupt_status); + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 1); + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0); + + for_each_set_bit(i, &interrupt_status, MALI_C55_NUM_IRQ_BITS) { + switch (i) { + case MALI_C55_IRQ_ISP_START: + mali_c55_isp_queue_event_sof(mali_c55); + + mali_c55_set_next_buffer(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR]); + if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) + mali_c55_set_next_buffer(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS]); + + /* + * When the ISP starts a frame we have some work to do: + * + * 1. Copy over the config for the **next** frame + * 2. Read out the metering stats for the **last** frame + */ + + curr_config = mali_c55_read(mali_c55, + MALI_C55_REG_PING_PONG_READ); + curr_config &= MALI_C55_REG_PING_PONG_READ_MASK; + curr_config >>= ffs(MALI_C55_REG_PING_PONG_READ_MASK) - 1; + mali_c55->next_config = curr_config ^ 1; + + /* + * Write the configuration parameters received from + * userspace into the configuration buffer, which will + * be transferred to the 'next' active config space at + * by mali_c55_swap_next_config(). + */ + mali_c55_params_write_config(mali_c55); + + mali_c55_stats_fill_buffer(mali_c55, + mali_c55->next_config ^ 1); + + mali_c55_swap_next_config(mali_c55); + + break; + case MALI_C55_IRQ_ISP_DONE: + /* + * TODO: Where the ISP has no Pong config fitted, we'd + * have to do the mali_c55_swap_next_config() call here. + */ + break; + case MALI_C55_IRQ_FR_Y_DONE: + mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR], + MALI_C55_PLANE_Y); + break; + case MALI_C55_IRQ_FR_UV_DONE: + mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR], + MALI_C55_PLANE_UV); + break; + case MALI_C55_IRQ_DS_Y_DONE: + mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS], + MALI_C55_PLANE_Y); + break; + case MALI_C55_IRQ_DS_UV_DONE: + mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS], + MALI_C55_PLANE_UV); + break; + default: + /* + * Only the above interrupts are currently unmasked. If + * we receive anything else here then something weird + * has gone on. + */ + dev_err(dev, "masked interrupt %s triggered\n", + mali_c55_interrupt_names[i]); + } + } + + return IRQ_HANDLED; +} + +static int mali_c55_init_context(struct mali_c55 *mali_c55, + struct resource *res) +{ + struct mali_c55_context *ctx = &mali_c55->context; + + ctx->base = res->start; + ctx->mali_c55 = mali_c55; + spin_lock_init(&ctx->lock); + + ctx->registers = kzalloc(MALI_C55_CONFIG_SPACE_SIZE, GFP_KERNEL); + if (!ctx->registers) + return -ENOMEM; + + /* + * The allocated memory is empty, we need to load the default + * register settings. We just read Ping; it's identical to Pong. + */ + memcpy_fromio(ctx->registers, + mali_c55->base + config_space_addrs[MALI_C55_CONFIG_PING], + MALI_C55_CONFIG_SPACE_SIZE); + + /* + * Some features of the ISP need to be disabled by default and only + * enabled at the same time as they're configured by a parameters buffer + */ + + /* Bypass the sqrt and square compression and expansion modules */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1, + MALI_C55_REG_BYPASS_1_FE_SQRT, + MALI_C55_REG_BYPASS_1_FE_SQRT); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_SQUARE_BE, + MALI_C55_REG_BYPASS_3_SQUARE_BE); + + /* Bypass the temper module */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2, + MALI_C55_REG_BYPASS_2_TEMPER); + + /* Disable the temper module's DMA read/write */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0); + + /* Bypass the colour noise reduction */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4, + MALI_C55_REG_BYPASS_4_CNR); + + /* Disable the sinter module */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG, + MALI_C55_SINTER_ENABLE_MASK, 0); + + /* Disable the RGB Gamma module for each output */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0); + + /* Disable the colour correction matrix */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0); + + return 0; +} + +static void __mali_c55_power_off(struct mali_c55 *mali_c55) +{ + reset_control_bulk_assert(ARRAY_SIZE(mali_c55->resets), mali_c55->resets); + clk_bulk_disable_unprepare(ARRAY_SIZE(mali_c55->clks), mali_c55->clks); +} + +static int __maybe_unused mali_c55_runtime_suspend(struct device *dev) +{ + struct mali_c55 *mali_c55 = dev_get_drvdata(dev); + + if (irq_has_action(mali_c55->irqnum)) + free_irq(mali_c55->irqnum, dev); + __mali_c55_power_off(mali_c55); + + return 0; +} + +static int __mali_c55_power_on(struct mali_c55 *mali_c55) +{ + int ret; + u32 val; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(mali_c55->clks), + mali_c55->clks); + if (ret) { + dev_err(mali_c55->dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_bulk_deassert(ARRAY_SIZE(mali_c55->resets), + mali_c55->resets); + if (ret) { + dev_err(mali_c55->dev, "failed to deassert resets\n"); + return ret; + } + + /* Use "software only" context management. */ + mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG, + MALI_C55_REG_MCU_CONFIG_OVERRIDE_MASK, 0x01); + + /* + * Mask the interrupts and clear any that were set, then unmask the ones + * that we actually want to handle. + */ + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_MASK_VECTOR, + MALI_C55_INTERRUPT_MASK_ALL); + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR_VECTOR, + MALI_C55_INTERRUPT_MASK_ALL); + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0x01); + mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0x00); + + mali_c55_update_bits(mali_c55, MALI_C55_REG_INTERRUPT_MASK_VECTOR, + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_ISP_START) | + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_ISP_DONE) | + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_FR_Y_DONE) | + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_FR_UV_DONE) | + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_DS_Y_DONE) | + MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_DS_UV_DONE), + 0x00); + + /* Set safe stop to ensure we're in a non-streaming state */ + mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST, + MALI_C55_INPUT_SAFE_STOP); + readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS, + val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + + return 0; +} + +static int __maybe_unused mali_c55_runtime_resume(struct device *dev) +{ + struct mali_c55 *mali_c55 = dev_get_drvdata(dev); + int ret; + + ret = __mali_c55_power_on(mali_c55); + if (ret) + return ret; + + /* + * The driver needs to transfer large amounts of register settings to + * the ISP each frame, using either a DMA transfer or memcpy. We use a + * threaded IRQ to avoid disabling interrupts the entire time that's + * happening. + */ + ret = request_threaded_irq(mali_c55->irqnum, NULL, mali_c55_isr, + IRQF_ONESHOT, dev_driver_string(dev), dev); + if (ret) { + __mali_c55_power_off(mali_c55); + dev_err(dev, "failed to request irq\n"); + } + + return ret; +} + +static const struct dev_pm_ops mali_c55_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(mali_c55_runtime_suspend, mali_c55_runtime_resume, + NULL) +}; + +static int mali_c55_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mali_c55 *mali_c55; + struct resource *res; + int ret; + + mali_c55 = devm_kzalloc(dev, sizeof(*mali_c55), GFP_KERNEL); + if (!mali_c55) + return -ENOMEM; + + mali_c55->dev = dev; + platform_set_drvdata(pdev, mali_c55); + + mali_c55->base = devm_platform_get_and_ioremap_resource(pdev, 0, + &res); + if (IS_ERR(mali_c55->base)) + return dev_err_probe(dev, PTR_ERR(mali_c55->base), + "failed to map IO memory\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(mali_c55_clk_names); i++) + mali_c55->clks[i].id = mali_c55_clk_names[i]; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(mali_c55->clks), mali_c55->clks); + if (ret) + return dev_err_probe(dev, ret, "failed to acquire clocks\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(mali_c55_reset_names); i++) + mali_c55->resets[i].id = mali_c55_reset_names[i]; + + ret = devm_reset_control_bulk_get_optional_shared(dev, + ARRAY_SIZE(mali_c55_reset_names), mali_c55->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to acquire resets\n"); + + of_reserved_mem_device_init(dev); + vb2_dma_contig_set_max_seg_size(dev, UINT_MAX); + + ret = __mali_c55_power_on(mali_c55); + if (ret) + return dev_err_probe(dev, ret, "failed to power on\n"); + + ret = mali_c55_check_hwcfg(mali_c55); + if (ret) + goto err_power_off; + + ret = mali_c55_init_context(mali_c55, res); + if (ret) + goto err_power_off; + + mali_c55->media_dev.dev = dev; + + pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = mali_c55_media_frameworks_init(mali_c55); + if (ret) + goto err_free_context_registers; + + pm_runtime_idle(&pdev->dev); + + mali_c55->irqnum = platform_get_irq(pdev, 0); + if (mali_c55->irqnum < 0) { + ret = mali_c55->irqnum; + dev_err(dev, "failed to get interrupt\n"); + goto err_deinit_media_frameworks; + } + + return 0; + +err_deinit_media_frameworks: + mali_c55_media_frameworks_deinit(mali_c55); + pm_runtime_disable(&pdev->dev); +err_free_context_registers: + kfree(mali_c55->context.registers); +err_power_off: + __mali_c55_power_off(mali_c55); + + return ret; +} + +static void mali_c55_remove(struct platform_device *pdev) +{ + struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); + + kfree(mali_c55->context.registers); + mali_c55_media_frameworks_deinit(mali_c55); +} + +static const struct of_device_id mali_c55_of_match[] = { + { .compatible = "arm,mali-c55", }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mali_c55_of_match); + +static struct platform_driver mali_c55_driver = { + .driver = { + .name = "mali-c55", + .of_match_table = mali_c55_of_match, + .pm = &mali_c55_pm_ops, + }, + .probe = mali_c55_probe, + .remove = mali_c55_remove, +}; + +module_platform_driver(mali_c55_driver); + +MODULE_AUTHOR("Daniel Scally <dan.scally@ideasonboard.com>"); +MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com>"); +MODULE_DESCRIPTION("ARM Mali-C55 ISP platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c new file mode 100644 index 000000000000..497f25fbdd13 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Image signal processor + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/media/arm/mali-c55-config.h> + +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/property.h> +#include <linux/string.h> + +#include <uapi/linux/media/arm/mali-c55-config.h> + +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +static const struct mali_c55_isp_format_info mali_c55_isp_fmts[] = { + { + .code = MEDIA_BUS_FMT_SRGGB20_1X20, + .shifted_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .order = MALI_C55_BAYER_ORDER_RGGB, + .bypass = false, + }, + { + .code = MEDIA_BUS_FMT_SGRBG20_1X20, + .shifted_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .order = MALI_C55_BAYER_ORDER_GRBG, + .bypass = false, + }, + { + .code = MEDIA_BUS_FMT_SGBRG20_1X20, + .shifted_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .order = MALI_C55_BAYER_ORDER_GBRG, + .bypass = false, + }, + { + .code = MEDIA_BUS_FMT_SBGGR20_1X20, + .shifted_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .order = MALI_C55_BAYER_ORDER_BGGR, + .bypass = false, + }, + { + .code = MEDIA_BUS_FMT_RGB202020_1X60, + .shifted_code = 0, /* Not relevant for this format */ + .order = 0, /* Not relevant for this format */ + .bypass = true, + } + /* + * TODO: Support MEDIA_BUS_FMT_YUV20_1X60 here. This is so that we can + * also support YUV input from a sensor passed-through to the output. At + * present we have no mechanism to test that though so it may have to + * wait a while... + */ +}; + +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_index(u32 index) +{ + if (index < ARRAY_SIZE(mali_c55_isp_fmts)) + return &mali_c55_isp_fmts[index]; + + return NULL; +} + +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_isp_fmts); i++) { + if (mali_c55_isp_fmts[i].code == code) + return &mali_c55_isp_fmts[i]; + } + + return NULL; +} + +const struct mali_c55_isp_format_info * +mali_c55_isp_get_mbus_config_by_shifted_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_isp_fmts); i++) { + if (mali_c55_isp_fmts[i].shifted_code == code) + return &mali_c55_isp_fmts[i]; + } + + return NULL; +} + +static void mali_c55_isp_stop(struct mali_c55 *mali_c55) +{ + u32 val; + + mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST, + MALI_C55_INPUT_SAFE_STOP); + readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS, + val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); +} + +static int mali_c55_isp_start(struct mali_c55 *mali_c55, + const struct v4l2_subdev_state *state) +{ + struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55); + const struct mali_c55_isp_format_info *cfg; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + u32 val; + int ret; + + mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG, + MALI_C55_REG_MCU_CONFIG_WRITE_MASK, + MALI_C55_REG_MCU_CONFIG_WRITE_PING); + + /* Apply input windowing */ + crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); + format = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SINK_VIDEO); + cfg = mali_c55_isp_get_mbus_config_by_code(format->code); + + mali_c55_write(mali_c55, MALI_C55_REG_HC_START, + MALI_C55_HC_START(crop->left)); + mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE, + MALI_C55_HC_SIZE(crop->width)); + mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE, + MALI_C55_VC_START(crop->top) | + MALI_C55_VC_SIZE(crop->height)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, + MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR, + MALI_C55_REG_ACTIVE_HEIGHT_MASK, + format->height << 16); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER, + MALI_C55_BAYER_ORDER_MASK, cfg->order); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH, + MALI_C55_INPUT_WIDTH_MASK, + MALI_C55_INPUT_WIDTH_20BIT); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS, + MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK, + cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK : + 0x00); + + mali_c55_params_write_config(mali_c55); + ret = mali_c55_config_write(ctx, MALI_C55_CONFIG_PING, true); + if (ret) { + dev_err(mali_c55->dev, "failed to write ISP config\n"); + return ret; + } + + mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST, + MALI_C55_INPUT_SAFE_START); + + ret = readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS, val, + val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + if (ret) { + mali_c55_isp_stop(mali_c55); + dev_err(mali_c55->dev, "timeout starting ISP\n"); + return ret; + } + + return 0; +} + +static int mali_c55_isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* + * Only the internal RGB processed format is allowed on the regular + * processing source pad. + */ + if (code->pad == MALI_C55_ISP_PAD_SOURCE_VIDEO) { + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_RGB121212_1X36; + return 0; + } + + /* On the sink and bypass pads all the supported formats are allowed. */ + if (code->index >= ARRAY_SIZE(mali_c55_isp_fmts)) + return -EINVAL; + + code->code = mali_c55_isp_fmts[code->index].code; + + return 0; +} + +static int mali_c55_isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct mali_c55_isp_format_info *cfg; + + if (fse->index > 0) + return -EINVAL; + + /* + * Only the internal RGB processed format is allowed on the regular + * processing source pad. + * + * On the sink and bypass pads all the supported formats are allowed. + */ + if (fse->pad == MALI_C55_ISP_PAD_SOURCE_VIDEO) { + if (fse->code != MEDIA_BUS_FMT_RGB121212_1X36) + return -EINVAL; + } else { + cfg = mali_c55_isp_get_mbus_config_by_code(fse->code); + if (!cfg) + return -EINVAL; + } + + fse->min_width = MALI_C55_MIN_WIDTH; + fse->min_height = MALI_C55_MIN_HEIGHT; + fse->max_width = MALI_C55_MAX_WIDTH; + fse->max_height = MALI_C55_MAX_HEIGHT; + + return 0; +} + +static int mali_c55_isp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; + const struct mali_c55_isp_format_info *cfg; + struct v4l2_rect *crop; + + /* + * Disallow set_fmt on the source pads; format is fixed and the sizes + * are the result of applying the sink crop rectangle to the sink + * format. + */ + if (format->pad != MALI_C55_ISP_PAD_SINK_VIDEO) + return v4l2_subdev_get_fmt(sd, state, format); + + sink_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SINK_VIDEO); + + cfg = mali_c55_isp_get_mbus_config_by_code(fmt->code); + sink_fmt->code = cfg ? fmt->code : MEDIA_BUS_FMT_SRGGB20_1X20; + + /* + * Clamp sizes in the accepted limits and clamp the crop rectangle in + * the new sizes. + */ + sink_fmt->width = clamp(fmt->width, MALI_C55_MIN_WIDTH, + MALI_C55_MAX_WIDTH); + sink_fmt->height = clamp(fmt->height, MALI_C55_MIN_HEIGHT, + MALI_C55_MAX_HEIGHT); + + *fmt = *sink_fmt; + + crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); + crop->left = 0; + crop->top = 0; + crop->width = sink_fmt->width; + crop->height = sink_fmt->height; + + /* + * Propagate format to source pads. On the 'regular' output pad use + * the internal RGB processed format, while on the bypass pad simply + * replicate the ISP sink format. The sizes on both pads are the same as + * the ISP sink crop rectangle. The "field" and "colorspace" fields are + * set in .init_state() and fixed for both source pads, as is the "code" + * field for the processed data source pad. + */ + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_VIDEO); + src_fmt->width = crop->width; + src_fmt->height = crop->height; + + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_BYPASS); + src_fmt->code = sink_fmt->code; + src_fmt->width = crop->width; + src_fmt->height = crop->height; + + return 0; +} + +static int mali_c55_isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad != MALI_C55_ISP_PAD_SINK_VIDEO || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); + + return 0; +} + +static int mali_c55_isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *src_fmt; + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + + if (sel->pad != MALI_C55_ISP_PAD_SINK_VIDEO || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, MALI_C55_ISP_PAD_SINK_VIDEO); + + sel->r.left = clamp_t(unsigned int, sel->r.left, 0, fmt->width); + sel->r.top = clamp_t(unsigned int, sel->r.top, 0, fmt->height); + sel->r.width = clamp_t(unsigned int, sel->r.width, MALI_C55_MIN_WIDTH, + fmt->width - sel->r.left); + sel->r.height = clamp_t(unsigned int, sel->r.height, + MALI_C55_MIN_HEIGHT, + fmt->height - sel->r.top); + + crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO); + *crop = sel->r; + + /* + * Propagate the crop rectangle sizes to the source pad format. The crop + * isn't propagated to the bypass source pad, because the bypassed data + * cannot be cropped. + */ + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_VIDEO); + src_fmt->width = crop->width; + src_fmt->height = crop->height; + + return 0; +} + +static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mali_c55_isp *isp = container_of(sd, struct mali_c55_isp, sd); + struct mali_c55 *mali_c55 = isp->mali_c55; + struct v4l2_subdev *src_sd; + struct media_pad *sink_pad; + int ret; + + /* + * We have two source pads, both of which have only a single stream. The + * core v4l2 code already validated those parameters so we can just get + * on with starting the ISP. + */ + + sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO]; + isp->remote_src = media_pad_remote_pad_unique(sink_pad); + src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity); + + isp->frame_sequence = 0; + ret = mali_c55_isp_start(mali_c55, state); + if (ret) { + dev_err(mali_c55->dev, "Failed to start ISP\n"); + isp->remote_src = NULL; + return ret; + } + + /* + * We only support a single input stream, so we can just enable the 1st + * entry in the streams mask. + */ + ret = v4l2_subdev_enable_streams(src_sd, isp->remote_src->index, BIT(0)); + if (ret) { + dev_err(mali_c55->dev, "Failed to start ISP source\n"); + mali_c55_isp_stop(mali_c55); + return ret; + } + + return 0; +} + +static int mali_c55_isp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mali_c55_isp *isp = container_of(sd, struct mali_c55_isp, sd); + struct mali_c55 *mali_c55 = isp->mali_c55; + struct v4l2_subdev *src_sd; + + if (isp->remote_src) { + src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity); + v4l2_subdev_disable_streams(src_sd, isp->remote_src->index, + BIT(0)); + } + isp->remote_src = NULL; + + mali_c55_isp_stop(mali_c55); + + return 0; +} + +static const struct v4l2_subdev_pad_ops mali_c55_isp_pad_ops = { + .enum_mbus_code = mali_c55_isp_enum_mbus_code, + .enum_frame_size = mali_c55_isp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mali_c55_isp_set_fmt, + .get_selection = mali_c55_isp_get_selection, + .set_selection = mali_c55_isp_set_selection, + .link_validate = v4l2_subdev_link_validate_default, + .enable_streams = mali_c55_isp_enable_streams, + .disable_streams = mali_c55_isp_disable_streams, +}; + +void mali_c55_isp_queue_event_sof(struct mali_c55 *mali_c55) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + }; + + event.u.frame_sync.frame_sequence = mali_c55->isp.frame_sequence; + v4l2_event_queue(mali_c55->isp.sd.devnode, &event); +} + +static int +mali_c55_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops mali_c55_isp_core_ops = { + .subscribe_event = mali_c55_isp_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops mali_c55_isp_ops = { + .pad = &mali_c55_isp_pad_ops, + .core = &mali_c55_isp_core_ops, +}; + +static int mali_c55_isp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect *in_crop; + + sink_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_VIDEO); + in_crop = v4l2_subdev_state_get_crop(state, + MALI_C55_ISP_PAD_SINK_VIDEO); + + sink_fmt->width = MALI_C55_DEFAULT_WIDTH; + sink_fmt->height = MALI_C55_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace); + sink_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace); + sink_fmt->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace, + sink_fmt->ycbcr_enc); + + *v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_BYPASS) = *sink_fmt; + + src_fmt->width = MALI_C55_DEFAULT_WIDTH; + src_fmt->height = MALI_C55_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_RGB121212_1X36; + src_fmt->colorspace = V4L2_COLORSPACE_SRGB; + src_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace); + src_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace); + src_fmt->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace, + sink_fmt->ycbcr_enc); + + in_crop->top = 0; + in_crop->left = 0; + in_crop->width = MALI_C55_DEFAULT_WIDTH; + in_crop->height = MALI_C55_DEFAULT_HEIGHT; + + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SOURCE_STATS); + sink_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_ISP_PAD_SINK_PARAMS); + + src_fmt->width = 0; + src_fmt->height = 0; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; + + sink_fmt->width = 0; + sink_fmt->height = 0; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; + + return 0; +} + +static const struct v4l2_subdev_internal_ops mali_c55_isp_internal_ops = { + .init_state = mali_c55_isp_init_state, +}; + +static int mali_c55_subdev_link_validate(struct media_link *link) +{ + /* + * Skip validation for the parameters sink pad, as the source is not + * a subdevice. + */ + if (link->sink->index == MALI_C55_ISP_PAD_SINK_PARAMS) + return 0; + + return v4l2_subdev_link_validate(link); +} + +static const struct media_entity_operations mali_c55_isp_media_ops = { + .link_validate = mali_c55_subdev_link_validate, +}; + +static int mali_c55_isp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + /* + * .s_ctrl() is a mandatory operation, but the driver has only a single + * read only control. If we got here, something went badly wrong. + */ + return -EINVAL; +} + +static const struct v4l2_ctrl_ops mali_c55_isp_ctrl_ops = { + .s_ctrl = mali_c55_isp_s_ctrl, +}; + +/* NOT const because the default needs to be filled in at runtime */ +static struct v4l2_ctrl_config mali_c55_isp_v4l2_custom_ctrls[] = { + { + .ops = &mali_c55_isp_ctrl_ops, + .id = V4L2_CID_MALI_C55_CAPABILITIES, + .name = "Mali-C55 ISP Capabilities", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = MALI_C55_GPS_PONG_FITTED | + MALI_C55_GPS_WDR_FITTED | + MALI_C55_GPS_COMPRESSION_FITTED | + MALI_C55_GPS_TEMPER_FITTED | + MALI_C55_GPS_SINTER_LITE_FITTED | + MALI_C55_GPS_SINTER_FITTED | + MALI_C55_GPS_IRIDIX_LTM_FITTED | + MALI_C55_GPS_IRIDIX_GTM_FITTED | + MALI_C55_GPS_CNR_FITTED | + MALI_C55_GPS_FRSCALER_FITTED | + MALI_C55_GPS_DS_PIPE_FITTED, + .def = 0, + }, +}; + +static int mali_c55_isp_init_controls(struct mali_c55 *mali_c55) +{ + struct v4l2_ctrl_handler *handler = &mali_c55->isp.handler; + struct v4l2_ctrl *capabilities; + int ret; + + ret = v4l2_ctrl_handler_init(handler, 1); + if (ret) + return ret; + + mali_c55_isp_v4l2_custom_ctrls[0].def = mali_c55->capabilities; + + capabilities = v4l2_ctrl_new_custom(handler, + &mali_c55_isp_v4l2_custom_ctrls[0], + NULL); + if (capabilities) + capabilities->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (handler->error) { + dev_err(mali_c55->dev, "failed to register capabilities control\n"); + ret = handler->error; + v4l2_ctrl_handler_free(handler); + return ret; + } + + mali_c55->isp.sd.ctrl_handler = handler; + + return 0; +} + +int mali_c55_register_isp(struct mali_c55 *mali_c55) +{ + struct mali_c55_isp *isp = &mali_c55->isp; + struct v4l2_subdev *sd = &isp->sd; + int ret; + + isp->mali_c55 = mali_c55; + + v4l2_subdev_init(sd, &mali_c55_isp_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->entity.ops = &mali_c55_isp_media_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + sd->internal_ops = &mali_c55_isp_internal_ops; + strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name)); + + isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + isp->pads[MALI_C55_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE; + isp->pads[MALI_C55_ISP_PAD_SOURCE_BYPASS].flags = MEDIA_PAD_FL_SOURCE; + isp->pads[MALI_C55_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; + isp->pads[MALI_C55_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(&sd->entity, MALI_C55_ISP_NUM_PADS, + isp->pads); + if (ret) + return ret; + + ret = mali_c55_isp_init_controls(mali_c55); + if (ret) + goto err_cleanup_media_entity; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_free_ctrl_handler; + + ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd); + if (ret) + goto err_cleanup_subdev; + + mutex_init(&isp->capture_lock); + + return 0; + +err_cleanup_subdev: + v4l2_subdev_cleanup(sd); +err_free_ctrl_handler: + v4l2_ctrl_handler_free(&isp->handler); +err_cleanup_media_entity: + media_entity_cleanup(&sd->entity); + isp->mali_c55 = NULL; + + return ret; +} + +void mali_c55_unregister_isp(struct mali_c55 *mali_c55) +{ + struct mali_c55_isp *isp = &mali_c55->isp; + + if (!isp->mali_c55) + return; + + mutex_destroy(&isp->capture_lock); + v4l2_device_unregister_subdev(&isp->sd); + v4l2_subdev_cleanup(&isp->sd); + media_entity_cleanup(&isp->sd.entity); +} diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c new file mode 100644 index 000000000000..082cda4f4f63 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -0,0 +1,819 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Configuration parameters output device + * + * Copyright (C) 2025 Ideas on Board Oy + */ +#include <linux/media/arm/mali-c55-config.h> +#include <linux/pm_runtime.h> + +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-isp.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +/** + * union mali_c55_params_block - Generalisation of a parameter block + * + * This union allows the driver to treat a block as a generic pointer to this + * union and safely access the header and block-specific struct without having + * to resort to casting. The header member is accessed first, and the type field + * checked which allows the driver to determine which of the other members + * should be used. The data member at the end allows a pointer to an address + * within the data member of :c:type:`mali_c55_params_buffer` to initialise a + * union variable. + * + * @header: Pointer to the shared header struct embedded as the + * first member of all the possible other members (except + * @data). This member would be accessed first and the type + * field checked to determine which of the other members + * should be accessed. + * @sensor_offs: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS + * @aexp_hist: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST and + * header->type == MALI_C55_PARAM_BLOCK_AEXP_IHIST + * @aexp_weights: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS + * and header->type = MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS + * @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN + * @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and + * header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP + * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG + * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION + * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS + * @data: Allows easy initialisation of a union variable with a + * pointer into a __u8 array. + */ +union mali_c55_params_block { + const struct v4l2_isp_params_block_header *header; + const struct mali_c55_params_sensor_off_preshading *sensor_offs; + const struct mali_c55_params_aexp_hist *aexp_hist; + const struct mali_c55_params_aexp_weights *aexp_weights; + const struct mali_c55_params_digital_gain *digital_gain; + const struct mali_c55_params_awb_gains *awb_gains; + const struct mali_c55_params_awb_config *awb_config; + const struct mali_c55_params_mesh_shading_config *shading_config; + const struct mali_c55_params_mesh_shading_selection *shading_selection; + const __u8 *data; +}; + +typedef void (*mali_c55_params_handler)(struct mali_c55 *mali_c55, + union mali_c55_params_block block); + +#define to_mali_c55_params_buf(vbuf) \ + container_of(vbuf, struct mali_c55_params_buf, vb) + +static void mali_c55_params_sensor_offs(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_sensor_off_preshading *p; + __u32 global_offset; + + p = block.sensor_offs; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, + MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH); + return; + } + + if (!(p->chan00 || p->chan01 || p->chan10 || p->chan11)) + return; + + mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_00, + p->chan00 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_01, + p->chan01 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_10, + p->chan10 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_11, + p->chan11 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); + + /* + * The average offset is applied as a global offset for the digital + * gain block + */ + global_offset = (p->chan00 + p->chan01 + p->chan10 + p->chan11) >> 2; + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN_OFFSET, + MALI_C55_DIGITAL_GAIN_OFFSET_MASK, + global_offset); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, + MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, + 0x00); +} + +static void mali_c55_params_aexp_hist(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_aexp_hist *params; + u32 disable_mask; + u32 disable_val; + u32 base; + + if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) { + disable_mask = MALI_C55_AEXP_HIST_DISABLE_MASK; + disable_val = MALI_C55_AEXP_HIST_DISABLE; + base = MALI_C55_REG_AEXP_HIST_BASE; + } else { + disable_mask = MALI_C55_AEXP_IHIST_DISABLE_MASK; + disable_val = MALI_C55_AEXP_IHIST_DISABLE; + base = MALI_C55_REG_AEXP_IHIST_BASE; + } + + params = block.aexp_hist; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + disable_mask, disable_val); + return; + } + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + disable_mask, false); + + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, + MALI_C55_AEXP_HIST_SKIP_X_MASK, params->skip_x); + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, + MALI_C55_AEXP_HIST_OFFSET_X_MASK, + MALI_C55_AEXP_HIST_OFFSET_X(params->offset_x)); + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, + MALI_C55_AEXP_HIST_SKIP_Y_MASK, + MALI_C55_AEXP_HIST_SKIP_Y(params->skip_y)); + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, + MALI_C55_AEXP_HIST_OFFSET_Y_MASK, + MALI_C55_AEXP_HIST_OFFSET_Y(params->offset_y)); + + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, + MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK, + params->scale_bottom); + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, + MALI_C55_AEXP_HIST_SCALE_TOP_MASK, + MALI_C55_AEXP_HIST_SCALE_TOP(params->scale_top)); + + mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET, + MALI_C55_AEXP_HIST_PLANE_MODE_MASK, + params->plane_mode); + + if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AEXP_HIST_SWITCH_MASK, + MALI_C55_AEXP_HIST_SWITCH(params->tap_point)); +} + +static void +mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_aexp_weights *params; + u32 base, val, addr; + + params = block.aexp_weights; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) + return; + + base = block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS ? + MALI_C55_REG_AEXP_HIST_BASE : + MALI_C55_REG_AEXP_IHIST_BASE; + + mali_c55_ctx_update_bits(mali_c55, + base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, + MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK, + params->nodes_used_horiz); + mali_c55_ctx_update_bits(mali_c55, + base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, + MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK, + MALI_C55_AEXP_HIST_NODES_USED_VERT(params->nodes_used_vert)); + + /* + * The zone weights array is a 225-element array of u8 values, but that + * is a bit annoying to handle given the ISP expects 32-bit writes. We + * just reinterpret it as 56-element array of 32-bit values for the + * purposes of this transaction. The last register is handled separately + * to stop static analysers worrying about buffer overflow. The 3 bytes + * of additional space at the end of the write is just padding for the + * array of weights in the ISP memory space anyway, so there's no risk + * of overwriting other registers. + */ + for (unsigned int i = 0; i < 56; i++) { + val = ((u32 *)params->zone_weights)[i] + & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK; + addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * i); + + mali_c55_ctx_write(mali_c55, addr, val); + } + + val = params->zone_weights[MALI_C55_MAX_ZONES - 1]; + addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56); +} + +static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_digital_gain *dgain; + u32 gain; + + dgain = block.digital_gain; + + /* + * If the block is flagged as disabled we write a gain of 1.0, which in + * Q5.8 format is 256. + */ + gain = block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE ? + 256 : dgain->gain; + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN, + MALI_C55_DIGITAL_GAIN_MASK, + gain); +} + +static void mali_c55_params_awb_gains(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_awb_gains *gains; + u32 gain00, gain01, gain10, gain11; + + gains = block.awb_gains; + + /* + * There are two places AWB gains can be set in the ISP; one affects the + * image output data and the other affects the statistics for the + * AEXP-0 tap point. + */ + u32 addr1 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? + MALI_C55_REG_AWB_GAINS1 : + MALI_C55_REG_AWB_GAINS1_AEXP; + u32 addr2 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? + MALI_C55_REG_AWB_GAINS2 : + MALI_C55_REG_AWB_GAINS2_AEXP; + + /* If the block is flagged disabled, set all of the gains to 1.0 */ + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + gain00 = 256; + gain01 = 256; + gain10 = 256; + gain11 = 256; + } else { + gain00 = gains->gain00; + gain01 = gains->gain01; + gain10 = gains->gain10; + gain11 = gains->gain11; + } + + mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN00_MASK, + gain00); + mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN01_MASK, + MALI_C55_AWB_GAIN01(gain01)); + mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN10_MASK, + gain10); + mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN11_MASK, + MALI_C55_AWB_GAIN11(gain11)); +} + +static void mali_c55_params_awb_config(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_awb_config *params; + + params = block.awb_config; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AWB_DISABLE_MASK, + MALI_C55_AWB_DISABLE_MASK); + return; + } + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AWB_DISABLE_MASK, false); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_STATS_MODE, + MALI_C55_AWB_STATS_MODE_MASK, params->stats_mode); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_WHITE_LEVEL, + MALI_C55_AWB_WHITE_LEVEL_MASK, params->white_level); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_BLACK_LEVEL, + MALI_C55_AWB_BLACK_LEVEL_MASK, params->black_level); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MAX, + MALI_C55_AWB_CR_MAX_MASK, params->cr_max); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MIN, + MALI_C55_AWB_CR_MIN_MASK, params->cr_min); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MAX, + MALI_C55_AWB_CB_MAX_MASK, params->cb_max); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MIN, + MALI_C55_AWB_CB_MIN_MASK, params->cb_min); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, + MALI_C55_AWB_NODES_USED_HORIZ_MASK, + params->nodes_used_horiz); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, + MALI_C55_AWB_NODES_USED_VERT_MASK, + MALI_C55_AWB_NODES_USED_VERT(params->nodes_used_vert)); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_HIGH, + MALI_C55_AWB_CR_HIGH_MASK, params->cr_high); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_LOW, + MALI_C55_AWB_CR_LOW_MASK, params->cr_low); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_HIGH, + MALI_C55_AWB_CB_HIGH_MASK, params->cb_high); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_LOW, + MALI_C55_AWB_CB_LOW_MASK, params->cb_low); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, + MALI_C55_AWB_SWITCH_MASK, + MALI_C55_AWB_SWITCH(params->tap_point)); +} + +static void mali_c55_params_lsc_config(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_mesh_shading_config *params; + unsigned int i; + u32 addr; + + params = block.shading_config; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_ENABLE_MASK, + false); + return; + } + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_ENABLE_MASK, true); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_MESH_SHOW_MASK, + MALI_C55_MESH_SHADING_MESH_SHOW(params->mesh_show)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_SCALE_MASK, + MALI_C55_MESH_SHADING_SCALE(params->mesh_scale)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_PAGE_R_MASK, + MALI_C55_MESH_SHADING_PAGE_R(params->mesh_page_r)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_PAGE_G_MASK, + MALI_C55_MESH_SHADING_PAGE_G(params->mesh_page_g)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_PAGE_B_MASK, + MALI_C55_MESH_SHADING_PAGE_B(params->mesh_page_b)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_MESH_WIDTH_MASK, + MALI_C55_MESH_SHADING_MESH_WIDTH(params->mesh_width)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, + MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK, + MALI_C55_MESH_SHADING_MESH_HEIGHT(params->mesh_height)); + + for (i = 0; i < MALI_C55_NUM_MESH_SHADING_ELEMENTS; i++) { + addr = MALI_C55_REG_MESH_SHADING_TABLES + (i * 4); + mali_c55_ctx_write(mali_c55, addr, params->mesh[i]); + } +} + +static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_mesh_shading_selection *params; + + params = block.shading_selection; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) + return; + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, + MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK, + params->mesh_alpha_bank_r); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, + MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK, + MALI_C55_MESH_SHADING_ALPHA_BANK_G(params->mesh_alpha_bank_g)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, + MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK, + MALI_C55_MESH_SHADING_ALPHA_BANK_B(params->mesh_alpha_bank_b)); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, + MALI_C55_MESH_SHADING_ALPHA_R_MASK, + params->mesh_alpha_r); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, + MALI_C55_MESH_SHADING_ALPHA_G_MASK, + MALI_C55_MESH_SHADING_ALPHA_G(params->mesh_alpha_g)); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, + MALI_C55_MESH_SHADING_ALPHA_B_MASK, + MALI_C55_MESH_SHADING_ALPHA_B(params->mesh_alpha_b)); + + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_MESH_SHADING_MESH_STRENGTH, + MALI_c55_MESH_STRENGTH_MASK, + params->mesh_strength); +} + +static const mali_c55_params_handler mali_c55_params_handlers[] = { + [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs, + [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist, + [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = &mali_c55_params_aexp_hist, + [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, + [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, + [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = &mali_c55_params_digital_gain, + [MALI_C55_PARAM_BLOCK_AWB_GAINS] = &mali_c55_params_awb_gains, + [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = &mali_c55_params_awb_config, + [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains, + [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config, + [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection, +}; + +static const struct v4l2_isp_params_block_type_info +mali_c55_params_block_types_info[] = { + [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = { + .size = sizeof(struct mali_c55_params_sensor_off_preshading), + }, + [MALI_C55_PARAM_BLOCK_AEXP_HIST] = { + .size = sizeof(struct mali_c55_params_aexp_hist), + }, + [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = { + .size = sizeof(struct mali_c55_params_aexp_hist), + }, + [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = { + .size = sizeof(struct mali_c55_params_aexp_weights), + }, + [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = { + .size = sizeof(struct mali_c55_params_aexp_weights), + }, + [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = { + .size = sizeof(struct mali_c55_params_digital_gain), + }, + [MALI_C55_PARAM_BLOCK_AWB_GAINS] = { + .size = sizeof(struct mali_c55_params_awb_gains), + }, + [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = { + .size = sizeof(struct mali_c55_params_awb_config), + }, + [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = { + .size = sizeof(struct mali_c55_params_awb_gains), + }, + [MALI_C55_PARAM_MESH_SHADING_CONFIG] = { + .size = sizeof(struct mali_c55_params_mesh_shading_config), + }, + [MALI_C55_PARAM_MESH_SHADING_SELECTION] = { + .size = sizeof(struct mali_c55_params_mesh_shading_selection), + }, +}; + +static_assert(ARRAY_SIZE(mali_c55_params_handlers) == + ARRAY_SIZE(mali_c55_params_block_types_info)); + +static int mali_c55_params_enum_fmt_meta_out(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_MALI_C55_PARAMS; + + return 0; +} + +static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + static const struct v4l2_meta_format mfmt = { + .dataformat = V4L2_META_FMT_MALI_C55_PARAMS, + .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), + }; + + f->fmt.meta = mfmt; + + return 0; +} + +static int mali_c55_params_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops mali_c55_params_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_out = mali_c55_params_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = mali_c55_params_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = mali_c55_params_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = mali_c55_params_g_fmt_meta_out, + .vidioc_querycap = mali_c55_params_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations mali_c55_params_v4l2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static int +mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + if (*num_planes && *num_planes > 1) + return -EINVAL; + + if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)) + return -EINVAL; + + *num_planes = 1; + + if (!sizes[0]) + sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE); + + return 0; +} + +static int mali_c55_params_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); + + buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), + GFP_KERNEL); + if (!buf->config) + return -ENOMEM; + + return 0; +} + +static void mali_c55_params_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); + + kvfree(buf->config); + buf->config = NULL; +} + +static int mali_c55_params_buf_prepare(struct vb2_buffer *vb) +{ + struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); + struct v4l2_isp_params_buffer *config = vb2_plane_vaddr(vb, 0); + struct mali_c55 *mali_c55 = params->mali_c55; + int ret; + + if (config->version != MALI_C55_PARAM_BUFFER_V1) { + dev_dbg(mali_c55->dev, + "Unsupported extensible format version: %u\n", + config->version); + return -EINVAL; + } + + ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb, + v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); + if (ret) + return ret; + + /* + * Copy the parameters buffer provided by userspace to the internal + * scratch buffer. This protects against the chance of userspace making + * changed to the buffer content whilst the driver processes it. + */ + + memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); + + return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config, + mali_c55_params_block_types_info, + ARRAY_SIZE(mali_c55_params_block_types_info)); +} + +static void mali_c55_params_buf_queue(struct vb2_buffer *vb) +{ + struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); + + spin_lock(¶ms->buffers.lock); + list_add_tail(&buf->queue, ¶ms->buffers.queue); + spin_unlock(¶ms->buffers.lock); +} + +static void mali_c55_params_return_buffers(struct mali_c55_params *params, + enum vb2_buffer_state state) +{ + struct mali_c55_params_buf *buf, *tmp; + + guard(spinlock)(¶ms->buffers.lock); + + list_for_each_entry_safe(buf, tmp, ¶ms->buffers.queue, queue) { + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int mali_c55_params_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct mali_c55_params *params = vb2_get_drv_priv(q); + struct mali_c55 *mali_c55 = params->mali_c55; + int ret; + + ret = pm_runtime_resume_and_get(mali_c55->dev); + if (ret) + goto err_return_buffers; + + ret = video_device_pipeline_alloc_start(¶ms->vdev); + if (ret) + goto err_pm_put; + + if (mali_c55_pipeline_ready(mali_c55)) { + ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + if (ret < 0) + goto err_stop_pipeline; + } + + return 0; + +err_stop_pipeline: + video_device_pipeline_stop(¶ms->vdev); +err_pm_put: + pm_runtime_put_autosuspend(mali_c55->dev); +err_return_buffers: + mali_c55_params_return_buffers(params, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void mali_c55_params_stop_streaming(struct vb2_queue *q) +{ + struct mali_c55_params *params = vb2_get_drv_priv(q); + struct mali_c55 *mali_c55 = params->mali_c55; + struct mali_c55_isp *isp = &mali_c55->isp; + + if (mali_c55_pipeline_ready(mali_c55)) { + if (v4l2_subdev_is_streaming(&isp->sd)) + v4l2_subdev_disable_streams(&isp->sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + } + + video_device_pipeline_stop(¶ms->vdev); + mali_c55_params_return_buffers(params, VB2_BUF_STATE_ERROR); + pm_runtime_put_autosuspend(params->mali_c55->dev); +} + +static const struct vb2_ops mali_c55_params_vb2_ops = { + .queue_setup = mali_c55_params_queue_setup, + .buf_init = mali_c55_params_buf_init, + .buf_cleanup = mali_c55_params_buf_cleanup, + .buf_queue = mali_c55_params_buf_queue, + .buf_prepare = mali_c55_params_buf_prepare, + .start_streaming = mali_c55_params_start_streaming, + .stop_streaming = mali_c55_params_stop_streaming, +}; + +void mali_c55_params_write_config(struct mali_c55 *mali_c55) +{ + struct mali_c55_params *params = &mali_c55->params; + struct v4l2_isp_params_buffer *config; + struct mali_c55_params_buf *buf; + size_t block_offset = 0; + size_t max_offset; + + spin_lock(¶ms->buffers.lock); + + buf = list_first_entry_or_null(¶ms->buffers.queue, + struct mali_c55_params_buf, queue); + if (buf) + list_del(&buf->queue); + spin_unlock(¶ms->buffers.lock); + + if (!buf) + return; + + buf->vb.sequence = mali_c55->isp.frame_sequence; + config = buf->config; + + max_offset = config->data_size; + + /* + * Walk the list of parameter blocks and process them. No validation is + * done here, as the contents of the config buffer are already checked + * when the buffer is queued. + */ + while (max_offset && block_offset < max_offset) { + union mali_c55_params_block block; + mali_c55_params_handler handler; + + block.data = &config->data[block_offset]; + + /* We checked the array index already in .buf_queue() */ + handler = mali_c55_params_handlers[block.header->type]; + handler(mali_c55, block); + + block_offset += block.header->size; + } + + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +void mali_c55_unregister_params(struct mali_c55 *mali_c55) +{ + struct mali_c55_params *params = &mali_c55->params; + + if (!video_is_registered(¶ms->vdev)) + return; + + vb2_video_unregister_device(¶ms->vdev); + media_entity_cleanup(¶ms->vdev.entity); + mutex_destroy(¶ms->lock); +} + +int mali_c55_register_params(struct mali_c55 *mali_c55) +{ + struct mali_c55_params *params = &mali_c55->params; + struct video_device *vdev = ¶ms->vdev; + struct vb2_queue *vb2q = ¶ms->queue; + int ret; + + mutex_init(¶ms->lock); + INIT_LIST_HEAD(¶ms->buffers.queue); + spin_lock_init(¶ms->buffers.lock); + + params->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(¶ms->vdev.entity, 1, ¶ms->pad); + if (ret) + goto err_destroy_mutex; + + vb2q->type = V4L2_BUF_TYPE_META_OUTPUT; + vb2q->io_modes = VB2_MMAP | VB2_DMABUF; + vb2q->drv_priv = params; + vb2q->mem_ops = &vb2_dma_contig_memops; + vb2q->ops = &mali_c55_params_vb2_ops; + vb2q->buf_struct_size = sizeof(struct mali_c55_params_buf); + vb2q->min_queued_buffers = 1; + vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2q->lock = ¶ms->lock; + vb2q->dev = mali_c55->dev; + + ret = vb2_queue_init(vb2q); + if (ret) { + dev_err(mali_c55->dev, "params vb2 queue init failed\n"); + goto err_cleanup_entity; + } + + strscpy(params->vdev.name, "mali-c55 3a params", + sizeof(params->vdev.name)); + vdev->release = video_device_release_empty; + vdev->fops = &mali_c55_params_v4l2_fops; + vdev->ioctl_ops = &mali_c55_params_v4l2_ioctl_ops; + vdev->lock = ¶ms->lock; + vdev->v4l2_dev = &mali_c55->v4l2_dev; + vdev->queue = ¶ms->queue; + vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->vfl_dir = VFL_DIR_TX; + video_set_drvdata(vdev, params); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(mali_c55->dev, + "failed to register params video device\n"); + goto err_release_vb2q; + } + + params->mali_c55 = mali_c55; + + return 0; + +err_release_vb2q: + vb2_queue_release(vb2q); +err_cleanup_entity: + media_entity_cleanup(¶ms->vdev.entity); +err_destroy_mutex: + mutex_destroy(¶ms->lock); + + return ret; +} diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h new file mode 100644 index 000000000000..f5a148add1c8 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h @@ -0,0 +1,449 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ARM Mali-C55 ISP Driver - Register definitions + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#ifndef _MALI_C55_REGISTERS_H +#define _MALI_C55_REGISTERS_H + +#include <linux/bits.h> + +/* ISP Common 0x00000 - 0x000ff */ + +#define MALI_C55_REG_API 0x00000 +#define MALI_C55_REG_PRODUCT 0x00004 +#define MALI_C55_REG_VERSION 0x00008 +#define MALI_C55_REG_REVISION 0x0000c +#define MALI_C55_REG_PULSE_MODE 0x0003c +#define MALI_C55_REG_INPUT_MODE_REQUEST 0x0009c +#define MALI_C55_INPUT_SAFE_STOP 0x00 +#define MALI_C55_INPUT_SAFE_START 0x01 +#define MALI_C55_REG_MODE_STATUS 0x000a0 +#define MALI_C55_REG_INTERRUPT_MASK_VECTOR 0x00030 +#define MALI_C55_INTERRUPT_MASK_ALL GENMASK(31, 0) + +#define MALI_C55_REG_GLOBAL_MONITOR 0x00050 + +#define MALI_C55_REG_GEN_VIDEO 0x00080 +#define MALI_C55_REG_GEN_VIDEO_ON_MASK BIT(0) +#define MALI_C55_REG_GEN_VIDEO_MULTI_MASK BIT(1) +#define MALI_C55_REG_GEN_PREFETCH_MASK GENMASK(31, 16) + +#define MALI_C55_REG_MCU_CONFIG 0x00020 +#define MALI_C55_REG_MCU_CONFIG_OVERRIDE_MASK BIT(0) +#define MALI_C55_REG_MCU_CONFIG_WRITE_MASK BIT(1) +#define MALI_C55_MCU_CONFIG_WRITE(x) ((x) << 1) +#define MALI_C55_REG_MCU_CONFIG_WRITE_PING BIT(1) +#define MALI_C55_REG_MCU_CONFIG_WRITE_PONG 0x00 +#define MALI_C55_REG_MULTI_CONTEXT_MODE_MASK BIT(8) +#define MALI_C55_REG_PING_PONG_READ 0x00024 +#define MALI_C55_REG_PING_PONG_READ_MASK BIT(2) + +#define MALI_C55_REG_INTERRUPT_CLEAR_VECTOR 0x00034 +#define MALI_C55_REG_INTERRUPT_CLEAR 0x00040 +#define MALI_C55_REG_INTERRUPT_STATUS_VECTOR 0x00044 + +enum mali_c55_interrupts { + MALI_C55_IRQ_ISP_START, + MALI_C55_IRQ_ISP_DONE, + MALI_C55_IRQ_MCM_ERROR, + MALI_C55_IRQ_BROKEN_FRAME_ERROR, + MALI_C55_IRQ_MET_AF_DONE, + MALI_C55_IRQ_MET_AEXP_DONE, + MALI_C55_IRQ_MET_AWB_DONE, + MALI_C55_IRQ_AEXP_1024_DONE, + MALI_C55_IRQ_IRIDIX_MET_DONE, + MALI_C55_IRQ_LUT_INIT_DONE, + MALI_C55_IRQ_FR_Y_DONE, + MALI_C55_IRQ_FR_UV_DONE, + MALI_C55_IRQ_DS_Y_DONE, + MALI_C55_IRQ_DS_UV_DONE, + MALI_C55_IRQ_LINEARIZATION_DONE, + MALI_C55_IRQ_RAW_FRONTEND_DONE, + MALI_C55_IRQ_NOISE_REDUCTION_DONE, + MALI_C55_IRQ_IRIDIX_DONE, + MALI_C55_IRQ_BAYER2RGB_DONE, + MALI_C55_IRQ_WATCHDOG_TIMER, + MALI_C55_IRQ_FRAME_COLLISION, + MALI_C55_IRQ_UNUSED, + MALI_C55_IRQ_DMA_ERROR, + MALI_C55_IRQ_INPUT_STOPPED, + MALI_C55_IRQ_MET_AWB_TARGET1_HIT, + MALI_C55_IRQ_MET_AWB_TARGET2_HIT, + MALI_C55_NUM_IRQ_BITS +}; + +#define MALI_C55_INTERRUPT_BIT(x) BIT(x) + +#define MALI_C55_REG_GLOBAL_PARAMETER_STATUS 0x00068 +#define MALI_C55_GPS_PONG_FITTED BIT(0) +#define MALI_C55_GPS_WDR_FITTED BIT(1) +#define MALI_C55_GPS_COMPRESSION_FITTED BIT(2) +#define MALI_C55_GPS_TEMPER_FITTED BIT(3) +#define MALI_C55_GPS_SINTER_LITE_FITTED BIT(4) +#define MALI_C55_GPS_SINTER_FITTED BIT(5) +#define MALI_C55_GPS_IRIDIX_LTM_FITTED BIT(6) +#define MALI_C55_GPS_IRIDIX_GTM_FITTED BIT(7) +#define MALI_C55_GPS_CNR_FITTED BIT(8) +#define MALI_C55_GPS_FRSCALER_FITTED BIT(9) +#define MALI_C55_GPS_DS_PIPE_FITTED BIT(10) + +#define MALI_C55_REG_BLANKING 0x00084 +#define MALI_C55_REG_HBLANK_MASK GENMASK(15, 0) +#define MALI_C55_REG_VBLANK_MASK GENMASK(31, 16) +#define MALI_C55_VBLANK(x) ((x) << 16) + +#define MALI_C55_REG_HC_START 0x00088 +#define MALI_C55_HC_START(h) (((h) & 0xffff) << 16) +#define MALI_C55_REG_HC_SIZE 0x0008c +#define MALI_C55_HC_SIZE(h) ((h) & 0xffff) +#define MALI_C55_REG_VC_START_SIZE 0x00094 +#define MALI_C55_VC_START(v) ((v) & 0xffff) +#define MALI_C55_VC_SIZE(v) (((v) & 0xffff) << 16) + +#define MALI_C55_REG_1024BIN_HIST 0x054a8 +#define MALI_C55_1024BIN_HIST_SIZE 4096 + +/* Ping/Pong Configuration Space */ +#define MALI_C55_REG_BASE_ADDR 0x18e88 +#define MALI_C55_REG_BYPASS_0 0x18eac +#define MALI_C55_REG_BYPASS_0_VIDEO_TEST BIT(0) +#define MALI_C55_REG_BYPASS_0_INPUT_FMT BIT(1) +#define MALI_C55_REG_BYPASS_0_DECOMPANDER BIT(2) +#define MALI_C55_REG_BYPASS_0_SENSOR_OFFSET_WDR BIT(3) +#define MALI_C55_REG_BYPASS_0_GAIN_WDR BIT(4) +#define MALI_C55_REG_BYPASS_0_FRAME_STITCH BIT(5) +#define MALI_C55_REG_BYPASS_1 0x18eb0 +#define MALI_C55_REG_BYPASS_1_DIGI_GAIN BIT(0) +#define MALI_C55_REG_BYPASS_1_FE_SENSOR_OFFS BIT(1) +#define MALI_C55_REG_BYPASS_1_FE_SQRT BIT(2) +#define MALI_C55_REG_BYPASS_1_RAW_FE BIT(3) +#define MALI_C55_REG_BYPASS_2 0x18eb8 +#define MALI_C55_REG_BYPASS_2_SINTER BIT(0) +#define MALI_C55_REG_BYPASS_2_TEMPER BIT(1) +#define MALI_C55_REG_BYPASS_3 0x18ebc +#define MALI_C55_REG_BYPASS_3_SQUARE_BE BIT(0) +#define MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH BIT(1) +#define MALI_C55_REG_BYPASS_3_MESH_SHADING BIT(3) +#define MALI_C55_REG_BYPASS_3_WHITE_BALANCE BIT(4) +#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(5) +#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(6) +#define MALI_C55_REG_BYPASS_4 0x18ec0 +#define MALI_C55_REG_BYPASS_4_DEMOSAIC_RGB BIT(1) +#define MALI_C55_REG_BYPASS_4_PF_CORRECTION BIT(3) +#define MALI_C55_REG_BYPASS_4_CCM BIT(4) +#define MALI_C55_REG_BYPASS_4_CNR BIT(5) +#define MALI_C55_REG_FR_BYPASS 0x18ec4 +#define MALI_C55_REG_DS_BYPASS 0x18ec8 +#define MALI_C55_BYPASS_CROP BIT(0) +#define MALI_C55_BYPASS_SCALER BIT(1) +#define MALI_C55_BYPASS_GAMMA_RGB BIT(2) +#define MALI_C55_BYPASS_SHARPEN BIT(3) +#define MALI_C55_BYPASS_CS_CONV BIT(4) +#define MALI_C55_REG_ISP_RAW_BYPASS 0x18ecc +#define MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK BIT(0) +#define MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK GENMASK(9, 8) +#define MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS (2 << 8) +#define MALI_C55_ISP_RAW_BYPASS_RGB_FR_BYPASS (1 << 8) +#define MALI_C55_ISP_RAW_BYPASS_DS_PIPE_DISABLE BIT(1) +#define MALI_C55_ISP_RAW_BYPASS_RAW_BYPASS BIT(0) + +#define MALI_C55_REG_ACTIVE_WIDTH_MASK 0xffff +#define MALI_C55_REG_ACTIVE_HEIGHT_MASK 0xffff0000 +#define MALI_C55_REG_BAYER_ORDER 0x18e8c +#define MALI_C55_BAYER_ORDER_MASK GENMASK(1, 0) +#define MALI_C55_BAYER_ORDER_RGGB 0 +#define MALI_C55_BAYER_ORDER_GRBG 1 +#define MALI_C55_BAYER_ORDER_GBRG 2 +#define MALI_C55_BAYER_ORDER_BGGR 3 + +#define MALI_C55_REG_METERING_CONFIG 0x18ed0 +#define MALI_C55_5BIN_HIST_DISABLE_MASK BIT(0) +#define MALI_C55_5BIN_HIST_SWITCH_MASK GENMASK(2, 1) +#define MALI_C55_5BIN_HIST_SWITCH(x) ((x) << 1) +#define MALI_C55_AF_DISABLE_MASK BIT(4) +#define MALI_C55_AF_SWITCH_MASK BIT(5) +#define MALI_C55_AWB_DISABLE_MASK BIT(8) +#define MALI_C55_AWB_SWITCH_MASK BIT(9) +#define MALI_C55_AWB_SWITCH(x) ((x) << 9) +#define MALI_C55_AEXP_HIST_DISABLE_MASK BIT(12) +#define MALI_C55_AEXP_HIST_DISABLE (0x01 << 12) +#define MALI_C55_AEXP_HIST_SWITCH_MASK GENMASK(14, 13) +#define MALI_C55_AEXP_HIST_SWITCH(x) ((x) << 13) +#define MALI_C55_AEXP_IHIST_DISABLE_MASK BIT(16) +#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 12) +#define MALI_C55_AEXP_SRC_MASK BIT(24) + +#define MALI_C55_REG_TPG_CH0 0x18ed8 +#define MALI_C55_TEST_PATTERN_ON_OFF BIT(0) +#define MALI_C55_TEST_PATTERN_RGB_MASK BIT(1) +#define MALI_C55_TEST_PATTERN_RGB(x) ((x) << 1) +#define MALI_C55_REG_TPG_R_BACKGROUND 0x18ee0 +#define MALI_C55_REG_TPG_G_BACKGROUND 0x18ee4 +#define MALI_C55_REG_TPG_B_BACKGROUND 0x18ee8 +#define MALI_C55_TPG_BACKGROUND_MAX 0xfffff +#define MALI_C55_REG_INPUT_WIDTH 0x18f98 +#define MALI_C55_INPUT_WIDTH_MASK GENMASK(18, 16) +#define MALI_C55_INPUT_WIDTH_8BIT (0 << 16) +#define MALI_C55_INPUT_WIDTH_10BIT (1 << 16) +#define MALI_C55_INPUT_WIDTH_12BIT (2 << 16) +#define MALI_C55_INPUT_WIDTH_14BIT (3 << 16) +#define MALI_C55_INPUT_WIDTH_16BIT (4 << 16) +#define MALI_C55_INPUT_WIDTH_20BIT (5 << 16) +#define MALI_C55_REG_SPACE_SIZE 0x4000 +#define MALI_C55_REG_CONFIG_SPACES_OFFSET 0x0ab6c +#define MALI_C55_CONFIG_SPACE_SIZE 0x1231c + +#define MALI_C55_REG_DIGITAL_GAIN 0x1926c +#define MALI_C55_DIGITAL_GAIN_MASK GENMASK(12, 0) +#define MALI_C55_REG_DIGITAL_GAIN_OFFSET 0x19270 +#define MALI_C55_DIGITAL_GAIN_OFFSET_MASK GENMASK(19, 0) + +#define MALI_C55_REG_SINTER_CONFIG 0x19348 +#define MALI_C55_SINTER_VIEW_FILTER_MASK GENMASK(1, 0) +#define MALI_C55_SINTER_SCALE_MODE_MASK GENMASK(3, 2) +#define MALI_C55_SINTER_ENABLE_MASK BIT(4) +#define MALI_C55_SINTER_FILTER_SELECT_MASK BIT(5) +#define MALI_C55_SINTER_INT_SELECT_MASK BIT(6) +#define MALI_C55_SINTER_RM_ENABLE_MASK BIT(7) + +/* Temper DMA */ +#define MALI_C55_REG_TEMPER_DMA_IO 0x1ab78 +#define MALI_C55_TEMPER_DMA_WRITE_ON BIT(0) +#define MALI_C55_TEMPER_DMA_READ_ON BIT(1) + +/* Black Level Correction Configuration */ +#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_00 0x1abcc +#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_01 0x1abd0 +#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_10 0x1abd4 +#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_11 0x1abd8 +#define MALI_C55_SENSOR_OFF_PRE_SHA_MASK 0xfffff + +/* Lens Mesh Shading Configuration */ +#define MALI_C55_REG_MESH_SHADING_TABLES 0x13074 +#define MALI_C55_REG_MESH_SHADING_CONFIG 0x1abfc +#define MALI_C55_MESH_SHADING_ENABLE_MASK BIT(0) +#define MALI_C55_MESH_SHADING_MESH_SHOW_MASK BIT(1) +#define MALI_C55_MESH_SHADING_MESH_SHOW(x) ((x) << 1) +#define MALI_C55_MESH_SHADING_SCALE_MASK GENMASK(4, 2) +#define MALI_C55_MESH_SHADING_SCALE(x) ((x) << 2) +#define MALI_C55_MESH_SHADING_PAGE_R_MASK GENMASK(9, 8) +#define MALI_C55_MESH_SHADING_PAGE_R(x) ((x) << 8) +#define MALI_C55_MESH_SHADING_PAGE_G_MASK GENMASK(11, 10) +#define MALI_C55_MESH_SHADING_PAGE_G(x) ((x) << 10) +#define MALI_C55_MESH_SHADING_PAGE_B_MASK GENMASK(13, 12) +#define MALI_C55_MESH_SHADING_PAGE_B(x) ((x) << 12) +#define MALI_C55_MESH_SHADING_MESH_WIDTH_MASK GENMASK(21, 16) +#define MALI_C55_MESH_SHADING_MESH_WIDTH(x) ((x) << 16) +#define MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK GENMASK(29, 24) +#define MALI_C55_MESH_SHADING_MESH_HEIGHT(x) ((x) << 24) + +#define MALI_C55_REG_MESH_SHADING_ALPHA_BANK 0x1ac04 +#define MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK GENMASK(2, 0) +#define MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK GENMASK(5, 3) +#define MALI_C55_MESH_SHADING_ALPHA_BANK_G(x) ((x) << 3) +#define MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK GENMASK(8, 6) +#define MALI_C55_MESH_SHADING_ALPHA_BANK_B(x) ((x) << 6) +#define MALI_C55_REG_MESH_SHADING_ALPHA 0x1ac08 +#define MALI_C55_MESH_SHADING_ALPHA_R_MASK GENMASK(7, 0) +#define MALI_C55_MESH_SHADING_ALPHA_G_MASK GENMASK(15, 8) +#define MALI_C55_MESH_SHADING_ALPHA_G(x) ((x) << 8) +#define MALI_C55_MESH_SHADING_ALPHA_B_MASK GENMASK(23, 16) +#define MALI_C55_MESH_SHADING_ALPHA_B(x) ((x) << 16) +#define MALI_C55_REG_MESH_SHADING_MESH_STRENGTH 0x1ac0c +#define MALI_c55_MESH_STRENGTH_MASK GENMASK(15, 0) + +/* AWB Gains Configuration */ +#define MALI_C55_REG_AWB_GAINS1 0x1ac10 +#define MALI_C55_AWB_GAIN00_MASK GENMASK(11, 0) +#define MALI_C55_AWB_GAIN01_MASK GENMASK(27, 16) +#define MALI_C55_AWB_GAIN01(x) ((x) << 16) +#define MALI_C55_REG_AWB_GAINS2 0x1ac14 +#define MALI_C55_AWB_GAIN10_MASK GENMASK(11, 0) +#define MALI_C55_AWB_GAIN11_MASK GENMASK(27, 16) +#define MALI_C55_AWB_GAIN11(x) ((x) << 16) +#define MALI_C55_REG_AWB_GAINS1_AEXP 0x1ac18 +#define MALI_C55_REG_AWB_GAINS2_AEXP 0x1ac1c + +/* Colour Correction Matrix Configuration */ +#define MALI_C55_REG_CCM_ENABLE 0x1b07c +#define MALI_C55_CCM_ENABLE_MASK BIT(0) +#define MALI_C55_REG_CCM_COEF_R_R 0x1b080 +#define MALI_C55_REG_CCM_COEF_R_G 0x1b084 +#define MALI_C55_REG_CCM_COEF_R_B 0x1b088 +#define MALI_C55_REG_CCM_COEF_G_R 0x1b090 +#define MALI_C55_REG_CCM_COEF_G_G 0x1b094 +#define MALI_C55_REG_CCM_COEF_G_B 0x1b098 +#define MALI_C55_REG_CCM_COEF_B_R 0x1b0a0 +#define MALI_C55_REG_CCM_COEF_B_G 0x1b0a4 +#define MALI_C55_REG_CCM_COEF_B_B 0x1b0a8 +#define MALI_C55_CCM_COEF_MASK GENMASK(12, 0) +#define MALI_C55_REG_CCM_ANTIFOG_GAIN_R 0x1b0b0 +#define MALI_C55_REG_CCM_ANTIFOG_GAIN_G 0x1b0b4 +#define MALI_C55_REG_CCM_ANTIFOG_GAIN_B 0x1b0b8 +#define MALI_C55_CCM_ANTIFOG_GAIN_MASK GENMASK(11, 0) +#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_R 0x1b0c0 +#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_G 0x1b0c4 +#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_B 0x1b0c8 +#define MALI_C55_CCM_ANTIFOG_OFFSET_MASK GENMASK(11, 0) + +/* AWB Statistics Configuration */ +#define MALI_C55_REG_AWB_STATS_MODE 0x1b29c +#define MALI_C55_AWB_STATS_MODE_MASK BIT(0) +#define MALI_C55_REG_AWB_WHITE_LEVEL 0x1b2a0 +#define MALI_C55_AWB_WHITE_LEVEL_MASK GENMASK(9, 0) +#define MALI_C55_REG_AWB_BLACK_LEVEL 0x1b2a4 +#define MALI_C55_AWB_BLACK_LEVEL_MASK GENMASK(9, 0) +#define MALI_C55_REG_AWB_CR_MAX 0x1b2a8 +#define MALI_C55_AWB_CR_MAX_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CR_MIN 0x1b2ac +#define MALI_C55_AWB_CR_MIN_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CB_MAX 0x1b2b0 +#define MALI_C55_AWB_CB_MAX_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CB_MIN 0x1b2b4 +#define MALI_C55_AWB_CB_MIN_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_NODES_USED 0x1b2c4 +#define MALI_C55_AWB_NODES_USED_HORIZ_MASK GENMASK(7, 0) +#define MALI_C55_AWB_NODES_USED_VERT_MASK GENMASK(15, 8) +#define MALI_C55_AWB_NODES_USED_VERT(x) ((x) << 8) +#define MALI_C55_REG_AWB_CR_HIGH 0x1b2c8 +#define MALI_C55_AWB_CR_HIGH_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CR_LOW 0x1b2cc +#define MALI_C55_AWB_CR_LOW_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CB_HIGH 0x1b2d0 +#define MALI_C55_AWB_CB_HIGH_MASK GENMASK(11, 0) +#define MALI_C55_REG_AWB_CB_LOW 0x1b2d4 +#define MALI_C55_AWB_CB_LOW_MASK GENMASK(11, 0) + +/* AEXP Metering Histogram Configuration */ +#define MALI_C55_REG_AEXP_HIST_BASE 0x1b730 +#define MALI_C55_REG_AEXP_IHIST_BASE 0x1bbac +#define MALI_C55_AEXP_HIST_SKIP_OFFSET 0 +#define MALI_C55_AEXP_HIST_SKIP_X_MASK GENMASK(2, 0) +#define MALI_C55_AEXP_HIST_SKIP_X(x) ((x) << 0) +#define MALI_C55_AEXP_HIST_OFFSET_X_MASK BIT(3) +#define MALI_C55_AEXP_HIST_OFFSET_X(x) ((x) << 3) +#define MALI_C55_AEXP_HIST_SKIP_Y_MASK GENMASK(6, 4) +#define MALI_C55_AEXP_HIST_SKIP_Y(x) ((x) << 4) +#define MALI_C55_AEXP_HIST_OFFSET_Y_MASK BIT(7) +#define MALI_C55_AEXP_HIST_OFFSET_Y(x) ((x) << 7) +#define MALI_C55_AEXP_HIST_SCALE_OFFSET 4 +#define MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK GENMASK(3, 0) +#define MALI_C55_AEXP_HIST_SCALE_TOP_MASK GENMASK(7, 4) +#define MALI_C55_AEXP_HIST_SCALE_TOP(x) ((x) << 4) +#define MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET 16 +#define MALI_C55_AEXP_HIST_PLANE_MODE_MASK GENMASK(2, 0) +#define MALI_C55_AEXP_HIST_NODES_USED_OFFSET 52 +#define MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK GENMASK(7, 0) +#define MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK GENMASK(15, 8) +#define MALI_C55_AEXP_HIST_NODES_USED_VERT(x) ((x) << 8) +#define MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET 56 +#define MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK 0x0f0f0f0f + +/* + * The Mali-C55 ISP has up to two output pipes; known as full resolution and + * down scaled. The register space for these is laid out identically, but offset + * by 372 bytes. + */ +#define MALI_C55_CAP_DEV_FR_REG_OFFSET 0x0 +#define MALI_C55_CAP_DEV_DS_REG_OFFSET 0x174 + +#define MALI_C55_REG_CS_CONV_CONFIG 0x1c098 +#define MALI_C55_CS_CONV_MATRIX_MASK BIT(0) +#define MALI_C55_CS_CONV_FILTER_MASK BIT(1) +#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_MASK BIT(2) +#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_MASK BIT(3) +#define MALI_C55_CS_CONV_FILTER_ENABLE (0x01 << 1) +#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_ENABLE (0x01 << 2) +#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_ENABLE (0x01 << 3) +#define MALI_C55_REG_Y_WRITER_MODE 0x1c0ec +#define MALI_C55_REG_UV_WRITER_MODE 0x1c144 +#define MALI_C55_WRITER_MODE_MASK GENMASK(4, 0) +#define MALI_C55_OUTPUT_DISABLED 0 +#define MALI_C55_OUTPUT_RGB32 1 +#define MALI_C55_OUTPUT_A2R10G10B10 2 +#define MALI_C55_OUTPUT_RGB565 3 +#define MALI_C55_OUTPUT_RGB24 4 +#define MALI_C55_OUTPUT_GEN32 5 +#define MALI_C55_OUTPUT_RAW16 6 +#define MALI_C55_OUTPUT_AYUV 8 +#define MALI_C55_OUTPUT_Y410 9 +#define MALI_C55_OUTPUT_YUY2 10 +#define MALI_C55_OUTPUT_UYVY 11 +#define MALI_C55_OUTPUT_Y210 12 +#define MALI_C55_OUTPUT_NV12_21 13 +#define MALI_C55_OUTPUT_YUV_420_422 17 +#define MALI_C55_OUTPUT_P210_P010 19 +#define MALI_C55_OUTPUT_YUV422 20 +#define MALI_C55_WRITER_SUBMODE_MASK GENMASK(7, 6) +#define MALI_C55_WRITER_SUBMODE(x) ((x) << 6) +#define MALI_C55_OUTPUT_PLANE_ALT0 0 +#define MALI_C55_OUTPUT_PLANE_ALT1 1 +#define MALI_C55_OUTPUT_PLANE_ALT2 2 +#define MALI_C55_WRITER_FRAME_WRITE_MASK BIT(9) +#define MALI_C55_WRITER_FRAME_WRITE_ENABLE (0x01 << 9) +#define MALI_C55_REG_ACTIVE_OUT_Y_SIZE 0x1c0f0 +#define MALI_C55_REG_ACTIVE_OUT_UV_SIZE 0x1c148 +#define MALI_C55_REG_ACTIVE_OUT_SIZE_W(w) ((w) << 0) +#define MALI_C55_REG_ACTIVE_OUT_SIZE_H(h) ((h) << 16) +#define MALI_C55_REG_Y_WRITER_BANKS_BASE 0x1c0f4 +#define MALI_C55_REG_Y_WRITER_BANKS_CONFIG 0x1c108 +#define MALI_C55_REG_Y_WRITER_MAX_BANKS_MASK GENMASK(2, 0) +#define MALI_C55_REG_Y_WRITER_BANKS_RESTART BIT(3) +#define MALI_C55_REG_Y_WRITER_OFFSET 0x1c10c +#define MALI_C55_REG_UV_WRITER_BANKS_BASE 0x1c14c +#define MALI_C55_REG_UV_WRITER_BANKS_CONFIG 0x1c160 +#define MALI_C55_REG_UV_WRITER_MAX_BANKS_MASK GENMASK(2, 0) +#define MALI_C55_REG_UV_WRITER_BANKS_RESTART BIT(3) +#define MALI_C55_REG_UV_WRITER_OFFSET 0x1c164 + +#define MALI_C55_REG_TEST_GEN_CH0_OFF_ON +#define MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE 0x18edc + +#define MALI_C55_REG_CROP_EN 0x1c028 +#define MALI_C55_CROP_ENABLE BIT(0) +#define MALI_C55_REG_CROP_X_START 0x1c02c +#define MALI_C55_REG_CROP_Y_START 0x1c030 +#define MALI_C55_REG_CROP_X_SIZE 0x1c034 +#define MALI_C55_REG_CROP_Y_SIZE 0x1c038 +#define MALI_C55_REG_SCALER_TIMEOUT_EN 0x1c040 +#define MALI_C55_SCALER_TIMEOUT_EN BIT(4) +#define MALI_C55_SCALER_TIMEOUT(t) ((t) << 16) +#define MALI_C55_REG_SCALER_IN_WIDTH 0x1c044 +#define MALI_C55_REG_SCALER_IN_HEIGHT 0x1c048 +#define MALI_C55_REG_SCALER_OUT_WIDTH 0x1c04c +#define MALI_C55_REG_SCALER_OUT_HEIGHT 0x1c050 +#define MALI_C55_REG_SCALER_HFILT_TINC 0x1c054 +#define MALI_C55_REG_SCALER_HFILT_COEF 0x1c058 +#define MALI_C55_REG_SCALER_VFILT_TINC 0x1c05c +#define MALI_C55_REG_SCALER_VFILT_COEF 0x1c060 + +#define MALI_C55_REG_GAMMA_RGB_ENABLE 0x1c064 +#define MALI_C55_GAMMA_ENABLE_MASK BIT(0) +#define MALI_C55_REG_GAMMA_GAINS_1 0x1c068 +#define MALI_C55_GAMMA_GAIN_R_MASK GENMASK(11, 0) +#define MALI_C55_GAMMA_GAIN_G_MASK GENMASK(27, 16) +#define MALI_C55_REG_GAMMA_GAINS_2 0x1c06c +#define MALI_C55_GAMMA_GAIN_B_MASK GENMASK(11, 0) +#define MALI_C55_REG_GAMMA_OFFSETS_1 0x1c070 +#define MALI_C55_GAMMA_OFFSET_R_MASK GENMASK(11, 0) +#define MALI_C55_GAMMA_OFFSET_G_MASK GENMASK(27, 16) +#define MALI_C55_REG_GAMMA_OFFSETS_2 0x1c074 +#define MALI_C55_GAMMA_OFFSET_B_MASK GENMASK(11, 0) + +/* + * A re-definition of an above register. These will usually be written on a per + * capture device basis and handled with mali_c55_cap_dev_write(), but on + * startup is written by core.c + */ +#define MALI_C55_REG_FR_GAMMA_RGB_ENABLE 0x1c064 +#define MALI_C55_REG_DS_GAMMA_RGB_ENABLE 0x1c1d8 + +#define MALI_C55_REG_FR_SCALER_HFILT 0x34a8 +#define MALI_C55_REG_FR_SCALER_VFILT 0x44a8 +#define MALI_C55_REG_DS_SCALER_HFILT 0x14a8 +#define MALI_C55_REG_DS_SCALER_VFILT 0x24a8 + +#endif /* _MALI_C55_REGISTERS_H */ diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c new file mode 100644 index 000000000000..a8d739af74b6 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Image signal processor + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/math.h> +#include <linux/minmax.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +/* Scaling factor in Q4.20 format. */ +#define MALI_C55_RSZ_SCALER_FACTOR (1U << 20) + +#define MALI_C55_RSZ_COEFS_BANKS 8 +#define MALI_C55_RSZ_COEFS_ENTRIES 64 + +static inline struct mali_c55_resizer * +sd_to_mali_c55_rsz(struct v4l2_subdev *sd) +{ + return container_of(sd, struct mali_c55_resizer, sd); +} + +static const unsigned int +mali_c55_rsz_filter_coeffs_h[MALI_C55_RSZ_COEFS_BANKS] + [MALI_C55_RSZ_COEFS_ENTRIES] = { + { /* Bank 0 */ + 0x24fc0000, 0x0000fc24, 0x27fc0000, 0x0000fc21, + 0x28fc0000, 0x0000fd1f, 0x2cfb0000, 0x0000fd1c, + 0x2efb0000, 0x0000fd1a, 0x30fb0000, 0x0000fe17, + 0x32fb0000, 0x0000fe15, 0x35fb0000, 0x0000fe12, + 0x35fc0000, 0x0000ff10, 0x37fc0000, 0x0000ff0e, + 0x39fc0000, 0x0000ff0c, 0x3afd0000, 0x0000ff0a, + 0x3afe0000, 0x00000008, 0x3cfe0000, 0x00000006, + 0x3dff0000, 0x00000004, 0x3d000000, 0x00000003, + 0x3c020000, 0x00000002, 0x3d030000, 0x00000000, + 0x3d040000, 0x000000ff, 0x3c060000, 0x000000fe, + 0x3a080000, 0x000000fe, 0x3a0aff00, 0x000000fd, + 0x390cff00, 0x000000fc, 0x370eff00, 0x000000fc, + 0x3510ff00, 0x000000fc, 0x3512fe00, 0x000000fb, + 0x3215fe00, 0x000000fb, 0x3017fe00, 0x000000fb, + 0x2e1afd00, 0x000000fb, 0x2c1cfd00, 0x000000fb, + 0x281ffd00, 0x000000fc, 0x2721fc00, 0x000000fc, + }, + { /* Bank 1 */ + 0x25fb0000, 0x0000fb25, 0x27fb0000, 0x0000fb23, + 0x29fb0000, 0x0000fb21, 0x2afc0000, 0x0000fb1f, + 0x2cfc0000, 0x0000fb1d, 0x2efc0000, 0x0000fb1b, + 0x2ffd0000, 0x0000fb19, 0x2ffe0000, 0x0000fc17, + 0x31fe0000, 0x0000fc15, 0x32ff0000, 0x0000fc13, + 0x3400ff00, 0x0000fc11, 0x3301ff00, 0x0000fd10, + 0x3402ff00, 0x0000fd0e, 0x3503ff00, 0x0000fd0c, + 0x3505ff00, 0x0000fd0a, 0x3506fe00, 0x0000fe09, + 0x3607fe00, 0x0000fe07, 0x3509fe00, 0x0000fe06, + 0x350afd00, 0x0000ff05, 0x350cfd00, 0x0000ff03, + 0x340efd00, 0x0000ff02, 0x3310fd00, 0x0000ff01, + 0x3411fc00, 0x0000ff00, 0x3213fc00, 0x000000ff, + 0x3115fc00, 0x000000fe, 0x2f17fc00, 0x000000fe, + 0x2f19fb00, 0x000000fd, 0x2e1bfb00, 0x000000fc, + 0x2c1dfb00, 0x000000fc, 0x2a1ffb00, 0x000000fc, + 0x2921fb00, 0x000000fb, 0x2723fb00, 0x000000fb, + }, + { /* Bank 2 */ + 0x1f010000, 0x0000011f, 0x21010000, 0x0000001e, + 0x21020000, 0x0000001d, 0x22020000, 0x0000001c, + 0x23030000, 0x0000ff1b, 0x2404ff00, 0x0000ff1a, + 0x2504ff00, 0x0000ff19, 0x2505ff00, 0x0000ff18, + 0x2606ff00, 0x0000fe17, 0x2607ff00, 0x0000fe16, + 0x2708ff00, 0x0000fe14, 0x2709ff00, 0x0000fe13, + 0x270aff00, 0x0000fe12, 0x280bfe00, 0x0000fe11, + 0x280cfe00, 0x0000fe10, 0x280dfe00, 0x0000fe0f, + 0x280efe00, 0x0000fe0e, 0x280ffe00, 0x0000fe0d, + 0x2810fe00, 0x0000fe0c, 0x2811fe00, 0x0000fe0b, + 0x2712fe00, 0x0000ff0a, 0x2713fe00, 0x0000ff09, + 0x2714fe00, 0x0000ff08, 0x2616fe00, 0x0000ff07, + 0x2617fe00, 0x0000ff06, 0x2518ff00, 0x0000ff05, + 0x2519ff00, 0x0000ff04, 0x241aff00, 0x0000ff04, + 0x231bff00, 0x00000003, 0x221c0000, 0x00000002, + 0x211d0000, 0x00000002, 0x211e0000, 0x00000001, + }, + { /* Bank 3 */ + 0x1b06ff00, 0x00ff061b, 0x1b07ff00, 0x00ff061a, + 0x1c07ff00, 0x00ff051a, 0x1c08ff00, 0x00ff0519, + 0x1c09ff00, 0x00ff0419, 0x1d09ff00, 0x00ff0418, + 0x1e0aff00, 0x00ff0317, 0x1e0aff00, 0x00ff0317, + 0x1e0bff00, 0x00ff0316, 0x1f0cff00, 0x00ff0215, + 0x1e0cff00, 0x00000215, 0x1e0dff00, 0x00000214, + 0x1e0e0000, 0x00000113, 0x1e0e0000, 0x00000113, + 0x1e0f0000, 0x00000112, 0x1f100000, 0x00000011, + 0x20100000, 0x00000010, 0x1f110000, 0x00000010, + 0x1e120100, 0x0000000f, 0x1e130100, 0x0000000e, + 0x1e130100, 0x0000000e, 0x1e140200, 0x0000ff0d, + 0x1e150200, 0x0000ff0c, 0x1f1502ff, 0x0000ff0c, + 0x1e1603ff, 0x0000ff0b, 0x1e1703ff, 0x0000ff0a, + 0x1e1703ff, 0x0000ff0a, 0x1d1804ff, 0x0000ff09, + 0x1c1904ff, 0x0000ff09, 0x1c1905ff, 0x0000ff08, + 0x1c1a05ff, 0x0000ff07, 0x1b1a06ff, 0x0000ff07, + }, + { /* Bank 4 */ + 0x17090000, 0x00000917, 0x18090000, 0x00000916, + 0x170a0100, 0x00000816, 0x170a0100, 0x00000816, + 0x180b0100, 0x00000715, 0x180b0100, 0x00000715, + 0x170c0100, 0x00000715, 0x190c0100, 0x00000614, + 0x180d0100, 0x00000614, 0x190d0200, 0x00000513, + 0x180e0200, 0x00000513, 0x180e0200, 0x00000513, + 0x1a0e0200, 0x00000412, 0x190f0200, 0x00000412, + 0x190f0300, 0x00000411, 0x18100300, 0x00000411, + 0x1a100300, 0x00000310, 0x18110400, 0x00000310, + 0x19110400, 0x0000030f, 0x19120400, 0x0000020f, + 0x1a120400, 0x0000020e, 0x18130500, 0x0000020e, + 0x18130500, 0x0000020e, 0x19130500, 0x0000020d, + 0x18140600, 0x0000010d, 0x19140600, 0x0000010c, + 0x17150700, 0x0000010c, 0x18150700, 0x0000010b, + 0x18150700, 0x0000010b, 0x17160800, 0x0000010a, + 0x17160800, 0x0000010a, 0x18160900, 0x00000009, + }, + { /* Bank 5 */ + 0x120b0300, 0x00030b12, 0x120c0300, 0x00030b11, + 0x110c0400, 0x00030b11, 0x110c0400, 0x00030b11, + 0x130c0400, 0x00020a11, 0x120d0400, 0x00020a11, + 0x110d0500, 0x00020a11, 0x110d0500, 0x00020a11, + 0x130d0500, 0x00010911, 0x130e0500, 0x00010910, + 0x120e0600, 0x00010910, 0x120e0600, 0x00010910, + 0x130e0600, 0x00010810, 0x120f0600, 0x00010810, + 0x120f0700, 0x00000810, 0x130f0700, 0x0000080f, + 0x140f0700, 0x0000070f, 0x130f0800, 0x0000070f, + 0x12100800, 0x0000070f, 0x12100801, 0x0000060f, + 0x13100801, 0x0000060e, 0x12100901, 0x0000060e, + 0x12100901, 0x0000060e, 0x13100901, 0x0000050e, + 0x13110901, 0x0000050d, 0x11110a02, 0x0000050d, + 0x11110a02, 0x0000050d, 0x12110a02, 0x0000040d, + 0x13110a02, 0x0000040c, 0x11110b03, 0x0000040c, + 0x11110b03, 0x0000040c, 0x12110b03, 0x0000030c, + }, + { /* Bank 6 */ + 0x0b0a0805, 0x00080a0c, 0x0b0a0805, 0x00080a0c, + 0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b, + 0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b, + 0x0d0a0805, 0x00070a0b, 0x0c0a0806, 0x00070a0b, + 0x0b0b0806, 0x00070a0b, 0x0c0b0806, 0x0007090b, + 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b, + 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b, + 0x0b0b0906, 0x0007090b, 0x0c0b0906, 0x0006090b, + 0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b, + 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b, + 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b, + 0x0b0b0907, 0x0006090b, 0x0c0b0907, 0x0006080b, + 0x0b0b0a07, 0x0006080b, 0x0c0b0a07, 0x0006080a, + 0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a, + 0x0d0b0a07, 0x0005080a, 0x0c0b0a08, 0x0005080a, + 0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a, + }, + { /* Bank 7 */ + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + 0x0909090a, 0x00090909, 0x0909090a, 0x00090909, + } +}; + +static const unsigned int +mali_c55_rsz_filter_coeffs_v[MALI_C55_RSZ_COEFS_BANKS] + [MALI_C55_RSZ_COEFS_ENTRIES] = { + { /* Bank 0 */ + 0x2424fc00, 0x000000fc, 0x2721fc00, 0x000000fc, + 0x281ffd00, 0x000000fc, 0x2c1cfd00, 0x000000fb, + 0x2e1afd00, 0x000000fb, 0x3017fe00, 0x000000fb, + 0x3215fe00, 0x000000fb, 0x3512fe00, 0x000000fb, + 0x3510ff00, 0x000000fc, 0x370eff00, 0x000000fc, + 0x390cff00, 0x000000fc, 0x3a0aff00, 0x000000fd, + 0x3a080000, 0x000000fe, 0x3c060000, 0x000000fe, + 0x3d040000, 0x000000ff, 0x3d030000, 0x00000000, + 0x3c020000, 0x00000002, 0x3d000000, 0x00000003, + 0x3dff0000, 0x00000004, 0x3cfe0000, 0x00000006, + 0x3afe0000, 0x00000008, 0x3afd0000, 0x0000ff0a, + 0x39fc0000, 0x0000ff0c, 0x37fc0000, 0x0000ff0e, + 0x35fc0000, 0x0000ff10, 0x35fb0000, 0x0000fe12, + 0x32fb0000, 0x0000fe15, 0x30fb0000, 0x0000fe17, + 0x2efb0000, 0x0000fd1a, 0x2cfb0000, 0x0000fd1c, + 0x28fc0000, 0x0000fd1f, 0x27fc0000, 0x0000fc21, + }, + { /* Bank 1 */ + 0x2525fb00, 0x000000fb, 0x2723fb00, 0x000000fb, + 0x2921fb00, 0x000000fb, 0x2a1ffb00, 0x000000fc, + 0x2c1dfb00, 0x000000fc, 0x2e1bfb00, 0x000000fc, + 0x2f19fb00, 0x000000fd, 0x2f17fc00, 0x000000fe, + 0x3115fc00, 0x000000fe, 0x3213fc00, 0x000000ff, + 0x3411fc00, 0x0000ff00, 0x3310fd00, 0x0000ff01, + 0x340efd00, 0x0000ff02, 0x350cfd00, 0x0000ff03, + 0x350afd00, 0x0000ff05, 0x3509fe00, 0x0000fe06, + 0x3607fe00, 0x0000fe07, 0x3506fe00, 0x0000fe09, + 0x3505ff00, 0x0000fd0a, 0x3503ff00, 0x0000fd0c, + 0x3402ff00, 0x0000fd0e, 0x3301ff00, 0x0000fd10, + 0x3400ff00, 0x0000fc11, 0x32ff0000, 0x0000fc13, + 0x31fe0000, 0x0000fc15, 0x2ffe0000, 0x0000fc17, + 0x2ffd0000, 0x0000fb19, 0x2efc0000, 0x0000fb1b, + 0x2cfc0000, 0x0000fb1d, 0x2afc0000, 0x0000fb1f, + 0x29fb0000, 0x0000fb21, 0x27fb0000, 0x0000fb23, + }, + { /* Bank 2 */ + 0x1f1f0100, 0x00000001, 0x211e0000, 0x00000001, + 0x211d0000, 0x00000002, 0x221c0000, 0x00000002, + 0x231bff00, 0x00000003, 0x241aff00, 0x0000ff04, + 0x2519ff00, 0x0000ff04, 0x2518ff00, 0x0000ff05, + 0x2617fe00, 0x0000ff06, 0x2616fe00, 0x0000ff07, + 0x2714fe00, 0x0000ff08, 0x2713fe00, 0x0000ff09, + 0x2712fe00, 0x0000ff0a, 0x2811fe00, 0x0000fe0b, + 0x2810fe00, 0x0000fe0c, 0x280ffe00, 0x0000fe0d, + 0x280efe00, 0x0000fe0e, 0x280dfe00, 0x0000fe0f, + 0x280cfe00, 0x0000fe10, 0x280bfe00, 0x0000fe11, + 0x270aff00, 0x0000fe12, 0x2709ff00, 0x0000fe13, + 0x2708ff00, 0x0000fe14, 0x2607ff00, 0x0000fe16, + 0x2606ff00, 0x0000fe17, 0x2505ff00, 0x0000ff18, + 0x2504ff00, 0x0000ff19, 0x2404ff00, 0x0000ff1a, + 0x23030000, 0x0000ff1b, 0x22020000, 0x0000001c, + 0x21020000, 0x0000001d, 0x21010000, 0x0000001e, + }, + { /* Bank 3 */ + 0x1b1b06ff, 0x0000ff06, 0x1b1a06ff, 0x0000ff07, + 0x1c1a05ff, 0x0000ff07, 0x1c1905ff, 0x0000ff08, + 0x1c1904ff, 0x0000ff09, 0x1d1804ff, 0x0000ff09, + 0x1e1703ff, 0x0000ff0a, 0x1e1703ff, 0x0000ff0a, + 0x1e1603ff, 0x0000ff0b, 0x1f1502ff, 0x0000ff0c, + 0x1e150200, 0x0000ff0c, 0x1e140200, 0x0000ff0d, + 0x1e130100, 0x0000000e, 0x1e130100, 0x0000000e, + 0x1e120100, 0x0000000f, 0x1f110000, 0x00000010, + 0x20100000, 0x00000010, 0x1f100000, 0x00000011, + 0x1e0f0000, 0x00000112, 0x1e0e0000, 0x00000113, + 0x1e0e0000, 0x00000113, 0x1e0dff00, 0x00000214, + 0x1e0cff00, 0x00000215, 0x1f0cff00, 0x00ff0215, + 0x1e0bff00, 0x00ff0316, 0x1e0aff00, 0x00ff0317, + 0x1e0aff00, 0x00ff0317, 0x1d09ff00, 0x00ff0418, + 0x1c09ff00, 0x00ff0419, 0x1c08ff00, 0x00ff0519, + 0x1c07ff00, 0x00ff051a, 0x1b07ff00, 0x00ff061a, + }, + { /* Bank 4 */ + 0x17170900, 0x00000009, 0x18160900, 0x00000009, + 0x17160800, 0x0000010a, 0x17160800, 0x0000010a, + 0x18150700, 0x0000010b, 0x18150700, 0x0000010b, + 0x17150700, 0x0000010c, 0x19140600, 0x0000010c, + 0x18140600, 0x0000010d, 0x19130500, 0x0000020d, + 0x18130500, 0x0000020e, 0x18130500, 0x0000020e, + 0x1a120400, 0x0000020e, 0x19120400, 0x0000020f, + 0x19110400, 0x0000030f, 0x18110400, 0x00000310, + 0x1a100300, 0x00000310, 0x18100300, 0x00000411, + 0x190f0300, 0x00000411, 0x190f0200, 0x00000412, + 0x1a0e0200, 0x00000412, 0x180e0200, 0x00000513, + 0x180e0200, 0x00000513, 0x190d0200, 0x00000513, + 0x180d0100, 0x00000614, 0x190c0100, 0x00000614, + 0x170c0100, 0x00000715, 0x180b0100, 0x00000715, + 0x180b0100, 0x00000715, 0x170a0100, 0x00000816, + 0x170a0100, 0x00000816, 0x18090000, 0x00000916, + }, + { /* Bank 5 */ + 0x12120b03, 0x0000030b, 0x12110b03, 0x0000030c, + 0x11110b03, 0x0000040c, 0x11110b03, 0x0000040c, + 0x13110a02, 0x0000040c, 0x12110a02, 0x0000040d, + 0x11110a02, 0x0000050d, 0x11110a02, 0x0000050d, + 0x13110901, 0x0000050d, 0x13100901, 0x0000050e, + 0x12100901, 0x0000060e, 0x12100901, 0x0000060e, + 0x13100801, 0x0000060e, 0x12100801, 0x0000060f, + 0x12100800, 0x0000070f, 0x130f0800, 0x0000070f, + 0x140f0700, 0x0000070f, 0x130f0700, 0x0000080f, + 0x120f0700, 0x00000810, 0x120f0600, 0x00010810, + 0x130e0600, 0x00010810, 0x120e0600, 0x00010910, + 0x120e0600, 0x00010910, 0x130e0500, 0x00010910, + 0x130d0500, 0x00010911, 0x110d0500, 0x00020a11, + 0x110d0500, 0x00020a11, 0x120d0400, 0x00020a11, + 0x130c0400, 0x00020a11, 0x110c0400, 0x00030b11, + 0x110c0400, 0x00030b11, 0x120c0300, 0x00030b11, + }, + { /* Bank 6 */ + 0x0b0c0a08, 0x0005080a, 0x0b0c0a08, 0x0005080a, + 0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a, + 0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a, + 0x0d0b0a07, 0x0005080a, 0x0c0b0a07, 0x0006080a, + 0x0b0b0a07, 0x0006080b, 0x0c0b0907, 0x0006080b, + 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b, + 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b, + 0x0b0b0907, 0x0006090b, 0x0c0b0906, 0x0006090b, + 0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b, + 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b, + 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b, + 0x0b0b0906, 0x0007090b, 0x0c0b0806, 0x0007090b, + 0x0b0b0806, 0x00070a0b, 0x0c0a0806, 0x00070a0b, + 0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b, + 0x0d0a0805, 0x00070a0b, 0x0c0a0805, 0x00080a0b, + 0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b, + }, + { /* Bank 7 */ + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + 0x09090909, 0x000a0909, 0x09090909, 0x000a0909, + } +}; + +static const unsigned int mali_c55_rsz_coef_banks_range_start[] = { + 770, 600, 460, 354, 273, 210, 162, 125 +}; + +/* + * Select the right filter coefficients bank based on the scaler input and the + * scaler output sizes ratio, set by the v4l2 crop and scale selection + * rectangles respectively. + */ +static unsigned int mali_c55_rsz_calculate_bank(struct mali_c55 *mali_c55, + unsigned int rsz_in, + unsigned int rsz_out) +{ + unsigned int rsz_ratio = (rsz_out * 1000U) / rsz_in; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mali_c55_rsz_coef_banks_range_start); i++) + if (rsz_ratio >= mali_c55_rsz_coef_banks_range_start[i]) + break; + + return i; +} + +static const u32 rsz_non_bypass_src_fmts[] = { + MEDIA_BUS_FMT_RGB121212_1X36, + MEDIA_BUS_FMT_YUV10_1X30 +}; + +static void mali_c55_resizer_program_coefficients(struct mali_c55_resizer *rsz) +{ + struct mali_c55 *mali_c55 = rsz->mali_c55; + unsigned int haddr = rsz->id == MALI_C55_RSZ_FR ? + MALI_C55_REG_FR_SCALER_HFILT : + MALI_C55_REG_DS_SCALER_HFILT; + unsigned int vaddr = rsz->id == MALI_C55_RSZ_FR ? + MALI_C55_REG_FR_SCALER_VFILT : + MALI_C55_REG_DS_SCALER_VFILT; + + for (unsigned int i = 0; i < MALI_C55_RSZ_COEFS_BANKS; i++) { + for (unsigned int j = 0; j < MALI_C55_RSZ_COEFS_ENTRIES; j++) { + mali_c55_write(mali_c55, haddr, + mali_c55_rsz_filter_coeffs_h[i][j]); + mali_c55_write(mali_c55, vaddr, + mali_c55_rsz_filter_coeffs_v[i][j]); + + haddr += sizeof(u32); + vaddr += sizeof(u32); + } + } +} + +static int mali_c55_rsz_program_crop(struct mali_c55_resizer *rsz, + const struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *fmt; + const struct v4l2_rect *crop; + + /* Verify if crop should be enabled. */ + fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD, 0); + crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0); + + if (fmt->width == crop->width && fmt->height == crop->height) + return MALI_C55_BYPASS_CROP; + + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_START, + crop->left); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_START, + crop->top); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_SIZE, + crop->width); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_SIZE, + crop->height); + + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_EN, + MALI_C55_CROP_ENABLE); + + return 0; +} + +static int mali_c55_rsz_program_resizer(struct mali_c55_resizer *rsz, + struct v4l2_subdev_state *state) +{ + struct mali_c55 *mali_c55 = rsz->mali_c55; + const struct v4l2_rect *crop, *scale; + unsigned int h_bank, v_bank; + u64 h_scale, v_scale; + + /* Verify if scaling should be enabled. */ + crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0); + scale = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD, 0); + + if (crop->width == scale->width && crop->height == scale->height) + return MALI_C55_BYPASS_SCALER; + + /* Program the scaler coefficients if the scaler is in use. */ + mali_c55_resizer_program_coefficients(rsz); + + /* Program the V/H scaling factor in Q4.20 format. */ + h_scale = crop->width * MALI_C55_RSZ_SCALER_FACTOR; + v_scale = crop->height * MALI_C55_RSZ_SCALER_FACTOR; + + do_div(h_scale, scale->width); + do_div(v_scale, scale->height); + + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_WIDTH, + crop->width); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_HEIGHT, + crop->height); + + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_WIDTH, + scale->width); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_HEIGHT, + scale->height); + + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_TINC, + h_scale); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_TINC, + v_scale); + + /* Select the scaler coefficients bank to use. */ + h_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->width, + scale->width); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_COEF, + h_bank); + + v_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->height, + scale->height); + mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_COEF, + v_bank); + + return 0; +} + +static void mali_c55_rsz_program(struct mali_c55_resizer *rsz, + struct v4l2_subdev_state *state) +{ + struct mali_c55 *mali_c55 = rsz->mali_c55; + u32 bypass = 0; + + /* Verify if cropping and scaling should be enabled. */ + bypass |= mali_c55_rsz_program_crop(rsz, state); + bypass |= mali_c55_rsz_program_resizer(rsz, state); + + mali_c55_ctx_update_bits(mali_c55, rsz->id == MALI_C55_RSZ_FR ? + MALI_C55_REG_FR_BYPASS : MALI_C55_REG_DS_BYPASS, + MALI_C55_BYPASS_CROP | MALI_C55_BYPASS_SCALER, + bypass); +} + +/* + * Inspect the routing table to know which of the two (mutually exclusive) + * routes is enabled and return the sink pad id of the active route. + */ +static unsigned int mali_c55_rsz_get_active_sink(struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_krouting *routing = &state->routing; + struct v4l2_subdev_route *route; + + /* A single route is enabled at a time. */ + for_each_active_route(routing, route) + return route->sink_pad; + + return MALI_C55_RSZ_SINK_PAD; +} + +/* + * When operating in bypass mode, the ISP takes input in a 20-bit format, but + * can only output 16-bit RAW bayer data (with the 4 least significant bits from + * the input being lost). Return the 16-bit version of the 20-bit input formats. + */ +static u32 mali_c55_rsz_shift_mbus_code(u32 mbus_code) +{ + const struct mali_c55_isp_format_info *fmt = + mali_c55_isp_get_mbus_config_by_code(mbus_code); + + if (!fmt) + return -EINVAL; + + return fmt->shifted_code; +} + +static int __mali_c55_rsz_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + const struct v4l2_subdev_krouting *routing) +{ + struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd); + unsigned int active_sink = UINT_MAX; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_subdev_route *route; + unsigned int active_routes = 0; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, 0); + if (ret) + return ret; + + /* Only a single route can be enabled at a time. */ + for_each_active_route(routing, route) { + if (++active_routes > 1) { + dev_dbg(rsz->mali_c55->dev, + "Only one route can be active"); + return -EINVAL; + } + + active_sink = route->sink_pad; + } + if (active_sink == UINT_MAX) { + dev_dbg(rsz->mali_c55->dev, "One route has to be active"); + return -EINVAL; + } + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) { + dev_dbg(rsz->mali_c55->dev, "Failed to set routing\n"); + return ret; + } + + fmt = v4l2_subdev_state_get_format(state, active_sink, 0); + fmt->width = MALI_C55_DEFAULT_WIDTH; + fmt->height = MALI_C55_DEFAULT_HEIGHT; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->field = V4L2_FIELD_NONE; + + if (active_sink == MALI_C55_RSZ_SINK_PAD) { + struct v4l2_rect *crop, *compose; + + fmt->code = MEDIA_BUS_FMT_RGB121212_1X36; + + crop = v4l2_subdev_state_get_crop(state, active_sink, 0); + compose = v4l2_subdev_state_get_compose(state, active_sink, 0); + + crop->left = 0; + crop->top = 0; + crop->width = MALI_C55_DEFAULT_WIDTH; + crop->height = MALI_C55_DEFAULT_HEIGHT; + + *compose = *crop; + } else { + fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; + } + + /* Propagate the format to the source pad */ + src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD, + 0); + *src_fmt = *fmt; + + /* In the event this is the bypass pad the mbus code needs correcting */ + if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD) + src_fmt->code = mali_c55_rsz_shift_mbus_code(src_fmt->code); + + return 0; +} + +static int mali_c55_rsz_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct mali_c55_isp_format_info *fmt; + struct v4l2_mbus_framefmt *sink_fmt; + u32 sink_pad; + + switch (code->pad) { + case MALI_C55_RSZ_SINK_PAD: + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_RGB121212_1X36; + + return 0; + case MALI_C55_RSZ_SOURCE_PAD: + sink_pad = mali_c55_rsz_get_active_sink(state); + sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0); + + /* + * If the active route is from the Bypass sink pad, then the + * source pad is a simple passthrough of the sink format, + * downshifted to 16-bits. + */ + + if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) { + if (code->index) + return -EINVAL; + + code->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code); + if (!code->code) + return -EINVAL; + + return 0; + } + + /* + * If the active route is from the non-bypass sink then we can + * select either RGB or conversion to YUV. + */ + + if (code->index >= ARRAY_SIZE(rsz_non_bypass_src_fmts)) + return -EINVAL; + + code->code = rsz_non_bypass_src_fmts[code->index]; + + return 0; + case MALI_C55_RSZ_SINK_BYPASS_PAD: + fmt = mali_c55_isp_get_mbus_config_by_index(code->index); + if (fmt) { + code->code = fmt->code; + return 0; + } + + break; + } + + return -EINVAL; +} + +static int mali_c55_rsz_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct mali_c55_isp_format_info *fmt; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *compose; + u32 sink_pad; + + switch (fse->pad) { + case MALI_C55_RSZ_SINK_PAD: + if (fse->index || fse->code != MEDIA_BUS_FMT_RGB121212_1X36) + return -EINVAL; + + fse->max_width = MALI_C55_MAX_WIDTH; + fse->max_height = MALI_C55_MAX_HEIGHT; + fse->min_width = MALI_C55_MIN_WIDTH; + fse->min_height = MALI_C55_MIN_HEIGHT; + + return 0; + case MALI_C55_RSZ_SOURCE_PAD: + sink_pad = mali_c55_rsz_get_active_sink(state); + sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0); + + if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) { + if (fse->index) + return -EINVAL; + + fmt = mali_c55_isp_get_mbus_config_by_shifted_code(fse->code); + if (!fmt) + return -EINVAL; + + fse->min_width = sink_fmt->width; + fse->max_width = sink_fmt->width; + fse->min_height = sink_fmt->height; + fse->max_height = sink_fmt->height; + + return 0; + } + + if ((fse->code != MEDIA_BUS_FMT_RGB121212_1X36 && + fse->code != MEDIA_BUS_FMT_YUV10_1X30) || fse->index > 1) + return -EINVAL; + + compose = v4l2_subdev_state_get_compose(state, + MALI_C55_RSZ_SINK_PAD, + 0); + + fse->min_width = compose->width; + fse->max_width = compose->width; + fse->min_height = compose->height; + fse->max_height = compose->height; + + return 0; + case MALI_C55_RSZ_SINK_BYPASS_PAD: + fmt = mali_c55_isp_get_mbus_config_by_code(fse->code); + if (fse->index || !fmt) + return -EINVAL; + + fse->max_width = MALI_C55_MAX_WIDTH; + fse->max_height = MALI_C55_MAX_HEIGHT; + fse->min_width = MALI_C55_MIN_WIDTH; + fse->min_height = MALI_C55_MIN_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int mali_c55_rsz_set_sink_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct v4l2_mbus_framefmt *sink_fmt; + unsigned int active_sink; + struct v4l2_rect *rect; + + sink_fmt = v4l2_subdev_state_get_format(state, format->pad, 0); + + /* + * Clamp to min/max and then reset crop and compose rectangles to the + * newly applied size. + */ + sink_fmt->width = clamp_t(unsigned int, fmt->width, + MALI_C55_MIN_WIDTH, MALI_C55_MAX_WIDTH); + sink_fmt->height = clamp_t(unsigned int, fmt->height, + MALI_C55_MIN_HEIGHT, MALI_C55_MAX_HEIGHT); + + /* + * Make sure the media bus code for the bypass pad is one of the + * supported ISP input media bus codes. Default it to SRGGB otherwise. + */ + if (format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD) + sink_fmt->code = mali_c55_isp_get_mbus_config_by_code(fmt->code) ? + fmt->code : MEDIA_BUS_FMT_SRGGB20_1X20; + + *fmt = *sink_fmt; + + if (format->pad == MALI_C55_RSZ_SINK_PAD) { + rect = v4l2_subdev_state_get_crop(state, format->pad); + rect->left = 0; + rect->top = 0; + rect->width = fmt->width; + rect->height = fmt->height; + + rect = v4l2_subdev_state_get_compose(state, format->pad); + rect->left = 0; + rect->top = 0; + rect->width = fmt->width; + rect->height = fmt->height; + } + + /* If format->pad is routed to the source pad, propagate the format. */ + active_sink = mali_c55_rsz_get_active_sink(state); + if (active_sink == format->pad) { + /* If the bypass route is used, downshift the code to 16bpp. */ + if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD) + fmt->code = mali_c55_rsz_shift_mbus_code(fmt->code); + + *v4l2_subdev_state_get_format(state, + MALI_C55_RSZ_SOURCE_PAD, 0) = *fmt; + } + + return 0; +} + +static int mali_c55_rsz_set_source_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + unsigned int active_sink; + + active_sink = mali_c55_rsz_get_active_sink(state); + sink_fmt = v4l2_subdev_state_get_format(state, active_sink, 0); + src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD); + + if (active_sink == MALI_C55_RSZ_SINK_PAD) { + /* + * Regular processing pipe: RGB121212 can be color-space + * converted to YUV101010. + */ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rsz_non_bypass_src_fmts); i++) { + if (fmt->code == rsz_non_bypass_src_fmts[i]) + break; + } + + src_fmt->code = i == ARRAY_SIZE(rsz_non_bypass_src_fmts) ? + MEDIA_BUS_FMT_RGB121212_1X36 : fmt->code; + } else { + /* + * Bypass pipe: the source format is the same as the bypass + * sink pad downshifted to 16bpp. + */ + fmt->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code); + } + + *fmt = *src_fmt; + + return 0; +} + +static int mali_c55_rsz_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + /* + * On sink pads fmt is either fixed for the 'regular' processing + * pad or a RAW format or 20-bit wide RGB/YUV format for the FR bypass + * pad. + * + * On source pad sizes are the result of crop+compose on the sink + * pad sizes, while the format depends on the active route. + */ + + if (format->pad == MALI_C55_RSZ_SINK_PAD || + format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD) + return mali_c55_rsz_set_sink_fmt(sd, state, format); + + return mali_c55_rsz_set_source_fmt(sd, state, format); +} + +static int mali_c55_rsz_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad != MALI_C55_RSZ_SINK_PAD) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + sel->r = sel->target == V4L2_SEL_TGT_CROP + ? *v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD) + : *v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD); + + return 0; +} + +static int mali_c55_rsz_set_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd); + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect *crop, *compose; + + sink_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD); + crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD); + compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + v4l2_subdev_is_streaming(sd)) { + /* + * At runtime the compose rectangle and output size cannot be + * changed so we need to clamp the crop rectangle such that the + * compose rectangle can fit within it. + */ + crop->left = clamp_t(unsigned int, sel->r.left, 0, + sink_fmt->width - compose->width); + crop->top = clamp_t(unsigned int, sel->r.top, 0, + sink_fmt->height - compose->height); + crop->width = clamp_t(unsigned int, sel->r.width, compose->width, + sink_fmt->width - crop->left); + crop->height = clamp_t(unsigned int, sel->r.height, compose->height, + sink_fmt->height - crop->top); + + mali_c55_rsz_program(rsz, state); + } else { + /* + * If we're not streaming we can utilise the ISP's full range + * and simply need to propagate the selected rectangle to the + * compose target and source pad format. + */ + crop->left = clamp_t(unsigned int, sel->r.left, 0, + sink_fmt->width); + crop->top = clamp_t(unsigned int, sel->r.top, 0, + sink_fmt->height); + crop->width = clamp_t(unsigned int, sel->r.width, + MALI_C55_MIN_WIDTH, + sink_fmt->width - crop->left); + crop->height = clamp_t(unsigned int, sel->r.height, + MALI_C55_MIN_HEIGHT, + sink_fmt->height - crop->top); + + *compose = *crop; + + src_fmt = v4l2_subdev_state_get_format(state, + MALI_C55_RSZ_SOURCE_PAD); + src_fmt->width = compose->width; + src_fmt->height = compose->height; + } + + sel->r = *crop; + return 0; +} + +static int mali_c55_rsz_set_compose(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd); + struct mali_c55 *mali_c55 = rsz->mali_c55; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_rect *compose, *crop; + + /* + * We cannot change the compose rectangle during streaming, as that + * would require a change in the output buffer size. + */ + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + /* + * In the FR pipe, the scaler is an optional component that may not be + * fitted. + */ + if (rsz->id == MALI_C55_RSZ_FR && + !(mali_c55->capabilities & MALI_C55_GPS_FRSCALER_FITTED)) + return -EINVAL; + + compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD); + crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD); + + compose->left = 0; + compose->top = 0; + compose->width = clamp_t(unsigned int, sel->r.width, crop->width / 8, + crop->width); + compose->height = clamp_t(unsigned int, sel->r.height, crop->height / 8, + crop->height); + + sel->r = *compose; + + /* + * We need to be sure to propagate the compose rectangle size to the + * source pad format. + */ + src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD); + src_fmt->width = compose->width; + src_fmt->height = compose->height; + + return 0; +} + +static int mali_c55_rsz_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad != MALI_C55_RSZ_SINK_PAD) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP) + return mali_c55_rsz_set_crop(sd, state, sel); + + if (sel->target == V4L2_SEL_TGT_COMPOSE) + return mali_c55_rsz_set_compose(sd, state, sel); + + return -EINVAL; +} + +static int mali_c55_rsz_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && + media_entity_is_streaming(&sd->entity)) + return -EBUSY; + + return __mali_c55_rsz_set_routing(sd, state, routing); +} + +static int mali_c55_rsz_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd); + struct mali_c55 *mali_c55 = rsz->mali_c55; + unsigned int sink_pad; + + sink_pad = mali_c55_rsz_get_active_sink(state); + if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) { + /* Bypass FR pipe processing if the bypass route is active. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS, + MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK, + MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS); + return 0; + } + + /* Disable bypass and use regular processing. */ + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS, + MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK, 0); + mali_c55_rsz_program(rsz, state); + + return 0; +} + +static int mali_c55_rsz_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + return 0; +} + +static const struct v4l2_subdev_pad_ops mali_c55_resizer_pad_ops = { + .enum_mbus_code = mali_c55_rsz_enum_mbus_code, + .enum_frame_size = mali_c55_rsz_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mali_c55_rsz_set_fmt, + .get_selection = mali_c55_rsz_get_selection, + .set_selection = mali_c55_rsz_set_selection, + .set_routing = mali_c55_rsz_set_routing, + .enable_streams = mali_c55_rsz_enable_streams, + .disable_streams = mali_c55_rsz_disable_streams, +}; + +static const struct v4l2_subdev_ops mali_c55_resizer_ops = { + .pad = &mali_c55_resizer_pad_ops, +}; + +static int mali_c55_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd); + struct v4l2_subdev_route routes[2] = { + { + .sink_pad = MALI_C55_RSZ_SINK_PAD, + .source_pad = MALI_C55_RSZ_SOURCE_PAD, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, { + .sink_pad = MALI_C55_RSZ_SINK_BYPASS_PAD, + .source_pad = MALI_C55_RSZ_SOURCE_PAD, + }, + }; + struct v4l2_subdev_krouting routing = { + .num_routes = rsz->num_routes, + .routes = routes, + }; + + return __mali_c55_rsz_set_routing(sd, state, &routing); +} + +static const struct v4l2_subdev_internal_ops mali_c55_resizer_internal_ops = { + .init_state = mali_c55_rsz_init_state, +}; + +static int mali_c55_register_resizer(struct mali_c55 *mali_c55, + unsigned int index) +{ + struct mali_c55_resizer *rsz = &mali_c55->resizers[index]; + struct v4l2_subdev *sd = &rsz->sd; + unsigned int num_pads; + int ret; + + rsz->id = index; + v4l2_subdev_init(sd, &mali_c55_resizer_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + sd->internal_ops = &mali_c55_resizer_internal_ops; + + rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK; + rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; + + if (rsz->id == MALI_C55_RSZ_FR) { + num_pads = MALI_C55_RSZ_NUM_PADS; + rsz->num_routes = 2; + + rsz->pads[MALI_C55_RSZ_SINK_BYPASS_PAD].flags = + MEDIA_PAD_FL_SINK; + + snprintf(sd->name, sizeof(sd->name), "%s resizer fr", + MALI_C55_DRIVER_NAME); + + } else { + num_pads = MALI_C55_RSZ_NUM_PADS - 1; + rsz->num_routes = 1; + + snprintf(sd->name, sizeof(sd->name), "%s resizer ds", + MALI_C55_DRIVER_NAME); + } + + ret = media_entity_pads_init(&sd->entity, num_pads, rsz->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_media_cleanup; + + ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd); + if (ret) + goto err_subdev_cleanup; + + rsz->cap_dev = &mali_c55->cap_devs[index]; + rsz->mali_c55 = mali_c55; + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_media_cleanup: + media_entity_cleanup(&sd->entity); + + return ret; +} + +static void mali_c55_unregister_resizer(struct mali_c55_resizer *rsz) +{ + if (!rsz->mali_c55) + return; + + v4l2_device_unregister_subdev(&rsz->sd); + v4l2_subdev_cleanup(&rsz->sd); + media_entity_cleanup(&rsz->sd.entity); +} + +int mali_c55_register_resizers(struct mali_c55 *mali_c55) +{ + int ret; + + ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_FR); + if (ret) + return ret; + + if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) { + ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_DS); + if (ret) + goto err_unregister_fr; + } + + return 0; + +err_unregister_fr: + mali_c55_unregister_resizer(&mali_c55->resizers[MALI_C55_RSZ_FR]); + + return ret; +} + +void mali_c55_unregister_resizers(struct mali_c55 *mali_c55) +{ + for (unsigned int i = 0; i < MALI_C55_NUM_RSZS; i++) + mali_c55_unregister_resizer(&mali_c55->resizers[i]); +} diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-stats.c b/drivers/media/platform/arm/mali-c55/mali-c55-stats.c new file mode 100644 index 000000000000..655e52a5f288 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-stats.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - 3A Statistics capture device + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/container_of.h> +#include <linux/dev_printk.h> +#include <linux/list.h> +#include <linux/media/arm/mali-c55-config.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/spinlock.h> +#include <linux/string.h> + +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +static const unsigned int metering_space_addrs[] = { + [MALI_C55_CONFIG_PING] = 0x095ac, + [MALI_C55_CONFIG_PONG] = 0x2156c, +}; + +static int mali_c55_stats_enum_fmt_meta_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_MALI_C55_STATS; + + return 0; +} + +static int mali_c55_stats_g_fmt_meta_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + static const struct v4l2_meta_format mfmt = { + .dataformat = V4L2_META_FMT_MALI_C55_STATS, + .buffersize = sizeof(struct mali_c55_stats_buffer) + }; + + f->fmt.meta = mfmt; + + return 0; +} + +static int mali_c55_stats_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops mali_c55_stats_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_cap = mali_c55_stats_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap, + .vidioc_querycap = mali_c55_stats_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations mali_c55_stats_v4l2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static int +mali_c55_stats_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + if (*num_planes && *num_planes > 1) + return -EINVAL; + + if (sizes[0] && sizes[0] < sizeof(struct mali_c55_stats_buffer)) + return -EINVAL; + + *num_planes = 1; + + if (!sizes[0]) + sizes[0] = sizeof(struct mali_c55_stats_buffer); + + return 0; +} + +static void mali_c55_stats_buf_queue(struct vb2_buffer *vb) +{ + struct mali_c55_stats *stats = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mali_c55_stats_buf *buf = container_of(vbuf, + struct mali_c55_stats_buf, vb); + + vb2_set_plane_payload(vb, 0, sizeof(struct mali_c55_stats_buffer)); + buf->segments_remaining = 2; + buf->failed = false; + + spin_lock(&stats->buffers.lock); + list_add_tail(&buf->queue, &stats->buffers.queue); + spin_unlock(&stats->buffers.lock); +} + +static void mali_c55_stats_return_buffers(struct mali_c55_stats *stats, + enum vb2_buffer_state state) +{ + struct mali_c55_stats_buf *buf, *tmp; + + guard(spinlock)(&stats->buffers.lock); + + list_for_each_entry_safe(buf, tmp, &stats->buffers.queue, queue) { + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int mali_c55_stats_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct mali_c55_stats *stats = vb2_get_drv_priv(q); + struct mali_c55 *mali_c55 = stats->mali_c55; + int ret; + + ret = pm_runtime_resume_and_get(mali_c55->dev); + if (ret) + goto err_return_buffers; + + ret = video_device_pipeline_alloc_start(&stats->vdev); + if (ret) + goto err_pm_put; + + if (mali_c55_pipeline_ready(mali_c55)) { + ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + if (ret < 0) + goto err_stop_pipeline; + } + + return 0; + +err_stop_pipeline: + video_device_pipeline_stop(&stats->vdev); +err_pm_put: + pm_runtime_put_autosuspend(mali_c55->dev); +err_return_buffers: + mali_c55_stats_return_buffers(stats, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void mali_c55_stats_stop_streaming(struct vb2_queue *q) +{ + struct mali_c55_stats *stats = vb2_get_drv_priv(q); + struct mali_c55 *mali_c55 = stats->mali_c55; + struct mali_c55_isp *isp = &mali_c55->isp; + + if (mali_c55_pipeline_ready(mali_c55)) { + if (v4l2_subdev_is_streaming(&isp->sd)) + v4l2_subdev_disable_streams(&isp->sd, + MALI_C55_ISP_PAD_SOURCE_VIDEO, + BIT(0)); + } + + video_device_pipeline_stop(&stats->vdev); + mali_c55_stats_return_buffers(stats, VB2_BUF_STATE_ERROR); + + pm_runtime_put_autosuspend(stats->mali_c55->dev); +} + +static const struct vb2_ops mali_c55_stats_vb2_ops = { + .queue_setup = mali_c55_stats_queue_setup, + .buf_queue = mali_c55_stats_buf_queue, + .start_streaming = mali_c55_stats_start_streaming, + .stop_streaming = mali_c55_stats_stop_streaming, +}; + +static void mali_c55_stats_cpu_read(struct mali_c55_stats *stats, + struct mali_c55_stats_buf *buf, + enum mali_c55_config_spaces cfg_space) +{ + struct mali_c55 *mali_c55 = stats->mali_c55; + const void __iomem *src; + size_t length; + void *dst; + + src = mali_c55->base + MALI_C55_REG_1024BIN_HIST; + dst = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + memcpy_fromio(dst, src, MALI_C55_1024BIN_HIST_SIZE); + + src = mali_c55->base + metering_space_addrs[cfg_space]; + dst += MALI_C55_1024BIN_HIST_SIZE; + length = sizeof(struct mali_c55_stats_buffer) - MALI_C55_1024BIN_HIST_SIZE; + memcpy_fromio(dst, src, length); +} + +void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55, + enum mali_c55_config_spaces cfg_space) +{ + struct mali_c55_stats *stats = &mali_c55->stats; + struct mali_c55_stats_buf *buf = NULL; + + spin_lock(&stats->buffers.lock); + if (!list_empty(&stats->buffers.queue)) { + buf = list_first_entry(&stats->buffers.queue, + struct mali_c55_stats_buf, queue); + list_del(&buf->queue); + } + spin_unlock(&stats->buffers.lock); + + if (!buf) + return; + + buf->vb.sequence = mali_c55->isp.frame_sequence; + buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns(); + + mali_c55_stats_cpu_read(stats, buf, cfg_space); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +void mali_c55_unregister_stats(struct mali_c55 *mali_c55) +{ + struct mali_c55_stats *stats = &mali_c55->stats; + + if (!video_is_registered(&stats->vdev)) + return; + + vb2_video_unregister_device(&stats->vdev); + media_entity_cleanup(&stats->vdev.entity); + + mutex_destroy(&stats->lock); +} + +int mali_c55_register_stats(struct mali_c55 *mali_c55) +{ + struct mali_c55_stats *stats = &mali_c55->stats; + struct video_device *vdev = &stats->vdev; + struct vb2_queue *vb2q = &stats->queue; + int ret; + + mutex_init(&stats->lock); + INIT_LIST_HEAD(&stats->buffers.queue); + spin_lock_init(&stats->buffers.lock); + + stats->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&stats->vdev.entity, 1, &stats->pad); + if (ret) + goto err_destroy_mutex; + + vb2q->type = V4L2_BUF_TYPE_META_CAPTURE; + vb2q->io_modes = VB2_MMAP | VB2_DMABUF; + vb2q->drv_priv = stats; + vb2q->mem_ops = &vb2_dma_contig_memops; + vb2q->ops = &mali_c55_stats_vb2_ops; + vb2q->buf_struct_size = sizeof(struct mali_c55_stats_buf); + vb2q->min_queued_buffers = 1; + vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2q->lock = &stats->lock; + vb2q->dev = mali_c55->dev; + + ret = vb2_queue_init(vb2q); + if (ret) { + dev_err(mali_c55->dev, "stats vb2 queue init failed\n"); + goto err_cleanup_entity; + } + + strscpy(stats->vdev.name, "mali-c55 3a stats", sizeof(stats->vdev.name)); + vdev->release = video_device_release_empty; + vdev->fops = &mali_c55_stats_v4l2_fops; + vdev->ioctl_ops = &mali_c55_stats_v4l2_ioctl_ops; + vdev->lock = &stats->lock; + vdev->v4l2_dev = &mali_c55->v4l2_dev; + vdev->queue = &stats->queue; + vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + video_set_drvdata(vdev, stats); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(mali_c55->dev, + "failed to register stats video device\n"); + goto err_release_vb2q; + } + + stats->mali_c55 = mali_c55; + + return 0; + +err_release_vb2q: + vb2_queue_release(vb2q); +err_cleanup_entity: + media_entity_cleanup(&stats->vdev.entity); +err_destroy_mutex: + + mutex_destroy(&stats->lock); + + return ret; +} diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c new file mode 100644 index 000000000000..1af5d2759a83 --- /dev/null +++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM Mali-C55 ISP Driver - Test pattern generator + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/minmax.h> +#include <linux/pm_runtime.h> +#include <linux/string.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "mali-c55-common.h" +#include "mali-c55-registers.h" + +#define MALI_C55_TPG_SRC_PAD 0 +#define MALI_C55_TPG_FIXED_HBLANK 0x20 +#define MALI_C55_TPG_DEFAULT_MIN_VBLANK 66 +#define MALI_C55_TPG_DEFAULT_DEF_VBLANK 626 +#define MALI_C55_TPG_MAX_VBLANK 0xffff +#define MALI_C55_TPG_PIXEL_RATE 100000000 + +static const char * const mali_c55_tpg_test_pattern_menu[] = { + "Flat field", + "Horizontal gradient", + "Vertical gradient", + "Vertical bars", + "Arbitrary rectangle", + "White frame on black field" +}; + +static const u32 mali_c55_tpg_mbus_codes[] = { + MEDIA_BUS_FMT_SRGGB20_1X20, + MEDIA_BUS_FMT_RGB202020_1X60, +}; + +static void mali_c55_tpg_update_vblank(struct mali_c55_tpg *tpg, + struct v4l2_mbus_framefmt *format) +{ + unsigned int def_vblank; + unsigned int min_vblank; + unsigned int hts; + int tgt_fps; + + hts = format->width + MALI_C55_TPG_FIXED_HBLANK; + + /* + * The ISP has minimum vertical blanking requirements that must be + * adhered to by the TPG. The minimum is a function of the Iridix blocks + * clocking requirements and the width of the image and horizontal + * blanking, but if we assume the worst case iVariance and sVariance + * values then it boils down to the below (plus one to the numerator to + * ensure the answer is rounded up). + */ + min_vblank = 15 + (120501 / hts); + + /* + * We need to set a sensible default vblank for whatever format height + * we happen to be given from set_fmt(). This function just targets + * an even multiple of 15fps. If we can't get 15fps, let's target 5fps. + * If we can't get 5fps we'll take whatever the minimum vblank gives us. + */ + tgt_fps = MALI_C55_TPG_PIXEL_RATE / hts / (format->height + min_vblank); + + if (tgt_fps < 5) + def_vblank = min_vblank; + else + def_vblank = (MALI_C55_TPG_PIXEL_RATE / hts + / max(rounddown(tgt_fps, 15), 5)) - format->height; + + def_vblank = ALIGN_DOWN(def_vblank, 2); + + __v4l2_ctrl_modify_range(tpg->ctrls.vblank, min_vblank, + MALI_C55_TPG_MAX_VBLANK, 1, def_vblank); + __v4l2_ctrl_s_ctrl(tpg->ctrls.vblank, def_vblank); +} + +static int mali_c55_tpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mali_c55_tpg *tpg = container_of(ctrl->handler, + struct mali_c55_tpg, + ctrls.handler); + struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg); + int ret = 0; + + if (!pm_runtime_get_if_in_use(mali_c55->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + mali_c55_ctx_write(mali_c55, + MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE, + ctrl->val); + break; + case V4L2_CID_VBLANK: + mali_c55_update_bits(mali_c55, MALI_C55_REG_BLANKING, + MALI_C55_REG_VBLANK_MASK, + MALI_C55_VBLANK(ctrl->val)); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put_autosuspend(mali_c55->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops mali_c55_tpg_ctrl_ops = { + .s_ctrl = &mali_c55_tpg_s_ctrl, +}; + +static void mali_c55_tpg_configure(struct mali_c55_tpg *tpg, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + u32 test_pattern_format; + + /* + * hblank needs setting, but is a read-only control and thus won't be + * called during __v4l2_ctrl_handler_setup(). Do it here instead. + */ + mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_BLANKING, + MALI_C55_REG_HBLANK_MASK, + MALI_C55_TPG_FIXED_HBLANK); + mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_GEN_VIDEO, + MALI_C55_REG_GEN_VIDEO_MULTI_MASK, + MALI_C55_REG_GEN_VIDEO_MULTI_MASK); + + fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD); + + test_pattern_format = fmt->code == MEDIA_BUS_FMT_RGB202020_1X60 ? + 0x01 : 0x0; + + mali_c55_ctx_update_bits(tpg->mali_c55, MALI_C55_REG_TPG_CH0, + MALI_C55_TEST_PATTERN_RGB_MASK, + MALI_C55_TEST_PATTERN_RGB(test_pattern_format)); +} + +static int mali_c55_tpg_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(mali_c55_tpg_mbus_codes)) + return -EINVAL; + + code->code = mali_c55_tpg_mbus_codes[code->index]; + + return 0; +} + +static int mali_c55_tpg_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + unsigned int i; + + if (fse->index > 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) { + if (fse->code == mali_c55_tpg_mbus_codes[i]) + break; + } + + if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes)) + return -EINVAL; + + fse->min_width = MALI_C55_MIN_WIDTH; + fse->max_width = MALI_C55_MAX_WIDTH; + fse->min_height = MALI_C55_MIN_HEIGHT; + fse->max_height = MALI_C55_MAX_HEIGHT; + + return 0; +} + +static int mali_c55_tpg_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd); + struct v4l2_mbus_framefmt *fmt; + unsigned int i; + + fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD); + fmt->code = format->format.code; + + for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) { + if (fmt->code == mali_c55_tpg_mbus_codes[i]) + break; + } + + if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes)) + fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; + + /* + * The TPG says that the test frame timing generation logic expects a + * minimum framesize of 4x4 pixels, but given the rest of the ISP can't + * handle anything smaller than 128x128 it seems pointless to allow a + * smaller frame. + */ + fmt->width = clamp(format->format.width, MALI_C55_MIN_WIDTH, + MALI_C55_MAX_WIDTH); + fmt->height = clamp(format->format.height, MALI_C55_MIN_HEIGHT, + MALI_C55_MAX_HEIGHT); + + format->format = *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + mali_c55_tpg_update_vblank(tpg, fmt); + + return 0; +} + +static int mali_c55_tpg_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd); + struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg); + + /* + * We only have a source pad and a single stream, and v4l2-core already + * validated both so we don't need to do that. One might reasonably + * expect the framesize to be set here given it's configurable in + * .set_fmt(), but it's done in the ISP subdevice's .enable_streams() + * instead, as the same register is also used to indicate the size of + * the data coming from the sensor. + */ + mali_c55_tpg_configure(tpg, state); + __v4l2_ctrl_handler_setup(sd->ctrl_handler); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0, + MALI_C55_TEST_PATTERN_ON_OFF, + MALI_C55_TEST_PATTERN_ON_OFF); + mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO, + MALI_C55_REG_GEN_VIDEO_ON_MASK, + MALI_C55_REG_GEN_VIDEO_ON_MASK); + + return 0; +} + +static int mali_c55_tpg_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd); + struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0, + MALI_C55_TEST_PATTERN_ON_OFF, 0x00); + mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO, + MALI_C55_REG_GEN_VIDEO_ON_MASK, 0x00); + + return 0; +} + +static const struct v4l2_subdev_pad_ops mali_c55_tpg_pad_ops = { + .enum_mbus_code = mali_c55_tpg_enum_mbus_code, + .enum_frame_size = mali_c55_tpg_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = mali_c55_tpg_set_fmt, + .enable_streams = mali_c55_tpg_enable_streams, + .disable_streams = mali_c55_tpg_disable_streams, +}; + +static const struct v4l2_subdev_core_ops mali_c55_isp_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops mali_c55_tpg_ops = { + .core = &mali_c55_isp_core_ops, + .pad = &mali_c55_tpg_pad_ops, +}; + +static int mali_c55_tpg_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD); + + fmt->width = MALI_C55_DEFAULT_WIDTH; + fmt->height = MALI_C55_DEFAULT_HEIGHT; + fmt->field = V4L2_FIELD_NONE; + fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false, + fmt->colorspace, + fmt->ycbcr_enc); + + return 0; +} + +static const struct v4l2_subdev_internal_ops mali_c55_tpg_internal_ops = { + .init_state = mali_c55_tpg_init_state, +}; + +static int mali_c55_tpg_init_controls(struct mali_c55 *mali_c55) +{ + struct mali_c55_tpg_ctrls *ctrls = &mali_c55->tpg.ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + int ret; + + ret = v4l2_ctrl_handler_init(&ctrls->handler, 4); + if (ret) + return ret; + + v4l2_ctrl_new_std_menu_items(&ctrls->handler, &mali_c55_tpg_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mali_c55_tpg_test_pattern_menu) - 1, + 0, 3, mali_c55_tpg_test_pattern_menu); + + /* + * We fix hblank at the minimum allowed value and control framerate + * solely through the vblank control. + */ + hblank = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops, + V4L2_CID_HBLANK, MALI_C55_TPG_FIXED_HBLANK, + MALI_C55_TPG_FIXED_HBLANK, 1, + MALI_C55_TPG_FIXED_HBLANK); + if (hblank) + hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrls->vblank = v4l2_ctrl_new_std(&ctrls->handler, + &mali_c55_tpg_ctrl_ops, + V4L2_CID_VBLANK, + MALI_C55_TPG_DEFAULT_MIN_VBLANK, + MALI_C55_TPG_MAX_VBLANK, 1, + MALI_C55_TPG_DEFAULT_DEF_VBLANK); + + pixel_rate = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops, + V4L2_CID_PIXEL_RATE, + MALI_C55_TPG_PIXEL_RATE, + MALI_C55_TPG_PIXEL_RATE, 1, + MALI_C55_TPG_PIXEL_RATE); + if (pixel_rate) + pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrls->handler.error) { + dev_err(mali_c55->dev, "Error during v4l2 controls init\n"); + v4l2_ctrl_handler_free(&ctrls->handler); + return ctrls->handler.error; + } + + mali_c55->tpg.sd.ctrl_handler = &ctrls->handler; + mali_c55->tpg.sd.state_lock = ctrls->handler.lock; + + return 0; +} + +int mali_c55_register_tpg(struct mali_c55 *mali_c55) +{ + struct mali_c55_tpg *tpg = &mali_c55->tpg; + struct v4l2_subdev *sd = &tpg->sd; + struct media_pad *pad = &tpg->pad; + int ret; + + v4l2_subdev_init(sd, &mali_c55_tpg_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + sd->internal_ops = &mali_c55_tpg_internal_ops; + strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name)); + + pad->flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 1, pad); + if (ret) { + dev_err(mali_c55->dev, + "Failed to initialize media entity pads\n"); + return ret; + } + + ret = mali_c55_tpg_init_controls(mali_c55); + if (ret) { + dev_err(mali_c55->dev, + "Error initialising controls\n"); + goto err_cleanup_media_entity; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_free_ctrl_handler; + + ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd); + if (ret) { + dev_err(mali_c55->dev, "Failed to register tpg subdev\n"); + goto err_cleanup_subdev; + } + + /* + * By default the colour settings lead to a very dim image that is + * nearly indistinguishable from black on some monitor settings. Ramp + * them up a bit so the image is brighter. + */ + mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_R_BACKGROUND, + MALI_C55_TPG_BACKGROUND_MAX); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_G_BACKGROUND, + MALI_C55_TPG_BACKGROUND_MAX); + mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_B_BACKGROUND, + MALI_C55_TPG_BACKGROUND_MAX); + + tpg->mali_c55 = mali_c55; + + return 0; + +err_cleanup_subdev: + v4l2_subdev_cleanup(sd); +err_free_ctrl_handler: + v4l2_ctrl_handler_free(&tpg->ctrls.handler); +err_cleanup_media_entity: + media_entity_cleanup(&sd->entity); + + return ret; +} + +void mali_c55_unregister_tpg(struct mali_c55 *mali_c55) +{ + struct mali_c55_tpg *tpg = &mali_c55->tpg; + + if (!tpg->mali_c55) + return; + + v4l2_device_unregister_subdev(&tpg->sd); + v4l2_ctrl_handler_free(&tpg->ctrls.handler); + v4l2_subdev_cleanup(&tpg->sd); + media_entity_cleanup(&tpg->sd.entity); +} diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c index 84ded154adfe..fa6b72c3bd93 100644 --- a/drivers/media/platform/chips-media/coda/coda-bit.c +++ b/drivers/media/platform/chips-media/coda/coda-bit.c @@ -1685,7 +1685,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c index 9a57b042d9fd..33f712ff8556 100644 --- a/drivers/media/platform/chips-media/coda/coda-common.c +++ b/drivers/media/platform/chips-media/coda/coda-common.c @@ -790,8 +790,6 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = get_q_data(ctx, f->type); if (!q_data) @@ -942,8 +940,6 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, ctx->codec = codec; dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (!dst_vq) - return -EINVAL; /* * Setting the capture queue format is not possible while the capture diff --git a/drivers/media/platform/chips-media/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c index 5746892658b1..fb150b87c773 100644 --- a/drivers/media/platform/chips-media/coda/coda-jpeg.c +++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c @@ -1245,7 +1245,7 @@ static void coda9_jpeg_finish_encode(struct coda_ctx *ctx) dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR : @@ -1472,7 +1472,7 @@ static void coda9_jpeg_finish_decode(struct coda_ctx *ctx) dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage); diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c index c4e0097cb8b7..1c6e076033ec 100644 --- a/drivers/media/platform/imagination/e5010-jpeg-enc.c +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -396,8 +396,6 @@ static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f) struct e5010_fmt *fmt; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) { v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n"); @@ -496,8 +494,6 @@ static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection struct v4l2_rect base_rect; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type); - if (!vq) - return -EINVAL; if (vb2_is_streaming(vq)) return -EBUSY; @@ -1358,7 +1354,7 @@ static void e5010_device_run(void *priv) s_vb->sequence = ctx->out_queue.sequence++; d_vb->sequence = ctx->cap_queue.sequence++; - v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false); + v4l2_m2m_buf_copy_metadata(s_vb, d_vb); if (ctx != e5010->last_context_run || ctx->update_qp) { dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n", diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index e07e57d4206b..af098874ead5 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -485,13 +485,8 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, static int vidioc_g_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f) { - struct vb2_queue *vq; struct deinterlace_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = get_q_data(f->type); f->fmt.pix.width = q_data->width; @@ -586,8 +581,6 @@ static int vidioc_s_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = get_q_data(f->type); if (!q_data) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 6268d651bdcf..d08fe365cbb2 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -302,17 +302,12 @@ static int mtk_jpeg_try_fmt_mplane(struct v4l2_pix_format_mplane *pix_mp, static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct vb2_queue *vq; struct mtk_jpeg_q_data *q_data = NULL; struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); struct mtk_jpeg_dev *jpeg = ctx->jpeg; int i; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = mtk_jpeg_get_q_data(ctx, f->type); pix_mp->width = q_data->pix_mp.width; @@ -416,8 +411,6 @@ static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx, int i; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = mtk_jpeg_get_q_data(ctx, f->type); @@ -1625,7 +1618,7 @@ retry_select: if (!dst_buf) goto getbuf_fail; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); mtk_jpegenc_set_hw_param(ctx, hw_id, src_buf, dst_buf); ret = pm_runtime_get_sync(comp_jpeg[hw_id]->dev); @@ -1721,7 +1714,7 @@ retry_select: if (!dst_buf) goto getbuf_fail; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); jpeg_dst_buf = mtk_jpeg_vb2_to_srcbuf(&dst_buf->vb2_buf); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c index e78e1d11093c..32372781daf5 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c @@ -530,7 +530,7 @@ static void mtk_jpegdec_timeout_work(struct work_struct *work) src_buf = cjpeg->hw_param.src_buffer; dst_buf = cjpeg->hw_param.dst_buffer; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); mtk_jpeg_dec_reset(cjpeg->reg_base); clk_disable_unprepare(cjpeg->jdec_clk.clks->clk); @@ -560,7 +560,7 @@ static irqreturn_t mtk_jpegdec_hw_irq_handler(int irq, void *priv) ctx = jpeg->hw_param.curr_ctx; src_buf = jpeg->hw_param.src_buffer; dst_buf = jpeg->hw_param.dst_buffer; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); irq_status = mtk_jpeg_dec_get_int_status(jpeg->reg_base); dec_irq_ret = mtk_jpeg_dec_enum_result(irq_status); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c index 9ab27aee302a..b6f5b2249f1f 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c @@ -261,7 +261,7 @@ static void mtk_jpegenc_timeout_work(struct work_struct *work) src_buf = cjpeg->hw_param.src_buffer; dst_buf = cjpeg->hw_param.dst_buffer; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); mtk_jpeg_enc_reset(cjpeg->reg_base); clk_disable_unprepare(cjpeg->venc_clk.clks->clk); @@ -289,7 +289,7 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv) ctx = jpeg->hw_param.curr_ctx; src_buf = jpeg->hw_param.src_buffer; dst_buf = jpeg->hw_param.dst_buffer; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); irq_status = readl(jpeg->reg_base + JPEG_ENC_INT_STS) & JPEG_ENC_INT_STATUS_MASK_ALLIRQ; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 6559d72d5d42..6d26d4aa1eef 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -157,10 +157,18 @@ void mdp_video_device_release(struct video_device *vdev) kfree(mdp); } +static void mdp_put_device(void *_dev) +{ + struct device *dev = _dev; + + put_device(dev); +} + static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id) { struct platform_device *mm_pdev = NULL; struct device **dev; + int ret; int i; if (!mdp) @@ -194,6 +202,11 @@ static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id) if (WARN_ON(!mm_pdev)) return -ENODEV; + ret = devm_add_action_or_reset(&mdp->pdev->dev, mdp_put_device, + &mm_pdev->dev); + if (ret) + return ret; + *dev = &mm_pdev->dev; } @@ -279,6 +292,7 @@ static int mdp_probe(struct platform_device *pdev) goto err_destroy_clock_wq; } mdp->scp = platform_get_drvdata(mm_pdev); + put_device(&mm_pdev->dev); } mdp->rproc_handle = scp_get_rproc(mdp->scp); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c index 9ef956b565a7..44140987cf5f 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c @@ -51,7 +51,7 @@ static void mdp_m2m_process_done(void *priv, int vb_state) ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC]; src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++; dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++; - v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true); + v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf); v4l2_m2m_buf_done(src_vbuf, vb_state); v4l2_m2m_buf_done(dst_vbuf, vb_state); diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c index 5ad3797836db..643d6fff088b 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c @@ -183,8 +183,8 @@ static void mtk_vcodec_dbgfs_vdec_init(struct mtk_vcodec_dec_dev *vcodec_dev) vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL); if (IS_ERR(vcodec_dev->dbgfs.vcodec_root)) - dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%ld\n", - PTR_ERR(vcodec_dev->dbgfs.vcodec_root)); + dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%pe\n", + vcodec_dev->dbgfs.vcodec_root); vcodec_root = vcodec_dev->dbgfs.vcodec_root; debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level); diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c index d7027d600208..3632037f78f5 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c @@ -47,30 +47,32 @@ static void mtk_vcodec_vpu_reset_dec_handler(void *priv) { struct mtk_vcodec_dec_dev *dev = priv; struct mtk_vcodec_dec_ctx *ctx; + unsigned long flags; dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_for_each_entry(ctx, &dev->ctx_list, list) { ctx->state = MTK_STATE_ABORT; mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); } - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); } static void mtk_vcodec_vpu_reset_enc_handler(void *priv) { struct mtk_vcodec_enc_dev *dev = priv; struct mtk_vcodec_enc_ctx *ctx; + unsigned long flags; dev_err(&dev->plat_dev->dev, "Watchdog timeout!!"); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_for_each_entry(ctx, &dev->ctx_list, list) { ctx->state = MTK_STATE_ABORT; mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id); } - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); } static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { @@ -117,8 +119,10 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_enc_handler, priv, rst_id); fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL); - if (!fw) + if (!fw) { + put_device(&fw_pdev->dev); return ERR_PTR(-ENOMEM); + } fw->type = VPU; fw->ops = &mtk_vcodec_vpu_msg; fw->pdev = fw_pdev; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index d691bd533103..1f32ba11a18c 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -674,15 +674,8 @@ static int vidioc_vdec_g_fmt(struct file *file, void *priv, { struct mtk_vcodec_dec_ctx *ctx = file_to_dec_ctx(file); struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct vb2_queue *vq; struct mtk_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_vdec_err(ctx, "no vb2 queue for type=%d", f->type); - return -EINVAL; - } - q_data = mtk_vdec_get_q_data(ctx, f->type); pix_mp->field = V4L2_FIELD_NONE; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c index 46d176e6de63..3b81fae9f913 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c @@ -198,6 +198,7 @@ static int fops_vcodec_open(struct file *file) struct mtk_vcodec_dec_ctx *ctx = NULL; int ret = 0, i, hw_count; struct vb2_queue *src_vq; + unsigned long flags; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -267,9 +268,9 @@ static int fops_vcodec_open(struct file *file) ctx->dev->vdec_pdata->init_vdec_params(ctx); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_add(&ctx->list, &dev->ctx_list); - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); mtk_vcodec_dbgfs_create(ctx); mutex_unlock(&dev->dev_mutex); @@ -294,6 +295,7 @@ static int fops_vcodec_release(struct file *file) { struct mtk_vcodec_dec_dev *dev = video_drvdata(file); struct mtk_vcodec_dec_ctx *ctx = file_to_dec_ctx(file); + unsigned long flags; mtk_v4l2_vdec_dbg(0, ctx, "[%d] decoder", ctx->id); mutex_lock(&dev->dev_mutex); @@ -312,9 +314,9 @@ static int fops_vcodec_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_hdl); mtk_vcodec_dbgfs_remove(dev, ctx->id); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_del_init(&ctx->list); - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); kfree(ctx); mutex_unlock(&dev->dev_mutex); return 0; @@ -407,7 +409,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) for (i = 0; i < MTK_VDEC_HW_MAX; i++) mutex_init(&dev->dec_mutex[i]); mutex_init(&dev->dev_mutex); - mutex_init(&dev->dev_ctx_lock); + spin_lock_init(&dev->dev_ctx_lock); spin_lock_init(&dev->irqlock); snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h index d047d7c580fb..9d68808e8f9c 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -285,7 +285,7 @@ struct mtk_vcodec_dec_dev { /* decoder hardware mutex lock */ struct mutex dec_mutex[MTK_VDEC_HW_MAX]; struct mutex dev_mutex; - struct mutex dev_ctx_lock; + spinlock_t dev_ctx_lock; struct workqueue_struct *decode_workqueue; spinlock_t irqlock; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c index bf21f2467a0f..7be4b6086920 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c @@ -1073,7 +1073,7 @@ static int vdec_av1_slice_setup_lat_from_src_buf(struct vdec_av1_slice_instance lat_buf->src_buf_req = src->vb2_buf.req_obj.req; dst = &lat_buf->ts_info; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); vsi->frame.cur_ts = dst->vb2_buf.timestamp; return 0; @@ -1780,7 +1780,7 @@ static int vdec_av1_slice_setup_core_to_dst_buf(struct vdec_av1_slice_instance * if (!dst) return -EINVAL; - v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst, true); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst); return 0; } @@ -1810,8 +1810,6 @@ static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *inst /* reference buffers */ vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!vq) - return -EINVAL; /* get current output buffer */ vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c index 1e1b32faac77..b9a5ea7887d3 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c @@ -367,7 +367,7 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb; v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, - &dst_buf_info->m2m_buf.vb, true); + &dst_buf_info->m2m_buf.vb); err = get_vdec_decode_parameters(inst); if (err) goto err_free_fb_out; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index 5b25e1679b51..9a9dc2f88d6e 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -570,7 +570,7 @@ static int vdec_h264_slice_setup_core_buffer_ext(struct vdec_h264_slice_inst *in } vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2); return 0; } @@ -674,7 +674,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) } vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2); vdec_h264_slice_fill_decode_reflist(inst, &inst->vsi_core->h264_slice_params, share_info); @@ -768,7 +768,8 @@ static int vdec_h264_slice_lat_decode_ext(void *h_vdec, struct mtk_vcodec_mem *b src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; - v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, + &lat_buf->ts_info); err = vdec_h264_slice_fill_decode_parameters(inst, share_info, &inst->vsi_ext->h264_slice_params); @@ -900,7 +901,8 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->vsi->dec.nal_info = buf[nal_start_idx]; lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; - v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, + &lat_buf->ts_info); err = vdec_h264_slice_fill_decode_parameters(inst, share_info, &inst->vsi->h264_slice_params); @@ -1039,7 +1041,7 @@ static int vdec_h264_slice_single_decode_ext(void *h_vdec, struct mtk_vcodec_mem inst->vsi_ctx_ext.dec.vdec_fb_va = (u64)(uintptr_t)fb; v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, - &dst_buf_info->m2m_buf.vb, true); + &dst_buf_info->m2m_buf.vb); err = get_vdec_sig_decode_parameters(inst); if (err) goto err_free_fb_out; @@ -1135,7 +1137,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb; v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, - &dst_buf_info->m2m_buf.vb, true); + &dst_buf_info->m2m_buf.vb); err = get_vdec_sig_decode_parameters(inst); if (err) goto err_free_fb_out; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c index 2725db882e5b..88eca50c2017 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c @@ -742,7 +742,8 @@ static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst, src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; - v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, + &lat_buf->ts_info); *res_chg = inst->resolution_changed; if (inst->resolution_changed) { @@ -847,7 +848,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, } vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2); return 0; } diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c index 232ef3bd246a..e1d4960553f2 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c @@ -358,7 +358,7 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, y_fb_dma, c_fb_dma); v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, - &dst_buf_info->m2m_buf.vb, true); + &dst_buf_info->m2m_buf.vb); err = vdec_vp8_slice_get_decode_parameters(inst); if (err) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index 47c302745c1d..cd1935014d76 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -706,7 +706,7 @@ int vdec_vp9_slice_setup_single_from_src_to_dst(struct vdec_vp9_slice_instance * if (!dst) return -EINVAL; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); return 0; } @@ -724,7 +724,7 @@ static int vdec_vp9_slice_setup_lat_from_src_buf(struct vdec_vp9_slice_instance lat_buf->src_buf_req = src->vb2_buf.req_obj.req; dst = &lat_buf->ts_info; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); return 0; } @@ -1652,7 +1652,7 @@ static int vdec_vp9_slice_setup_core_to_dst_buf(struct vdec_vp9_slice_instance * if (!dst) return -EINVAL; - v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst, true); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst); return 0; } @@ -1686,8 +1686,6 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst /* reference buffers */ vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!vq) - return -EINVAL; /* get current output buffer */ vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index 145958206e38..40b97f114cf6 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -75,16 +75,17 @@ static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *ms static bool vpu_dec_check_ap_inst(struct mtk_vcodec_dec_dev *dec_dev, struct vdec_vpu_inst *vpu) { struct mtk_vcodec_dec_ctx *ctx; + unsigned long flags; int ret = false; - mutex_lock(&dec_dev->dev_ctx_lock); + spin_lock_irqsave(&dec_dev->dev_ctx_lock, flags); list_for_each_entry(ctx, &dec_dev->ctx_list, list) { if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { ret = true; break; } } - mutex_unlock(&dec_dev->dev_ctx_lock); + spin_unlock_irqrestore(&dec_dev->dev_ctx_lock, flags); return ret; } diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c index d815e962ab89..6faf3f659e75 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -421,10 +421,6 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, const struct mtk_video_fmt *fmt; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_venc_err(ctx, "fail to get vq"); - return -EINVAL; - } if (vb2_is_busy(vq)) { mtk_v4l2_venc_err(ctx, "queue busy"); @@ -476,10 +472,6 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, const struct mtk_video_fmt *fmt; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_venc_err(ctx, "fail to get vq"); - return -EINVAL; - } if (vb2_is_busy(vq)) { mtk_v4l2_venc_err(ctx, "queue busy"); @@ -524,15 +516,9 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct mtk_vcodec_enc_ctx *ctx = file_to_enc_ctx(file); - struct vb2_queue *vq; struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); int i; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - pix->width = q_data->coded_width; pix->height = q_data->coded_height; pix->pixelformat = q_data->fmt->fourcc; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c index fb1c3bdc2dae..82b8ff38e8f1 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c @@ -117,6 +117,7 @@ static int fops_vcodec_open(struct file *file) struct mtk_vcodec_enc_ctx *ctx = NULL; int ret = 0; struct vb2_queue *src_vq; + unsigned long flags; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -176,9 +177,9 @@ static int fops_vcodec_open(struct file *file) mtk_v4l2_venc_dbg(2, ctx, "Create instance [%d]@%p m2m_ctx=%p ", ctx->id, ctx, ctx->m2m_ctx); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_add(&ctx->list, &dev->ctx_list); - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); mutex_unlock(&dev->dev_mutex); mtk_v4l2_venc_dbg(0, ctx, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), @@ -203,6 +204,7 @@ static int fops_vcodec_release(struct file *file) { struct mtk_vcodec_enc_dev *dev = video_drvdata(file); struct mtk_vcodec_enc_ctx *ctx = file_to_enc_ctx(file); + unsigned long flags; mtk_v4l2_venc_dbg(1, ctx, "[%d] encoder", ctx->id); mutex_lock(&dev->dev_mutex); @@ -213,9 +215,9 @@ static int fops_vcodec_release(struct file *file) v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); - mutex_lock(&dev->dev_ctx_lock); + spin_lock_irqsave(&dev->dev_ctx_lock, flags); list_del_init(&ctx->list); - mutex_unlock(&dev->dev_ctx_lock); + spin_unlock_irqrestore(&dev->dev_ctx_lock, flags); kfree(ctx); mutex_unlock(&dev->dev_mutex); return 0; @@ -297,7 +299,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mutex_init(&dev->enc_mutex); mutex_init(&dev->dev_mutex); - mutex_init(&dev->dev_ctx_lock); + spin_lock_init(&dev->dev_ctx_lock); spin_lock_init(&dev->irqlock); snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h index 5b304a551236..0cddfa13594f 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h @@ -206,7 +206,7 @@ struct mtk_vcodec_enc_dev { /* encoder hardware mutex lock */ struct mutex enc_mutex; struct mutex dev_mutex; - struct mutex dev_ctx_lock; + spinlock_t dev_ctx_lock; struct workqueue_struct *encode_workqueue; int enc_irq; diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c index 51bb7ee141b9..3c229b1f6b21 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c @@ -45,16 +45,17 @@ static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) static bool vpu_enc_check_ap_inst(struct mtk_vcodec_enc_dev *enc_dev, struct venc_vpu_inst *vpu) { struct mtk_vcodec_enc_ctx *ctx; + unsigned long flags; int ret = false; - mutex_lock(&enc_dev->dev_ctx_lock); + spin_lock_irqsave(&enc_dev->dev_ctx_lock, flags); list_for_each_entry(ctx, &enc_dev->ctx_list, list) { if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) { ret = true; break; } } - mutex_unlock(&enc_dev->dev_ctx_lock); + spin_unlock_irqrestore(&enc_dev->dev_ctx_lock, flags); return ret; } diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index 45f8f6904867..2a2211671fd9 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -776,7 +776,7 @@ static int tegra_vde_h264_setup_frames(struct tegra_ctx *ctx, * If userspace doesn't tell us frame's type, then we will try decode * as-is. */ - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) tb->b_frame = true; diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index 97744c7b7c03..4aaf9c3fff53 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -735,13 +735,8 @@ static int dw100_enum_framesizes(struct file *file, void *priv, static int dw100_g_fmt_vid(struct file *file, void *priv, struct v4l2_format *f) { struct dw100_ctx *ctx = dw100_file2ctx(file); - struct vb2_queue *vq; struct dw100_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = dw100_get_q_data(ctx, f->type); f->fmt.pix_mp = q_data->pix_fmt; @@ -803,8 +798,6 @@ static int dw100_s_fmt(struct dw100_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = dw100_get_q_data(ctx, f->type); if (!q_data) @@ -1437,7 +1430,7 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), in_vb->sequence, out_vb->sequence); - v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); + v4l2_m2m_buf_copy_metadata(in_vb, out_vb); /* Now, let's deal with hardware ... */ dw100_hw_master_bus_disable(dw_dev); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index df3ccdf767ba..9e4a813489c0 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1537,7 +1537,7 @@ static void mxc_jpeg_device_run(void *priv) src_buf->sequence = q_data_out->sequence++; dst_buf->sequence = q_data_cap->sequence++; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf); if (q_data_cap->fmt->mem_planes != dst_buf->vb2_buf.num_planes) { @@ -2491,8 +2491,6 @@ static int mxc_jpeg_s_fmt(struct mxc_jpeg_ctx *ctx, struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) { v4l2_err(&jpeg->v4l2_dev, "queue busy\n"); @@ -2528,8 +2526,6 @@ static int mxc_jpeg_s_fmt_vid_out(struct file *file, void *priv, return 0; dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, cap_type); - if (!dst_vq) - return -EINVAL; if (vb2_is_busy(dst_vq)) return 0; diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index d5de7854f579..088b2945aee3 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -351,6 +351,8 @@ struct mipi_csis_device { u32 hs_settle; u32 clk_settle; + unsigned int num_data_lanes; + spinlock_t slock; /* Protect events */ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS]; struct dentry *debugfs_root; @@ -573,7 +575,7 @@ static void mipi_csis_system_enable(struct mipi_csis_device *csis, int on) val = mipi_csis_read(csis, MIPI_CSIS_DPHY_CMN_CTRL); val &= ~MIPI_CSIS_DPHY_CMN_CTRL_ENABLE; if (on) { - mask = (1 << (csis->bus.num_data_lanes + 1)) - 1; + mask = (1 << (csis->num_data_lanes + 1)) - 1; val |= (mask & MIPI_CSIS_DPHY_CMN_CTRL_ENABLE); } mipi_csis_write(csis, MIPI_CSIS_DPHY_CMN_CTRL, val); @@ -623,7 +625,7 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis, /* Calculate the line rate from the pixel rate. */ link_freq = v4l2_get_link_freq(csis->source.pad, csis_fmt->width, - csis->bus.num_data_lanes * 2); + csis->num_data_lanes * 2); if (link_freq < 0) { dev_err(csis->dev, "Unable to obtain link frequency: %d\n", (int)link_freq); @@ -668,7 +670,7 @@ static void mipi_csis_set_params(struct mipi_csis_device *csis, const struct v4l2_mbus_framefmt *format, const struct csis_pix_format *csis_fmt) { - int lanes = csis->bus.num_data_lanes; + int lanes = csis->num_data_lanes; u32 val; val = mipi_csis_read(csis, MIPI_CSIS_CMN_CTRL); @@ -1032,6 +1034,12 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK); csis_fmt = find_csis_format(format->code); + ret = v4l2_get_active_data_lanes(csis->source.pad, csis->bus.num_data_lanes); + if (ret < 0) + goto err_unlock; + + csis->num_data_lanes = ret; + ret = mipi_csis_calculate_params(csis, csis_fmt); if (ret < 0) goto err_unlock; @@ -1366,8 +1374,9 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) } csis->bus = vep.bus.mipi_csi2; + csis->num_data_lanes = csis->bus.num_data_lanes; - dev_dbg(csis->dev, "data lanes: %d\n", csis->bus.num_data_lanes); + dev_dbg(csis->dev, "max data lanes: %d\n", csis->bus.num_data_lanes); dev_dbg(csis->dev, "flags: 0x%08x\n", csis->bus.flags); asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep, @@ -1481,6 +1490,7 @@ static int mipi_csis_parse_dt(struct mipi_csis_device *csis) struct device_node *node = csis->dev->of_node; of_property_read_u32(node, "clock-frequency", &csis->clk_frequency); + dev_dbg(csis->dev, "clock frequency: %u\n", csis->clk_frequency); csis->num_channels = 1; of_property_read_u32(node, "fsl,num-channels", &csis->num_channels); @@ -1566,9 +1576,6 @@ static int mipi_csis_probe(struct platform_device *pdev) goto err_unregister_all; } - dev_info(dev, "lanes: %d, freq: %u\n", - csis->bus.num_data_lanes, csis->clk_frequency); - return 0; err_unregister_all: @@ -1634,4 +1641,3 @@ module_platform_driver(mipi_csis_driver); MODULE_DESCRIPTION("i.MX7 & i.MX8 MIPI CSI-2 receiver driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx-mipi-csi2"); diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index 6cc9b07ea53a..3f9a67a6bd4d 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -1180,13 +1180,8 @@ static int pxp_enum_fmt_vid_out(struct file *file, void *priv, static int pxp_g_fmt(struct pxp_ctx *ctx, struct v4l2_format *f) { - struct vb2_queue *vq; struct pxp_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = get_q_data(ctx, f->type); f->fmt.pix.width = q_data->width; @@ -1329,8 +1324,6 @@ static int pxp_s_fmt(struct pxp_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = get_q_data(ctx, f->type); if (!q_data) diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 34a92642bbfe..933a5f39f9f4 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -2290,4 +2290,3 @@ module_platform_driver(imx7_csi_driver); MODULE_DESCRIPTION("i.MX7 CSI subdev driver"); MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx7-csi"); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index adc8d9960bf0..c3d411ddf492 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -314,6 +314,28 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .has_36bit_dma = true, }; +static const struct mxc_isi_plat_data mxc_imx8qm_data = { + .model = MXC_ISI_IMX8QM, + .num_ports = 5, + .num_channels = 8, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_qm, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + +static const struct mxc_isi_plat_data mxc_imx8qxp_data = { + .model = MXC_ISI_IMX8QXP, + .num_ports = 5, + .num_channels = 6, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + static const struct mxc_isi_plat_data mxc_imx8ulp_data = { .model = MXC_ISI_IMX8ULP, .num_ports = 1, @@ -325,37 +347,26 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = { .has_36bit_dma = false, }; -static const struct mxc_isi_plat_data mxc_imx93_data = { - .model = MXC_ISI_IMX93, +static const struct mxc_isi_plat_data mxc_imx91_data = { + .model = MXC_ISI_IMX91, .num_ports = 1, .num_channels = 1, .reg_offset = 0, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, .buf_active_reverse = true, - .gasket_ops = &mxc_imx93_gasket_ops, - .has_36bit_dma = false, -}; - -static const struct mxc_isi_plat_data mxc_imx8qm_data = { - .model = MXC_ISI_IMX8QM, - .num_ports = 5, - .num_channels = 8, - .reg_offset = 0x10000, - .ier_reg = &mxc_imx8_isi_ier_qm, - .set_thd = &mxc_imx8_isi_thd_v1, - .buf_active_reverse = true, .has_36bit_dma = false, }; -static const struct mxc_isi_plat_data mxc_imx8qxp_data = { - .model = MXC_ISI_IMX8QXP, - .num_ports = 5, - .num_channels = 6, - .reg_offset = 0x10000, +static const struct mxc_isi_plat_data mxc_imx93_data = { + .model = MXC_ISI_IMX93, + .num_ports = 1, + .num_channels = 1, + .reg_offset = 0, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, .buf_active_reverse = true, + .gasket_ops = &mxc_imx93_gasket_ops, .has_36bit_dma = false, }; @@ -547,6 +558,7 @@ static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data }, { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data }, { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, + { .compatible = "fsl,imx91-isi", .data = &mxc_imx91_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index e84af5127e4e..3cbd35305af0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -160,6 +160,7 @@ enum model { MXC_ISI_IMX8QM, MXC_ISI_IMX8QXP, MXC_ISI_IMX8ULP, + MXC_ISI_IMX91, MXC_ISI_IMX93, }; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c index f69c3b5d4782..58ec7eddcd3d 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c @@ -3,6 +3,8 @@ * Copyright 2019-2023 NXP */ +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/regmap.h> #include <media/mipi-csi2.h> @@ -16,8 +18,7 @@ #define GASKET_BASE(n) (0x0060 + (n) * 0x30) #define GASKET_CTRL 0x0000 -#define GASKET_CTRL_DATA_TYPE(dt) ((dt) << 8) -#define GASKET_CTRL_DATA_TYPE_MASK (0x3f << 8) +#define GASKET_CTRL_DATA_TYPE(dt) FIELD_PREP(GENMASK(13, 8), dt) #define GASKET_CTRL_DUAL_COMP_ENABLE BIT(1) #define GASKET_CTRL_ENABLE BIT(0) @@ -57,9 +58,10 @@ const struct mxc_gasket_ops mxc_imx8_gasket_ops = { * i.MX93 gasket */ -#define DISP_MIX_CAMERA_MUX 0x30 -#define DISP_MIX_CAMERA_MUX_DATA_TYPE(x) (((x) & 0x3f) << 3) -#define DISP_MIX_CAMERA_MUX_GASKET_ENABLE BIT(16) +#define DISP_MIX_CAMERA_MUX 0x30 +#define DISP_MIX_CAMERA_MUX_DATA_TYPE(x) FIELD_PREP(GENMASK(8, 3), x) +#define DISP_MIX_CAMERA_MUX_GASKET_ENABLE BIT(16) +#define DISP_MIX_CAMERA_MUX_GASKET_SOURCE_TYPE BIT(17) static void mxc_imx93_gasket_enable(struct mxc_isi_dev *isi, const struct v4l2_mbus_frame_desc *fd, @@ -70,6 +72,16 @@ static void mxc_imx93_gasket_enable(struct mxc_isi_dev *isi, val = DISP_MIX_CAMERA_MUX_DATA_TYPE(fd->entry[0].bus.csi2.dt); val |= DISP_MIX_CAMERA_MUX_GASKET_ENABLE; + + /* + * CAMERA MUX + * - [17]: Selects source input to gasket + * 0: Data from MIPI CSI + * 1: Data from parallel camera + */ + if (fd->type == V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL) + val |= DISP_MIX_CAMERA_MUX_GASKET_SOURCE_TYPE; + regmap_write(isi->gasket, DISP_MIX_CAMERA_MUX, val); } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index 00afcbfbdde4..f425ac786854 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -107,7 +107,7 @@ static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status) src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, false); + v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf); src_vbuf->sequence = ctx->queues.out.sequence++; dst_vbuf->sequence = ctx->queues.cap.sequence++; @@ -554,8 +554,6 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh, struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) return -EBUSY; diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index 3a4645f59a44..371b4e81328c 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -418,8 +418,8 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state, src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity); if (IS_ERR(src_pad)) { - dev_err(state->dev, "can't get source pad of %s (%ld)\n", - sd_state->sd->name, PTR_ERR(src_pad)); + dev_err(state->dev, "can't get source pad of %s (%pe)\n", + sd_state->sd->name, src_pad); return PTR_ERR(src_pad); } @@ -1114,4 +1114,3 @@ module_platform_driver(imx8mq_mipi_csi_driver); MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver"); MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx8mq-mipi-csi2"); diff --git a/drivers/media/platform/nxp/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c index 3aae8c0b690c..02d57229b9b3 100644 --- a/drivers/media/platform/nxp/mx2_emmaprp.c +++ b/drivers/media/platform/nxp/mx2_emmaprp.c @@ -431,13 +431,8 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) { - struct vb2_queue *vq; struct emmaprp_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = get_q_data(ctx, f->type); f->fmt.pix.width = q_data->width; @@ -540,8 +535,6 @@ static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) int ret; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = get_q_data(ctx, f->type); if (!q_data) diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 23960d02877d..5e349b491513 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -23,6 +23,7 @@ qcom-camss-objs += \ camss-vfe-680.o \ camss-vfe-gen3.o \ camss-vfe-gen1.o \ + camss-vfe-vbif.o \ camss-vfe.o \ camss-video.o \ camss-format.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index a229ba04b158..619abbf60781 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -587,6 +587,102 @@ csiphy_lane_regs lane_regs_sm8550[] = { {0x0C64, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, }; +/* GEN2 2.2.0 2PH 4 lane DPHY mode */ +static const struct +csiphy_lane_regs lane_regs_sm8650[] = { + {0x0e94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0ea0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e94, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS}, + {0x0e30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e0c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e38, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e2c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e34, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e1c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e3c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e04, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0e08, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0e10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x00a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS}, + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x04a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x08a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS}, + {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0838, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x082c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0834, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x083c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0804, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0808, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0c94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0ca0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c94, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS}, + {0x0c30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c00, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c38, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c2c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c34, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c1c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c3c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c08, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0c10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, +}; + /* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */ static const struct csiphy_lane_regs lane_regs_x1e80100[] = { @@ -914,6 +1010,7 @@ static bool csiphy_is_gen2(u32 version) case CAMSS_8300: case CAMSS_845: case CAMSS_8550: + case CAMSS_8650: case CAMSS_8775P: case CAMSS_X1E80100: ret = true; @@ -1018,6 +1115,11 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8550); regs->offset = 0x1000; break; + case CAMSS_8650: + regs->lane_regs = &lane_regs_sm8650[0]; + regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8650); + regs->offset = 0x1000; + break; case CAMSS_8300: case CAMSS_8775P: regs->lane_regs = &lane_regs_sa8775p[0]; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 2de97f58f9ae..a734fb7dde0a 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -600,6 +600,7 @@ int msm_csiphy_subdev_init(struct camss *camss, return PTR_ERR(csiphy->base); if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x39 || camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_8x96) { csiphy->base_clk_mux = diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 2dc585c6123d..aaf3caa42d33 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1112,6 +1112,8 @@ int msm_ispif_subdev_init(struct camss *camss, /* Number of ISPIF lines - same as number of CSID hardware modules */ if (camss->res->version == CAMSS_8x16) ispif->line_num = 2; + else if (camss->res->version == CAMSS_8x39) + ispif->line_num = 3; else if (camss->res->version == CAMSS_8x96 || camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_660) @@ -1128,7 +1130,8 @@ int msm_ispif_subdev_init(struct camss *camss, ispif->line[i].ispif = ispif; ispif->line[i].id = i; - if (camss->res->version == CAMSS_8x16) { + if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x39) { ispif->line[i].formats = ispif_formats_8x16; ispif->line[i].nformats = ARRAY_SIZE(ispif_formats_8x16); @@ -1162,7 +1165,8 @@ int msm_ispif_subdev_init(struct camss *camss, ispif->irq = ret; snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", dev_name(dev), MSM_ISPIF_NAME); - if (camss->res->version == CAMSS_8x16) + if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x39) ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); else if (camss->res->version == CAMSS_8x96 || diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 901677293d97..9cf1ccdb2fe7 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -15,6 +15,7 @@ #include "camss.h" #include "camss-vfe.h" #include "camss-vfe-gen1.h" +#include "camss-vfe-vbif.h" #define VFE_0_HW_VERSION 0x000 @@ -733,6 +734,7 @@ static void vfe_set_qos(struct vfe_device *vfe) { u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + int ret; writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); @@ -742,6 +744,16 @@ static void vfe_set_qos(struct vfe_device *vfe) writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); + + /* SoC-specific VBIF settings */ + if (vfe->res->has_vbif) { + ret = vfe_vbif_apply_settings(vfe); + if (ret < 0) { + dev_err_ratelimited(vfe->camss->dev, + "VFE: VBIF error %d\n", + ret); + } + } } static void vfe_set_ds(struct vfe_device *vfe) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-vbif.c b/drivers/media/platform/qcom/camss/camss-vfe-vbif.c new file mode 100644 index 000000000000..911f8da02f1f --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-vbif.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * camss-vfe-vbif.c + * + * Qualcomm MSM Camera Subsystem - VFE VBIF Module + * + * Copyright (c) 2025, The Linux Foundation. All rights reserved. + * + */ + +#include <linux/io.h> + +#include "camss.h" +#include "camss-vfe.h" +#include "camss-vfe-vbif.h" + +#define VBIF_FIXED_SORT_EN 0x30 +#define VBIF_FIXED_SORT_SEL0 0x34 + +void vfe_vbif_write_reg(struct vfe_device *vfe, u32 reg, u32 val) +{ + writel_relaxed(val, vfe->vbif_base + reg); +} + +int vfe_vbif_apply_settings(struct vfe_device *vfe) +{ + vfe_vbif_write_reg(vfe, VBIF_FIXED_SORT_EN, 0xfff); + vfe_vbif_write_reg(vfe, VBIF_FIXED_SORT_SEL0, 0x555000); + + return 0; +} diff --git a/drivers/media/platform/qcom/camss/camss-vfe-vbif.h b/drivers/media/platform/qcom/camss/camss-vfe-vbif.h new file mode 100644 index 000000000000..502db629e961 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-vbif.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * camss-vfe-vbif.h + * + * Qualcomm MSM Camera Subsystem - VFE VBIF Module + * + * Copyright (c) 2025, The Linux Foundation. All rights reserved. + * + */ +#ifndef QC_MSM_CAMSS_VFE_VBIF_H +#define QC_MSM_CAMSS_VFE_VBIF_H + +#include "camss-vfe.h" + +void vfe_vbif_write_reg(struct vfe_device *vfe, u32 reg, u32 val); + +int vfe_vbif_apply_settings(struct vfe_device *vfe); + +#endif /* QC_MSM_CAMSS_VFE_VBIF_H */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index dff8d0a1e8c2..9c7ad8aa4058 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -290,6 +290,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, switch (vfe->camss->res->version) { case CAMSS_8x16: + case CAMSS_8x39: case CAMSS_8x53: switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_1X16: @@ -348,6 +349,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_8300: case CAMSS_845: case CAMSS_8550: + case CAMSS_8650: case CAMSS_8775P: case CAMSS_X1E80100: switch (sink_code) { @@ -541,7 +543,7 @@ int vfe_enable_output_v2(struct vfe_line *line) ops->vfe_wm_start(vfe, output->wm_idx[0], line); - for (i = 0; i < 2; i++) { + for (i = 0; i < CAMSS_INIT_BUF_COUNT; i++) { output->buf[i] = vfe_buf_get_pending(output); if (!output->buf[i]) break; @@ -914,7 +916,8 @@ static int vfe_match_clock_names(struct vfe_device *vfe, return (!strcmp(clock->name, vfe_name) || !strcmp(clock->name, vfe_lite_name) || !strcmp(clock->name, "vfe_lite") || - !strcmp(clock->name, "camnoc_axi")); + !strcmp(clock->name, "camnoc_axi") || + !strcmp(clock->name, "camnoc_rt_axi")); } /* @@ -1827,6 +1830,15 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, return PTR_ERR(vfe->base); } + if (vfe->res->has_vbif) { + vfe->vbif_base = devm_platform_ioremap_resource_byname(pdev, + vfe->res->vbif_name); + if (IS_ERR(vfe->vbif_base)) { + dev_err(dev, "could not map vbif memory\n"); + return PTR_ERR(vfe->vbif_base); + } + } + /* Interrupt */ ret = platform_get_irq_byname(pdev, res->interrupt[0]); @@ -1995,6 +2007,7 @@ static int vfe_bpl_align(struct vfe_device *vfe) case CAMSS_8300: case CAMSS_845: case CAMSS_8550: + case CAMSS_8650: case CAMSS_8775P: case CAMSS_X1E80100: ret = 16; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0300efdb1c46..ae9dad353a37 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -136,6 +136,8 @@ struct vfe_subdev_resources { u8 line_num; bool has_pd; char *pd_name; + bool has_vbif; + char *vbif_name; const struct vfe_hw_ops *hw_ops; const struct camss_formats *formats_rdi; const struct camss_formats *formats_pix; @@ -145,6 +147,7 @@ struct vfe_device { struct camss *camss; u8 id; void __iomem *base; + void __iomem *vbif_base; u32 irq; char irq_name[30]; struct camss_clock *clock; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 2fbcd0e343aa..fcc2b2c3cba0 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -154,6 +154,149 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { } }; +static const struct camss_subdev_resources csiphy_res_8x39[] = { + /* CSIPHY0 */ + { + .regulators = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 0 }, + { 100000000, 200000000 } }, + .reg = { "csiphy0", "csiphy0_clk_mux" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } + }, + + /* CSIPHY1 */ + { + .regulators = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 0 }, + { 100000000, 200000000 } }, + .reg = { "csiphy1", "csiphy1_clk_mux" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 + } + } +}; + +static const struct camss_subdev_resources csid_res_8x39[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 0 }, + { 0 }, + { 100000000, 200000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } + }, + + /* CSID1 */ + { + .regulators = {}, + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 0 }, + { 0 }, + { 100000000, 200000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } + }, + + /* CSID2 */ + { + .regulators = {}, + .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", + "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 0 }, + { 0 }, + { 100000000, 200000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_4_1 + } + }, +}; + +static const struct camss_subdev_resources ispif_res_8x39 = { + /* ISPIF */ + .clock = { "top_ahb", "ispif_ahb", "ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi", + "csi2", "csi2_pix", "csi2_rdi" }, + .clock_for_reset = { "vfe0", "csi_vfe0" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = { "ispif" }, +}; + +static const struct camss_subdev_resources vfe_res_8x39[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "top_ahb", "ispif_ahb", "vfe0", "csi_vfe0", + "vfe_ahb", "vfe_axi", "ahb" }, + .clock_rate = { { 0 }, + { 40000000, 80000000 }, + { 50000000, 80000000, 100000000, 160000000, + 177780000, 200000000, 266670000, 320000000, + 400000000, 465000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_vbif = true, + .vbif_name = "vfe0_vbif", + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 + } + } +}; + static const struct camss_subdev_resources csid_res_8x53[] = { /* CSID0 */ { @@ -2617,6 +2760,317 @@ static const struct resources_icc icc_res_sm8550[] = { }, }; +static const struct camss_subdev_resources csiphy_res_sm8650[] = { + /* CSIPHY0 */ + { + .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, + /* CSIPHY1 */ + { + .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, + /* CSIPHY2 */ + { + .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, + /* CSIPHY3 */ + { + .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", }, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .id = 3, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, + /* CSIPHY4 */ + { + .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", }, + .clock = { "csiphy4", "csiphy4_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" }, + .csiphy = { + .id = 4, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, + /* CSIPHY5 */ + { + .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", }, + .clock = { "csiphy5", "csiphy5_timer" }, + .clock_rate = { { 400000000 }, + { 400000000 } }, + .reg = { "csiphy5" }, + .interrupt = { "csiphy5" }, + .csiphy = { + .id = 5, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845, + }, + }, +}; + +static const struct camss_subdev_resources csid_res_sm8650[] = { + /* CSID0 */ + { + .regulators = { }, + .clock = { "csid", "csiphy_rx" }, + .clock_rate = { { 400000000 }, + { 400000000, 480000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_gen3, + .formats = &csid_formats_gen2, + }, + }, + /* CSID1 */ + { + .regulators = { }, + .clock = { "csid", "csiphy_rx" }, + .clock_rate = { { 400000000 }, + { 400000000, 480000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_gen3, + .formats = &csid_formats_gen2, + }, + }, + /* CSID2 */ + { + .regulators = { }, + .clock = { "csid", "csiphy_rx" }, + .clock_rate = { { 400000000 }, + { 400000000, 480000000 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_gen3, + .formats = &csid_formats_gen2, + }, + }, + /* CSID3 lite */ + { + .regulators = { }, + .clock = { "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx" }, + .clock_rate = { { 0 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite0" }, + .interrupt = { "csid_lite0" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_gen3, + .formats = &csid_formats_gen2, + }, + }, + /* CSID4 lite */ + { + .regulators = { }, + .clock = { "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx" }, + .clock_rate = { { 0 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite1" }, + .interrupt = { "csid_lite1" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_gen3, + .formats = &csid_formats_gen2, + }, + }, +}; + +static const struct camss_subdev_resources vfe_res_sm8650[] = { + /* VFE0 */ + { + .regulators = { }, + .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb", + "camnoc_axi", "vfe0_fast_ahb", "vfe0", "cpas_vfe0", + "qdss_debug_xo", + }, + .clock_rate = { { 0 }, + { 80000000 }, + { 300000000, 400000000 }, + { 300000000, 400000000 }, + { 0 }, + { 466000000, 594000000, 675000000, 785000000 }, + { 0 }, + { 0 }, + }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_gen3, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* VFE1 */ + { + .regulators = { }, + .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb", + "camnoc_axi", "vfe1_fast_ahb", "vfe1", "cpas_vfe1", + "qdss_debug_xo", + }, + .clock_rate = { { 0 }, + { 80000000 }, + { 300000000, 400000000 }, + { 300000000, 400000000 }, + { 0 }, + { 466000000, 594000000, 675000000, 785000000 }, + { 0 }, + { 0 }, + }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_gen3, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* VFE2 */ + { + .regulators = { }, + .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb", + "camnoc_axi", "vfe2_fast_ahb", "vfe2", "cpas_vfe2", + "qdss_debug_xo", + }, + .clock_rate = { { 0 }, + { 80000000 }, + { 300000000, 400000000 }, + { 300000000, 400000000 }, + { 0 }, + { 466000000, 594000000, 675000000, 785000000 }, + { 0 }, + { 0 }, + }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife2", + .hw_ops = &vfe_ops_gen3, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* VFE3 lite */ + { + .regulators = { }, + .clock = { "gcc_axi_hf", "cpas_ahb", "camnoc_axi", + "vfe_lite_ahb", "vfe_lite", "cpas_vfe_lite", + "qdss_debug_xo", + }, + .clock_rate = { { 0 }, + { 80000000 }, + { 300000000, 400000000 }, + { 0 }, + { 400000000, 480000000 }, + { 0 }, + { 0 }, + }, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_gen3, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* VFE4 lite */ + { + .regulators = { }, + .clock = { "gcc_axi_hf", "cpas_ahb", "camnoc_axi", + "vfe_lite_ahb", "vfe_lite", "cpas_vfe_lite", + "qdss_debug_xo", + }, + .clock_rate = { { 0 }, + { 80000000 }, + { 300000000, 400000000 }, + { 0 }, + { 400000000, 480000000 }, + { 0 }, + { 0 }, + }, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_gen3, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, +}; + +static const struct resources_icc icc_res_sm8650[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "hf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_8300[] = { /* CSIPHY0 */ { @@ -4171,6 +4625,7 @@ static int camss_probe(struct platform_device *pdev) return -ENOMEM; if (camss->res->version == CAMSS_8x16 || + camss->res->version == CAMSS_8x39 || camss->res->version == CAMSS_8x53 || camss->res->version == CAMSS_8x96) { camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL); @@ -4302,6 +4757,17 @@ static const struct camss_resources msm8916_resources = { .vfe_num = ARRAY_SIZE(vfe_res_8x16), }; +static const struct camss_resources msm8939_resources = { + .version = CAMSS_8x39, + .csiphy_res = csiphy_res_8x39, + .csid_res = csid_res_8x39, + .ispif_res = &ispif_res_8x39, + .vfe_res = vfe_res_8x39, + .csiphy_num = ARRAY_SIZE(csiphy_res_8x39), + .csid_num = ARRAY_SIZE(csid_res_8x39), + .vfe_num = ARRAY_SIZE(vfe_res_8x39), +}; + static const struct camss_resources msm8953_resources = { .version = CAMSS_8x53, .icc_res = icc_res_8x53, @@ -4452,6 +4918,20 @@ static const struct camss_resources sm8550_resources = { .vfe_num = ARRAY_SIZE(vfe_res_8550), }; +static const struct camss_resources sm8650_resources = { + .version = CAMSS_8650, + .pd_name = "top", + .csiphy_res = csiphy_res_sm8650, + .csid_res = csid_res_sm8650, + .csid_wrapper_res = &csid_wrapper_res_sm8550, + .vfe_res = vfe_res_sm8650, + .icc_res = icc_res_sm8650, + .icc_path_num = ARRAY_SIZE(icc_res_sm8650), + .csiphy_num = ARRAY_SIZE(csiphy_res_sm8650), + .csid_num = ARRAY_SIZE(csid_res_sm8650), + .vfe_num = ARRAY_SIZE(vfe_res_sm8650), +}; + static const struct camss_resources x1e80100_resources = { .version = CAMSS_X1E80100, .pd_name = "top", @@ -4468,6 +4948,7 @@ static const struct camss_resources x1e80100_resources = { static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss", .data = &msm8916_resources }, + { .compatible = "qcom,msm8939-camss", .data = &msm8939_resources }, { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources }, { .compatible = "qcom,msm8996-camss", .data = &msm8996_resources }, { .compatible = "qcom,qcm2290-camss", .data = &qcm2290_resources }, @@ -4480,6 +4961,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, + { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources }, { .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources }, { } }; @@ -4537,7 +5019,6 @@ static struct platform_driver qcom_camss_driver = { module_platform_driver(qcom_camss_driver); -MODULE_ALIAS("platform:qcom-camss"); MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index a70fbc78ccc3..9d9a62640e25 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -41,6 +41,7 @@ (to_camss_index(ptr_module, index)->dev) #define CAMSS_RES_MAX 17 +#define CAMSS_INIT_BUF_COUNT 2 struct camss_subdev_resources { char *regulators[CAMSS_RES_MAX]; @@ -81,6 +82,7 @@ enum camss_version { CAMSS_2290, CAMSS_7280, CAMSS_8x16, + CAMSS_8x39, CAMSS_8x53, CAMSS_8x96, CAMSS_8250, @@ -88,6 +90,7 @@ enum camss_version { CAMSS_8300, CAMSS_845, CAMSS_8550, + CAMSS_8650, CAMSS_8775P, CAMSS_X1E80100, }; diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 13270cd6d899..fad3be044e5f 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -26,7 +26,7 @@ qcom-iris-objs += iris_buffer.o \ iris_vpu_common.o \ ifeq ($(CONFIG_VIDEO_QCOM_VENUS),) -qcom-iris-objs += iris_platform_sm8250.o +qcom-iris-objs += iris_platform_gen1.o endif obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index c0900038e7de..b89b1ee06cce 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -171,9 +171,14 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst) static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst) { u32 y_plane, uv_plane, y_stride, uv_stride; - struct v4l2_format *f = inst->fmt_dst; u32 uv_meta_stride, uv_meta_plane; u32 y_meta_stride, y_meta_plane; + struct v4l2_format *f = NULL; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1), META_STRIDE_ALIGNED); @@ -261,7 +266,10 @@ int iris_get_buffer_size(struct iris_inst *inst, case BUF_INPUT: return iris_dec_bitstream_buffer_size(inst); case BUF_OUTPUT: - return iris_yuv_buffer_size_nv12(inst); + if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) + return iris_yuv_buffer_size_qc08c(inst); + else + return iris_yuv_buffer_size_nv12(inst); case BUF_DPB: return iris_yuv_buffer_size_qc08c(inst); default: @@ -270,7 +278,10 @@ int iris_get_buffer_size(struct iris_inst *inst, } else { switch (buffer_type) { case BUF_INPUT: - return iris_yuv_buffer_size_nv12(inst); + if (inst->fmt_src->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) + return iris_yuv_buffer_size_qc08c(inst); + else + return iris_yuv_buffer_size_nv12(inst); case BUF_OUTPUT: return iris_enc_bitstream_buffer_size(inst); default: diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c index 9fc663bdaf3f..7f1c7fe144f7 100644 --- a/drivers/media/platform/qcom/iris/iris_common.c +++ b/drivers/media/platform/qcom/iris/iris_common.c @@ -91,12 +91,14 @@ int iris_process_streamon_input(struct iris_inst *inst) int iris_process_streamon_output(struct iris_inst *inst) { const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; - bool drain_active = false, drc_active = false; enum iris_inst_sub_state clear_sub_state = 0; + bool drain_active, drc_active, first_ipsc; int ret = 0; iris_scale_power(inst); + first_ipsc = inst->sub_state & IRIS_INST_SUB_FIRST_IPSC; + drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN && inst->sub_state & IRIS_INST_SUB_DRAIN_LAST; @@ -108,7 +110,8 @@ int iris_process_streamon_output(struct iris_inst *inst) else if (drain_active) clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST; - if (inst->domain == DECODER && inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { + /* Input internal buffer reconfiguration required in case of resolution change */ + if (first_ipsc || drc_active) { ret = iris_alloc_and_queue_input_int_bufs(inst); if (ret) return ret; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 754a5ad718bc..c0b3a09ad3e3 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -301,7 +301,7 @@ error: void iris_session_init_caps(struct iris_core *core) { - struct platform_inst_fw_cap *caps; + const struct platform_inst_fw_cap *caps; u32 i, num_cap, cap_id; caps = core->iris_platform_data->inst_fw_caps_dec; @@ -313,13 +313,23 @@ void iris_session_init_caps(struct iris_core *core) continue; core->inst_fw_caps_dec[cap_id].cap_id = caps[i].cap_id; - core->inst_fw_caps_dec[cap_id].min = caps[i].min; - core->inst_fw_caps_dec[cap_id].max = caps[i].max; core->inst_fw_caps_dec[cap_id].step_or_mask = caps[i].step_or_mask; - core->inst_fw_caps_dec[cap_id].value = caps[i].value; core->inst_fw_caps_dec[cap_id].flags = caps[i].flags; core->inst_fw_caps_dec[cap_id].hfi_id = caps[i].hfi_id; core->inst_fw_caps_dec[cap_id].set = caps[i].set; + + if (cap_id == PIPE) { + core->inst_fw_caps_dec[cap_id].value = + core->iris_platform_data->num_vpp_pipe; + core->inst_fw_caps_dec[cap_id].min = + core->iris_platform_data->num_vpp_pipe; + core->inst_fw_caps_dec[cap_id].max = + core->iris_platform_data->num_vpp_pipe; + } else { + core->inst_fw_caps_dec[cap_id].min = caps[i].min; + core->inst_fw_caps_dec[cap_id].max = caps[i].max; + core->inst_fw_caps_dec[cap_id].value = caps[i].value; + } } caps = core->iris_platform_data->inst_fw_caps_enc; diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 9ab499fad946..679444327ed7 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -19,8 +19,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) u32 pas_id = core->iris_platform_data->pas_id; const struct firmware *firmware = NULL; struct device *dev = core->dev; - struct reserved_mem *rmem; - struct device_node *node; + struct resource res; phys_addr_t mem_phys; size_t res_size; ssize_t fw_size; @@ -30,17 +29,12 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4) return -EINVAL; - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!node) - return -EINVAL; - - rmem = of_reserved_mem_lookup(node); - of_node_put(node); - if (!rmem) - return -EINVAL; + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); + if (ret) + return ret; - mem_phys = rmem->base; - res_size = rmem->size; + mem_phys = res.start; + res_size = resource_size(&res); ret = request_firmware(&firmware, fw_name, dev); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index e1788c266bb1..52da7ef7bab0 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -774,27 +774,29 @@ static int iris_hfi_gen1_set_raw_format(struct iris_inst *inst, u32 plane) pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; if (iris_split_mode_enabled(inst)) { fmt.buffer_type = HFI_BUFFER_OUTPUT; - fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? - HFI_COLOR_FORMAT_NV12_UBWC : 0; + fmt.format = HFI_COLOR_FORMAT_NV12_UBWC; ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt)); if (ret) return ret; fmt.buffer_type = HFI_BUFFER_OUTPUT2; - fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0; + fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? + HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC; ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt)); } else { fmt.buffer_type = HFI_BUFFER_OUTPUT; - fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0; + fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? + HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC; ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt)); } } else { pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; fmt.buffer_type = HFI_BUFFER_INPUT; - fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0; + fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? + HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC; ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt)); } @@ -806,6 +808,9 @@ static int iris_hfi_gen1_set_format_constraints(struct iris_inst *inst, u32 plan const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO; struct hfi_uncompressed_plane_actual_constraints_info pconstraint; + if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) + return 0; + pconstraint.buffer_type = HFI_BUFFER_OUTPUT2; pconstraint.num_planes = 2; pconstraint.plane_format[0].stride_multiples = 128; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 4ce71a142508..6a772db2ec33 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -422,6 +422,20 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst, u32 plane) sizeof(u32)); } +static int iris_hfi_gen2_set_opb_enable(struct iris_inst *inst, u32 plane) +{ + u32 port = iris_hfi_gen2_get_port(inst, plane); + u32 opb_enable = iris_split_mode_enabled(inst); + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_OPB_ENABLE, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32, + &opb_enable, + sizeof(u32)); +} + static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane) { u32 port = iris_hfi_gen2_get_port(inst, plane); @@ -429,10 +443,12 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; - hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0; + hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? + HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC; } else { pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; - hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0; + hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? + HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC; } return iris_hfi_gen2_session_set_property(inst, @@ -527,6 +543,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace }, {HFI_PROP_PROFILE, iris_hfi_gen2_set_profile }, {HFI_PROP_LEVEL, iris_hfi_gen2_set_level }, + {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat }, {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index aa1f795f5626..1b6a4dbac828 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -91,6 +91,7 @@ enum hfi_seq_header_mode { #define HFI_PROP_BUFFER_MARK 0x0300016c #define HFI_PROP_RAW_RESOLUTION 0x03000178 #define HFI_PROP_TOTAL_PEAK_BITRATE 0x0300017C +#define HFI_PROP_OPB_ENABLE 0x03000184 #define HFI_PROP_COMV_BUFFER_COUNT 0x03000193 #define HFI_PROP_END 0x03FFFFFF diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 5982d7adefea..62fbb30691ff 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -15,12 +15,17 @@ #define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240 -enum iris_fmt_type { +enum iris_fmt_type_out { IRIS_FMT_H264, IRIS_FMT_HEVC, IRIS_FMT_VP9, }; +enum iris_fmt_type_cap { + IRIS_FMT_NV12, + IRIS_FMT_QC08C, +}; + struct iris_fmt { u32 pixfmt; u32 type; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 58d05e0a112e..8d8cdb56a3c7 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -41,16 +41,19 @@ enum pipe_type { PIPE_4 = 4, }; -extern struct iris_platform_data qcs8300_data; -extern struct iris_platform_data sm8250_data; -extern struct iris_platform_data sm8550_data; -extern struct iris_platform_data sm8650_data; -extern struct iris_platform_data sm8750_data; +extern const struct iris_platform_data qcs8300_data; +extern const struct iris_platform_data sc7280_data; +extern const struct iris_platform_data sm8250_data; +extern const struct iris_platform_data sm8550_data; +extern const struct iris_platform_data sm8650_data; +extern const struct iris_platform_data sm8750_data; enum platform_clk_type { IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */ IRIS_CTRL_CLK, + IRIS_AHB_CLK, IRIS_HW_CLK, + IRIS_HW_AHB_CLK, IRIS_AXI1_CLK, IRIS_CTRL_FREERUN_CLK, IRIS_HW_FREERUN_CLK, @@ -215,15 +218,16 @@ struct iris_platform_data { const char *fwname; u32 pas_id; struct platform_inst_caps *inst_caps; - struct platform_inst_fw_cap *inst_fw_caps_dec; + const struct platform_inst_fw_cap *inst_fw_caps_dec; u32 inst_fw_caps_dec_size; - struct platform_inst_fw_cap *inst_fw_caps_enc; + const struct platform_inst_fw_cap *inst_fw_caps_enc; u32 inst_fw_caps_enc_size; struct tz_cp_config *tz_cp_config_data; u32 core_arch; u32 hw_response_timeout; struct ubwc_config_data *ubwc_config; u32 num_vpp_pipe; + bool no_aon; u32 max_session_count; /* max number of macroblocks per frame supported */ u32 max_core_mbpf; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 16486284f8ac..34cbeb8f52e2 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -12,18 +12,18 @@ #include "iris_vpu_buffer.h" #include "iris_vpu_common.h" +#include "iris_platform_sc7280.h" + #define BITRATE_MIN 32000 #define BITRATE_MAX 160000000 #define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2) #define BITRATE_STEP 100 -static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { +static const struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { { .cap_id = PIPE, - .min = PIPE_1, - .max = PIPE_4, + /* .max, .min and .value are set via platform data */ .step_or_mask = 1, - .value = PIPE_4, .hfi_id = HFI_PROPERTY_PARAM_WORK_ROUTE, .set = iris_set_pipe, }, @@ -38,7 +38,7 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { }, }; -static struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { +static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { { .cap_id = STAGE, .min = STAGE_1, @@ -314,7 +314,7 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { BUF_SCRATCH_2, }; -struct iris_platform_data sm8250_data = { +const struct iris_platform_data sm8250_data = { .get_instance = iris_hfi_gen1_get_instance, .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, @@ -364,3 +364,54 @@ struct iris_platform_data sm8250_data = { .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; + +const struct iris_platform_data sc7280_data = { + .get_instance = iris_hfi_gen1_get_instance, + .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, + .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu2_ops, + .set_preset_registers = iris_set_sm8250_preset_registers, + .icc_tbl = sm8250_icc_table, + .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), + .bw_tbl_dec = sc7280_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), + .pmdomain_tbl = sm8250_pmdomain_table, + .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), + .opp_pd_tbl = sc7280_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), + .clk_tbl = sc7280_clk_table, + .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu20_p1.mbn", + .pas_id = IRIS_PAS_ID, + .inst_caps = &platform_inst_cap_sm8250, + .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, + .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), + .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, + .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), + .tz_cp_config_data = &tz_cp_config_sm8250, + .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, + .num_vpp_pipe = 1, + .no_aon = true, + .max_session_count = 16, + .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, + /* max spec for SC7280 is 4096x2176@60fps */ + .max_core_mbps = 4096 * 2176 / 256 * 60, + .dec_input_config_params_default = + sm8250_vdec_input_config_param_default, + .dec_input_config_params_default_size = + ARRAY_SIZE(sm8250_vdec_input_config_param_default), + .enc_input_config_params = sm8250_venc_input_config_param, + .enc_input_config_params_size = + ARRAY_SIZE(sm8250_venc_input_config_param), + + .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, + .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), + .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, + .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), + + .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 36d69cc73986..c1989240c248 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -19,7 +19,7 @@ #define VIDEO_ARCH_LX 1 #define BITRATE_MAX 245000000 -static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { +static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, @@ -160,10 +160,8 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { }, { .cap_id = PIPE, - .min = PIPE_1, - .max = PIPE_4, + /* .max, .min and .value are set via platform data */ .step_or_mask = 1, - .value = PIPE_4, .hfi_id = HFI_PROP_PIPE, .set = iris_set_pipe, }, @@ -203,7 +201,7 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { }, }; -static struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { +static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { { .cap_id = PROFILE_H264, .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, @@ -691,6 +689,7 @@ static const u32 sm8550_venc_input_config_params[] = { }; static const u32 sm8550_vdec_output_config_params[] = { + HFI_PROP_OPB_ENABLE, HFI_PROP_COLOR_FORMAT, HFI_PROP_LINEAR_STRIDE_SCANLINE, }; @@ -737,7 +736,7 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { BUF_SCRATCH_2, }; -struct iris_platform_data sm8550_data = { +const struct iris_platform_data sm8550_data = { .get_instance = iris_hfi_gen2_get_instance, .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, @@ -827,7 +826,7 @@ struct iris_platform_data sm8550_data = { * - controller_rst_tbl to sm8650_controller_reset_table * - fwname to "qcom/vpu/vpu33_p4.mbn" */ -struct iris_platform_data sm8650_data = { +const struct iris_platform_data sm8650_data = { .get_instance = iris_hfi_gen2_get_instance, .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, @@ -912,7 +911,7 @@ struct iris_platform_data sm8650_data = { .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; -struct iris_platform_data sm8750_data = { +const struct iris_platform_data sm8750_data = { .get_instance = iris_hfi_gen2_get_instance, .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, @@ -996,9 +995,8 @@ struct iris_platform_data sm8750_data = { /* * Shares most of SM8550 data except: * - inst_caps to platform_inst_cap_qcs8300 - * - inst_fw_caps to inst_fw_cap_qcs8300 */ -struct iris_platform_data qcs8300_data = { +const struct iris_platform_data qcs8300_data = { .get_instance = iris_hfi_gen2_get_instance, .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, @@ -1022,10 +1020,10 @@ struct iris_platform_data qcs8300_data = { .fwname = "qcom/vpu/vpu30_p4_s6.mbn", .pas_id = IRIS_PAS_ID, .inst_caps = &platform_inst_cap_qcs8300, - .inst_fw_caps_dec = inst_fw_cap_qcs8300_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_qcs8300_dec), - .inst_fw_caps_enc = inst_fw_cap_qcs8300_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_qcs8300_enc), + .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, + .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), + .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, + .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = &tz_cp_config_sm8550, .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h index 35ea0efade73..61025f1e965b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -3,537 +3,8 @@ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ -#define BITRATE_MAX 245000000 - -static struct platform_inst_fw_cap inst_fw_cap_qcs8300_dec[] = { - { - .cap_id = PROFILE_H264, - .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH), - .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = PROFILE_HEVC, - .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), - .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = PROFILE_VP9, - .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) | - BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2), - .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = LEVEL_H264, - .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), - .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = LEVEL_HEVC, - .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, - .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2), - .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = LEVEL_VP9, - .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, - .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0), - .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = TIER, - .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH), - .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, - .hfi_id = HFI_PROP_TIER, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = INPUT_BUF_HOST_MAX_COUNT, - .min = DEFAULT_MAX_HOST_BUF_COUNT, - .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, - .step_or_mask = 1, - .value = DEFAULT_MAX_HOST_BUF_COUNT, - .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, - .flags = CAP_FLAG_INPUT_PORT, - .set = iris_set_u32, - }, - { - .cap_id = STAGE, - .min = STAGE_1, - .max = STAGE_2, - .step_or_mask = 1, - .value = STAGE_2, - .hfi_id = HFI_PROP_STAGE, - .set = iris_set_stage, - }, - { - .cap_id = PIPE, - .min = PIPE_1, - .max = PIPE_2, - .step_or_mask = 1, - .value = PIPE_2, - .hfi_id = HFI_PROP_PIPE, - .set = iris_set_pipe, - }, - { - .cap_id = POC, - .min = 0, - .max = 2, - .step_or_mask = 1, - .value = 1, - .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE, - }, - { - .cap_id = CODED_FRAMES, - .min = CODED_FRAMES_PROGRESSIVE, - .max = CODED_FRAMES_PROGRESSIVE, - .step_or_mask = 0, - .value = CODED_FRAMES_PROGRESSIVE, - .hfi_id = HFI_PROP_CODED_FRAMES, - }, - { - .cap_id = BIT_DEPTH, - .min = BIT_DEPTH_8, - .max = BIT_DEPTH_8, - .step_or_mask = 1, - .value = BIT_DEPTH_8, - .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, - }, - { - .cap_id = RAP_FRAME, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 1, - .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME, - .flags = CAP_FLAG_INPUT_PORT, - .set = iris_set_u32, - }, -}; - -static struct platform_inst_fw_cap inst_fw_cap_qcs8300_enc[] = { - { - .cap_id = PROFILE_H264, - .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH), - .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = PROFILE_HEVC, - .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), - .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = LEVEL_H264, - .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0), - .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_0, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = LEVEL_HEVC, - .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, - .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) | - BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2), - .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = STAGE, - .min = STAGE_1, - .max = STAGE_2, - .step_or_mask = 1, - .value = STAGE_2, - .hfi_id = HFI_PROP_STAGE, - }, - { - .cap_id = HEADER_MODE, - .min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, - .max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | - BIT(V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), - .value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - .hfi_id = HFI_PROP_SEQ_HEADER_MODE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = PREPEND_SPSPPS_TO_IDR, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 0, - }, - { - .cap_id = BITRATE, - .min = 1, - .max = BITRATE_MAX, - .step_or_mask = 1, - .value = BITRATE_DEFAULT, - .hfi_id = HFI_PROP_TOTAL_BITRATE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = BITRATE_PEAK, - .min = 1, - .max = BITRATE_MAX, - .step_or_mask = 1, - .value = BITRATE_DEFAULT, - .hfi_id = HFI_PROP_TOTAL_PEAK_BITRATE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = BITRATE_MODE, - .min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | - BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR), - .value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .hfi_id = HFI_PROP_RATE_CONTROL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = FRAME_SKIP_MODE, - .min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, - .max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) | - BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT) | - BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT), - .value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = FRAME_RC_ENABLE, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 1, - }, - { - .cap_id = GOP_SIZE, - .min = 0, - .max = INT_MAX, - .step_or_mask = 1, - .value = 2 * DEFAULT_FPS - 1, - .hfi_id = HFI_PROP_MAX_GOP_FRAMES, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = ENTROPY_MODE, - .min = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, - .max = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | - BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC), - .value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, - .hfi_id = HFI_PROP_CABAC_SESSION, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - }, - { - .cap_id = MIN_FRAME_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - .hfi_id = HFI_PROP_MIN_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT, - }, - { - .cap_id = MIN_FRAME_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - .hfi_id = HFI_PROP_MIN_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT, - }, - { - .cap_id = MAX_FRAME_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - .hfi_id = HFI_PROP_MAX_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT, - }, - { - .cap_id = MAX_FRAME_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - .hfi_id = HFI_PROP_MAX_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT, - }, - { - .cap_id = I_FRAME_MIN_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = I_FRAME_MIN_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = P_FRAME_MIN_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = P_FRAME_MIN_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = B_FRAME_MIN_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = B_FRAME_MIN_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MIN_QP_8BIT, - }, - { - .cap_id = I_FRAME_MAX_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = I_FRAME_MAX_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = P_FRAME_MAX_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = P_FRAME_MAX_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = B_FRAME_MAX_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = B_FRAME_MAX_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = MAX_QP, - }, - { - .cap_id = I_FRAME_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = I_FRAME_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = P_FRAME_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = P_FRAME_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = B_FRAME_QP_H264, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, - { - .cap_id = B_FRAME_QP_HEVC, - .min = MIN_QP_8BIT, - .max = MAX_QP, - .step_or_mask = 1, - .value = DEFAULT_QP, - .hfi_id = HFI_PROP_QP_PACKED, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | - CAP_FLAG_DYNAMIC_ALLOWED, - }, -}; +#ifndef __IRIS_PLATFORM_QCS8300_H__ +#define __IRIS_PLATFORM_QCS8300_H__ static struct platform_inst_caps platform_inst_cap_qcs8300 = { .min_frame_width = 96, @@ -548,3 +19,5 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = { .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h new file mode 100644 index 000000000000..f1bef4d4bcfe --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_SC7280_H__ +#define __IRIS_PLATFORM_SC7280_H__ + +static const struct bw_info sc7280_bw_table_dec[] = { + { ((3840 * 2160) / 256) * 60, 1896000, }, + { ((3840 * 2160) / 256) * 30, 968000, }, + { ((1920 * 1080) / 256) * 60, 618000, }, + { ((1920 * 1080) / 256) * 30, 318000, }, +}; + +static const char * const sc7280_opp_pd_table[] = { "cx" }; + +static const struct platform_clk_data sc7280_clk_table[] = { + {IRIS_CTRL_CLK, "core" }, + {IRIS_AXI_CLK, "iface" }, + {IRIS_AHB_CLK, "bus" }, + {IRIS_HW_CLK, "vcodec_core" }, + {IRIS_HW_AHB_CLK, "vcodec_bus" }, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 00e99be16e08..9bc9b34c2576 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -358,6 +358,10 @@ static const struct of_device_id iris_dt_match[] = { }, #if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) { + .compatible = "qcom,sc7280-venus", + .data = &sc7280_data, + }, + { .compatible = "qcom,sm8250-venus", .data = &sm8250_data, }, diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c index cf32f268b703..164490c49c95 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.c +++ b/drivers/media/platform/qcom/iris/iris_resources.c @@ -112,7 +112,7 @@ int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk clock = iris_get_clk_by_type(core, clk_type); if (!clock) - return -EINVAL; + return -ENOENT; return clk_prepare_enable(clock); } diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index 85c70a62b1fd..e2f1131de431 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -34,7 +34,8 @@ int iris_get_mbpf(struct iris_inst *inst) bool iris_split_mode_enabled(struct iris_inst *inst) { - return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12; + return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; } void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index 139b821f7952..db8768d8a8f6 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -231,6 +231,8 @@ void iris_vb2_stop_streaming(struct vb2_queue *q) return; mutex_lock(&inst->lock); + if (inst->state == IRIS_INST_ERROR) + goto exit; if (!V4L2_TYPE_IS_OUTPUT(q->type) && !V4L2_TYPE_IS_CAPTURE(q->type)) @@ -241,10 +243,10 @@ void iris_vb2_stop_streaming(struct vb2_queue *q) goto exit; exit: - iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); - if (ret) + if (ret) { + iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); iris_inst_change_state(inst, IRIS_INST_ERROR); - + } mutex_unlock(&inst->lock); } diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index ae13c3e1b426..69ffe52590d3 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -67,7 +67,7 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_vdec_formats[] = { +static const struct iris_fmt iris_vdec_formats_out[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, @@ -82,12 +82,35 @@ static const struct iris_fmt iris_vdec_formats[] = { }, }; +static const struct iris_fmt iris_vdec_formats_cap[] = { + [IRIS_FMT_NV12] = { + .pixfmt = V4L2_PIX_FMT_NV12, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, + [IRIS_FMT_QC08C] = { + .pixfmt = V4L2_PIX_FMT_QC08C, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, +}; + static const struct iris_fmt * find_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - unsigned int size = ARRAY_SIZE(iris_vdec_formats); - const struct iris_fmt *fmt = iris_vdec_formats; + const struct iris_fmt *fmt = NULL; + unsigned int size = 0; unsigned int i; + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + fmt = iris_vdec_formats_out; + size = ARRAY_SIZE(iris_vdec_formats_out); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt = iris_vdec_formats_cap; + size = ARRAY_SIZE(iris_vdec_formats_cap); + break; + default: + return NULL; + } for (i = 0; i < size; i++) { if (fmt[i].pixfmt == pixfmt) @@ -103,8 +126,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) static const struct iris_fmt * find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = iris_vdec_formats; - unsigned int size = ARRAY_SIZE(iris_vdec_formats); + const struct iris_fmt *fmt = NULL; + unsigned int size = 0; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + fmt = iris_vdec_formats_out; + size = ARRAY_SIZE(iris_vdec_formats_out); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt = iris_vdec_formats_cap; + size = ARRAY_SIZE(iris_vdec_formats_cap); + break; + default: + return NULL; + } if (index >= size || fmt[index].type != type) return NULL; @@ -126,9 +162,10 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (f->index) + fmt = find_format_by_index(inst, f->index, f->type); + if (!fmt) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_NV12; + f->pixelformat = fmt->pixfmt; break; default: return -EINVAL; @@ -157,7 +194,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) { + if (!fmt) { f_inst = inst->fmt_dst; f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; @@ -190,8 +227,6 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) u32 codec_align; q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -238,10 +273,11 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) inst->crop.height = f->fmt.pix_mp.height; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + return -EINVAL; + fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) - return -EINVAL; fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); @@ -268,7 +304,8 @@ int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat) { const struct iris_fmt *fmt = NULL; - if (pixelformat != V4L2_PIX_FMT_NV12) { + fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!fmt) { fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); if (!fmt) return -EINVAL; diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index 099bd5ed4ae0..5830eba93c68 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -80,7 +80,7 @@ void iris_venc_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_venc_formats[] = { +static const struct iris_fmt iris_venc_formats_cap[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, @@ -91,12 +91,35 @@ static const struct iris_fmt iris_venc_formats[] = { }, }; +static const struct iris_fmt iris_venc_formats_out[] = { + [IRIS_FMT_NV12] = { + .pixfmt = V4L2_PIX_FMT_NV12, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_QC08C] = { + .pixfmt = V4L2_PIX_FMT_QC08C, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + static const struct iris_fmt * find_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = iris_venc_formats; - unsigned int size = ARRAY_SIZE(iris_venc_formats); + const struct iris_fmt *fmt = NULL; + unsigned int size = 0; unsigned int i; + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + fmt = iris_venc_formats_out; + size = ARRAY_SIZE(iris_venc_formats_out); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt = iris_venc_formats_cap; + size = ARRAY_SIZE(iris_venc_formats_cap); + break; + default: + return NULL; + } for (i = 0; i < size; i++) { if (fmt[i].pixfmt == pixfmt) @@ -112,8 +135,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) static const struct iris_fmt * find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = iris_venc_formats; - unsigned int size = ARRAY_SIZE(iris_venc_formats); + const struct iris_fmt *fmt = NULL; + unsigned int size = 0; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + fmt = iris_venc_formats_out; + size = ARRAY_SIZE(iris_venc_formats_out); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt = iris_venc_formats_cap; + size = ARRAY_SIZE(iris_venc_formats_cap); + break; + default: + return NULL; + } if (index >= size || fmt[index].type != type) return NULL; @@ -127,9 +163,11 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (f->index) + fmt = find_format_by_index(inst, f->index, f->type); + if (!fmt) return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_NV12; + + f->pixelformat = fmt->pixfmt; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); @@ -156,7 +194,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) fmt = find_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) { + if (!fmt) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -221,7 +259,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) iris_venc_try_fmt(inst, f); - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) + if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) return -EINVAL; fmt = inst->fmt_src; @@ -269,8 +307,6 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f) struct vb2_queue *q; q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -289,7 +325,8 @@ int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat) { const struct iris_fmt *fmt = NULL; - if (pixelformat != V4L2_PIX_FMT_NV12) { + fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!fmt) { fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); if (!fmt) return -EINVAL; diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index d38d0f6961cd..c9b881923ef1 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -630,7 +630,7 @@ unlock: return ret; } -static struct v4l2_file_operations iris_v4l2_file_ops = { +static const struct v4l2_file_operations iris_v4l2_file_ops = { .owner = THIS_MODULE, .open = iris_open, .release = iris_close, diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index de7d142316d2..9c103a2e4e4e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -3,9 +3,15 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include <linux/bits.h> +#include <linux/iopoll.h> +#include <linux/reset.h> + #include "iris_instance.h" #include "iris_vpu_common.h" +#include "iris_vpu_register_defines.h" + static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index bb98950e018f..515dd55a3377 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -222,12 +222,14 @@ int iris_vpu_power_off_controller(struct iris_core *core) writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); - writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + if (!core->iris_platform_data->no_aon) { + writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); - ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, - val, val & BIT(0), 200, 2000); - if (ret) - goto disable_power; + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, + val, val & BIT(0), 200, 2000); + if (ret) + goto disable_power; + } writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); @@ -250,6 +252,7 @@ int iris_vpu_power_off_controller(struct iris_core *core) writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); disable_power: + iris_disable_unprepare_clock(core, IRIS_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); iris_disable_unprepare_clock(core, IRIS_AXI_CLK); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); @@ -261,6 +264,7 @@ void iris_vpu_power_off_hw(struct iris_core *core) { dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); } @@ -294,11 +298,17 @@ int iris_vpu_power_on_controller(struct iris_core *core) ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); if (ret) - goto err_disable_clock; + goto err_disable_axi_clock; + + ret = iris_prepare_enable_clock(core, IRIS_AHB_CLK); + if (ret && ret != -ENOENT) + goto err_disable_ctrl_clock; return 0; -err_disable_clock: +err_disable_ctrl_clock: + iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); +err_disable_axi_clock: iris_disable_unprepare_clock(core, IRIS_AXI_CLK); err_disable_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); @@ -318,13 +328,19 @@ int iris_vpu_power_on_hw(struct iris_core *core) if (ret) goto err_disable_power; + ret = iris_prepare_enable_clock(core, IRIS_HW_AHB_CLK); + if (ret && ret != -ENOENT) + goto err_disable_hw_clock; + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); if (ret) - goto err_disable_clock; + goto err_disable_hw_ahb_clock; return 0; -err_disable_clock: +err_disable_hw_ahb_clock: + iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); +err_disable_hw_clock: iris_disable_unprepare_clock(core, IRIS_HW_CLK); err_disable_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index abf959b8f3a6..24d2b2fd0340 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -1146,6 +1146,5 @@ static struct platform_driver qcom_venus_driver = { }; module_platform_driver(qcom_venus_driver); -MODULE_ALIAS("platform:qcom-venus"); MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index af0ac40bec9b..1de7436713ed 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -9,7 +9,6 @@ #include <linux/iommu.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_address.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/of_device.h> @@ -83,8 +82,7 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, phys_addr_t *mem_phys, size_t *mem_size) { const struct firmware *mdt; - struct reserved_mem *rmem; - struct device_node *node; + struct resource res; struct device *dev; ssize_t fw_size; void *mem_va; @@ -94,15 +92,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, *mem_size = 0; dev = core->dev; - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!node) { - dev_err(dev, "no memory-region specified\n"); - return -EINVAL; - } - - rmem = of_reserved_mem_lookup(node); - of_node_put(node); - if (!rmem) { + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); + if (ret) { dev_err(dev, "failed to lookup reserved memory-region\n"); return -EINVAL; } @@ -117,8 +108,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, goto err_release_fw; } - *mem_phys = rmem->base; - *mem_size = rmem->size; + *mem_phys = res.start; + *mem_size = resource_size(&res); if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) { ret = -EINVAL; diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 55c27345b7d8..4a6641fdffcf 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -329,8 +329,6 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) struct vb2_queue *q; q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -1778,12 +1776,9 @@ static int vdec_probe(struct platform_device *pdev) struct venus_core *core; int ret; - if (!dev->parent) - return -EPROBE_DEFER; - core = dev_get_drvdata(dev->parent); if (!core) - return -EPROBE_DEFER; + return -EINVAL; platform_set_drvdata(pdev, core); @@ -1882,6 +1877,5 @@ static struct platform_driver qcom_venus_dec_driver = { }; module_platform_driver(qcom_venus_dec_driver); -MODULE_ALIAS("platform:qcom-venus-decoder"); MODULE_DESCRIPTION("Qualcomm Venus video decoder driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index fba07557a399..b478b982a80d 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -241,8 +241,6 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) struct vb2_queue *q; q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); - if (!q) - return -EINVAL; if (vb2_is_busy(q)) return -EBUSY; @@ -1560,12 +1558,9 @@ static int venc_probe(struct platform_device *pdev) struct venus_core *core; int ret; - if (!dev->parent) - return -EPROBE_DEFER; - core = dev_get_drvdata(dev->parent); if (!core) - return -EPROBE_DEFER; + return -EINVAL; platform_set_drvdata(pdev, core); @@ -1664,6 +1659,5 @@ static struct platform_driver qcom_venus_enc_driver = { }; module_platform_driver(qcom_venus_enc_driver); -MODULE_ALIAS("platform:qcom-venus-encoder"); MODULE_DESCRIPTION("Qualcomm Venus video encoder driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig index 27a54fa79083..bd8247c0b8aa 100644 --- a/drivers/media/platform/renesas/Kconfig +++ b/drivers/media/platform/renesas/Kconfig @@ -42,6 +42,7 @@ config VIDEO_SH_VOU source "drivers/media/platform/renesas/rcar-isp/Kconfig" source "drivers/media/platform/renesas/rcar-vin/Kconfig" source "drivers/media/platform/renesas/rzg2l-cru/Kconfig" +source "drivers/media/platform/renesas/rzv2h-ivc/Kconfig" # Mem2mem drivers diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile index 1127259c09d6..b6b4abf01db2 100644 --- a/drivers/media/platform/renesas/Makefile +++ b/drivers/media/platform/renesas/Makefile @@ -6,6 +6,7 @@ obj-y += rcar-isp/ obj-y += rcar-vin/ obj-y += rzg2l-cru/ +obj-y += rzv2h-ivc/ obj-y += vsp1/ obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index 11bf47fb8266..0844934f7aa6 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -1246,6 +1246,7 @@ static struct device_node *rcar_drif_bond_enabled(struct platform_device *p) if (np && of_device_is_available(np)) return np; + of_node_put(np); return NULL; } diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c index e615c56083f1..672869815f63 100644 --- a/drivers/media/platform/renesas/rcar_fdp1.c +++ b/drivers/media/platform/renesas/rcar_fdp1.c @@ -1409,9 +1409,6 @@ static int fdp1_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct fdp1_ctx *ctx = file_to_ctx(file); struct fdp1_q_data *q_data; - if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) - return -EINVAL; - q_data = get_q_data(ctx, f->type); f->fmt.pix_mp = q_data->format; @@ -2302,8 +2299,7 @@ static int fdp1_probe(struct platform_device *pdev) fdp1->fcp = rcar_fcp_get(fcp_node); of_node_put(fcp_node); if (IS_ERR(fdp1->fcp)) { - dev_dbg(&pdev->dev, "FCP not found (%ld)\n", - PTR_ERR(fdp1->fcp)); + dev_dbg(&pdev->dev, "FCP not found (%pe)\n", fdp1->fcp); return PTR_ERR(fdp1->fcp); } } diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c index 46ea259a2bb9..a6d26b446494 100644 --- a/drivers/media/platform/renesas/rcar_jpu.c +++ b/drivers/media/platform/renesas/rcar_jpu.c @@ -825,9 +825,6 @@ static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct jpu_ctx *ctx = file_to_ctx(file); - if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) - return -EINVAL; - return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type); } @@ -841,8 +838,6 @@ static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f) int ret; vq = v4l2_m2m_get_vq(m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) { v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__); @@ -866,9 +861,6 @@ static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct jpu_ctx *ctx = file_to_ctx(file); struct jpu_q_data *q_data; - if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type)) - return -EINVAL; - q_data = jpu_get_q_data(ctx, f->type); f->fmt.pix_mp = q_data->format; @@ -1701,7 +1693,6 @@ static void jpu_remove(struct platform_device *pdev) v4l2_device_unregister(&jpu->v4l2_dev); } -#ifdef CONFIG_PM_SLEEP static int jpu_suspend(struct device *dev) { struct jpu *jpu = dev_get_drvdata(dev); @@ -1725,11 +1716,8 @@ static int jpu_resume(struct device *dev) return 0; } -#endif -static const struct dev_pm_ops jpu_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(jpu_pm_ops, jpu_suspend, jpu_resume); static struct platform_driver jpu_driver = { .probe = jpu_probe, @@ -1737,7 +1725,7 @@ static struct platform_driver jpu_driver = { .driver = { .of_match_table = jpu_dt_ids, .name = DRV_NAME, - .pm = &jpu_pm_ops, + .pm = pm_sleep_ptr(&jpu_pm_ops), }, }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 1520211e7418..0fbdae280fdc 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -303,8 +303,8 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); if (IS_ERR(remote_pad)) { - dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", - csi2->remote_source->name, PTR_ERR(remote_pad)); + dev_err(csi2->dev, "can't get source pad of %s (%pe)\n", + csi2->remote_source->name, remote_pad); return PTR_ERR(remote_pad); } @@ -722,8 +722,8 @@ static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); if (IS_ERR(remote_pad)) { - dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", - csi2->remote_source->name, PTR_ERR(remote_pad)); + dev_err(csi2->dev, "can't get source pad of %s (%pe)\n", + csi2->remote_source->name, remote_pad); return PTR_ERR(remote_pad); } return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc, diff --git a/drivers/media/platform/renesas/rzv2h-ivc/Kconfig b/drivers/media/platform/renesas/rzv2h-ivc/Kconfig new file mode 100644 index 000000000000..eb6c6ce1caba --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_RZV2H_IVC + tristate "Renesas RZ/V2H(P) Input Video Control block driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_RENESAS || COMPILE_TEST + depends on OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + Support for the Renesas RZ/V2H(P) Input Video Control Block + (IVC). + + To compile this driver as a module, choose M here: the + module will be called rzv2h-ivc. diff --git a/drivers/media/platform/renesas/rzv2h-ivc/Makefile b/drivers/media/platform/renesas/rzv2h-ivc/Makefile new file mode 100644 index 000000000000..080ee3570f09 --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +rzv2h-ivc-y := rzv2h-ivc-dev.o rzv2h-ivc-subdev.o rzv2h-ivc-video.o + +obj-$(CONFIG_VIDEO_RZV2H_IVC) += rzv2h-ivc.o diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c new file mode 100644 index 000000000000..e9857eb5b51a --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/V2H(P) Input Video Control Block driver + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include "rzv2h-ivc.h" + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val) +{ + writel(val, ivc->base + addr); +} + +void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr, + u32 mask, u32 val) +{ + u32 orig, new; + + orig = readl(ivc->base + addr); + + new = orig & ~mask; + new |= val & mask; + + if (new != orig) + writel(new, ivc->base + addr); +} + +static int rzv2h_ivc_get_hardware_resources(struct rzv2h_ivc *ivc, + struct platform_device *pdev) +{ + static const char * const resource_names[RZV2H_IVC_NUM_HW_RESOURCES] = { + "reg", + "axi", + "isp", + }; + struct resource *res; + int ret; + + ivc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(ivc->base)) + return dev_err_probe(ivc->dev, PTR_ERR(ivc->base), + "failed to map IO memory\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(resource_names); i++) + ivc->clks[i].id = resource_names[i]; + + ret = devm_clk_bulk_get(ivc->dev, ARRAY_SIZE(resource_names), ivc->clks); + if (ret) + return dev_err_probe(ivc->dev, ret, "failed to acquire clks\n"); + + for (unsigned int i = 0; i < ARRAY_SIZE(resource_names); i++) + ivc->resets[i].id = resource_names[i]; + + ret = devm_reset_control_bulk_get_optional_shared(ivc->dev, + ARRAY_SIZE(resource_names), + ivc->resets); + if (ret) + return dev_err_probe(ivc->dev, ret, "failed to acquire resets\n"); + + return 0; +} + +static void rzv2h_ivc_global_config(struct rzv2h_ivc *ivc) +{ + /* Currently we only support single-exposure input */ + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PLNUM, RZV2H_IVC_ONE_EXPOSURE); + + /* + * Datasheet says we should disable the interrupts before changing mode + * to avoid spurious IFP interrupt. + */ + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_INT_EN, 0x0); + + /* + * RZ/V2H(P) documentation says software controlled single context mode + * is not supported, and currently the driver does not support the + * multi-context mode. That being so we just set single context sw-hw + * mode. + */ + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_CONTEXT, + RZV2H_IVC_SINGLE_CONTEXT_SW_HW_CFG); + + /* + * We enable the frame end interrupt so that we know when we should send + * follow-up frames. + */ + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_INT_EN, RZV2H_IVC_VVAL_IFPE); +} + +static irqreturn_t rzv2h_ivc_isr(int irq, void *context) +{ + struct device *dev = context; + struct rzv2h_ivc *ivc = dev_get_drvdata(dev); + + guard(spinlock)(&ivc->spinlock); + + /* IRQ should never be triggered before vvalid_ifp has been reset to 2 */ + if (WARN_ON(!ivc->vvalid_ifp)) + return IRQ_HANDLED; + + /* + * The first interrupt indicates that the buffer transfer has been + * completed. + */ + if (--ivc->vvalid_ifp) { + rzv2h_ivc_buffer_done(ivc); + return IRQ_HANDLED; + } + + /* + * The second interrupt indicates that the post-frame transfer VBLANK + * has completed, we can now schedule a new frame transfer, if any. + */ + queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + + return IRQ_HANDLED; +} + +static int rzv2h_ivc_runtime_resume(struct device *dev) +{ + struct rzv2h_ivc *ivc = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(ivc->clks), ivc->clks); + if (ret) { + dev_err(ivc->dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_bulk_deassert(ARRAY_SIZE(ivc->resets), ivc->resets); + if (ret) { + dev_err(ivc->dev, "failed to deassert resets\n"); + goto err_disable_clks; + } + + rzv2h_ivc_global_config(ivc); + + ret = request_irq(ivc->irqnum, rzv2h_ivc_isr, 0, dev_driver_string(dev), + dev); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto err_assert_resets; + } + + return 0; + +err_assert_resets: + reset_control_bulk_assert(ARRAY_SIZE(ivc->resets), ivc->resets); +err_disable_clks: + clk_bulk_disable_unprepare(ARRAY_SIZE(ivc->clks), ivc->clks); + + return ret; +} + +static int rzv2h_ivc_runtime_suspend(struct device *dev) +{ + struct rzv2h_ivc *ivc = dev_get_drvdata(dev); + + reset_control_bulk_assert(ARRAY_SIZE(ivc->resets), ivc->resets); + clk_bulk_disable_unprepare(ARRAY_SIZE(ivc->clks), ivc->clks); + free_irq(ivc->irqnum, dev); + + return 0; +} + +static const struct dev_pm_ops rzv2h_ivc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(rzv2h_ivc_runtime_suspend, rzv2h_ivc_runtime_resume, + NULL) +}; + +static int rzv2h_ivc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rzv2h_ivc *ivc; + int ret; + + ivc = devm_kzalloc(dev, sizeof(*ivc), GFP_KERNEL); + if (!ivc) + return -ENOMEM; + + ivc->dev = dev; + platform_set_drvdata(pdev, ivc); + + ret = devm_mutex_init(dev, &ivc->lock); + if (ret) + return ret; + + spin_lock_init(&ivc->spinlock); + + ret = rzv2h_ivc_get_hardware_resources(ivc, pdev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ivc->irqnum = platform_get_irq(pdev, 0); + if (ivc->irqnum < 0) + return ivc->irqnum; + + ret = rzv2h_ivc_initialise_subdevice(ivc); + if (ret) + goto err_disable_pm_runtime; + + return 0; + +err_disable_pm_runtime: + pm_runtime_disable(dev); + + return ret; +} + +static void rzv2h_ivc_remove(struct platform_device *pdev) +{ + struct rzv2h_ivc *ivc = platform_get_drvdata(pdev); + + rzv2h_deinit_video_dev_and_queue(ivc); + rzv2h_ivc_deinit_subdevice(ivc); +} + +static const struct of_device_id rzv2h_ivc_of_match[] = { + { .compatible = "renesas,r9a09g057-ivc", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzv2h_ivc_of_match); + +static struct platform_driver rzv2h_ivc_driver = { + .driver = { + .name = "rzv2h-ivc", + .of_match_table = rzv2h_ivc_of_match, + .pm = &rzv2h_ivc_pm_ops, + }, + .probe = rzv2h_ivc_probe, + .remove = rzv2h_ivc_remove, +}; + +module_platform_driver(rzv2h_ivc_driver); + +MODULE_AUTHOR("Daniel Scally <dan.scally@ideasonboard.com>"); +MODULE_DESCRIPTION("Renesas RZ/V2H(P) Input Video Control Block driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c new file mode 100644 index 000000000000..b1659544eaa0 --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/V2H(P) Input Video Control Block driver + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include "rzv2h-ivc.h" + +#include <linux/media.h> +#include <linux/media-bus-format.h> +#include <linux/v4l2-mediabus.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> + +#define RZV2H_IVC_N_INPUTS_PER_OUTPUT 6 + +/* + * We support 8/10/12/14/16/20 bit input in any bayer order, but the output + * format is fixed at 20-bits with the same order as the input. + */ +static const struct { + u32 inputs[RZV2H_IVC_N_INPUTS_PER_OUTPUT]; + u32 output; +} rzv2h_ivc_formats[] = { + { + .inputs = { + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SBGGR16_1X16, + MEDIA_BUS_FMT_SBGGR20_1X20, + }, + .output = MEDIA_BUS_FMT_SBGGR20_1X20 + }, + { + .inputs = { + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGBRG16_1X16, + MEDIA_BUS_FMT_SGBRG20_1X20, + }, + .output = MEDIA_BUS_FMT_SGBRG20_1X20 + }, + { + .inputs = { + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SGRBG16_1X16, + MEDIA_BUS_FMT_SGRBG20_1X20, + }, + .output = MEDIA_BUS_FMT_SGRBG20_1X20 + }, + { + .inputs = { + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_SRGGB16_1X16, + MEDIA_BUS_FMT_SRGGB20_1X20, + }, + .output = MEDIA_BUS_FMT_SRGGB20_1X20 + }, +}; + +static u32 rzv2h_ivc_get_mbus_output_from_input(u32 mbus_code) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++) { + for (j = 0; j < RZV2H_IVC_N_INPUTS_PER_OUTPUT; j++) { + if (rzv2h_ivc_formats[i].inputs[j] == mbus_code) + return rzv2h_ivc_formats[i].output; + } + } + + return 0; +} + +static int rzv2h_ivc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct v4l2_mbus_framefmt *fmt; + unsigned int order_index; + unsigned int index; + + /* + * On the source pad, only the 20-bit format corresponding to the sink + * pad format's bayer order is supported. + */ + if (code->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) { + if (code->index) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SINK_PAD); + code->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code); + + return 0; + } + + if (code->index >= ARRAY_SIZE(rzv2h_ivc_formats) * + RZV2H_IVC_N_INPUTS_PER_OUTPUT) + return -EINVAL; + + order_index = code->index / RZV2H_IVC_N_INPUTS_PER_OUTPUT; + index = code->index % RZV2H_IVC_N_INPUTS_PER_OUTPUT; + + code->code = rzv2h_ivc_formats[order_index].inputs[index]; + + return 0; +} + +static int rzv2h_ivc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct v4l2_mbus_framefmt *fmt; + + if (fse->index > 0) + return -EINVAL; + + if (fse->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) { + fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SINK_PAD); + + if (fse->code != rzv2h_ivc_get_mbus_output_from_input(fmt->code)) + return -EINVAL; + + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + + return 0; + } + + if (!rzv2h_ivc_get_mbus_output_from_input(fse->code)) + return -EINVAL; + + fse->min_width = RZV2H_IVC_MIN_WIDTH; + fse->max_width = RZV2H_IVC_MAX_WIDTH; + fse->min_height = RZV2H_IVC_MIN_HEIGHT; + fse->max_height = RZV2H_IVC_MAX_HEIGHT; + + return 0; +} + +static int rzv2h_ivc_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct v4l2_mbus_framefmt *src_fmt, *sink_fmt; + + if (format->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) + return v4l2_subdev_get_fmt(sd, state, format); + + sink_fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SINK_PAD); + + sink_fmt->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code) ? + fmt->code : rzv2h_ivc_formats[0].inputs[0]; + + sink_fmt->width = clamp(fmt->width, RZV2H_IVC_MIN_WIDTH, + RZV2H_IVC_MAX_WIDTH); + sink_fmt->height = clamp(fmt->height, RZV2H_IVC_MIN_HEIGHT, + RZV2H_IVC_MAX_HEIGHT); + + *fmt = *sink_fmt; + + src_fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SOURCE_PAD); + *src_fmt = *sink_fmt; + src_fmt->code = rzv2h_ivc_get_mbus_output_from_input(sink_fmt->code); + + return 0; +} + +static int rzv2h_ivc_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + /* + * We have a single source pad, which has a single stream. V4L2 core has + * already validated those things. The actual power-on and programming + * of registers will be done through the video device's .vidioc_streamon + * so there's nothing to actually do here... + */ + + return 0; +} + +static int rzv2h_ivc_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + return 0; +} + +static const struct v4l2_subdev_pad_ops rzv2h_ivc_pad_ops = { + .enum_mbus_code = rzv2h_ivc_enum_mbus_code, + .enum_frame_size = rzv2h_ivc_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = rzv2h_ivc_set_fmt, + .enable_streams = rzv2h_ivc_enable_streams, + .disable_streams = rzv2h_ivc_disable_streams, +}; + +static const struct v4l2_subdev_core_ops rzv2h_ivc_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops rzv2h_ivc_subdev_ops = { + .core = &rzv2h_ivc_core_ops, + .pad = &rzv2h_ivc_pad_ops, +}; + +static int rzv2h_ivc_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SINK_PAD); + sink_fmt->width = RZV2H_IVC_DEFAULT_WIDTH; + sink_fmt->height = RZV2H_IVC_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16; + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace); + sink_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace); + sink_fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + sink_fmt->colorspace, + sink_fmt->ycbcr_enc); + + src_fmt = v4l2_subdev_state_get_format(state, + RZV2H_IVC_SUBDEV_SOURCE_PAD); + + *src_fmt = *sink_fmt; + src_fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20; + + return 0; +} + +static int rzv2h_ivc_registered(struct v4l2_subdev *sd) +{ + struct rzv2h_ivc *ivc = container_of(sd, struct rzv2h_ivc, subdev.sd); + + return rzv2h_ivc_init_vdev(ivc, sd->v4l2_dev); +} + +static const struct v4l2_subdev_internal_ops rzv2h_ivc_subdev_internal_ops = { + .init_state = rzv2h_ivc_init_state, + .registered = rzv2h_ivc_registered, +}; + +static int rzv2h_ivc_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->source->entity); + struct rzv2h_ivc *ivc = video_get_drvdata(vdev); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->sink->entity); + const struct rzv2h_ivc_format *fmt; + const struct v4l2_pix_format_mplane *pix; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *mf; + unsigned int i; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); + mf = v4l2_subdev_state_get_format(state, link->sink->index); + + pix = &ivc->format.pix; + fmt = ivc->format.fmt; + + if (mf->width != pix->width || mf->height != pix->height) { + dev_dbg(ivc->dev, + "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + mf->width, mf->height, pix->width, pix->height); + ret = -EPIPE; + } + + for (i = 0; i < ARRAY_SIZE(fmt->mbus_codes); i++) + if (mf->code == fmt->mbus_codes[i]) + break; + + if (i == ARRAY_SIZE(fmt->mbus_codes)) { + dev_dbg(ivc->dev, + "link '%s':%u -> '%s':%u not valid: pixel format %p4cc cannot produce mbus_code 0x%04x\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + &pix->pixelformat, mf->code); + ret = -EPIPE; + } + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct media_entity_operations rzv2h_ivc_media_ops = { + .link_validate = rzv2h_ivc_link_validate, +}; + +int rzv2h_ivc_initialise_subdevice(struct rzv2h_ivc *ivc) +{ + struct v4l2_subdev *sd; + int ret; + + /* Initialise subdevice */ + sd = &ivc->subdev.sd; + sd->dev = ivc->dev; + v4l2_subdev_init(sd, &rzv2h_ivc_subdev_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->internal_ops = &rzv2h_ivc_subdev_internal_ops; + sd->entity.ops = &rzv2h_ivc_media_ops; + + ivc->subdev.pads[RZV2H_IVC_SUBDEV_SINK_PAD].flags = MEDIA_PAD_FL_SINK; + ivc->subdev.pads[RZV2H_IVC_SUBDEV_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; + + snprintf(sd->name, sizeof(sd->name), "rzv2h ivc block"); + + ret = media_entity_pads_init(&sd->entity, RZV2H_IVC_NUM_SUBDEV_PADS, + ivc->subdev.pads); + if (ret) { + dev_err(ivc->dev, "failed to initialise media entity\n"); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(ivc->dev, "failed to finalize subdev init\n"); + goto err_cleanup_subdev_entity; + } + + ret = v4l2_async_register_subdev(sd); + if (ret) { + dev_err(ivc->dev, "failed to register subdevice\n"); + goto err_cleanup_subdev; + } + + return 0; + +err_cleanup_subdev: + v4l2_subdev_cleanup(sd); +err_cleanup_subdev_entity: + media_entity_cleanup(&sd->entity); + + return ret; +} + +void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc) +{ + struct v4l2_subdev *sd = &ivc->subdev.sd; + + v4l2_subdev_cleanup(sd); + media_entity_remove_links(&sd->entity); + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); +} diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c new file mode 100644 index 000000000000..799453250b85 --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/V2H(P) Input Video Control Block driver + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include "rzv2h-ivc.h" + +#include <linux/cleanup.h> +#include <linux/iopoll.h> +#include <linux/lockdep.h> +#include <linux/media-bus-format.h> +#include <linux/minmax.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#define RZV2H_IVC_FIXED_HBLANK 0x20 +#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 15 + (120501 / (hts))) + +struct rzv2h_ivc_buf { + struct vb2_v4l2_buffer vb; + struct list_head queue; + dma_addr_t addr; +}; + +#define to_rzv2h_ivc_buf(vbuf) \ + container_of(vbuf, struct rzv2h_ivc_buf, vb) + +static const struct rzv2h_ivc_format rzv2h_ivc_formats[] = { + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR8_1X8, + }, + .dtype = MIPI_CSI2_DT_RAW8, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .mbus_codes = { + MEDIA_BUS_FMT_SGBRG8_1X8, + }, + .dtype = MIPI_CSI2_DT_RAW8, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .mbus_codes = { + MEDIA_BUS_FMT_SGRBG8_1X8, + }, + .dtype = MIPI_CSI2_DT_RAW8, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .mbus_codes = { + MEDIA_BUS_FMT_SRGGB8_1X8, + }, + .dtype = MIPI_CSI2_DT_RAW8, + }, + { + .fourcc = V4L2_PIX_FMT_RAW_CRU10, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10 + }, + .dtype = MIPI_CSI2_DT_RAW10, + }, + { + .fourcc = V4L2_PIX_FMT_RAW_CRU12, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12 + }, + .dtype = MIPI_CSI2_DT_RAW12, + }, + { + .fourcc = V4L2_PIX_FMT_RAW_CRU14, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14 + }, + .dtype = MIPI_CSI2_DT_RAW14, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .mbus_codes = { + MEDIA_BUS_FMT_SBGGR16_1X16, + }, + .dtype = MIPI_CSI2_DT_RAW16, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .mbus_codes = { + MEDIA_BUS_FMT_SGBRG16_1X16, + }, + .dtype = MIPI_CSI2_DT_RAW16, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .mbus_codes = { + MEDIA_BUS_FMT_SGRBG16_1X16, + }, + .dtype = MIPI_CSI2_DT_RAW16, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .mbus_codes = { + MEDIA_BUS_FMT_SRGGB16_1X16, + }, + .dtype = MIPI_CSI2_DT_RAW16, + }, +}; + +void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc) +{ + struct rzv2h_ivc_buf *buf; + + lockdep_assert_in_irq(); + + scoped_guard(spinlock, &ivc->buffers.lock) { + if (!ivc->buffers.curr) + return; + + buf = ivc->buffers.curr; + ivc->buffers.curr = NULL; + } + + buf->vb.sequence = ivc->buffers.sequence++; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void rzv2h_ivc_transfer_buffer(struct work_struct *work) +{ + struct rzv2h_ivc *ivc = container_of(work, struct rzv2h_ivc, + buffers.work); + struct rzv2h_ivc_buf *buf; + + /* Setup buffers */ + scoped_guard(spinlock_irqsave, &ivc->buffers.lock) { + buf = list_first_entry_or_null(&ivc->buffers.queue, + struct rzv2h_ivc_buf, queue); + } + + if (!buf) + return; + + list_del(&buf->queue); + + ivc->buffers.curr = buf; + buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_SADDL_P0, buf->addr); + + scoped_guard(spinlock_irqsave, &ivc->spinlock) { + ivc->vvalid_ifp = 2; + } + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_FRCON, 0x1); +} + +static int rzv2h_ivc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); + + if (*num_planes && *num_planes > 1) + return -EINVAL; + + if (sizes[0] && sizes[0] < ivc->format.pix.plane_fmt[0].sizeimage) + return -EINVAL; + + *num_planes = 1; + + if (!sizes[0]) + sizes[0] = ivc->format.pix.plane_fmt[0].sizeimage; + + return 0; +} + +static void rzv2h_ivc_buf_queue(struct vb2_buffer *vb) +{ + struct rzv2h_ivc *ivc = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rzv2h_ivc_buf *buf = to_rzv2h_ivc_buf(vbuf); + + scoped_guard(spinlock_irq, &ivc->buffers.lock) { + list_add_tail(&buf->queue, &ivc->buffers.queue); + } + + scoped_guard(spinlock_irq, &ivc->spinlock) { + if (vb2_is_streaming(vb->vb2_queue) && !ivc->vvalid_ifp) + queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + } +} + +static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc) +{ + const struct rzv2h_ivc_format *fmt = ivc->format.fmt; + struct v4l2_pix_format_mplane *pix = &ivc->format.pix; + unsigned int vblank; + unsigned int hts; + + /* Currently only CRU packed pixel formats are supported */ + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, + RZV2H_IVC_INPUT_FMT_CRU_PACKED); + + rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_PXFMT, + RZV2H_IVC_PXFMT_DTYPE, fmt->dtype); + + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_HSIZE, pix->width); + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_VSIZE, pix->height); + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_STRD, + pix->plane_fmt[0].bytesperline); + + /* + * The ISP has minimum vertical blanking requirements that must be + * adhered to by the IVC. The minimum is a function of the Iridix blocks + * clocking requirements and the width of the image and horizontal + * blanking, but if we assume the worst case then it boils down to the + * below (plus one to the numerator to ensure the answer is rounded up) + */ + + hts = pix->width + RZV2H_IVC_FIXED_HBLANK; + vblank = RZV2H_IVC_MIN_VBLANK(hts); + + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_BLANK, + RZV2H_IVC_VBLANK(vblank)); +} + +static void rzv2h_ivc_return_buffers(struct rzv2h_ivc *ivc, + enum vb2_buffer_state state) +{ + struct rzv2h_ivc_buf *buf, *tmp; + + guard(spinlock_irqsave)(&ivc->buffers.lock); + + if (ivc->buffers.curr) { + vb2_buffer_done(&ivc->buffers.curr->vb.vb2_buf, state); + ivc->buffers.curr = NULL; + } + + list_for_each_entry_safe(buf, tmp, &ivc->buffers.queue, queue) { + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); + int ret; + + ivc->buffers.sequence = 0; + ivc->vvalid_ifp = 0; + + ret = pm_runtime_resume_and_get(ivc->dev); + if (ret) + goto err_return_buffers; + + ret = video_device_pipeline_alloc_start(&ivc->vdev.dev); + if (ret) { + dev_err(ivc->dev, "failed to start media pipeline\n"); + goto err_pm_runtime_put; + } + + rzv2h_ivc_format_configure(ivc); + + queue_work(ivc->buffers.async_wq, &ivc->buffers.work); + + return 0; + +err_pm_runtime_put: + pm_runtime_put(ivc->dev); +err_return_buffers: + rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) +{ + struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); + u32 val = 0; + + rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, 0x1); + readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, + val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + + rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); + video_device_pipeline_stop(&ivc->vdev.dev); + pm_runtime_put_autosuspend(ivc->dev); +} + +static const struct vb2_ops rzv2h_ivc_vb2_ops = { + .queue_setup = &rzv2h_ivc_queue_setup, + .buf_queue = &rzv2h_ivc_buf_queue, + .start_streaming = &rzv2h_ivc_start_streaming, + .stop_streaming = &rzv2h_ivc_stop_streaming, +}; + +static const struct rzv2h_ivc_format * +rzv2h_ivc_format_from_pixelformat(u32 fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++) + if (fourcc == rzv2h_ivc_formats[i].fourcc) + return &rzv2h_ivc_formats[i]; + + return &rzv2h_ivc_formats[0]; +} + +static int rzv2h_ivc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(rzv2h_ivc_formats)) + return -EINVAL; + + f->pixelformat = rzv2h_ivc_formats[f->index].fourcc; + return 0; +} + +static int rzv2h_ivc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rzv2h_ivc *ivc = video_drvdata(file); + + f->fmt.pix_mp = ivc->format.pix; + + return 0; +} + +static void rzv2h_ivc_try_fmt(struct v4l2_pix_format_mplane *pix, + const struct rzv2h_ivc_format *fmt) +{ + pix->pixelformat = fmt->fourcc; + + pix->width = clamp(pix->width, RZV2H_IVC_MIN_WIDTH, + RZV2H_IVC_MAX_WIDTH); + pix->height = clamp(pix->height, RZV2H_IVC_MIN_HEIGHT, + RZV2H_IVC_MAX_HEIGHT); + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_RAW; + pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + pix->colorspace, + pix->ycbcr_enc); + + v4l2_fill_pixfmt_mp(pix, pix->pixelformat, pix->width, pix->height); +} + +static void rzv2h_ivc_set_format(struct rzv2h_ivc *ivc, + struct v4l2_pix_format_mplane *pix) +{ + const struct rzv2h_ivc_format *fmt; + + fmt = rzv2h_ivc_format_from_pixelformat(pix->pixelformat); + + rzv2h_ivc_try_fmt(pix, fmt); + ivc->format.pix = *pix; + ivc->format.fmt = fmt; +} + +static int rzv2h_ivc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rzv2h_ivc *ivc = video_drvdata(file); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + + if (vb2_is_busy(&ivc->vdev.vb2q)) + return -EBUSY; + + rzv2h_ivc_set_format(ivc, pix); + + return 0; +} + +static int rzv2h_ivc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + const struct rzv2h_ivc_format *fmt; + + fmt = rzv2h_ivc_format_from_pixelformat(f->fmt.pix.pixelformat); + rzv2h_ivc_try_fmt(&f->fmt.pix_mp, fmt); + + return 0; +} + +static int rzv2h_ivc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "rzv2h-ivc", sizeof(cap->driver)); + strscpy(cap->card, "Renesas Input Video Control", sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops rzv2h_ivc_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_vid_out = rzv2h_ivc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = rzv2h_ivc_g_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane = rzv2h_ivc_s_fmt_vid_out, + .vidioc_try_fmt_vid_out_mplane = rzv2h_ivc_try_fmt_vid_out, + .vidioc_querycap = rzv2h_ivc_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations rzv2h_ivc_v4l2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev) +{ + struct v4l2_pix_format_mplane pix = { }; + struct video_device *vdev; + struct vb2_queue *vb2q; + int ret; + + spin_lock_init(&ivc->buffers.lock); + INIT_LIST_HEAD(&ivc->buffers.queue); + INIT_WORK(&ivc->buffers.work, rzv2h_ivc_transfer_buffer); + + ivc->buffers.async_wq = alloc_workqueue("rzv2h-ivc", 0, 0); + if (!ivc->buffers.async_wq) + return -EINVAL; + + /* Initialise vb2 queue */ + vb2q = &ivc->vdev.vb2q; + vb2q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + vb2q->io_modes = VB2_MMAP | VB2_DMABUF; + vb2q->drv_priv = ivc; + vb2q->mem_ops = &vb2_dma_contig_memops; + vb2q->ops = &rzv2h_ivc_vb2_ops; + vb2q->buf_struct_size = sizeof(struct rzv2h_ivc_buf); + vb2q->min_queued_buffers = 0; + vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2q->lock = &ivc->lock; + vb2q->dev = ivc->dev; + + ret = vb2_queue_init(vb2q); + if (ret) { + dev_err(ivc->dev, "vb2 queue init failed\n"); + goto err_destroy_workqueue; + } + + /* Initialise Video Device */ + vdev = &ivc->vdev.dev; + strscpy(vdev->name, "rzv2h-ivc", sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &rzv2h_ivc_v4l2_fops; + vdev->ioctl_ops = &rzv2h_ivc_v4l2_ioctl_ops; + vdev->lock = &ivc->lock; + vdev->v4l2_dev = v4l2_dev; + vdev->queue = vb2q; + vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_TX; + video_set_drvdata(vdev, ivc); + + pix.pixelformat = V4L2_PIX_FMT_SRGGB16; + pix.width = RZV2H_IVC_DEFAULT_WIDTH; + pix.height = RZV2H_IVC_DEFAULT_HEIGHT; + rzv2h_ivc_set_format(ivc, &pix); + + ivc->vdev.pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ivc->vdev.dev.entity, 1, &ivc->vdev.pad); + if (ret) + goto err_release_vb2q; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(ivc->dev, "failed to register IVC video device\n"); + goto err_cleanup_vdev_entity; + } + + ret = media_create_pad_link(&vdev->entity, 0, &ivc->subdev.sd.entity, + RZV2H_IVC_SUBDEV_SINK_PAD, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(ivc->dev, "failed to create media link\n"); + goto err_unregister_vdev; + } + + return 0; + +err_unregister_vdev: + video_unregister_device(vdev); +err_cleanup_vdev_entity: + media_entity_cleanup(&vdev->entity); +err_release_vb2q: + vb2_queue_release(vb2q); +err_destroy_workqueue: + destroy_workqueue(ivc->buffers.async_wq); + + return ret; +} + +void rzv2h_deinit_video_dev_and_queue(struct rzv2h_ivc *ivc) +{ + struct video_device *vdev = &ivc->vdev.dev; + struct vb2_queue *vb2q = &ivc->vdev.vb2q; + + vb2_video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + vb2_queue_release(vb2q); +} diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h new file mode 100644 index 000000000000..3bcaab990b0f --- /dev/null +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Renesas RZ/V2H(P) Input Video Control Block driver + * + * Copyright (C) 2025 Ideas on Board Oy + */ + +#include <linux/clk.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/reset.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <linux/workqueue.h> + +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + +#define RZV2H_IVC_REG_AXIRX_PLNUM 0x0000 +#define RZV2H_IVC_ONE_EXPOSURE 0x00 +#define RZV2H_IVC_TWO_EXPOSURE 0x01 +#define RZV2H_IVC_REG_AXIRX_PXFMT 0x0004 +#define RZV2H_IVC_INPUT_FMT_MIPI (0 << 16) +#define RZV2H_IVC_INPUT_FMT_CRU_PACKED BIT(16) +#define RZV2H_IVC_PXFMT_DTYPE GENMASK(7, 0) +#define RZV2H_IVC_REG_AXIRX_SADDL_P0 0x0010 +#define RZV2H_IVC_REG_AXIRX_SADDH_P0 0x0014 +#define RZV2H_IVC_REG_AXIRX_SADDL_P1 0x0018 +#define RZV2H_IVC_REG_AXIRX_SADDH_P1 0x001c +#define RZV2H_IVC_REG_AXIRX_HSIZE 0x0020 +#define RZV2H_IVC_REG_AXIRX_VSIZE 0x0024 +#define RZV2H_IVC_REG_AXIRX_BLANK 0x0028 +#define RZV2H_IVC_VBLANK(x) ((x) << 16) +#define RZV2H_IVC_REG_AXIRX_STRD 0x0030 +#define RZV2H_IVC_REG_AXIRX_ISSU 0x0040 +#define RZV2H_IVC_REG_AXIRX_ERACT 0x0048 +#define RZV2H_IVC_REG_FM_CONTEXT 0x0100 +#define RZV2H_IVC_SOFTWARE_CFG 0x00 +#define RZV2H_IVC_SINGLE_CONTEXT_SW_HW_CFG BIT(0) +#define RZV2H_IVC_MULTI_CONTEXT_SW_HW_CFG BIT(1) +#define RZV2H_IVC_REG_FM_MCON 0x0104 +#define RZV2H_IVC_REG_FM_FRCON 0x0108 +#define RZV2H_IVC_REG_FM_STOP 0x010c +#define RZV2H_IVC_REG_FM_INT_EN 0x0120 +#define RZV2H_IVC_VVAL_IFPE BIT(0) +#define RZV2H_IVC_REG_FM_INT_STA 0x0124 +#define RZV2H_IVC_REG_AXIRX_FIFOCAP0 0x0208 +#define RZV2H_IVC_REG_CORE_CAPCON 0x020c +#define RZV2H_IVC_REG_CORE_FIFOCAP0 0x0228 +#define RZV2H_IVC_REG_CORE_FIFOCAP1 0x022c + +#define RZV2H_IVC_MIN_WIDTH 640 +#define RZV2H_IVC_MAX_WIDTH 4096 +#define RZV2H_IVC_MIN_HEIGHT 480 +#define RZV2H_IVC_MAX_HEIGHT 4096 +#define RZV2H_IVC_DEFAULT_WIDTH 1920 +#define RZV2H_IVC_DEFAULT_HEIGHT 1080 + +#define RZV2H_IVC_NUM_HW_RESOURCES 3 + +struct device; + +enum rzv2h_ivc_subdev_pads { + RZV2H_IVC_SUBDEV_SINK_PAD, + RZV2H_IVC_SUBDEV_SOURCE_PAD, + RZV2H_IVC_NUM_SUBDEV_PADS +}; + +struct rzv2h_ivc_format { + u32 fourcc; + /* + * The CRU packed pixel formats are bayer-order agnostic, so each could + * support any one of the 4 possible media bus formats. + */ + u32 mbus_codes[4]; + u8 dtype; +}; + +struct rzv2h_ivc { + struct device *dev; + void __iomem *base; + struct clk_bulk_data clks[RZV2H_IVC_NUM_HW_RESOURCES]; + struct reset_control_bulk_data resets[RZV2H_IVC_NUM_HW_RESOURCES]; + int irqnum; + u8 vvalid_ifp; + + struct { + struct video_device dev; + struct vb2_queue vb2q; + struct media_pad pad; + } vdev; + + struct { + struct v4l2_subdev sd; + struct media_pad pads[RZV2H_IVC_NUM_SUBDEV_PADS]; + } subdev; + + struct { + /* Spinlock to guard buffer queue */ + spinlock_t lock; + struct workqueue_struct *async_wq; + struct work_struct work; + struct list_head queue; + struct rzv2h_ivc_buf *curr; + unsigned int sequence; + } buffers; + + struct { + struct v4l2_pix_format_mplane pix; + const struct rzv2h_ivc_format *fmt; + } format; + + /* Mutex to provide to vb2 */ + struct mutex lock; + /* Lock to protect the interrupt counter */ + spinlock_t spinlock; +}; + +int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev); +void rzv2h_deinit_video_dev_and_queue(struct rzv2h_ivc *ivc); +void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc); +int rzv2h_ivc_initialise_subdevice(struct rzv2h_ivc *ivc); +void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc); +void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val); +void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr, + u32 mask, u32 val); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 6c64657fc4f3..2de515c497eb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -954,8 +954,7 @@ static int vsp1_probe(struct platform_device *pdev) vsp1->fcp = rcar_fcp_get(fcp_node); of_node_put(fcp_node); if (IS_ERR(vsp1->fcp)) { - dev_dbg(&pdev->dev, "FCP not found (%ld)\n", - PTR_ERR(vsp1->fcp)); + dev_dbg(&pdev->dev, "FCP not found (%pe)\n", vsp1->fcp); return PTR_ERR(vsp1->fcp); } diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig index 9bbeec4996aa..ba401d32f01b 100644 --- a/drivers/media/platform/rockchip/Kconfig +++ b/drivers/media/platform/rockchip/Kconfig @@ -3,5 +3,6 @@ comment "Rockchip media platform drivers" source "drivers/media/platform/rockchip/rga/Kconfig" +source "drivers/media/platform/rockchip/rkcif/Kconfig" source "drivers/media/platform/rockchip/rkisp1/Kconfig" source "drivers/media/platform/rockchip/rkvdec/Kconfig" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile index 286dc5c53f7e..0e0b2cbbd4bd 100644 --- a/drivers/media/platform/rockchip/Makefile +++ b/drivers/media/platform/rockchip/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += rga/ +obj-y += rkcif/ obj-y += rkisp1/ obj-y += rkvdec/ diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 776046de979a..43f6a8d99381 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -75,7 +75,7 @@ static irqreturn_t rga_isr(int irq, void *prv) WARN_ON(!src); WARN_ON(!dst); - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); dst->sequence = ctx->csequence++; @@ -463,12 +463,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = file_to_rga_ctx(file); - struct vb2_queue *vq; struct rga_frame *frm; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); diff --git a/drivers/media/platform/rockchip/rkcif/Kconfig b/drivers/media/platform/rockchip/rkcif/Kconfig new file mode 100644 index 000000000000..efd82ac35bd8 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/Kconfig @@ -0,0 +1,18 @@ +config VIDEO_ROCKCHIP_CIF + tristate "Rockchip Camera Interface (CIF)" + depends on VIDEO_DEV + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on V4L_PLATFORM_DRIVERS + depends on PM && COMMON_CLK + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + This is a driver for Rockchip Camera Interface (CIF). It is featured + in many Rockchips SoCs in different variations, such as the PX30 + Video Input Processor (VIP, one Digital Video Port (DVP)) or the + RK3568 Video Capture (VICAP, one DVP, one MIPI CSI-2 receiver) unit. + + To compile this driver as a module, choose M here: the module + will be called rockchip-cif. diff --git a/drivers/media/platform/rockchip/rkcif/Makefile b/drivers/media/platform/rockchip/rkcif/Makefile new file mode 100644 index 000000000000..dca2bf45159f --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o + +rockchip-cif-objs += rkcif-capture-dvp.o +rockchip-cif-objs += rkcif-capture-mipi.o +rockchip-cif-objs += rkcif-dev.o +rockchip-cif-objs += rkcif-interface.o +rockchip-cif-objs += rkcif-stream.o diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c new file mode 100644 index 000000000000..dbaf7636aeeb --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com> + * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#include <media/v4l2-common.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#include "rkcif-capture-dvp.h" +#include "rkcif-common.h" +#include "rkcif-interface.h" +#include "rkcif-regs.h" +#include "rkcif-stream.h" + +static const struct rkcif_output_fmt dvp_out_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 | + RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 | + RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 | + RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 | + RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 | + RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 | + RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 | + RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 | + RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR666, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .cplanes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .cplanes = 1, + }, +}; + +static const struct rkcif_input_fmt px30_dvp_in_fmts[] = { + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + } +}; + +const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data = { + .in_fmts = px30_dvp_in_fmts, + .in_fmts_num = ARRAY_SIZE(px30_dvp_in_fmts), + .out_fmts = dvp_out_fmts, + .out_fmts_num = ARRAY_SIZE(dvp_out_fmts), + .has_scaler = true, + .regs = { + [RKCIF_DVP_CTRL] = 0x00, + [RKCIF_DVP_INTEN] = 0x04, + [RKCIF_DVP_INTSTAT] = 0x08, + [RKCIF_DVP_FOR] = 0x0c, + [RKCIF_DVP_LINE_NUM_ADDR] = 0x10, + [RKCIF_DVP_FRM0_ADDR_Y] = 0x14, + [RKCIF_DVP_FRM0_ADDR_UV] = 0x18, + [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c, + [RKCIF_DVP_FRM1_ADDR_UV] = 0x20, + [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24, + [RKCIF_DVP_SET_SIZE] = 0x28, + [RKCIF_DVP_SCL_CTRL] = 0x48, + [RKCIF_DVP_FRAME_STATUS] = 0x60, + [RKCIF_DVP_LAST_LINE] = 0x68, + [RKCIF_DVP_LAST_PIX] = 0x6c, + }, +}; + +static const struct rkcif_input_fmt rk3568_dvp_in_fmts[] = { + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY, + .fmt_type = RKCIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV | + RKCIF_FORMAT_INPUT_MODE_BT1120 | + RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV | + RKCIF_FORMAT_INPUT_MODE_BT1120, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU | + RKCIF_FORMAT_INPUT_MODE_BT1120 | + RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU | + RKCIF_FORMAT_INPUT_MODE_BT1120, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV | + RKCIF_FORMAT_INPUT_MODE_BT1120 | + RKCIF_FORMAT_BT1120_YC_SWAP | + RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV | + RKCIF_FORMAT_BT1120_YC_SWAP | + RKCIF_FORMAT_INPUT_MODE_BT1120, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU | + RKCIF_FORMAT_INPUT_MODE_BT1120 | + RKCIF_FORMAT_BT1120_YC_SWAP | + RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 | + RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU | + RKCIF_FORMAT_BT1120_YC_SWAP | + RKCIF_FORMAT_INPUT_MODE_BT1120, + .field = V4L2_FIELD_INTERLACED, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_8, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_10, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, + { + .mbus_code = MEDIA_BUS_FMT_Y12_1X12, + .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW | + RKCIF_FORMAT_RAW_DATA_WIDTH_12, + .fmt_type = RKCIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, +}; + +static void rk3568_dvp_grf_setup(struct rkcif_device *rkcif) +{ + u32 con1 = RK3568_GRF_WRITE_ENABLE(RK3568_GRF_VI_CON1_CIF_DATAPATH | + RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM); + + if (!rkcif->grf) + return; + + con1 |= rkcif->interfaces[RKCIF_DVP].dvp.dvp_clk_delay & + RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM; + + if (rkcif->interfaces[RKCIF_DVP].vep.bus.parallel.flags & + V4L2_MBUS_PCLK_SAMPLE_DUALEDGE) + con1 |= RK3568_GRF_VI_CON1_CIF_DATAPATH; + + regmap_write(rkcif->grf, RK3568_GRF_VI_CON1, con1); +} + +const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data = { + .in_fmts = rk3568_dvp_in_fmts, + .in_fmts_num = ARRAY_SIZE(rk3568_dvp_in_fmts), + .out_fmts = dvp_out_fmts, + .out_fmts_num = ARRAY_SIZE(dvp_out_fmts), + .setup = rk3568_dvp_grf_setup, + .has_scaler = false, + .regs = { + [RKCIF_DVP_CTRL] = 0x00, + [RKCIF_DVP_INTEN] = 0x04, + [RKCIF_DVP_INTSTAT] = 0x08, + [RKCIF_DVP_FOR] = 0x0c, + [RKCIF_DVP_LINE_NUM_ADDR] = 0x2c, + [RKCIF_DVP_FRM0_ADDR_Y] = 0x14, + [RKCIF_DVP_FRM0_ADDR_UV] = 0x18, + [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c, + [RKCIF_DVP_FRM1_ADDR_UV] = 0x20, + [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24, + [RKCIF_DVP_SET_SIZE] = 0x28, + [RKCIF_DVP_CROP] = 0x34, + [RKCIF_DVP_FRAME_STATUS] = 0x3c, + [RKCIF_DVP_LAST_LINE] = 0x44, + [RKCIF_DVP_LAST_PIX] = 0x48, + }, +}; + +static inline unsigned int rkcif_dvp_get_addr(struct rkcif_device *rkcif, + unsigned int index) +{ + if (WARN_ON_ONCE(index >= RKCIF_DVP_REGISTER_MAX)) + return RKCIF_REGISTER_NOTSUPPORTED; + + return rkcif->match_data->dvp->regs[index]; +} + +static inline __maybe_unused void rkcif_dvp_write(struct rkcif_device *rkcif, + unsigned int index, u32 val) +{ + unsigned int addr = rkcif_dvp_get_addr(rkcif, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return; + + writel(val, rkcif->base_addr + addr); +} + +static inline __maybe_unused u32 rkcif_dvp_read(struct rkcif_device *rkcif, + unsigned int index) +{ + unsigned int addr = rkcif_dvp_get_addr(rkcif, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return 0; + + return readl(rkcif->base_addr + addr); +} + +static void rkcif_dvp_queue_buffer(struct rkcif_stream *stream, + unsigned int index) +{ + struct rkcif_device *rkcif = stream->rkcif; + struct rkcif_buffer *buffer = stream->buffers[index]; + u32 frm_addr_y, frm_addr_uv; + + frm_addr_y = index ? RKCIF_DVP_FRM1_ADDR_Y : RKCIF_DVP_FRM0_ADDR_Y; + frm_addr_uv = index ? RKCIF_DVP_FRM1_ADDR_UV : RKCIF_DVP_FRM0_ADDR_UV; + + rkcif_dvp_write(rkcif, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]); + rkcif_dvp_write(rkcif, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_UV]); +} + +static int rkcif_dvp_start_streaming(struct rkcif_stream *stream) +{ + struct rkcif_device *rkcif = stream->rkcif; + struct rkcif_interface *interface = stream->interface; + struct v4l2_mbus_config_parallel *parallel; + struct v4l2_mbus_framefmt *source_fmt; + struct v4l2_subdev_state *state; + const struct rkcif_input_fmt *active_in_fmt; + const struct rkcif_output_fmt *active_out_fmt; + u32 val = 0; + int ret = -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(&interface->sd); + source_fmt = v4l2_subdev_state_get_format(state, RKCIF_IF_PAD_SRC, + stream->id); + if (!source_fmt) + goto out; + + active_in_fmt = rkcif_interface_find_input_fmt(interface, false, + source_fmt->code); + active_out_fmt = rkcif_stream_find_output_fmt(stream, false, + stream->pix.pixelformat); + if (!active_in_fmt || !active_out_fmt) + goto out; + + parallel = &interface->vep.bus.parallel; + if (parallel->bus_width == 16 && + (parallel->flags & V4L2_MBUS_PCLK_SAMPLE_DUALEDGE)) + val |= RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES; + val |= active_in_fmt->dvp_fmt_val; + val |= active_out_fmt->dvp_fmt_val; + rkcif_dvp_write(rkcif, RKCIF_DVP_FOR, val); + + val = stream->pix.width; + if (active_in_fmt->fmt_type == RKCIF_FMT_TYPE_RAW) + val = stream->pix.width * 2; + rkcif_dvp_write(rkcif, RKCIF_DVP_VIR_LINE_WIDTH, val); + + val = RKCIF_XY_COORD(stream->pix.width, stream->pix.height); + rkcif_dvp_write(rkcif, RKCIF_DVP_SET_SIZE, val); + + rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, RKCIF_FRAME_STAT_CLS); + rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, RKCIF_INTSTAT_CLS); + if (rkcif->match_data->dvp->has_scaler) { + val = active_in_fmt->fmt_type == RKCIF_FMT_TYPE_YUV ? + RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS : + RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS; + rkcif_dvp_write(rkcif, RKCIF_DVP_SCL_CTRL, val); + } + + rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN, + RKCIF_INTEN_FRAME_END_EN | + RKCIF_INTEN_PST_INF_FRAME_END_EN); + + rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, + RKCIF_CTRL_AXI_BURST_16 | RKCIF_CTRL_MODE_PINGPONG | + RKCIF_CTRL_ENABLE_CAPTURE); + + ret = 0; + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static void rkcif_dvp_stop_streaming(struct rkcif_stream *stream) +{ + struct rkcif_device *rkcif = stream->rkcif; + u32 val; + + val = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL); + rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, + val & (~RKCIF_CTRL_ENABLE_CAPTURE)); + rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN, 0x0); + rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, 0x3ff); + rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, 0x0); + + stream->stopping = false; +} + +static void rkcif_dvp_reset_stream(struct rkcif_device *rkcif) +{ + u32 ctl = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL); + + rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, + ctl & (~RKCIF_CTRL_ENABLE_CAPTURE)); + rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, ctl | RKCIF_CTRL_ENABLE_CAPTURE); +} + +static void rkcif_dvp_set_crop(struct rkcif_stream *stream, u16 left, u16 top) +{ + struct rkcif_device *rkcif = stream->rkcif; + u32 val; + + val = RKCIF_XY_COORD(left, top); + rkcif_dvp_write(rkcif, RKCIF_DVP_CROP, val); +} + +irqreturn_t rkcif_dvp_isr(int irq, void *ctx) +{ + struct device *dev = ctx; + struct rkcif_device *rkcif = dev_get_drvdata(dev); + struct rkcif_stream *stream; + u32 intstat, lastline, lastpix, cif_frmst; + irqreturn_t ret = IRQ_NONE; + + if (!rkcif->match_data->dvp) + return ret; + + intstat = rkcif_dvp_read(rkcif, RKCIF_DVP_INTSTAT); + cif_frmst = rkcif_dvp_read(rkcif, RKCIF_DVP_FRAME_STATUS); + lastline = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_LINE)); + lastpix = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_PIX)); + + if (intstat & RKCIF_INTSTAT_FRAME_END) { + rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, + RKCIF_INTSTAT_FRAME_END_CLR | + RKCIF_INTSTAT_LINE_END_CLR); + + stream = &rkcif->interfaces[RKCIF_DVP].streams[RKCIF_ID0]; + + if (stream->stopping) { + rkcif_dvp_stop_streaming(stream); + wake_up(&stream->wq_stopped); + ret = IRQ_HANDLED; + goto out; + } + + if (lastline != stream->pix.height) { + v4l2_err(&rkcif->v4l2_dev, + "bad frame, irq:%#x frmst:%#x size:%dx%d\n", + intstat, cif_frmst, lastpix, lastline); + + rkcif_dvp_reset_stream(rkcif); + } + + rkcif_stream_pingpong(stream); + + ret = IRQ_HANDLED; + } +out: + return ret; +} + +int rkcif_dvp_register(struct rkcif_device *rkcif) +{ + struct rkcif_interface *interface; + unsigned int streams_num; + int ret; + + if (!rkcif->match_data->dvp) + return 0; + + interface = &rkcif->interfaces[RKCIF_DVP]; + interface->index = RKCIF_DVP; + interface->type = RKCIF_IF_DVP; + interface->in_fmts = rkcif->match_data->dvp->in_fmts; + interface->in_fmts_num = rkcif->match_data->dvp->in_fmts_num; + interface->set_crop = rkcif_dvp_set_crop; + ret = rkcif_interface_register(rkcif, interface); + if (ret) + return ret; + + if (rkcif->match_data->dvp->setup) + rkcif->match_data->dvp->setup(rkcif); + + streams_num = rkcif->match_data->dvp->has_ids ? 4 : 1; + for (unsigned int i = 0; i < streams_num; i++) { + struct rkcif_stream *stream = &interface->streams[i]; + + stream->id = i; + stream->interface = interface; + stream->out_fmts = rkcif->match_data->dvp->out_fmts; + stream->out_fmts_num = rkcif->match_data->dvp->out_fmts_num; + stream->queue_buffer = rkcif_dvp_queue_buffer; + stream->start_streaming = rkcif_dvp_start_streaming; + stream->stop_streaming = rkcif_dvp_stop_streaming; + + ret = rkcif_stream_register(rkcif, stream); + if (ret) + goto err_streams_unregister; + + interface->streams_num++; + } + + return 0; + +err_streams_unregister: + for (unsigned int i = 0; i < interface->streams_num; i++) + rkcif_stream_unregister(&interface->streams[i]); + + rkcif_interface_unregister(interface); + + return ret; +} + +void rkcif_dvp_unregister(struct rkcif_device *rkcif) +{ + struct rkcif_interface *interface; + + if (!rkcif->match_data->dvp) + return; + + interface = &rkcif->interfaces[RKCIF_DVP]; + + for (unsigned int i = 0; i < interface->streams_num; i++) + rkcif_stream_unregister(&interface->streams[i]); + + rkcif_interface_unregister(interface); +} diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h new file mode 100644 index 000000000000..a4ed37833bd6 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#ifndef _RKCIF_CAPTURE_DVP_H +#define _RKCIF_CAPTURE_DVP_H + +#include "rkcif-common.h" + +extern const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data; +extern const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data; + +int rkcif_dvp_register(struct rkcif_device *rkcif); + +void rkcif_dvp_unregister(struct rkcif_device *rkcif); + +irqreturn_t rkcif_dvp_isr(int irq, void *ctx); + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c new file mode 100644 index 000000000000..1b81bcc067ef --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#include <linux/interrupt.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#include "rkcif-capture-mipi.h" +#include "rkcif-common.h" +#include "rkcif-interface.h" +#include "rkcif-regs.h" +#include "rkcif-stream.h" + +#define RK3568_MIPI_CTRL0_HIGH_ALIGN BIT(31) +#define RK3568_MIPI_CTRL0_UV_SWAP_EN BIT(7) +#define RK3568_MIPI_CTRL0_COMPACT_EN BIT(6) +#define RK3568_MIPI_CTRL0_CROP_EN BIT(5) +#define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1) + +#define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10) +#define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8) +#define RKCIF_MIPI_CTRL0_CAP_EN BIT(0) + +#define RKCIF_MIPI_INT_FRAME0_END(id) BIT(8 + (id) * 2 + 0) +#define RKCIF_MIPI_INT_FRAME1_END(id) BIT(8 + (id) * 2 + 1) + +static const struct rkcif_output_fmt mipi_out_fmts[] = { + /* YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_YUV422_8B, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_YUV422_8B, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_YUV422_8B, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_YUV422_8B, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + /* RGB formats */ + { + .fourcc = V4L2_PIX_FMT_RGB24, + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RGB888, + .type = RKCIF_MIPI_TYPE_RGB888, + }, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .mbus_code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RGB888, + .type = RKCIF_MIPI_TYPE_RGB888, + }, + }, + /* Bayer formats */ + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW8, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW8, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW8, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW8, + .type = RKCIF_MIPI_TYPE_RAW8, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW10, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW10, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .cplanes = 1, + .mipi = { + .dt = MIPI_CSI2_DT_RAW12, + .compact = true, + .type = RKCIF_MIPI_TYPE_RAW12, + }, + }, +}; + +static const struct rkcif_input_fmt mipi_in_fmts[] = { + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + }, + /* RGB formats */ + { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + }, + { + .mbus_code = MEDIA_BUS_FMT_BGR888_1X24, + }, + /* Bayer formats */ + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + }, +}; + +static u32 +rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream, + const struct rkcif_output_fmt *active_out_fmt) +{ + u32 ctrl0 = 0; + + ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); + ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; + ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN; + + if (active_out_fmt->mipi.compact) + ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN; + + switch (active_out_fmt->mipi.type) { + case RKCIF_MIPI_TYPE_RAW8: + break; + case RKCIF_MIPI_TYPE_RAW10: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RAW12: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x2); + break; + case RKCIF_MIPI_TYPE_RGB888: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x3); + break; + case RKCIF_MIPI_TYPE_YUV422SP: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x4); + break; + case RKCIF_MIPI_TYPE_YUV420SP: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x5); + break; + case RKCIF_MIPI_TYPE_YUV400: + ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x6); + break; + default: + break; + } + + return ctrl0; +} + +const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = { + .mipi_num = 1, + .mipi_ctrl0 = rkcif_rk3568_mipi_ctrl0, + .regs = { + [RKCIF_MIPI_CTRL] = 0x20, + [RKCIF_MIPI_INTEN] = 0xa4, + [RKCIF_MIPI_INTSTAT] = 0xa8, + }, + .regs_id = { + [RKCIF_ID0] = { + [RKCIF_MIPI_CTRL0] = 0x00, + [RKCIF_MIPI_CTRL1] = 0x04, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34, + [RKCIF_MIPI_FRAME0_VLW_UV] = 0x3c, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30, + [RKCIF_MIPI_FRAME1_VLW_Y] = 0x38, + [RKCIF_MIPI_FRAME1_VLW_UV] = 0x40, + [RKCIF_MIPI_CROP_START] = 0xbc, + }, + [RKCIF_ID1] = { + [RKCIF_MIPI_CTRL0] = 0x08, + [RKCIF_MIPI_CTRL1] = 0x0c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x44, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x4c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x54, + [RKCIF_MIPI_FRAME0_VLW_UV] = 0x5c, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x48, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x50, + [RKCIF_MIPI_FRAME1_VLW_Y] = 0x58, + [RKCIF_MIPI_FRAME1_VLW_UV] = 0x60, + [RKCIF_MIPI_CROP_START] = 0xc0, + }, + [RKCIF_ID2] = { + [RKCIF_MIPI_CTRL0] = 0x10, + [RKCIF_MIPI_CTRL1] = 0x14, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x64, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x6c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x74, + [RKCIF_MIPI_FRAME0_VLW_UV] = 0x7c, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x68, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x70, + [RKCIF_MIPI_FRAME1_VLW_Y] = 0x78, + [RKCIF_MIPI_FRAME1_VLW_UV] = 0x80, + [RKCIF_MIPI_CROP_START] = 0xc4, + }, + [RKCIF_ID3] = { + [RKCIF_MIPI_CTRL0] = 0x18, + [RKCIF_MIPI_CTRL1] = 0x1c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x84, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x8c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x94, + [RKCIF_MIPI_FRAME0_VLW_UV] = 0x9c, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x88, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x90, + [RKCIF_MIPI_FRAME1_VLW_Y] = 0x98, + [RKCIF_MIPI_FRAME1_VLW_UV] = 0xa0, + [RKCIF_MIPI_CROP_START] = 0xc8, + }, + }, + .blocks = { + { + .offset = 0x80, + }, + }, +}; + +static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface, + unsigned int index) +{ + struct rkcif_device *rkcif = interface->rkcif; + unsigned int block, offset, reg; + + block = interface->index - RKCIF_MIPI_BASE; + + if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) || + WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX)) + return RKCIF_REGISTER_NOTSUPPORTED; + + offset = rkcif->match_data->mipi->blocks[block].offset; + reg = rkcif->match_data->mipi->regs[index]; + if (reg == RKCIF_REGISTER_NOTSUPPORTED) + return reg; + + return offset + reg; +} + +static inline unsigned int rkcif_mipi_id_get_reg(struct rkcif_stream *stream, + unsigned int index) +{ + struct rkcif_device *rkcif = stream->rkcif; + unsigned int block, id, offset, reg; + + block = stream->interface->index - RKCIF_MIPI_BASE; + id = stream->id; + + if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) || + WARN_ON_ONCE(id > RKCIF_ID_MAX) || + WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX)) + return RKCIF_REGISTER_NOTSUPPORTED; + + offset = rkcif->match_data->mipi->blocks[block].offset; + reg = rkcif->match_data->mipi->regs_id[id][index]; + if (reg == RKCIF_REGISTER_NOTSUPPORTED) + return reg; + + return offset + reg; +} + +static inline __maybe_unused void +rkcif_mipi_write(struct rkcif_interface *interface, unsigned int index, u32 val) +{ + unsigned int addr = rkcif_mipi_get_reg(interface, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return; + + writel(val, interface->rkcif->base_addr + addr); +} + +static inline __maybe_unused void +rkcif_mipi_stream_write(struct rkcif_stream *stream, unsigned int index, + u32 val) +{ + unsigned int addr = rkcif_mipi_id_get_reg(stream, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return; + + writel(val, stream->rkcif->base_addr + addr); +} + +static inline __maybe_unused u32 +rkcif_mipi_read(struct rkcif_interface *interface, unsigned int index) +{ + unsigned int addr = rkcif_mipi_get_reg(interface, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return 0; + + return readl(interface->rkcif->base_addr + addr); +} + +static inline __maybe_unused u32 +rkcif_mipi_stream_read(struct rkcif_stream *stream, unsigned int index) +{ + unsigned int addr = rkcif_mipi_id_get_reg(stream, index); + + if (addr == RKCIF_REGISTER_NOTSUPPORTED) + return 0; + + return readl(stream->rkcif->base_addr + addr); +} + +static void rkcif_mipi_queue_buffer(struct rkcif_stream *stream, + unsigned int index) +{ + struct rkcif_buffer *buffer = stream->buffers[index]; + u32 frm_addr_y, frm_addr_uv; + + frm_addr_y = index ? RKCIF_MIPI_FRAME1_ADDR_Y : + RKCIF_MIPI_FRAME0_ADDR_Y; + frm_addr_uv = index ? RKCIF_MIPI_FRAME1_ADDR_UV : + RKCIF_MIPI_FRAME0_ADDR_UV; + + rkcif_mipi_stream_write(stream, frm_addr_y, + buffer->buff_addr[RKCIF_PLANE_Y]); + rkcif_mipi_stream_write(stream, frm_addr_uv, + buffer->buff_addr[RKCIF_PLANE_UV]); +} + +static int rkcif_mipi_start_streaming(struct rkcif_stream *stream) +{ + struct rkcif_interface *interface = stream->interface; + const struct rkcif_output_fmt *active_out_fmt; + const struct rkcif_mipi_match_data *match_data; + struct v4l2_subdev_state *state; + u32 ctrl0 = 0, ctrl1 = 0, int_temp = 0, int_mask = 0, vlw = 0; + u16 height, width; + int ret = -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(&interface->sd); + + active_out_fmt = rkcif_stream_find_output_fmt(stream, false, + stream->pix.pixelformat); + if (!active_out_fmt) + goto out; + + height = stream->pix.height; + width = stream->pix.width; + vlw = stream->pix.plane_fmt[0].bytesperline; + + match_data = stream->rkcif->match_data->mipi; + if (match_data->mipi_ctrl0) + ctrl0 = match_data->mipi_ctrl0(stream, active_out_fmt); + + ctrl1 = RKCIF_XY_COORD(width, height); + + int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id); + int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id); + + int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN); + int_temp |= int_mask; + rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp); + + int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT); + int_temp &= ~int_mask; + rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp); + + rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_Y, vlw); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_Y, vlw); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_UV, vlw); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_UV, vlw); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, 0x0); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0); + + ret = 0; + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static void rkcif_mipi_stop_streaming(struct rkcif_stream *stream) +{ + struct rkcif_interface *interface = stream->interface; + struct v4l2_subdev_state *state; + u32 int_temp = 0, int_mask = 0; + + state = v4l2_subdev_lock_and_get_active_state(&interface->sd); + + rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, 0); + + int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id); + int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id); + + int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN); + int_temp &= ~int_mask; + rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp); + + int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT); + int_temp &= ~int_mask; + rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp); + + stream->stopping = false; + + v4l2_subdev_unlock_state(state); +} + +static void rkcif_mipi_set_crop(struct rkcif_stream *stream, u16 left, u16 top) +{ + u32 val; + + val = RKCIF_XY_COORD(left, top); + rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, val); +} + +irqreturn_t rkcif_mipi_isr(int irq, void *ctx) +{ + struct device *dev = ctx; + struct rkcif_device *rkcif = dev_get_drvdata(dev); + irqreturn_t ret = IRQ_NONE; + u32 intstat; + + for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) { + enum rkcif_interface_index index = RKCIF_MIPI_BASE + i; + struct rkcif_interface *interface = &rkcif->interfaces[index]; + + intstat = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT); + rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, intstat); + + for (unsigned int j = 0; j < interface->streams_num; j++) { + struct rkcif_stream *stream = &interface->streams[j]; + + if (intstat & RKCIF_MIPI_INT_FRAME0_END(stream->id) || + intstat & RKCIF_MIPI_INT_FRAME1_END(stream->id)) { + ret = IRQ_HANDLED; + + if (stream->stopping) { + rkcif_mipi_stop_streaming(stream); + wake_up(&stream->wq_stopped); + continue; + } + + rkcif_stream_pingpong(stream); + } + } + } + + return ret; +} + +int rkcif_mipi_register(struct rkcif_device *rkcif) +{ + int ret; + + if (!rkcif->match_data->mipi) + return 0; + + for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) { + enum rkcif_interface_index index = RKCIF_MIPI_BASE + i; + struct rkcif_interface *interface = &rkcif->interfaces[index]; + + interface->index = index; + interface->type = RKCIF_IF_MIPI; + interface->in_fmts = mipi_in_fmts; + interface->in_fmts_num = ARRAY_SIZE(mipi_in_fmts); + interface->set_crop = rkcif_mipi_set_crop; + interface->streams_num = 0; + ret = rkcif_interface_register(rkcif, interface); + if (ret) + continue; + + for (unsigned int j = 0; j < RKCIF_ID_MAX; j++) { + struct rkcif_stream *stream = &interface->streams[j]; + + stream->id = j; + stream->interface = interface; + stream->out_fmts = mipi_out_fmts; + stream->out_fmts_num = ARRAY_SIZE(mipi_out_fmts); + stream->queue_buffer = rkcif_mipi_queue_buffer; + stream->start_streaming = rkcif_mipi_start_streaming; + stream->stop_streaming = rkcif_mipi_stop_streaming; + ret = rkcif_stream_register(rkcif, stream); + if (ret) + goto err; + interface->streams_num++; + } + } + + return 0; + +err: + for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) { + enum rkcif_interface_index index = RKCIF_MIPI_BASE + i; + struct rkcif_interface *interface = &rkcif->interfaces[index]; + + for (unsigned int j = 0; j < interface->streams_num; j++) + rkcif_stream_unregister(&interface->streams[j]); + + rkcif_interface_unregister(interface); + } + return ret; +} + +void rkcif_mipi_unregister(struct rkcif_device *rkcif) +{ + if (!rkcif->match_data->mipi) + return; + + for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) { + enum rkcif_interface_index index = RKCIF_MIPI_BASE + i; + struct rkcif_interface *interface = &rkcif->interfaces[index]; + + for (unsigned int j = 0; j < interface->streams_num; j++) + rkcif_stream_unregister(&interface->streams[j]); + + rkcif_interface_unregister(interface); + } +} diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h new file mode 100644 index 000000000000..7f16eadc474c --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#ifndef _RKCIF_CAPTURE_MIPI_H +#define _RKCIF_CAPTURE_MIPI_H + +#include "rkcif-common.h" + +extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data; + +int rkcif_mipi_register(struct rkcif_device *rkcif); + +void rkcif_mipi_unregister(struct rkcif_device *rkcif); + +irqreturn_t rkcif_mipi_isr(int irq, void *ctx); + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h new file mode 100644 index 000000000000..dd92cfbc879f --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#ifndef _RKCIF_COMMON_H +#define _RKCIF_COMMON_H + +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/regmap.h> + +#include <media/media-device.h> +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-v4l2.h> + +#include "rkcif-regs.h" + +#define RKCIF_DRIVER_NAME "rockchip-cif" +#define RKCIF_CLK_MAX 4 + +enum rkcif_format_type { + RKCIF_FMT_TYPE_INVALID, + RKCIF_FMT_TYPE_YUV, + RKCIF_FMT_TYPE_RAW, +}; + +enum rkcif_id_index { + RKCIF_ID0, + RKCIF_ID1, + RKCIF_ID2, + RKCIF_ID3, + RKCIF_ID_MAX +}; + +enum rkcif_interface_index { + RKCIF_DVP, + RKCIF_MIPI_BASE, + RKCIF_MIPI1 = RKCIF_MIPI_BASE, + RKCIF_MIPI2, + RKCIF_MIPI3, + RKCIF_MIPI4, + RKCIF_MIPI5, + RKCIF_MIPI6, + RKCIF_MIPI_MAX, + RKCIF_IF_MAX = RKCIF_MIPI_MAX +}; + +enum rkcif_interface_pad_index { + RKCIF_IF_PAD_SINK, + RKCIF_IF_PAD_SRC, + RKCIF_IF_PAD_MAX +}; + +enum rkcif_interface_status { + RKCIF_IF_INACTIVE, + RKCIF_IF_ACTIVE, +}; + +enum rkcif_interface_type { + RKCIF_IF_INVALID, + RKCIF_IF_DVP, + RKCIF_IF_MIPI, +}; + +enum rkcif_mipi_format_type { + RKCIF_MIPI_TYPE_INVALID, + RKCIF_MIPI_TYPE_RAW8, + RKCIF_MIPI_TYPE_RAW10, + RKCIF_MIPI_TYPE_RAW12, + RKCIF_MIPI_TYPE_RGB888, + RKCIF_MIPI_TYPE_YUV422SP, + RKCIF_MIPI_TYPE_YUV420SP, + RKCIF_MIPI_TYPE_YUV400, +}; + +struct rkcif_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; + dma_addr_t buff_addr[VIDEO_MAX_PLANES]; + bool is_dummy; +}; + +struct rkcif_dummy_buffer { + struct rkcif_buffer buffer; + void *vaddr; + u32 size; +}; + +enum rkcif_plane_index { + RKCIF_PLANE_Y, + RKCIF_PLANE_UV, + RKCIF_PLANE_MAX +}; + +struct rkcif_input_fmt { + u32 mbus_code; + + enum rkcif_format_type fmt_type; + enum v4l2_field field; + + union { + u32 dvp_fmt_val; + }; +}; + +struct rkcif_output_fmt { + u32 fourcc; + u32 mbus_code; + u8 cplanes; + u8 depth; + + union { + u32 dvp_fmt_val; + struct { + u8 dt; + bool compact; + enum rkcif_mipi_format_type type; + } mipi; + }; +}; + +struct rkcif_interface; + +struct rkcif_remote { + struct v4l2_async_connection async_conn; + struct v4l2_subdev *sd; + + struct rkcif_interface *interface; +}; + +struct rkcif_stream { + enum rkcif_id_index id; + struct rkcif_device *rkcif; + struct rkcif_interface *interface; + const struct rkcif_output_fmt *out_fmts; + unsigned int out_fmts_num; + + /* in ping-pong mode, two buffers can be provided to the HW */ + struct rkcif_buffer *buffers[2]; + int frame_idx; + int frame_phase; + + /* in case of no available buffer, HW can write to the dummy buffer */ + struct rkcif_dummy_buffer dummy; + + bool stopping; + wait_queue_head_t wq_stopped; + + /* queue of available buffers plus spinlock that protects it */ + spinlock_t driver_queue_lock; + struct list_head driver_queue; + + /* lock used by the V4L2 core */ + struct mutex vlock; + + struct media_pad pad; + struct media_pipeline pipeline; + struct v4l2_pix_format_mplane pix; + struct vb2_queue buf_queue; + struct video_device vdev; + + void (*queue_buffer)(struct rkcif_stream *stream, unsigned int index); + int (*start_streaming)(struct rkcif_stream *stream); + void (*stop_streaming)(struct rkcif_stream *stream); +}; + +struct rkcif_dvp { + u32 dvp_clk_delay; +}; + +struct rkcif_interface { + enum rkcif_interface_type type; + enum rkcif_interface_status status; + enum rkcif_interface_index index; + struct rkcif_device *rkcif; + struct rkcif_remote *remote; + struct rkcif_stream streams[RKCIF_ID_MAX]; + unsigned int streams_num; + const struct rkcif_input_fmt *in_fmts; + unsigned int in_fmts_num; + + struct media_pad pads[RKCIF_IF_PAD_MAX]; + struct v4l2_fwnode_endpoint vep; + struct v4l2_subdev sd; + + union { + struct rkcif_dvp dvp; + }; + + void (*set_crop)(struct rkcif_stream *stream, u16 left, u16 top); +}; + +struct rkcif_mipi_match_data { + unsigned int mipi_num; + unsigned int regs[RKCIF_MIPI_REGISTER_MAX]; + unsigned int regs_id[RKCIF_ID_MAX][RKCIF_MIPI_ID_REGISTER_MAX]; + u32 (*mipi_ctrl0)(struct rkcif_stream *stream, + const struct rkcif_output_fmt *active_out_fmt); + struct { + unsigned int offset; + } blocks[RKCIF_MIPI_MAX - RKCIF_MIPI_BASE]; +}; + +struct rkcif_dvp_match_data { + const struct rkcif_input_fmt *in_fmts; + unsigned int in_fmts_num; + const struct rkcif_output_fmt *out_fmts; + unsigned int out_fmts_num; + void (*setup)(struct rkcif_device *rkcif); + bool has_scaler; + bool has_ids; + unsigned int regs[RKCIF_DVP_REGISTER_MAX]; +}; + +struct rkcif_match_data { + const char *const *clks; + unsigned int clks_num; + const struct rkcif_dvp_match_data *dvp; + const struct rkcif_mipi_match_data *mipi; +}; + +struct rkcif_device { + struct device *dev; + + const struct rkcif_match_data *match_data; + struct clk_bulk_data clks[RKCIF_CLK_MAX]; + unsigned int clks_num; + struct regmap *grf; + struct reset_control *reset; + void __iomem *base_addr; + + struct rkcif_interface interfaces[RKCIF_IF_MAX]; + + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; +}; + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c new file mode 100644 index 000000000000..b4cf1146f131 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com> + * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> + +#include "rkcif-capture-dvp.h" +#include "rkcif-capture-mipi.h" +#include "rkcif-common.h" + +static const char *const px30_vip_clks[] = { + "aclk", + "hclk", + "pclk", +}; + +static const struct rkcif_match_data px30_vip_match_data = { + .clks = px30_vip_clks, + .clks_num = ARRAY_SIZE(px30_vip_clks), + .dvp = &rkcif_px30_vip_dvp_match_data, +}; + +static const char *const rk3568_vicap_clks[] = { + "aclk", + "hclk", + "dclk", + "iclk", +}; + +static const struct rkcif_match_data rk3568_vicap_match_data = { + .clks = rk3568_vicap_clks, + .clks_num = ARRAY_SIZE(rk3568_vicap_clks), + .dvp = &rkcif_rk3568_vicap_dvp_match_data, + .mipi = &rkcif_rk3568_vicap_mipi_match_data, +}; + +static const struct of_device_id rkcif_plat_of_match[] = { + { + .compatible = "rockchip,px30-vip", + .data = &px30_vip_match_data, + }, + { + .compatible = "rockchip,rk3568-vicap", + .data = &rk3568_vicap_match_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rkcif_plat_of_match); + +static int rkcif_register(struct rkcif_device *rkcif) +{ + int ret; + + ret = rkcif_dvp_register(rkcif); + if (ret && ret != -ENODEV) + goto err; + + ret = rkcif_mipi_register(rkcif); + if (ret && ret != -ENODEV) + goto err_dvp_unregister; + + return 0; + +err_dvp_unregister: + rkcif_dvp_unregister(rkcif); +err: + return ret; +} + +static void rkcif_unregister(struct rkcif_device *rkcif) +{ + rkcif_mipi_unregister(rkcif); + rkcif_dvp_unregister(rkcif); +} + +static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asd) +{ + struct rkcif_device *rkcif = + container_of(notifier, struct rkcif_device, notifier); + struct rkcif_remote *remote = + container_of(asd, struct rkcif_remote, async_conn); + struct media_pad *sink_pad = + &remote->interface->pads[RKCIF_IF_PAD_SINK]; + int ret; + + ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(rkcif->dev, "failed to link source pad of %s\n", + sd->name); + return ret; + } + + remote->sd = sd; + + return 0; +} + +static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct rkcif_device *rkcif = + container_of(notifier, struct rkcif_device, notifier); + + return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations rkcif_notifier_ops = { + .bound = rkcif_notifier_bound, + .complete = rkcif_notifier_complete, +}; + +static irqreturn_t rkcif_isr(int irq, void *ctx) +{ + irqreturn_t ret = IRQ_NONE; + + if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED) + ret = IRQ_HANDLED; + + if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED) + ret = IRQ_HANDLED; + + return ret; +} + +static int rkcif_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rkcif_device *rkcif; + int ret, irq; + + rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL); + if (!rkcif) + return -ENOMEM; + + rkcif->match_data = of_device_get_match_data(dev); + if (!rkcif->match_data) + return -ENODEV; + + dev_set_drvdata(dev, rkcif); + rkcif->dev = dev; + + rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rkcif->base_addr)) + return PTR_ERR(rkcif->base_addr); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED, + dev_driver_string(dev), dev); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq\n"); + + if (rkcif->match_data->clks_num > RKCIF_CLK_MAX) + return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n"); + + rkcif->clks_num = rkcif->match_data->clks_num; + for (unsigned int i = 0; i < rkcif->clks_num; i++) + rkcif->clks[i].id = rkcif->match_data->clks[i]; + ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks); + if (ret) + return dev_err_probe(dev, ret, "failed to get clocks\n"); + + rkcif->reset = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(rkcif->reset)) + return PTR_ERR(rkcif->reset); + + rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(rkcif->grf)) + rkcif->grf = NULL; + + pm_runtime_enable(&pdev->dev); + + rkcif->media_dev.dev = dev; + strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME, + sizeof(rkcif->media_dev.model)); + media_device_init(&rkcif->media_dev); + + rkcif->v4l2_dev.mdev = &rkcif->media_dev; + ret = v4l2_device_register(dev, &rkcif->v4l2_dev); + if (ret) + goto err_media_dev_cleanup; + + ret = media_device_register(&rkcif->media_dev); + if (ret < 0) { + dev_err(dev, "failed to register media device: %d\n", ret); + goto err_v4l2_dev_unregister; + } + + v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev); + rkcif->notifier.ops = &rkcif_notifier_ops; + + ret = rkcif_register(rkcif); + if (ret) { + dev_err(dev, "failed to register media entities: %d\n", ret); + goto err_notifier_cleanup; + } + + ret = v4l2_async_nf_register(&rkcif->notifier); + if (ret) + goto err_rkcif_unregister; + + return 0; + +err_rkcif_unregister: + rkcif_unregister(rkcif); +err_notifier_cleanup: + v4l2_async_nf_cleanup(&rkcif->notifier); + media_device_unregister(&rkcif->media_dev); +err_v4l2_dev_unregister: + v4l2_device_unregister(&rkcif->v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(&rkcif->media_dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static void rkcif_remove(struct platform_device *pdev) +{ + struct rkcif_device *rkcif = platform_get_drvdata(pdev); + + v4l2_async_nf_unregister(&rkcif->notifier); + rkcif_unregister(rkcif); + v4l2_async_nf_cleanup(&rkcif->notifier); + media_device_unregister(&rkcif->media_dev); + v4l2_device_unregister(&rkcif->v4l2_dev); + media_device_cleanup(&rkcif->media_dev); + pm_runtime_disable(&pdev->dev); +} + +static int rkcif_runtime_suspend(struct device *dev) +{ + struct rkcif_device *rkcif = dev_get_drvdata(dev); + + /* + * Reset CIF (CRU, DMA, FIFOs) to allow a clean resume. + * Since this resets the IOMMU too, we cannot issue this reset when + * resuming. + */ + reset_control_assert(rkcif->reset); + udelay(5); + reset_control_deassert(rkcif->reset); + + clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks); + + return 0; +} + +static int rkcif_runtime_resume(struct device *dev) +{ + struct rkcif_device *rkcif = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rkcif_plat_pm_ops = { + .runtime_suspend = rkcif_runtime_suspend, + .runtime_resume = rkcif_runtime_resume, +}; + +static struct platform_driver rkcif_plat_drv = { + .driver = { + .name = RKCIF_DRIVER_NAME, + .of_match_table = rkcif_plat_of_match, + .pm = &rkcif_plat_pm_ops, + }, + .probe = rkcif_probe, + .remove = rkcif_remove, +}; +module_platform_driver(rkcif_plat_drv); + +MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c new file mode 100644 index 000000000000..523103872b7a --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#include <media/v4l2-common.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#include "rkcif-common.h" +#include "rkcif-interface.h" + +static inline struct rkcif_interface *to_rkcif_interface(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rkcif_interface, sd); +} + +static const struct media_entity_operations rkcif_interface_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +static int rkcif_interface_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct rkcif_interface *interface = to_rkcif_interface(sd); + const struct rkcif_input_fmt *input; + struct v4l2_mbus_framefmt *sink, *src; + struct v4l2_rect *crop; + u32 other_pad, other_stream; + int ret; + + /* the format on the source pad always matches the sink pad */ + if (format->pad == RKCIF_IF_PAD_SRC) + return v4l2_subdev_get_fmt(sd, state, format); + + input = rkcif_interface_find_input_fmt(interface, true, + format->format.code); + format->format.code = input->mbus_code; + + sink = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!sink) + return -EINVAL; + + *sink = format->format; + + /* propagate the format to the source pad */ + src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!src) + return -EINVAL; + + *src = *sink; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + format->pad, format->stream, + &other_pad, &other_stream); + if (ret) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); + if (!crop) + return -EINVAL; + + /* reset crop */ + crop->left = 0; + crop->top = 0; + crop->width = sink->width; + crop->height = sink->height; + + return 0; +} + +static int rkcif_interface_get_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *sink; + struct v4l2_rect *crop; + int ret = 0; + + if (sel->pad != RKCIF_IF_PAD_SRC) + return -EINVAL; + + sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad, + sel->stream); + if (!sink) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); + if (!crop) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = sink->width; + sel->r.height = sink->height; + break; + case V4L2_SEL_TGT_CROP: + sel->r = *crop; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int rkcif_interface_set_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *sink, *src; + struct v4l2_rect *crop; + + if (sel->pad != RKCIF_IF_PAD_SRC || sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad, + sel->stream); + if (!sink) + return -EINVAL; + + src = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); + if (!src) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); + if (!crop) + return -EINVAL; + + *crop = sel->r; + + src->height = sel->r.height; + src->width = sel->r.width; + + return 0; +} + +static int rkcif_interface_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + for (unsigned int i = 0; i < routing->num_routes; i++) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream >= RKCIF_ID_MAX) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing(sd, state, routing); + + return ret; +} + +static int rkcif_interface_apply_crop(struct rkcif_stream *stream, + struct v4l2_subdev_state *state) +{ + struct rkcif_interface *interface = stream->interface; + struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, RKCIF_IF_PAD_SRC, stream->id); + if (!crop) + return -EINVAL; + + if (interface->set_crop) + interface->set_crop(stream, crop->left, crop->top); + + return 0; +} + +static int rkcif_interface_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct rkcif_interface *interface = to_rkcif_interface(sd); + struct rkcif_stream *stream; + struct v4l2_subdev_route *route; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 mask; + + remote_pad = + media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* DVP has one crop setting for all IDs */ + if (interface->type == RKCIF_IF_DVP) { + stream = &interface->streams[RKCIF_ID0]; + rkcif_interface_apply_crop(stream, state); + } else { + for_each_active_route(&state->routing, route) { + stream = &interface->streams[route->sink_stream]; + rkcif_interface_apply_crop(stream, state); + } + } + + mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK, + RKCIF_IF_PAD_SRC, &streams_mask); + + return v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); +} + +static int rkcif_interface_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 mask; + + remote_pad = + media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK, + RKCIF_IF_PAD_SRC, &streams_mask); + + return v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); +} + +static const struct v4l2_subdev_pad_ops rkcif_interface_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = rkcif_interface_set_fmt, + .get_selection = rkcif_interface_get_sel, + .set_selection = rkcif_interface_set_sel, + .set_routing = rkcif_interface_set_routing, + .enable_streams = rkcif_interface_enable_streams, + .disable_streams = rkcif_interface_disable_streams, +}; + +static const struct v4l2_subdev_ops rkcif_interface_ops = { + .pad = &rkcif_interface_pad_ops, +}; + +static int rkcif_interface_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct rkcif_interface *interface = to_rkcif_interface(sd); + struct v4l2_subdev_route routes[] = { + { + .sink_pad = RKCIF_IF_PAD_SINK, + .sink_stream = 0, + .source_pad = RKCIF_IF_PAD_SRC, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + const struct v4l2_mbus_framefmt dvp_default_format = { + .width = 3840, + .height = 2160, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_REC709, + .ycbcr_enc = V4L2_YCBCR_ENC_709, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, + }; + const struct v4l2_mbus_framefmt mipi_default_format = { + .width = 3840, + .height = 2160, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, + }; + const struct v4l2_mbus_framefmt *default_format; + int ret; + + default_format = (interface->type == RKCIF_IF_DVP) ? + &dvp_default_format : + &mipi_default_format; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, + default_format); + + return ret; +} + +static const struct v4l2_subdev_internal_ops rkcif_interface_internal_ops = { + .init_state = rkcif_interface_init_state, +}; + +static int rkcif_interface_add(struct rkcif_interface *interface) +{ + struct rkcif_device *rkcif = interface->rkcif; + struct rkcif_remote *remote; + struct v4l2_async_notifier *ntf = &rkcif->notifier; + struct v4l2_fwnode_endpoint *vep = &interface->vep; + struct device *dev = rkcif->dev; + struct fwnode_handle *ep; + u32 dvp_clk_delay = 0; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), interface->index, + 0, 0); + if (!ep) + return -ENODEV; + + vep->bus_type = V4L2_MBUS_UNKNOWN; + ret = v4l2_fwnode_endpoint_parse(ep, vep); + if (ret) + goto complete; + + if (interface->type == RKCIF_IF_DVP) { + if (vep->bus_type != V4L2_MBUS_BT656 && + vep->bus_type != V4L2_MBUS_PARALLEL) { + ret = dev_err_probe(dev, -EINVAL, + "unsupported bus type\n"); + goto complete; + } + + fwnode_property_read_u32(ep, "rockchip,dvp-clk-delay", + &dvp_clk_delay); + interface->dvp.dvp_clk_delay = dvp_clk_delay; + } + + remote = v4l2_async_nf_add_fwnode_remote(ntf, ep, struct rkcif_remote); + if (IS_ERR(remote)) { + ret = PTR_ERR(remote); + goto complete; + } + + remote->interface = interface; + interface->remote = remote; + interface->status = RKCIF_IF_ACTIVE; + ret = 0; + +complete: + fwnode_handle_put(ep); + + return ret; +} + +int rkcif_interface_register(struct rkcif_device *rkcif, + struct rkcif_interface *interface) +{ + struct media_pad *pads = interface->pads; + struct v4l2_subdev *sd = &interface->sd; + int ret; + + interface->rkcif = rkcif; + + v4l2_subdev_init(sd, &rkcif_interface_ops); + sd->dev = rkcif->dev; + sd->entity.ops = &rkcif_interface_media_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + sd->internal_ops = &rkcif_interface_internal_ops; + sd->owner = THIS_MODULE; + + if (interface->type == RKCIF_IF_DVP) + snprintf(sd->name, sizeof(sd->name), "rkcif-dvp0"); + else if (interface->type == RKCIF_IF_MIPI) + snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d", + interface->index - RKCIF_MIPI_BASE); + + pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads); + if (ret) + goto err; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(&rkcif->v4l2_dev, sd); + if (ret) { + dev_err(sd->dev, "failed to register subdev\n"); + goto err_subdev_cleanup; + } + + ret = rkcif_interface_add(interface); + if (ret) + goto err_subdev_unregister; + + return 0; + +err_subdev_unregister: + v4l2_device_unregister_subdev(sd); +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); +err: + return ret; +} + +void rkcif_interface_unregister(struct rkcif_interface *interface) +{ + struct v4l2_subdev *sd = &interface->sd; + + if (interface->status != RKCIF_IF_ACTIVE) + return; + + v4l2_device_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); +} + +const struct rkcif_input_fmt * +rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def, + u32 mbus_code) +{ + const struct rkcif_input_fmt *fmt; + + WARN_ON(interface->in_fmts_num == 0); + + for (unsigned int i = 0; i < interface->in_fmts_num; i++) { + fmt = &interface->in_fmts[i]; + if (fmt->mbus_code == mbus_code) + return fmt; + } + if (ret_def) + return &interface->in_fmts[0]; + else + return NULL; +} diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.h b/drivers/media/platform/rockchip/rkcif/rkcif-interface.h new file mode 100644 index 000000000000..f13aa28b6fa7 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Abstraction for the INTERFACE and CROP parts of the different CIF variants. + * They shall be represented as V4L2 subdevice with one sink pad and one + * source pad. The sink pad is connected to a subdevice: either the subdevice + * provided by the driver of the companion chip connected to the DVP, or the + * subdevice provided by the MIPI CSI-2 receiver driver. The source pad is + * to V4l2 device(s) provided by one or many instance(s) of the DMA + * abstraction. + * + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#ifndef _RKCIF_INTERFACE_H +#define _RKCIF_INTERFACE_H + +#include "rkcif-common.h" + +int rkcif_interface_register(struct rkcif_device *rkcif, + struct rkcif_interface *interface); + +void rkcif_interface_unregister(struct rkcif_interface *interface); + +const struct rkcif_input_fmt * +rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def, + u32 mbus_code); + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h new file mode 100644 index 000000000000..3cf7ee19de30 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com> + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + */ + +#ifndef _RKCIF_REGS_H +#define _RKCIF_REGS_H + +#define RKCIF_REGISTER_NOTSUPPORTED 0x420000 +#define RKCIF_FETCH_Y(VAL) ((VAL) & 0x1fff) +#define RKCIF_XY_COORD(x, y) (((y) << 16) | (x)) + +/* DVP register contents */ +#define RKCIF_CTRL_ENABLE_CAPTURE BIT(0) +#define RKCIF_CTRL_MODE_PINGPONG BIT(1) +#define RKCIF_CTRL_MODE_LINELOOP BIT(2) +#define RKCIF_CTRL_AXI_BURST_16 (0xf << 12) + +#define RKCIF_INTEN_FRAME_END_EN BIT(0) +#define RKCIF_INTEN_LINE_ERR_EN BIT(2) +#define RKCIF_INTEN_BUS_ERR_EN BIT(6) +#define RKCIF_INTEN_SCL_ERR_EN BIT(7) +#define RKCIF_INTEN_PST_INF_FRAME_END_EN BIT(9) + +#define RKCIF_INTSTAT_CLS 0x3ff +#define RKCIF_INTSTAT_FRAME_END BIT(0) +#define RKCIF_INTSTAT_LINE_END BIT(1) +#define RKCIF_INTSTAT_LINE_ERR BIT(2) +#define RKCIF_INTSTAT_PIX_ERR BIT(3) +#define RKCIF_INTSTAT_DFIFO_OF BIT(5) +#define RKCIF_INTSTAT_BUS_ERR BIT(6) +#define RKCIF_INTSTAT_PRE_INF_FRAME_END BIT(8) +#define RKCIF_INTSTAT_PST_INF_FRAME_END BIT(9) +#define RKCIF_INTSTAT_FRAME_END_CLR BIT(0) +#define RKCIF_INTSTAT_LINE_END_CLR BIT(1) +#define RKCIF_INTSTAT_LINE_ERR_CLR BIT(2) +#define RKCIF_INTSTAT_PST_INF_FRAME_END_CLR BIT(9) +#define RKCIF_INTSTAT_ERR 0xfc + +#define RKCIF_FRAME_STAT_CLS 0x00 +#define RKCIF_FRAME_FRM0_STAT_CLS 0x20 + +#define RKCIF_FORMAT_VSY_HIGH_ACTIVE BIT(0) +#define RKCIF_FORMAT_HSY_LOW_ACTIVE BIT(1) + +#define RKCIF_FORMAT_INPUT_MODE_YUV (0x00 << 2) +#define RKCIF_FORMAT_INPUT_MODE_PAL (0x02 << 2) +#define RKCIF_FORMAT_INPUT_MODE_NTSC (0x03 << 2) +#define RKCIF_FORMAT_INPUT_MODE_BT1120 (0x07 << 2) +#define RKCIF_FORMAT_INPUT_MODE_RAW (0x04 << 2) +#define RKCIF_FORMAT_INPUT_MODE_JPEG (0x05 << 2) +#define RKCIF_FORMAT_INPUT_MODE_MIPI (0x06 << 2) + +#define RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY (0x00 << 5) +#define RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU (0x01 << 5) +#define RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY (0x02 << 5) +#define RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV (0x03 << 5) +#define RKCIF_FORMAT_YUV_INPUT_422 (0x00 << 7) +#define RKCIF_FORMAT_YUV_INPUT_420 BIT(7) + +#define RKCIF_FORMAT_INPUT_420_ORDER_ODD BIT(8) + +#define RKCIF_FORMAT_CCIR_INPUT_ORDER_EVEN BIT(9) + +#define RKCIF_FORMAT_RAW_DATA_WIDTH_8 (0x00 << 11) +#define RKCIF_FORMAT_RAW_DATA_WIDTH_10 (0x01 << 11) +#define RKCIF_FORMAT_RAW_DATA_WIDTH_12 (0x02 << 11) + +#define RKCIF_FORMAT_YUV_OUTPUT_422 (0x00 << 16) +#define RKCIF_FORMAT_YUV_OUTPUT_420 BIT(16) + +#define RKCIF_FORMAT_OUTPUT_420_ORDER_EVEN (0x00 << 17) +#define RKCIF_FORMAT_OUTPUT_420_ORDER_ODD BIT(17) + +#define RKCIF_FORMAT_RAWD_DATA_LITTLE_ENDIAN (0x00 << 18) +#define RKCIF_FORMAT_RAWD_DATA_BIG_ENDIAN BIT(18) + +#define RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV (0x00 << 19) +#define RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU BIT(19) + +#define RKCIF_FORMAT_BT1120_CLOCK_SINGLE_EDGES (0x00 << 24) +#define RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES BIT(24) +#define RKCIF_FORMAT_BT1120_TRANSMIT_INTERFACE (0x00 << 25) +#define RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS BIT(25) +#define RKCIF_FORMAT_BT1120_YC_SWAP BIT(26) + +#define RKCIF_SCL_CTRL_ENABLE_SCL_DOWN BIT(0) +#define RKCIF_SCL_CTRL_ENABLE_SCL_UP BIT(1) +#define RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS BIT(4) +#define RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS BIT(5) +#define RKCIF_SCL_CTRL_ENABLE_32BIT_BYPASS BIT(6) +#define RKCIF_SCL_CTRL_DISABLE_32BIT_BYPASS (0x00 << 6) + +#define RKCIF_INTSTAT_F0_READY BIT(0) +#define RKCIF_INTSTAT_F1_READY BIT(1) + +/* GRF register offsets and contents */ +#define RK3568_GRF_VI_CON0 0x340 +#define RK3568_GRF_VI_CON1 0x344 +#define RK3568_GRF_VI_STATUS0 0x348 + +#define RK3568_GRF_VI_CON1_CIF_DATAPATH BIT(9) +#define RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM GENMASK(6, 0) + +#define RK3568_GRF_WRITE_ENABLE(x) ((x) << 16) + +enum rkcif_dvp_register_index { + RKCIF_DVP_CTRL, + RKCIF_DVP_INTEN, + RKCIF_DVP_INTSTAT, + RKCIF_DVP_FOR, + RKCIF_DVP_LINE_NUM_ADDR, + RKCIF_DVP_FRM0_ADDR_Y, + RKCIF_DVP_FRM0_ADDR_UV, + RKCIF_DVP_FRM1_ADDR_Y, + RKCIF_DVP_FRM1_ADDR_UV, + RKCIF_DVP_VIR_LINE_WIDTH, + RKCIF_DVP_SET_SIZE, + RKCIF_DVP_SCL_CTRL, + RKCIF_DVP_CROP, + RKCIF_DVP_FRAME_STATUS, + RKCIF_DVP_LAST_LINE, + RKCIF_DVP_LAST_PIX, + RKCIF_DVP_REGISTER_MAX +}; + +enum rkcif_mipi_register_index { + RKCIF_MIPI_CTRL, + RKCIF_MIPI_INTEN, + RKCIF_MIPI_INTSTAT, + RKCIF_MIPI_REGISTER_MAX +}; + +enum rkcif_mipi_id_register_index { + RKCIF_MIPI_CTRL0, + RKCIF_MIPI_CTRL1, + RKCIF_MIPI_FRAME0_ADDR_Y, + RKCIF_MIPI_FRAME0_ADDR_UV, + RKCIF_MIPI_FRAME0_VLW_Y, + RKCIF_MIPI_FRAME0_VLW_UV, + RKCIF_MIPI_FRAME1_ADDR_Y, + RKCIF_MIPI_FRAME1_ADDR_UV, + RKCIF_MIPI_FRAME1_VLW_Y, + RKCIF_MIPI_FRAME1_VLW_UV, + RKCIF_MIPI_CROP_START, + RKCIF_MIPI_ID_REGISTER_MAX +}; + +#endif diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c new file mode 100644 index 000000000000..e00010a91e8b --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Camera Interface (CIF) Driver + * + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#include <linux/pm_runtime.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "rkcif-common.h" +#include "rkcif-stream.h" + +#define CIF_REQ_BUFS_MIN 1 +#define CIF_MIN_WIDTH 64 +#define CIF_MIN_HEIGHT 64 +#define CIF_MAX_WIDTH 8192 +#define CIF_MAX_HEIGHT 8192 + +static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct rkcif_buffer, vb); +} + +static inline struct rkcif_stream *to_rkcif_stream(struct video_device *vdev) +{ + return container_of(vdev, struct rkcif_stream, vdev); +} + +static struct rkcif_buffer *rkcif_stream_pop_buffer(struct rkcif_stream *stream) +{ + struct rkcif_buffer *buffer; + + guard(spinlock_irqsave)(&stream->driver_queue_lock); + + if (list_empty(&stream->driver_queue)) + return NULL; + + buffer = list_first_entry(&stream->driver_queue, struct rkcif_buffer, + queue); + list_del(&buffer->queue); + + return buffer; +} + +static void rkcif_stream_push_buffer(struct rkcif_stream *stream, + struct rkcif_buffer *buffer) +{ + guard(spinlock_irqsave)(&stream->driver_queue_lock); + + list_add_tail(&buffer->queue, &stream->driver_queue); +} + +static inline void rkcif_stream_return_buffer(struct rkcif_buffer *buffer, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb = &buffer->vb; + + vb2_buffer_done(&vb->vb2_buf, state); +} + +static void rkcif_stream_complete_buffer(struct rkcif_stream *stream, + struct rkcif_buffer *buffer) +{ + struct vb2_v4l2_buffer *vb = &buffer->vb; + + vb->vb2_buf.timestamp = ktime_get_ns(); + vb->sequence = stream->frame_idx; + vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); + stream->frame_idx++; +} + +void rkcif_stream_pingpong(struct rkcif_stream *stream) +{ + struct rkcif_buffer *buffer; + + buffer = stream->buffers[stream->frame_phase]; + if (!buffer->is_dummy) + rkcif_stream_complete_buffer(stream, buffer); + + buffer = rkcif_stream_pop_buffer(stream); + if (buffer) { + stream->buffers[stream->frame_phase] = buffer; + stream->buffers[stream->frame_phase]->is_dummy = false; + } else { + stream->buffers[stream->frame_phase] = &stream->dummy.buffer; + stream->buffers[stream->frame_phase]->is_dummy = true; + dev_dbg(stream->rkcif->dev, + "no buffer available, frame will be dropped\n"); + } + + if (stream->queue_buffer) + stream->queue_buffer(stream, stream->frame_phase); + + stream->frame_phase = 1 - stream->frame_phase; +} + +static int rkcif_stream_init_buffers(struct rkcif_stream *stream) +{ + struct v4l2_pix_format_mplane *pix = &stream->pix; + + stream->buffers[0] = rkcif_stream_pop_buffer(stream); + if (!stream->buffers[0]) + goto err_buff_0; + + stream->buffers[1] = rkcif_stream_pop_buffer(stream); + if (!stream->buffers[1]) + goto err_buff_1; + + if (stream->queue_buffer) { + stream->queue_buffer(stream, 0); + stream->queue_buffer(stream, 1); + } + + stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage; + stream->dummy.vaddr = + dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size, + &stream->dummy.buffer.buff_addr[0], GFP_KERNEL, + DMA_ATTR_NO_KERNEL_MAPPING); + if (!stream->dummy.vaddr) + goto err_dummy; + + for (unsigned int i = 1; i < pix->num_planes; i++) + stream->dummy.buffer.buff_addr[i] = + stream->dummy.buffer.buff_addr[i - 1] + + pix->plane_fmt[i - 1].bytesperline * pix->height; + + return 0; + +err_dummy: + rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED); + stream->buffers[1] = NULL; + +err_buff_1: + rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED); + stream->buffers[0] = NULL; +err_buff_0: + return -EINVAL; +} + +static void rkcif_stream_return_all_buffers(struct rkcif_stream *stream, + enum vb2_buffer_state state) +{ + struct rkcif_buffer *buffer; + + if (stream->buffers[0] && !stream->buffers[0]->is_dummy) { + rkcif_stream_return_buffer(stream->buffers[0], state); + stream->buffers[0] = NULL; + } + + if (stream->buffers[1] && !stream->buffers[1]->is_dummy) { + rkcif_stream_return_buffer(stream->buffers[1], state); + stream->buffers[1] = NULL; + } + + while ((buffer = rkcif_stream_pop_buffer(stream))) + rkcif_stream_return_buffer(buffer, state); + + if (stream->dummy.vaddr) { + dma_free_attrs(stream->rkcif->dev, stream->dummy.size, + stream->dummy.vaddr, + stream->dummy.buffer.buff_addr[0], + DMA_ATTR_NO_KERNEL_MAPPING); + stream->dummy.vaddr = NULL; + } +} + +static int rkcif_stream_setup_queue(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct rkcif_stream *stream = queue->drv_priv; + struct v4l2_pix_format_mplane *pix = &stream->pix; + + if (*num_planes) { + if (*num_planes != pix->num_planes) + return -EINVAL; + + for (unsigned int i = 0; i < pix->num_planes; i++) + if (sizes[i] < pix->plane_fmt[i].sizeimage) + return -EINVAL; + } else { + *num_planes = pix->num_planes; + for (unsigned int i = 0; i < pix->num_planes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + } + + return 0; +} + +static int rkcif_stream_prepare_buffer(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf); + struct rkcif_stream *stream = vb->vb2_queue->drv_priv; + const struct rkcif_output_fmt *fmt; + struct v4l2_pix_format_mplane *pix = &stream->pix; + unsigned int i; + + memset(buffer->buff_addr, 0, sizeof(buffer->buff_addr)); + for (i = 0; i < pix->num_planes; i++) + buffer->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + + /* apply fallback for non-mplane formats, if required */ + if (pix->num_planes == 1) { + fmt = rkcif_stream_find_output_fmt(stream, true, + pix->pixelformat); + for (i = 1; i < fmt->cplanes; i++) + buffer->buff_addr[i] = + buffer->buff_addr[i - 1] + + pix->plane_fmt[i - 1].bytesperline * + pix->height; + } + + for (i = 0; i < pix->num_planes; i++) { + unsigned long size = pix->plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < size) { + dev_err(stream->rkcif->dev, + "user buffer too small (%ld < %ld)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void rkcif_stream_queue_buffer(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf); + struct rkcif_stream *stream = vb->vb2_queue->drv_priv; + + rkcif_stream_push_buffer(stream, buffer); +} + +static int rkcif_stream_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct rkcif_stream *stream = queue->drv_priv; + struct rkcif_device *rkcif = stream->rkcif; + u64 mask; + int ret; + + stream->frame_idx = 0; + stream->frame_phase = 0; + + ret = video_device_pipeline_start(&stream->vdev, &stream->pipeline); + if (ret) { + dev_err(rkcif->dev, "failed to start pipeline %d\n", ret); + goto err_out; + } + + ret = pm_runtime_resume_and_get(rkcif->dev); + if (ret < 0) { + dev_err(rkcif->dev, "failed to get runtime pm, %d\n", ret); + goto err_pipeline_stop; + } + + ret = rkcif_stream_init_buffers(stream); + if (ret) + goto err_runtime_put; + + if (stream->start_streaming) { + ret = stream->start_streaming(stream); + if (ret < 0) + goto err_runtime_put; + } + + mask = BIT_ULL(stream->id); + ret = v4l2_subdev_enable_streams(&stream->interface->sd, + RKCIF_IF_PAD_SRC, mask); + if (ret < 0) + goto err_stop_stream; + + return 0; + +err_stop_stream: + if (stream->stop_streaming) + stream->stop_streaming(stream); +err_runtime_put: + pm_runtime_put(rkcif->dev); +err_pipeline_stop: + video_device_pipeline_stop(&stream->vdev); +err_out: + rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void rkcif_stream_stop_streaming(struct vb2_queue *queue) +{ + struct rkcif_stream *stream = queue->drv_priv; + struct rkcif_device *rkcif = stream->rkcif; + u64 mask; + int ret; + + mask = BIT_ULL(stream->id); + v4l2_subdev_disable_streams(&stream->interface->sd, RKCIF_IF_PAD_SRC, + mask); + + stream->stopping = true; + ret = wait_event_timeout(stream->wq_stopped, !stream->stopping, + msecs_to_jiffies(1000)); + + if (!ret && stream->stop_streaming) + stream->stop_streaming(stream); + + pm_runtime_put(rkcif->dev); + + rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_ERROR); + + video_device_pipeline_stop(&stream->vdev); +} + +static const struct vb2_ops rkcif_stream_vb2_ops = { + .queue_setup = rkcif_stream_setup_queue, + .buf_prepare = rkcif_stream_prepare_buffer, + .buf_queue = rkcif_stream_queue_buffer, + .start_streaming = rkcif_stream_start_streaming, + .stop_streaming = rkcif_stream_stop_streaming, +}; + +static int rkcif_stream_fill_format(struct rkcif_stream *stream, + struct v4l2_pix_format_mplane *pix) +{ + const struct rkcif_output_fmt *fmt; + u32 height, width; + int ret; + + fmt = rkcif_stream_find_output_fmt(stream, true, pix->pixelformat); + height = clamp_t(u32, pix->height, CIF_MIN_HEIGHT, CIF_MAX_HEIGHT); + width = clamp_t(u32, pix->width, CIF_MIN_WIDTH, CIF_MAX_WIDTH); + ret = v4l2_fill_pixfmt_mp(pix, fmt->fourcc, width, height); + if (ret) + return ret; + + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int rkcif_stream_try_format(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkcif_stream *stream = video_drvdata(file); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + + return rkcif_stream_fill_format(stream, pix); +} + +static int rkcif_stream_set_format(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkcif_stream *stream = video_drvdata(file); + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + int ret; + + if (vb2_is_busy(&stream->buf_queue)) + return -EBUSY; + + ret = rkcif_stream_try_format(file, priv, f); + if (ret) + return ret; + + stream->pix = *pix; + + return 0; +} + +static int rkcif_stream_get_format(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkcif_stream *stream = video_drvdata(file); + + f->fmt.pix_mp = stream->pix; + + return 0; +} + +static int rkcif_stream_enum_formats(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkcif_stream *stream = video_drvdata(file); + + if (f->index >= stream->out_fmts_num) + return -EINVAL; + + f->pixelformat = stream->out_fmts[f->index].fourcc; + + return 0; +} + +static int rkcif_stream_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct rkcif_stream *stream = video_drvdata(file); + + if (fsize->index > 0) + return -EINVAL; + + if (!rkcif_stream_find_output_fmt(stream, false, fsize->pixel_format)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = CIF_MIN_WIDTH; + fsize->stepwise.max_width = CIF_MAX_WIDTH; + fsize->stepwise.step_width = 8; + fsize->stepwise.min_height = CIF_MIN_HEIGHT; + fsize->stepwise.max_height = CIF_MAX_HEIGHT; + fsize->stepwise.step_height = 8; + + return 0; +} + +static int rkcif_stream_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkcif_stream *stream = video_drvdata(file); + struct device *dev = stream->rkcif->dev; + + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + strscpy(cap->card, dev->driver->name, sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops rkcif_stream_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_try_fmt_vid_cap_mplane = rkcif_stream_try_format, + .vidioc_s_fmt_vid_cap_mplane = rkcif_stream_set_format, + .vidioc_g_fmt_vid_cap_mplane = rkcif_stream_get_format, + .vidioc_enum_fmt_vid_cap = rkcif_stream_enum_formats, + .vidioc_enum_framesizes = rkcif_stream_enum_framesizes, + .vidioc_querycap = rkcif_stream_querycap, +}; + +static int rkcif_stream_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct v4l2_mbus_framefmt *source_fmt; + struct v4l2_subdev *sd; + struct v4l2_subdev_state *state; + struct rkcif_stream *stream = to_rkcif_stream(vdev); + int ret = -EINVAL; + + if (!media_entity_remote_source_pad_unique(link->sink->entity)) + return -ENOTCONN; + + sd = media_entity_to_v4l2_subdev(link->source->entity); + + state = v4l2_subdev_lock_and_get_active_state(sd); + + source_fmt = v4l2_subdev_state_get_format(state, link->source->index, + stream->id); + if (!source_fmt) + goto out; + + if (source_fmt->height != stream->pix.height || + source_fmt->width != stream->pix.width) { + dev_dbg(stream->rkcif->dev, + "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + source_fmt->width, source_fmt->height, + stream->pix.width, stream->pix.height); + goto out; + } + + ret = 0; + +out: + v4l2_subdev_unlock_state(state); + return ret; +} + +static const struct media_entity_operations rkcif_stream_media_ops = { + .link_validate = rkcif_stream_link_validate, +}; + +static const struct v4l2_file_operations rkcif_stream_file_ops = { + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static int rkcif_stream_init_vb2_queue(struct vb2_queue *q, + struct rkcif_stream *stream) +{ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = stream; + q->ops = &rkcif_stream_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct rkcif_buffer); + q->min_queued_buffers = CIF_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &stream->vlock; + q->dev = stream->rkcif->dev; + + return vb2_queue_init(q); +} + +int rkcif_stream_register(struct rkcif_device *rkcif, + struct rkcif_stream *stream) +{ + struct rkcif_interface *interface = stream->interface; + struct v4l2_device *v4l2_dev = &rkcif->v4l2_dev; + struct video_device *vdev = &stream->vdev; + u32 link_flags = 0; + int ret; + + stream->rkcif = rkcif; + + INIT_LIST_HEAD(&stream->driver_queue); + spin_lock_init(&stream->driver_queue_lock); + + init_waitqueue_head(&stream->wq_stopped); + + mutex_init(&stream->vlock); + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->entity.ops = &rkcif_stream_media_ops; + vdev->fops = &rkcif_stream_file_ops; + vdev->ioctl_ops = &rkcif_stream_ioctl_ops; + vdev->lock = &stream->vlock; + vdev->minor = -1; + vdev->release = video_device_release_empty; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + video_set_drvdata(vdev, stream); + + stream->pad.flags = MEDIA_PAD_FL_SINK; + + stream->pix.height = CIF_MIN_HEIGHT; + stream->pix.width = CIF_MIN_WIDTH; + rkcif_stream_fill_format(stream, &stream->pix); + + rkcif_stream_init_vb2_queue(&stream->buf_queue, stream); + + vdev->queue = &stream->buf_queue; + if (interface->type == RKCIF_IF_DVP) + snprintf(vdev->name, sizeof(vdev->name), "rkcif-dvp0-id%d", + stream->id); + else if (interface->type == RKCIF_IF_MIPI) + snprintf(vdev->name, sizeof(vdev->name), "rkcif-mipi%d-id%d", + interface->index - RKCIF_MIPI_BASE, stream->id); + + ret = media_entity_pads_init(&vdev->entity, 1, &stream->pad); + if (ret < 0) { + dev_err(rkcif->dev, + "failed to initialize stream media pad: %d\n", ret); + return ret; + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(rkcif->dev, "failed to register video device: %d\n", + ret); + goto err_media_entity_cleanup; + } + + /* enable only stream ID0 by default */ + if (stream->id == RKCIF_ID0) + link_flags |= MEDIA_LNK_FL_ENABLED; + + ret = media_create_pad_link(&interface->sd.entity, RKCIF_IF_PAD_SRC, + &stream->vdev.entity, 0, link_flags); + if (ret) { + dev_err(rkcif->dev, "failed to link stream media pad: %d\n", + ret); + goto err_video_unregister; + } + + v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name, + vdev->num); + + return 0; + +err_video_unregister: + video_unregister_device(&stream->vdev); +err_media_entity_cleanup: + media_entity_cleanup(&stream->vdev.entity); + return ret; +} + +void rkcif_stream_unregister(struct rkcif_stream *stream) +{ + video_unregister_device(&stream->vdev); + media_entity_cleanup(&stream->vdev.entity); +} + +const struct rkcif_output_fmt * +rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def, + u32 pixelfmt) +{ + const struct rkcif_output_fmt *fmt; + + WARN_ON(stream->out_fmts_num == 0); + + for (unsigned int i = 0; i < stream->out_fmts_num; i++) { + fmt = &stream->out_fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + + if (ret_def) + return &stream->out_fmts[0]; + else + return NULL; +} diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.h b/drivers/media/platform/rockchip/rkcif/rkcif-stream.h new file mode 100644 index 000000000000..590faf5d1a87 --- /dev/null +++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Camera Interface (CIF) Driver + * + * Abstraction for the DMA part and the ping-pong scheme (a double-buffering + * mechanism) of the different CIF variants. + * Each stream is represented as V4L2 device whose corresponding media entity + * has one sink pad. + * The sink pad is connected to an instance of the INTERFACE/CROP abstraction + * in rkcif-interface.c. + * + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2025 Collabora, Ltd. + */ + +#ifndef _RKCIF_STREAM_H +#define _RKCIF_STREAM_H + +#include "rkcif-common.h" + +void rkcif_stream_pingpong(struct rkcif_stream *stream); + +int rkcif_stream_register(struct rkcif_device *rkcif, + struct rkcif_stream *stream); + +void rkcif_stream_unregister(struct rkcif_stream *stream); + +const struct rkcif_output_fmt * +rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def, + u32 pixelfmt); + +#endif diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig index 731c9acbf6ef..f53eb1f3f3e7 100644 --- a/drivers/media/platform/rockchip/rkisp1/Kconfig +++ b/drivers/media/platform/rockchip/rkisp1/Kconfig @@ -10,6 +10,7 @@ config VIDEO_ROCKCHIP_ISP1 select VIDEOBUF2_VMALLOC select V4L2_FWNODE select GENERIC_PHY_MIPI_DPHY + select V4L2_ISP default n help Enable this to support the Image Signal Processing (ISP) module diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 6028ecdd23de..5e6a4d5f6fd1 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -227,6 +227,7 @@ struct rkisp1_isp { struct media_pad pads[RKISP1_ISP_PAD_MAX]; const struct rkisp1_mbus_info *sink_fmt; __u32 frame_sequence; + bool frame_active; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 841e58c20f7f..ddc6182f3e4b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -368,8 +368,8 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable) source_pad = media_entity_remote_source_pad_unique(&sd->entity); if (IS_ERR(source_pad)) { - dev_dbg(rkisp1->dev, "Failed to get source for CSI: %ld\n", - PTR_ERR(source_pad)); + dev_dbg(rkisp1->dev, "Failed to get source for CSI: %pe\n", + source_pad); return -EPIPE; } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 8c29a1c9309a..2311672cedb1 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -936,8 +936,8 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO]; source_pad = media_pad_remote_pad_unique(sink_pad); if (IS_ERR(source_pad)) { - dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n", - PTR_ERR(source_pad)); + dev_dbg(rkisp1->dev, "Failed to get source for ISP: %pe\n", + source_pad); return -EPIPE; } @@ -965,6 +965,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } isp->frame_sequence = -1; + isp->frame_active = false; sd_state = v4l2_subdev_lock_and_get_active_state(sd); @@ -1086,12 +1087,15 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1) * Interrupt handlers */ -static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp) +static void rkisp1_isp_sof(struct rkisp1_isp *isp) { struct v4l2_event event = { .type = V4L2_EVENT_FRAME_SYNC, }; + isp->frame_sequence++; + isp->frame_active = true; + event.u.frame_sync.frame_sequence = isp->frame_sequence; v4l2_event_queue(isp->sd.devnode, &event); } @@ -1111,15 +1115,20 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status); - /* Vertical sync signal, starting generating new frame */ - if (status & RKISP1_CIF_ISP_V_START) { - rkisp1->isp.frame_sequence++; - rkisp1_isp_queue_event_sof(&rkisp1->isp); + /* + * Vertical sync signal, starting new frame. Defer handling of vsync + * after RKISP1_CIF_ISP_FRAME if the previous frame was not completed + * yet. + */ + if (status & RKISP1_CIF_ISP_V_START && !rkisp1->isp.frame_active) { + status &= ~RKISP1_CIF_ISP_V_START; + rkisp1_isp_sof(&rkisp1->isp); if (status & RKISP1_CIF_ISP_FRAME) { WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n"); rkisp1->debug.irq_delay++; } } + if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) { /* Clear pic_size_error */ isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR); @@ -1138,6 +1147,7 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) if (status & RKISP1_CIF_ISP_FRAME) { u32 isp_ris; + rkisp1->isp.frame_active = false; rkisp1->debug.complete_frames++; /* New frame from the sensor received */ @@ -1152,5 +1162,12 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx) rkisp1_params_isr(rkisp1); } + /* + * Deferred handling of vsync if RKISP1_CIF_ISP_V_START and + * RKISP1_CIF_ISP_FRAME occurred in the same irq. + */ + if (status & RKISP1_CIF_ISP_V_START) + rkisp1_isp_sof(&rkisp1->isp); + return IRQ_HANDLED; } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index f1585f8fa0f4..c9f88635224c 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -6,12 +6,14 @@ */ #include <linux/bitfield.h> +#include <linux/build_bug.h> #include <linux/math.h> #include <linux/string.h> #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-isp.h> #include <media/videobuf2-core.h> #include <media/videobuf2-vmalloc.h> /* for ISP params */ @@ -2097,122 +2099,133 @@ typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); static const struct rkisp1_ext_params_handler { - size_t size; rkisp1_block_handler handler; unsigned int group; unsigned int features; } rkisp1_ext_params_handlers[] = { [RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = { - .size = sizeof(struct rkisp1_ext_params_bls_config), .handler = rkisp1_ext_params_bls, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, .features = RKISP1_FEATURE_BLS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = { - .size = sizeof(struct rkisp1_ext_params_dpcc_config), .handler = rkisp1_ext_params_dpcc, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_SDG] = { - .size = sizeof(struct rkisp1_ext_params_sdg_config), .handler = rkisp1_ext_params_sdg, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_GAIN] = { - .size = sizeof(struct rkisp1_ext_params_awb_gain_config), .handler = rkisp1_ext_params_awbg, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_FLT] = { - .size = sizeof(struct rkisp1_ext_params_flt_config), .handler = rkisp1_ext_params_flt, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_BDM] = { - .size = sizeof(struct rkisp1_ext_params_bdm_config), .handler = rkisp1_ext_params_bdm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_CTK] = { - .size = sizeof(struct rkisp1_ext_params_ctk_config), .handler = rkisp1_ext_params_ctk, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_GOC] = { - .size = sizeof(struct rkisp1_ext_params_goc_config), .handler = rkisp1_ext_params_goc, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF] = { - .size = sizeof(struct rkisp1_ext_params_dpf_config), .handler = rkisp1_ext_params_dpf, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF_STRENGTH] = { - .size = sizeof(struct rkisp1_ext_params_dpf_strength_config), .handler = rkisp1_ext_params_dpfs, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_CPROC] = { - .size = sizeof(struct rkisp1_ext_params_cproc_config), .handler = rkisp1_ext_params_cproc, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_IE] = { - .size = sizeof(struct rkisp1_ext_params_ie_config), .handler = rkisp1_ext_params_ie, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_LSC] = { - .size = sizeof(struct rkisp1_ext_params_lsc_config), .handler = rkisp1_ext_params_lsc, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_MEAS] = { - .size = sizeof(struct rkisp1_ext_params_awb_meas_config), .handler = rkisp1_ext_params_awbm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS] = { - .size = sizeof(struct rkisp1_ext_params_hst_config), .handler = rkisp1_ext_params_hstm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS] = { - .size = sizeof(struct rkisp1_ext_params_aec_config), .handler = rkisp1_ext_params_aecm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS] = { - .size = sizeof(struct rkisp1_ext_params_afc_config), .handler = rkisp1_ext_params_afcm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS] = { - .size = sizeof(struct rkisp1_ext_params_compand_bls_config), .handler = rkisp1_ext_params_compand_bls, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, .features = RKISP1_FEATURE_COMPAND, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND] = { - .size = sizeof(struct rkisp1_ext_params_compand_curve_config), .handler = rkisp1_ext_params_compand_expand, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, .features = RKISP1_FEATURE_COMPAND, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS] = { - .size = sizeof(struct rkisp1_ext_params_compand_curve_config), .handler = rkisp1_ext_params_compand_compress, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, .features = RKISP1_FEATURE_COMPAND, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR] = { - .size = sizeof(struct rkisp1_ext_params_wdr_config), .handler = rkisp1_ext_params_wdr, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, }; +#define RKISP1_PARAMS_BLOCK_INFO(block, data) \ + [RKISP1_EXT_PARAMS_BLOCK_TYPE_ ## block] = { \ + .size = sizeof(struct rkisp1_ext_params_ ## data ## _config), \ + } + +static const struct v4l2_isp_params_block_type_info +rkisp1_ext_params_block_types_info[] = { + RKISP1_PARAMS_BLOCK_INFO(BLS, bls), + RKISP1_PARAMS_BLOCK_INFO(DPCC, dpcc), + RKISP1_PARAMS_BLOCK_INFO(SDG, sdg), + RKISP1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain), + RKISP1_PARAMS_BLOCK_INFO(FLT, flt), + RKISP1_PARAMS_BLOCK_INFO(BDM, bdm), + RKISP1_PARAMS_BLOCK_INFO(CTK, ctk), + RKISP1_PARAMS_BLOCK_INFO(GOC, goc), + RKISP1_PARAMS_BLOCK_INFO(DPF, dpf), + RKISP1_PARAMS_BLOCK_INFO(DPF_STRENGTH, dpf_strength), + RKISP1_PARAMS_BLOCK_INFO(CPROC, cproc), + RKISP1_PARAMS_BLOCK_INFO(IE, ie), + RKISP1_PARAMS_BLOCK_INFO(LSC, lsc), + RKISP1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas), + RKISP1_PARAMS_BLOCK_INFO(HST_MEAS, hst), + RKISP1_PARAMS_BLOCK_INFO(AEC_MEAS, aec), + RKISP1_PARAMS_BLOCK_INFO(AFC_MEAS, afc), + RKISP1_PARAMS_BLOCK_INFO(COMPAND_BLS, compand_bls), + RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve), + RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve), + RKISP1_PARAMS_BLOCK_INFO(WDR, wdr), +}; + +static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) == + ARRAY_SIZE(rkisp1_ext_params_block_types_info)); + static void rkisp1_ext_params_config(struct rkisp1_params *params, struct rkisp1_ext_params_cfg *cfg, u32 block_group_mask) @@ -2646,31 +2659,16 @@ static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params, { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); - size_t header_size = offsetof(struct rkisp1_ext_params_cfg, data); struct rkisp1_ext_params_cfg *cfg = params_buf->cfg; size_t payload_size = vb2_get_plane_payload(vb, 0); struct rkisp1_ext_params_cfg *usr_cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0); - size_t block_offset = 0; - size_t cfg_size; - - /* - * Validate the buffer payload size before copying the parameters. The - * payload has to be smaller than the destination buffer size and larger - * than the header size. - */ - if (payload_size > params->metafmt->buffersize) { - dev_dbg(params->rkisp1->dev, - "Too large buffer payload size %zu\n", payload_size); - return -EINVAL; - } + int ret; - if (payload_size < header_size) { - dev_dbg(params->rkisp1->dev, - "Buffer payload %zu smaller than header size %zu\n", - payload_size, header_size); - return -EINVAL; - } + ret = v4l2_isp_params_validate_buffer_size(params->rkisp1->dev, vb, + params->metafmt->buffersize); + if (ret) + return ret; /* * Copy the parameters buffer to the internal scratch buffer to avoid @@ -2678,71 +2676,10 @@ static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params, */ memcpy(cfg, usr_cfg, payload_size); - /* Only v1 is supported at the moment. */ - if (cfg->version != RKISP1_EXT_PARAM_BUFFER_V1) { - dev_dbg(params->rkisp1->dev, - "Unsupported extensible format version: %u\n", - cfg->version); - return -EINVAL; - } - - /* Validate the size reported in the parameters buffer header. */ - cfg_size = header_size + cfg->data_size; - if (cfg_size != payload_size) { - dev_dbg(params->rkisp1->dev, - "Data size %zu different than buffer payload size %zu\n", - cfg_size, payload_size); - return -EINVAL; - } - - /* Walk the list of parameter blocks and validate them. */ - cfg_size = cfg->data_size; - while (cfg_size >= sizeof(struct rkisp1_ext_params_block_header)) { - const struct rkisp1_ext_params_block_header *block; - const struct rkisp1_ext_params_handler *handler; - - block = (const struct rkisp1_ext_params_block_header *) - &cfg->data[block_offset]; - - if (block->type >= ARRAY_SIZE(rkisp1_ext_params_handlers)) { - dev_dbg(params->rkisp1->dev, - "Invalid parameters block type\n"); - return -EINVAL; - } - - if (block->size > cfg_size) { - dev_dbg(params->rkisp1->dev, - "Premature end of parameters data\n"); - return -EINVAL; - } - - if ((block->flags & (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | - RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) == - (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | - RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) { - dev_dbg(params->rkisp1->dev, - "Invalid parameters block flags\n"); - return -EINVAL; - } - - handler = &rkisp1_ext_params_handlers[block->type]; - if (block->size != handler->size) { - dev_dbg(params->rkisp1->dev, - "Invalid parameters block size\n"); - return -EINVAL; - } - - block_offset += block->size; - cfg_size -= block->size; - } - - if (cfg_size) { - dev_dbg(params->rkisp1->dev, - "Unexpected data after the parameters buffer end\n"); - return -EINVAL; - } - - return 0; + return v4l2_isp_params_validate_buffer(params->rkisp1->dev, vb, + (struct v4l2_isp_params_buffer *)cfg, + rkisp1_ext_params_block_types_info, + ARRAY_SIZE(rkisp1_ext_params_block_types_info)); } static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile index cb86b429cfaa..a77122641d14 100644 --- a/drivers/media/platform/rockchip/rkvdec/Makefile +++ b/drivers/media/platform/rockchip/rkvdec/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o -rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-vp9.o +rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-hevc.o rkvdec-vp9.o diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c new file mode 100644 index 000000000000..eac4ea604949 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c @@ -0,0 +1,1848 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder driver + * + * Copyright (C) 2023 Collabora, Ltd. + * Sebastian Fricke <sebastian.fricke@collabora.com> + */ + +#include <linux/types.h> + +#define RKV_CABAC_TABLE_SIZE 27456 + +/* + * This file is #include from rkvdec-hevc.c and not compiled. + */ +static const u8 rkvdec_hevc_cabac_table[RKV_CABAC_TABLE_SIZE] = { + 0x07, 0x0f, 0x48, 0x58, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f, + 0x68, 0x48, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x40, + 0x40, 0x68, 0x58, 0x60, 0x40, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x60, + 0x60, 0x50, 0x58, 0x50, 0x07, 0x58, 0x68, 0x50, 0x58, 0x68, 0x68, 0x68, 0x68, 0x68, 0x50, + 0x48, 0x68, 0x60, 0x60, 0x50, 0x58, 0x50, 0x07, 0x58, 0x68, 0x50, 0x58, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x50, 0x48, 0x68, 0x48, 0x48, 0x1f, 0x58, 0x68, 0x68, 0x58, 0x60, 0x60, 0x60, + 0x50, 0x50, 0x50, 0x48, 0x58, 0x58, 0x37, 0x07, 0x58, 0x48, 0x58, 0x58, 0x37, 0x07, 0x58, + 0x48, 0x58, 0x58, 0x37, 0x07, 0x58, 0x50, 0x48, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07, + 0x0f, 0x48, 0x68, 0x0f, 0x48, 0x68, 0x40, 0x40, 0x50, 0x50, 0x07, 0x40, 0x50, 0x0f, 0x40, + 0x48, 0x07, 0x40, 0x27, 0x50, 0x48, 0x48, 0x40, 0x0f, 0x50, 0x37, 0x1f, 0x1f, 0x50, 0x37, + 0x40, 0x27, 0x40, 0x07, 0x0f, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0f, 0x47, 0x57, + 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f, 0x66, 0x47, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x00, 0x00, 0x67, 0x57, 0x5e, + 0x00, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x47, 0x5f, 0x5f, 0x4f, 0x57, 0x4f, + 0x07, 0x57, 0x67, 0x4f, 0x57, 0x67, 0x67, 0x67, 0x67, 0x66, 0x4f, 0x47, 0x66, 0x5f, 0x5f, + 0x4f, 0x57, 0x4f, 0x07, 0x57, 0x67, 0x4f, 0x57, 0x67, 0x67, 0x67, 0x67, 0x66, 0x4f, 0x47, + 0x66, 0x46, 0x48, 0x20, 0x57, 0x67, 0x67, 0x57, 0x5f, 0x5f, 0x5e, 0x4f, 0x4f, 0x4f, 0x47, + 0x57, 0x57, 0x37, 0x07, 0x57, 0x47, 0x57, 0x57, 0x37, 0x07, 0x57, 0x47, 0x57, 0x57, 0x37, + 0x07, 0x57, 0x4f, 0x47, 0x1f, 0x1f, 0x0f, 0x10, 0x0f, 0x10, 0x07, 0x10, 0x47, 0x67, 0x10, + 0x47, 0x67, 0x40, 0x40, 0x4f, 0x4e, 0x08, 0x00, 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x01, 0x27, + 0x4e, 0x47, 0x47, 0x00, 0x0f, 0x4f, 0x37, 0x1f, 0x1f, 0x4f, 0x36, 0x00, 0x27, 0x00, 0x07, + 0x10, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0e, 0x47, 0x57, 0x58, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x0e, 0x40, 0x40, 0x40, 0x0e, 0x64, 0x47, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x00, 0x00, 0x66, 0x57, 0x5d, 0x00, 0x1e, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x47, 0x47, 0x5e, 0x5e, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f, + 0x56, 0x66, 0x67, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x64, 0x5e, 0x5e, 0x4e, 0x56, 0x4f, 0x07, + 0x56, 0x66, 0x4f, 0x56, 0x66, 0x67, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x64, 0x45, 0x48, 0x20, + 0x57, 0x66, 0x66, 0x56, 0x5e, 0x5e, 0x5d, 0x4e, 0x4e, 0x4e, 0x46, 0x56, 0x57, 0x36, 0x07, + 0x56, 0x46, 0x56, 0x57, 0x36, 0x07, 0x56, 0x46, 0x56, 0x57, 0x36, 0x07, 0x56, 0x4f, 0x47, + 0x1e, 0x1e, 0x0f, 0x10, 0x0f, 0x10, 0x07, 0x10, 0x47, 0x66, 0x10, 0x47, 0x66, 0x40, 0x40, + 0x4f, 0x4d, 0x08, 0x00, 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x03, 0x27, 0x4d, 0x47, 0x46, 0x01, + 0x0f, 0x4f, 0x36, 0x1f, 0x1e, 0x4f, 0x34, 0x01, 0x26, 0x00, 0x07, 0x10, 0x17, 0x0f, 0x0f, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x0d, 0x47, 0x57, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0e, 0x40, + 0x40, 0x40, 0x0e, 0x62, 0x47, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x07, 0x00, 0x00, 0x65, 0x57, 0x5c, 0x00, 0x1e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x47, 0x47, 0x5d, 0x5d, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f, 0x55, 0x65, 0x67, 0x66, + 0x65, 0x63, 0x4d, 0x46, 0x62, 0x5d, 0x5d, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f, 0x55, + 0x65, 0x67, 0x66, 0x65, 0x63, 0x4d, 0x46, 0x62, 0x44, 0x48, 0x20, 0x57, 0x65, 0x65, 0x56, + 0x5d, 0x5d, 0x5c, 0x4e, 0x4d, 0x4e, 0x45, 0x56, 0x57, 0x36, 0x07, 0x56, 0x45, 0x56, 0x57, + 0x36, 0x07, 0x56, 0x45, 0x56, 0x57, 0x36, 0x07, 0x56, 0x4f, 0x47, 0x1e, 0x1e, 0x0f, 0x10, + 0x0f, 0x10, 0x07, 0x10, 0x47, 0x65, 0x10, 0x47, 0x65, 0x40, 0x40, 0x4f, 0x4c, 0x08, 0x00, + 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x04, 0x27, 0x4c, 0x47, 0x45, 0x01, 0x0f, 0x4f, 0x36, 0x1f, + 0x1e, 0x4f, 0x33, 0x01, 0x25, 0x00, 0x07, 0x10, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x0c, 0x46, 0x56, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0d, 0x40, 0x40, 0x40, 0x0d, 0x60, + 0x46, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01, + 0x64, 0x56, 0x5b, 0x01, 0x1d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x46, 0x46, 0x5c, 0x5c, + 0x4d, 0x55, 0x4e, 0x07, 0x55, 0x65, 0x4e, 0x54, 0x64, 0x66, 0x65, 0x64, 0x61, 0x4c, 0x45, + 0x60, 0x5c, 0x5c, 0x4d, 0x55, 0x4e, 0x07, 0x55, 0x65, 0x4e, 0x54, 0x64, 0x66, 0x65, 0x64, + 0x61, 0x4c, 0x45, 0x60, 0x43, 0x49, 0x21, 0x56, 0x64, 0x64, 0x55, 0x5c, 0x5c, 0x5b, 0x4d, + 0x4c, 0x4d, 0x44, 0x55, 0x56, 0x35, 0x07, 0x55, 0x44, 0x55, 0x56, 0x35, 0x07, 0x55, 0x44, + 0x55, 0x56, 0x35, 0x07, 0x55, 0x4e, 0x46, 0x1d, 0x1d, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11, + 0x46, 0x64, 0x11, 0x46, 0x64, 0x40, 0x40, 0x4e, 0x4b, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46, + 0x07, 0x06, 0x27, 0x4b, 0x46, 0x44, 0x02, 0x0f, 0x4e, 0x35, 0x1e, 0x1d, 0x4e, 0x31, 0x02, + 0x24, 0x01, 0x07, 0x11, 0x16, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0b, 0x46, 0x56, 0x58, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x0c, 0x40, 0x40, 0x40, 0x0c, 0x5e, 0x46, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01, 0x63, 0x56, 0x59, 0x01, + 0x1c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x46, 0x46, 0x5b, 0x5b, 0x4c, 0x54, 0x4e, 0x07, + 0x54, 0x64, 0x4e, 0x53, 0x63, 0x66, 0x64, 0x63, 0x60, 0x4b, 0x44, 0x5e, 0x5b, 0x5b, 0x4c, + 0x54, 0x4e, 0x07, 0x54, 0x64, 0x4e, 0x53, 0x63, 0x66, 0x64, 0x63, 0x60, 0x4b, 0x44, 0x5e, + 0x41, 0x49, 0x21, 0x56, 0x63, 0x63, 0x54, 0x5b, 0x5b, 0x59, 0x4c, 0x4b, 0x4c, 0x43, 0x54, + 0x56, 0x34, 0x07, 0x54, 0x43, 0x54, 0x56, 0x34, 0x07, 0x54, 0x43, 0x54, 0x56, 0x34, 0x07, + 0x54, 0x4e, 0x46, 0x1c, 0x1c, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11, 0x46, 0x63, 0x11, 0x46, + 0x63, 0x40, 0x40, 0x4e, 0x49, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46, 0x07, 0x07, 0x27, 0x49, + 0x46, 0x43, 0x03, 0x0f, 0x4e, 0x34, 0x1e, 0x1c, 0x4e, 0x30, 0x03, 0x23, 0x01, 0x07, 0x11, + 0x16, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0a, 0x46, 0x56, 0x58, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x0c, 0x40, 0x40, 0x40, 0x0c, 0x5c, 0x46, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01, 0x62, 0x56, 0x58, 0x01, 0x1c, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x46, 0x46, 0x5a, 0x5a, 0x4c, 0x54, 0x4e, 0x07, 0x54, 0x64, 0x4e, 0x52, + 0x62, 0x66, 0x64, 0x62, 0x5e, 0x4a, 0x44, 0x5c, 0x5a, 0x5a, 0x4c, 0x54, 0x4e, 0x07, 0x54, + 0x64, 0x4e, 0x52, 0x62, 0x66, 0x64, 0x62, 0x5e, 0x4a, 0x44, 0x5c, 0x40, 0x49, 0x21, 0x56, + 0x62, 0x62, 0x54, 0x5a, 0x5a, 0x58, 0x4c, 0x4a, 0x4c, 0x42, 0x54, 0x56, 0x34, 0x07, 0x54, + 0x42, 0x54, 0x56, 0x34, 0x07, 0x54, 0x42, 0x54, 0x56, 0x34, 0x07, 0x54, 0x4e, 0x46, 0x1c, + 0x1c, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11, 0x46, 0x62, 0x11, 0x46, 0x62, 0x40, 0x40, 0x4e, + 0x48, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46, 0x07, 0x09, 0x27, 0x48, 0x46, 0x42, 0x03, 0x0f, + 0x4e, 0x34, 0x1e, 0x1c, 0x4e, 0x2e, 0x03, 0x22, 0x01, 0x07, 0x11, 0x16, 0x0f, 0x0f, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x09, 0x45, 0x55, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0b, 0x40, 0x40, + 0x40, 0x0b, 0x5a, 0x45, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x07, 0x02, 0x02, 0x61, 0x55, 0x57, 0x02, 0x1b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, + 0x45, 0x59, 0x59, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x51, 0x61, 0x65, 0x63, 0x61, + 0x5d, 0x49, 0x43, 0x5a, 0x59, 0x59, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x51, 0x61, + 0x65, 0x63, 0x61, 0x5d, 0x49, 0x43, 0x5a, 0x00, 0x4a, 0x22, 0x55, 0x61, 0x61, 0x53, 0x59, + 0x59, 0x57, 0x4b, 0x49, 0x4b, 0x41, 0x53, 0x55, 0x33, 0x07, 0x53, 0x41, 0x53, 0x55, 0x33, + 0x07, 0x53, 0x41, 0x53, 0x55, 0x33, 0x07, 0x53, 0x4d, 0x45, 0x1b, 0x1b, 0x0f, 0x12, 0x0f, + 0x12, 0x07, 0x12, 0x45, 0x61, 0x12, 0x45, 0x61, 0x40, 0x40, 0x4d, 0x47, 0x0a, 0x02, 0x4d, + 0x0f, 0x02, 0x45, 0x07, 0x0a, 0x27, 0x47, 0x45, 0x41, 0x04, 0x0f, 0x4d, 0x33, 0x1d, 0x1b, + 0x4d, 0x2d, 0x04, 0x21, 0x02, 0x07, 0x12, 0x15, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, + 0x45, 0x55, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0a, 0x40, 0x40, 0x40, 0x0a, 0x59, 0x45, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x02, 0x02, 0x60, + 0x55, 0x56, 0x02, 0x1a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x45, 0x58, 0x58, 0x4b, + 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x50, 0x60, 0x65, 0x63, 0x60, 0x5b, 0x48, 0x43, 0x59, + 0x58, 0x58, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x50, 0x60, 0x65, 0x63, 0x60, 0x5b, + 0x48, 0x43, 0x59, 0x01, 0x4a, 0x22, 0x55, 0x60, 0x60, 0x53, 0x58, 0x58, 0x56, 0x4b, 0x48, + 0x4b, 0x40, 0x53, 0x55, 0x32, 0x07, 0x53, 0x40, 0x53, 0x55, 0x32, 0x07, 0x53, 0x40, 0x53, + 0x55, 0x32, 0x07, 0x53, 0x4d, 0x45, 0x1a, 0x1a, 0x0f, 0x12, 0x0f, 0x12, 0x07, 0x12, 0x45, + 0x60, 0x12, 0x45, 0x60, 0x40, 0x40, 0x4d, 0x46, 0x0a, 0x02, 0x4d, 0x0f, 0x02, 0x45, 0x07, + 0x0c, 0x27, 0x46, 0x45, 0x40, 0x04, 0x0f, 0x4d, 0x32, 0x1d, 0x1a, 0x4d, 0x2b, 0x04, 0x20, + 0x02, 0x07, 0x12, 0x15, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x45, 0x55, 0x58, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x0a, 0x40, 0x40, 0x40, 0x0a, 0x57, 0x45, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x02, 0x02, 0x5f, 0x55, 0x54, 0x02, 0x1a, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x45, 0x57, 0x57, 0x4a, 0x52, 0x4d, 0x07, 0x52, + 0x62, 0x4d, 0x4f, 0x5f, 0x65, 0x62, 0x5f, 0x59, 0x47, 0x42, 0x57, 0x57, 0x57, 0x4a, 0x52, + 0x4d, 0x07, 0x52, 0x62, 0x4d, 0x4f, 0x5f, 0x65, 0x62, 0x5f, 0x59, 0x47, 0x42, 0x57, 0x03, + 0x4a, 0x22, 0x55, 0x5f, 0x5f, 0x52, 0x57, 0x57, 0x54, 0x4a, 0x47, 0x4a, 0x00, 0x52, 0x55, + 0x32, 0x07, 0x52, 0x00, 0x52, 0x55, 0x32, 0x07, 0x52, 0x00, 0x52, 0x55, 0x32, 0x07, 0x52, + 0x4d, 0x45, 0x1a, 0x1a, 0x0f, 0x12, 0x0f, 0x12, 0x07, 0x12, 0x45, 0x5f, 0x12, 0x45, 0x5f, + 0x40, 0x40, 0x4d, 0x44, 0x0a, 0x02, 0x4d, 0x0f, 0x02, 0x45, 0x07, 0x0e, 0x27, 0x44, 0x45, + 0x00, 0x05, 0x0f, 0x4d, 0x32, 0x1d, 0x1a, 0x4d, 0x29, 0x05, 0x1f, 0x02, 0x07, 0x12, 0x15, + 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x44, 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x09, 0x40, 0x40, 0x40, 0x09, 0x55, 0x44, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x07, 0x03, 0x03, 0x5e, 0x54, 0x53, 0x03, 0x19, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x44, 0x44, 0x56, 0x56, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4e, 0x5e, + 0x64, 0x61, 0x5e, 0x58, 0x46, 0x41, 0x55, 0x56, 0x56, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, + 0x4c, 0x4e, 0x5e, 0x64, 0x61, 0x5e, 0x58, 0x46, 0x41, 0x55, 0x04, 0x4b, 0x23, 0x54, 0x5e, + 0x5e, 0x51, 0x56, 0x56, 0x53, 0x49, 0x46, 0x49, 0x01, 0x51, 0x54, 0x31, 0x07, 0x51, 0x01, + 0x51, 0x54, 0x31, 0x07, 0x51, 0x01, 0x51, 0x54, 0x31, 0x07, 0x51, 0x4c, 0x44, 0x19, 0x19, + 0x0f, 0x13, 0x0f, 0x13, 0x07, 0x13, 0x44, 0x5e, 0x13, 0x44, 0x5e, 0x40, 0x40, 0x4c, 0x43, + 0x0b, 0x03, 0x4c, 0x0f, 0x03, 0x44, 0x07, 0x0f, 0x27, 0x43, 0x44, 0x01, 0x06, 0x0f, 0x4c, + 0x31, 0x1c, 0x19, 0x4c, 0x28, 0x06, 0x1e, 0x03, 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x05, 0x44, 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x09, 0x40, 0x40, 0x40, + 0x09, 0x53, 0x44, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, + 0x03, 0x03, 0x5d, 0x54, 0x52, 0x03, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, + 0x55, 0x55, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4d, 0x5d, 0x64, 0x61, 0x5d, 0x56, + 0x45, 0x41, 0x53, 0x55, 0x55, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4d, 0x5d, 0x64, + 0x61, 0x5d, 0x56, 0x45, 0x41, 0x53, 0x05, 0x4b, 0x23, 0x54, 0x5d, 0x5d, 0x51, 0x55, 0x55, + 0x52, 0x49, 0x45, 0x49, 0x02, 0x51, 0x54, 0x31, 0x07, 0x51, 0x02, 0x51, 0x54, 0x31, 0x07, + 0x51, 0x02, 0x51, 0x54, 0x31, 0x07, 0x51, 0x4c, 0x44, 0x19, 0x19, 0x0f, 0x13, 0x0f, 0x13, + 0x07, 0x13, 0x44, 0x5d, 0x13, 0x44, 0x5d, 0x40, 0x40, 0x4c, 0x42, 0x0b, 0x03, 0x4c, 0x0f, + 0x03, 0x44, 0x07, 0x11, 0x27, 0x42, 0x44, 0x02, 0x06, 0x0f, 0x4c, 0x31, 0x1c, 0x19, 0x4c, + 0x26, 0x06, 0x1d, 0x03, 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x44, + 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x08, 0x40, 0x40, 0x40, 0x08, 0x51, 0x44, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x03, 0x03, 0x5c, 0x54, + 0x51, 0x03, 0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x54, 0x54, 0x48, 0x50, + 0x4c, 0x07, 0x50, 0x60, 0x4c, 0x4c, 0x5c, 0x64, 0x60, 0x5c, 0x55, 0x44, 0x40, 0x51, 0x54, + 0x54, 0x48, 0x50, 0x4c, 0x07, 0x50, 0x60, 0x4c, 0x4c, 0x5c, 0x64, 0x60, 0x5c, 0x55, 0x44, + 0x40, 0x51, 0x06, 0x4b, 0x23, 0x54, 0x5c, 0x5c, 0x50, 0x54, 0x54, 0x51, 0x48, 0x44, 0x48, + 0x03, 0x50, 0x54, 0x30, 0x07, 0x50, 0x03, 0x50, 0x54, 0x30, 0x07, 0x50, 0x03, 0x50, 0x54, + 0x30, 0x07, 0x50, 0x4c, 0x44, 0x18, 0x18, 0x0f, 0x13, 0x0f, 0x13, 0x07, 0x13, 0x44, 0x5c, + 0x13, 0x44, 0x5c, 0x40, 0x40, 0x4c, 0x41, 0x0b, 0x03, 0x4c, 0x0f, 0x03, 0x44, 0x07, 0x12, + 0x27, 0x41, 0x44, 0x03, 0x07, 0x0f, 0x4c, 0x30, 0x1c, 0x18, 0x4c, 0x25, 0x07, 0x1c, 0x03, + 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x43, 0x53, 0x58, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x07, 0x40, 0x40, 0x40, 0x07, 0x4f, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04, 0x04, 0x5b, 0x53, 0x4f, 0x04, 0x17, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x53, 0x53, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f, + 0x4b, 0x4b, 0x5b, 0x63, 0x5f, 0x5b, 0x53, 0x43, 0x00, 0x4f, 0x53, 0x53, 0x47, 0x4f, 0x4b, + 0x07, 0x4f, 0x5f, 0x4b, 0x4b, 0x5b, 0x63, 0x5f, 0x5b, 0x53, 0x43, 0x00, 0x4f, 0x08, 0x4c, + 0x24, 0x53, 0x5b, 0x5b, 0x4f, 0x53, 0x53, 0x4f, 0x47, 0x43, 0x47, 0x04, 0x4f, 0x53, 0x2f, + 0x07, 0x4f, 0x04, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x04, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x4b, + 0x43, 0x17, 0x17, 0x0f, 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x5b, 0x14, 0x43, 0x5b, 0x40, + 0x40, 0x4b, 0x00, 0x0c, 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x14, 0x27, 0x00, 0x43, 0x04, + 0x08, 0x0f, 0x4b, 0x2f, 0x1b, 0x17, 0x4b, 0x23, 0x08, 0x1b, 0x04, 0x07, 0x14, 0x13, 0x0f, + 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x43, 0x53, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, + 0x40, 0x40, 0x40, 0x07, 0x4d, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x07, 0x04, 0x04, 0x5a, 0x53, 0x4e, 0x04, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x43, 0x43, 0x52, 0x52, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f, 0x4b, 0x4a, 0x5a, 0x63, + 0x5f, 0x5a, 0x52, 0x42, 0x00, 0x4d, 0x52, 0x52, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f, 0x4b, + 0x4a, 0x5a, 0x63, 0x5f, 0x5a, 0x52, 0x42, 0x00, 0x4d, 0x09, 0x4c, 0x24, 0x53, 0x5a, 0x5a, + 0x4f, 0x52, 0x52, 0x4e, 0x47, 0x42, 0x47, 0x05, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x05, 0x4f, + 0x53, 0x2f, 0x07, 0x4f, 0x05, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x4b, 0x43, 0x17, 0x17, 0x0f, + 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x5a, 0x14, 0x43, 0x5a, 0x40, 0x40, 0x4b, 0x01, 0x0c, + 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x15, 0x27, 0x01, 0x43, 0x05, 0x08, 0x0f, 0x4b, 0x2f, + 0x1b, 0x17, 0x4b, 0x22, 0x08, 0x1a, 0x04, 0x07, 0x14, 0x13, 0x0f, 0x0f, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x01, 0x43, 0x53, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x40, 0x40, 0x40, 0x06, + 0x4b, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04, + 0x04, 0x59, 0x53, 0x4d, 0x04, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x51, + 0x51, 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x50, 0x41, + 0x01, 0x4b, 0x51, 0x51, 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, + 0x59, 0x50, 0x41, 0x01, 0x4b, 0x0a, 0x4c, 0x24, 0x53, 0x59, 0x59, 0x4e, 0x51, 0x51, 0x4d, + 0x46, 0x41, 0x46, 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e, + 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e, 0x4b, 0x43, 0x16, 0x16, 0x0f, 0x14, 0x0f, 0x14, 0x07, + 0x14, 0x43, 0x59, 0x14, 0x43, 0x59, 0x40, 0x40, 0x4b, 0x02, 0x0c, 0x04, 0x4b, 0x0f, 0x04, + 0x43, 0x07, 0x17, 0x27, 0x02, 0x43, 0x06, 0x09, 0x0f, 0x4b, 0x2e, 0x1b, 0x16, 0x4b, 0x20, + 0x09, 0x19, 0x04, 0x07, 0x14, 0x13, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x43, 0x53, + 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x05, 0x4a, 0x43, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04, 0x04, 0x59, 0x53, 0x4c, + 0x04, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x51, 0x51, 0x46, 0x4e, 0x4b, + 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x4f, 0x41, 0x01, 0x4a, 0x51, 0x51, + 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x4f, 0x41, 0x01, + 0x4a, 0x0b, 0x4d, 0x24, 0x53, 0x59, 0x59, 0x4e, 0x51, 0x51, 0x4c, 0x46, 0x41, 0x46, 0x06, + 0x4e, 0x53, 0x2d, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2d, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2d, + 0x07, 0x4e, 0x4b, 0x43, 0x15, 0x15, 0x0f, 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x59, 0x14, + 0x43, 0x59, 0x40, 0x40, 0x4b, 0x03, 0x0c, 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x18, 0x27, + 0x03, 0x43, 0x06, 0x09, 0x0f, 0x4b, 0x2d, 0x1a, 0x15, 0x4b, 0x1e, 0x09, 0x18, 0x04, 0x07, + 0x14, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x05, 0x48, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x05, 0x05, 0x58, 0x52, 0x4a, 0x05, 0x15, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x50, 0x50, 0x45, 0x4d, 0x4a, 0x07, 0x4d, 0x5d, 0x4a, + 0x48, 0x58, 0x62, 0x5d, 0x58, 0x4d, 0x40, 0x02, 0x48, 0x50, 0x50, 0x45, 0x4d, 0x4a, 0x07, + 0x4d, 0x5d, 0x4a, 0x48, 0x58, 0x62, 0x5d, 0x58, 0x4d, 0x40, 0x02, 0x48, 0x0d, 0x4d, 0x25, + 0x52, 0x58, 0x58, 0x4d, 0x50, 0x50, 0x4a, 0x45, 0x40, 0x45, 0x07, 0x4d, 0x52, 0x2d, 0x07, + 0x4d, 0x07, 0x4d, 0x52, 0x2d, 0x07, 0x4d, 0x07, 0x4d, 0x52, 0x2d, 0x07, 0x4d, 0x4a, 0x42, + 0x15, 0x15, 0x0f, 0x15, 0x0f, 0x15, 0x07, 0x15, 0x42, 0x58, 0x15, 0x42, 0x58, 0x40, 0x40, + 0x4a, 0x05, 0x0d, 0x05, 0x4a, 0x0f, 0x05, 0x42, 0x07, 0x1a, 0x27, 0x05, 0x42, 0x07, 0x0a, + 0x0f, 0x4a, 0x2d, 0x1a, 0x15, 0x4a, 0x1d, 0x0a, 0x18, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x40, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x40, + 0x40, 0x40, 0x04, 0x46, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x07, 0x05, 0x05, 0x57, 0x52, 0x49, 0x05, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x4f, 0x4f, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x47, 0x57, 0x62, 0x5c, + 0x57, 0x4b, 0x00, 0x03, 0x46, 0x4f, 0x4f, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x47, + 0x57, 0x62, 0x5c, 0x57, 0x4b, 0x00, 0x03, 0x46, 0x0e, 0x4d, 0x25, 0x52, 0x57, 0x57, 0x4c, + 0x4f, 0x4f, 0x49, 0x44, 0x00, 0x44, 0x08, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x08, 0x4c, 0x52, + 0x2c, 0x07, 0x4c, 0x08, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x4a, 0x42, 0x14, 0x14, 0x0f, 0x15, + 0x0f, 0x15, 0x07, 0x15, 0x42, 0x57, 0x15, 0x42, 0x57, 0x40, 0x40, 0x4a, 0x06, 0x0d, 0x05, + 0x4a, 0x0f, 0x05, 0x42, 0x07, 0x1c, 0x27, 0x06, 0x42, 0x08, 0x0b, 0x0f, 0x4a, 0x2c, 0x1a, + 0x14, 0x4a, 0x1b, 0x0b, 0x17, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x41, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x40, 0x40, 0x40, 0x04, 0x44, + 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x05, 0x05, + 0x56, 0x52, 0x48, 0x05, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4e, 0x4e, + 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x46, 0x56, 0x62, 0x5c, 0x56, 0x4a, 0x01, 0x03, + 0x44, 0x4e, 0x4e, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x46, 0x56, 0x62, 0x5c, 0x56, + 0x4a, 0x01, 0x03, 0x44, 0x0f, 0x4d, 0x25, 0x52, 0x56, 0x56, 0x4c, 0x4e, 0x4e, 0x48, 0x44, + 0x01, 0x44, 0x09, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x09, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x09, + 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x4a, 0x42, 0x14, 0x14, 0x0f, 0x15, 0x0f, 0x15, 0x07, 0x15, + 0x42, 0x56, 0x15, 0x42, 0x56, 0x40, 0x40, 0x4a, 0x07, 0x0d, 0x05, 0x4a, 0x0f, 0x05, 0x42, + 0x07, 0x1d, 0x27, 0x07, 0x42, 0x09, 0x0b, 0x0f, 0x4a, 0x2c, 0x1a, 0x14, 0x4a, 0x1a, 0x0b, + 0x16, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x41, 0x51, 0x58, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x03, 0x40, 0x40, 0x40, 0x03, 0x42, 0x41, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x06, 0x06, 0x55, 0x51, 0x47, 0x06, + 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4d, 0x4d, 0x43, 0x4b, 0x49, 0x07, + 0x4b, 0x5b, 0x49, 0x45, 0x55, 0x61, 0x5b, 0x55, 0x48, 0x02, 0x04, 0x42, 0x4d, 0x4d, 0x43, + 0x4b, 0x49, 0x07, 0x4b, 0x5b, 0x49, 0x45, 0x55, 0x61, 0x5b, 0x55, 0x48, 0x02, 0x04, 0x42, + 0x10, 0x4e, 0x26, 0x51, 0x55, 0x55, 0x4b, 0x4d, 0x4d, 0x47, 0x43, 0x02, 0x43, 0x0a, 0x4b, + 0x51, 0x2b, 0x07, 0x4b, 0x0a, 0x4b, 0x51, 0x2b, 0x07, 0x4b, 0x0a, 0x4b, 0x51, 0x2b, 0x07, + 0x4b, 0x49, 0x41, 0x13, 0x13, 0x0f, 0x16, 0x0f, 0x16, 0x07, 0x16, 0x41, 0x55, 0x16, 0x41, + 0x55, 0x40, 0x40, 0x49, 0x08, 0x0e, 0x06, 0x49, 0x0f, 0x06, 0x41, 0x07, 0x1f, 0x27, 0x08, + 0x41, 0x0a, 0x0c, 0x0f, 0x49, 0x2b, 0x19, 0x13, 0x49, 0x18, 0x0c, 0x15, 0x06, 0x07, 0x16, + 0x11, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x43, 0x41, 0x51, 0x58, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x02, 0x40, 0x40, 0x40, 0x02, 0x40, 0x41, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x07, 0x06, 0x06, 0x54, 0x51, 0x45, 0x06, 0x12, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x41, 0x41, 0x4c, 0x4c, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x44, + 0x54, 0x61, 0x5a, 0x54, 0x47, 0x03, 0x05, 0x40, 0x4c, 0x4c, 0x42, 0x4a, 0x49, 0x07, 0x4a, + 0x5a, 0x49, 0x44, 0x54, 0x61, 0x5a, 0x54, 0x47, 0x03, 0x05, 0x40, 0x12, 0x4e, 0x26, 0x51, + 0x54, 0x54, 0x4a, 0x4c, 0x4c, 0x45, 0x42, 0x03, 0x42, 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a, + 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x49, 0x41, 0x12, + 0x12, 0x0f, 0x16, 0x0f, 0x16, 0x07, 0x16, 0x41, 0x54, 0x16, 0x41, 0x54, 0x40, 0x40, 0x49, + 0x0a, 0x0e, 0x06, 0x49, 0x0f, 0x06, 0x41, 0x07, 0x20, 0x27, 0x0a, 0x41, 0x0b, 0x0d, 0x0f, + 0x49, 0x2a, 0x19, 0x12, 0x49, 0x17, 0x0d, 0x14, 0x06, 0x07, 0x16, 0x11, 0x0f, 0x0f, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x44, 0x41, 0x51, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, 0x40, + 0x40, 0x02, 0x01, 0x41, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x07, 0x06, 0x06, 0x53, 0x51, 0x44, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, + 0x41, 0x4b, 0x4b, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x43, 0x53, 0x61, 0x5a, 0x53, + 0x45, 0x04, 0x05, 0x01, 0x4b, 0x4b, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x43, 0x53, + 0x61, 0x5a, 0x53, 0x45, 0x04, 0x05, 0x01, 0x13, 0x4e, 0x26, 0x51, 0x53, 0x53, 0x4a, 0x4b, + 0x4b, 0x44, 0x42, 0x04, 0x42, 0x0c, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x0c, 0x4a, 0x51, 0x2a, + 0x07, 0x4a, 0x0c, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x49, 0x41, 0x12, 0x12, 0x0f, 0x16, 0x0f, + 0x16, 0x07, 0x16, 0x41, 0x53, 0x16, 0x41, 0x53, 0x40, 0x40, 0x49, 0x0b, 0x0e, 0x06, 0x49, + 0x0f, 0x06, 0x41, 0x07, 0x22, 0x27, 0x0b, 0x41, 0x0c, 0x0d, 0x0f, 0x49, 0x2a, 0x19, 0x12, + 0x49, 0x15, 0x0d, 0x13, 0x06, 0x07, 0x16, 0x11, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x45, + 0x40, 0x50, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x40, 0x40, 0x40, 0x01, 0x03, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x52, + 0x50, 0x43, 0x07, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x41, + 0x49, 0x48, 0x07, 0x49, 0x59, 0x48, 0x42, 0x52, 0x60, 0x59, 0x52, 0x44, 0x05, 0x06, 0x03, + 0x4a, 0x4a, 0x41, 0x49, 0x48, 0x07, 0x49, 0x59, 0x48, 0x42, 0x52, 0x60, 0x59, 0x52, 0x44, + 0x05, 0x06, 0x03, 0x14, 0x4f, 0x27, 0x50, 0x52, 0x52, 0x49, 0x4a, 0x4a, 0x43, 0x41, 0x05, + 0x41, 0x0d, 0x49, 0x50, 0x29, 0x07, 0x49, 0x0d, 0x49, 0x50, 0x29, 0x07, 0x49, 0x0d, 0x49, + 0x50, 0x29, 0x07, 0x49, 0x48, 0x40, 0x11, 0x11, 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40, + 0x52, 0x17, 0x40, 0x52, 0x40, 0x40, 0x48, 0x0c, 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07, + 0x23, 0x27, 0x0c, 0x40, 0x0d, 0x0e, 0x0f, 0x48, 0x29, 0x18, 0x11, 0x48, 0x14, 0x0e, 0x12, + 0x07, 0x07, 0x17, 0x10, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x46, 0x40, 0x50, 0x58, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x40, 0x40, 0x40, 0x00, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x51, 0x50, 0x42, 0x07, 0x10, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x41, 0x49, 0x48, 0x07, 0x49, + 0x59, 0x48, 0x41, 0x51, 0x60, 0x59, 0x51, 0x42, 0x06, 0x06, 0x04, 0x49, 0x49, 0x41, 0x49, + 0x48, 0x07, 0x49, 0x59, 0x48, 0x41, 0x51, 0x60, 0x59, 0x51, 0x42, 0x06, 0x06, 0x04, 0x15, + 0x4f, 0x27, 0x50, 0x51, 0x51, 0x49, 0x49, 0x49, 0x42, 0x41, 0x06, 0x41, 0x0e, 0x49, 0x50, + 0x28, 0x07, 0x49, 0x0e, 0x49, 0x50, 0x28, 0x07, 0x49, 0x0e, 0x49, 0x50, 0x28, 0x07, 0x49, + 0x48, 0x40, 0x10, 0x10, 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40, 0x51, 0x17, 0x40, 0x51, + 0x40, 0x40, 0x48, 0x0d, 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07, 0x25, 0x27, 0x0d, 0x40, + 0x0e, 0x0e, 0x0f, 0x48, 0x28, 0x18, 0x10, 0x48, 0x12, 0x0e, 0x11, 0x07, 0x07, 0x17, 0x10, + 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x40, 0x50, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x40, 0x40, 0x40, 0x00, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x50, 0x50, 0x40, 0x07, 0x10, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x07, 0x48, 0x58, 0x48, 0x40, 0x50, + 0x60, 0x58, 0x50, 0x40, 0x07, 0x07, 0x06, 0x48, 0x48, 0x40, 0x48, 0x48, 0x07, 0x48, 0x58, + 0x48, 0x40, 0x50, 0x60, 0x58, 0x50, 0x40, 0x07, 0x07, 0x06, 0x17, 0x4f, 0x27, 0x50, 0x50, + 0x50, 0x48, 0x48, 0x48, 0x40, 0x40, 0x07, 0x40, 0x0f, 0x48, 0x50, 0x28, 0x07, 0x48, 0x0f, + 0x48, 0x50, 0x28, 0x07, 0x48, 0x0f, 0x48, 0x50, 0x28, 0x07, 0x48, 0x48, 0x40, 0x10, 0x10, + 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40, 0x50, 0x17, 0x40, 0x50, 0x40, 0x40, 0x48, 0x0f, + 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07, 0x27, 0x27, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x48, + 0x28, 0x18, 0x10, 0x48, 0x10, 0x0f, 0x10, 0x07, 0x07, 0x17, 0x10, 0x0f, 0x0f, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x48, 0x00, 0x4f, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x08, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, + 0x08, 0x08, 0x4f, 0x4f, 0x00, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x47, 0x47, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x00, 0x4f, 0x5f, 0x57, 0x4f, 0x00, + 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x00, 0x4f, 0x5f, + 0x57, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x18, 0x50, 0x28, 0x4f, 0x4f, 0x4f, 0x47, 0x47, 0x47, + 0x00, 0x00, 0x08, 0x00, 0x10, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x10, 0x47, 0x4f, 0x27, 0x07, + 0x47, 0x10, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x47, 0x00, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, + 0x07, 0x18, 0x00, 0x4f, 0x18, 0x00, 0x4f, 0x40, 0x40, 0x47, 0x10, 0x10, 0x08, 0x47, 0x0f, + 0x08, 0x00, 0x07, 0x28, 0x27, 0x10, 0x00, 0x10, 0x10, 0x0f, 0x47, 0x27, 0x17, 0x0f, 0x47, + 0x0f, 0x10, 0x0f, 0x08, 0x07, 0x18, 0x0f, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x49, 0x00, + 0x4f, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0a, 0x00, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x08, 0x08, 0x4e, 0x4f, + 0x01, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x46, 0x00, 0x47, + 0x47, 0x07, 0x47, 0x57, 0x47, 0x01, 0x4e, 0x5f, 0x57, 0x4e, 0x02, 0x09, 0x08, 0x0a, 0x46, + 0x46, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x01, 0x4e, 0x5f, 0x57, 0x4e, 0x02, 0x09, + 0x08, 0x0a, 0x19, 0x50, 0x28, 0x4f, 0x4e, 0x4e, 0x47, 0x46, 0x46, 0x01, 0x00, 0x09, 0x00, + 0x11, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x11, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x11, 0x47, 0x4f, + 0x27, 0x07, 0x47, 0x47, 0x00, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x07, 0x18, 0x00, 0x4e, + 0x18, 0x00, 0x4e, 0x40, 0x40, 0x47, 0x11, 0x10, 0x08, 0x47, 0x0f, 0x08, 0x00, 0x07, 0x2a, + 0x27, 0x11, 0x00, 0x11, 0x10, 0x0f, 0x47, 0x27, 0x17, 0x0f, 0x47, 0x0d, 0x10, 0x0e, 0x08, + 0x07, 0x18, 0x0f, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4a, 0x00, 0x4f, 0x58, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x41, 0x40, 0x40, 0x40, 0x41, 0x0c, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x08, 0x08, 0x4d, 0x4f, 0x02, 0x08, 0x0e, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x45, 0x45, 0x01, 0x46, 0x47, 0x07, 0x46, 0x56, + 0x47, 0x02, 0x4d, 0x5f, 0x56, 0x4d, 0x03, 0x0a, 0x09, 0x0c, 0x45, 0x45, 0x01, 0x46, 0x47, + 0x07, 0x46, 0x56, 0x47, 0x02, 0x4d, 0x5f, 0x56, 0x4d, 0x03, 0x0a, 0x09, 0x0c, 0x1a, 0x50, + 0x28, 0x4f, 0x4d, 0x4d, 0x46, 0x45, 0x45, 0x02, 0x01, 0x0a, 0x01, 0x12, 0x46, 0x4f, 0x26, + 0x07, 0x46, 0x12, 0x46, 0x4f, 0x26, 0x07, 0x46, 0x12, 0x46, 0x4f, 0x26, 0x07, 0x46, 0x47, + 0x00, 0x0e, 0x0e, 0x0f, 0x18, 0x0f, 0x18, 0x07, 0x18, 0x00, 0x4d, 0x18, 0x00, 0x4d, 0x40, + 0x40, 0x47, 0x12, 0x10, 0x08, 0x47, 0x0f, 0x08, 0x00, 0x07, 0x2b, 0x27, 0x12, 0x00, 0x12, + 0x11, 0x0f, 0x47, 0x26, 0x17, 0x0e, 0x47, 0x0c, 0x11, 0x0d, 0x08, 0x07, 0x18, 0x0f, 0x0f, + 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x4b, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, + 0x40, 0x40, 0x40, 0x42, 0x0e, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x07, 0x09, 0x09, 0x4c, 0x4e, 0x04, 0x09, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x01, 0x01, 0x44, 0x44, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x03, 0x4c, 0x5e, + 0x55, 0x4c, 0x05, 0x0b, 0x0a, 0x0e, 0x44, 0x44, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, + 0x03, 0x4c, 0x5e, 0x55, 0x4c, 0x05, 0x0b, 0x0a, 0x0e, 0x1c, 0x51, 0x29, 0x4e, 0x4c, 0x4c, + 0x45, 0x44, 0x44, 0x04, 0x02, 0x0b, 0x02, 0x13, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x13, 0x45, + 0x4e, 0x25, 0x07, 0x45, 0x13, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x46, 0x01, 0x0d, 0x0d, 0x0f, + 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4c, 0x19, 0x01, 0x4c, 0x40, 0x40, 0x46, 0x14, 0x11, + 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x2d, 0x27, 0x14, 0x01, 0x13, 0x12, 0x0f, 0x46, 0x25, + 0x16, 0x0d, 0x46, 0x0a, 0x12, 0x0c, 0x09, 0x07, 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x4c, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x42, + 0x10, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09, + 0x09, 0x4b, 0x4e, 0x05, 0x09, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x43, + 0x43, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x04, 0x4b, 0x5e, 0x55, 0x4b, 0x06, 0x0c, + 0x0a, 0x10, 0x43, 0x43, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x04, 0x4b, 0x5e, 0x55, + 0x4b, 0x06, 0x0c, 0x0a, 0x10, 0x1d, 0x51, 0x29, 0x4e, 0x4b, 0x4b, 0x45, 0x43, 0x43, 0x05, + 0x02, 0x0c, 0x02, 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45, + 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x46, 0x01, 0x0d, 0x0d, 0x0f, 0x19, 0x0f, 0x19, 0x07, + 0x19, 0x01, 0x4b, 0x19, 0x01, 0x4b, 0x40, 0x40, 0x46, 0x15, 0x11, 0x09, 0x46, 0x0f, 0x09, + 0x01, 0x07, 0x2e, 0x27, 0x15, 0x01, 0x14, 0x12, 0x0f, 0x46, 0x25, 0x16, 0x0d, 0x46, 0x09, + 0x12, 0x0b, 0x09, 0x07, 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4d, 0x01, 0x4e, + 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x40, 0x40, 0x40, 0x43, 0x12, 0x01, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09, 0x09, 0x4a, 0x4e, 0x06, + 0x09, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x42, 0x42, 0x03, 0x44, 0x46, + 0x07, 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x08, 0x0d, 0x0b, 0x12, 0x42, 0x42, + 0x03, 0x44, 0x46, 0x07, 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x08, 0x0d, 0x0b, + 0x12, 0x1e, 0x51, 0x29, 0x4e, 0x4a, 0x4a, 0x44, 0x42, 0x42, 0x06, 0x03, 0x0d, 0x03, 0x15, + 0x44, 0x4e, 0x24, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x24, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x24, + 0x07, 0x44, 0x46, 0x01, 0x0c, 0x0c, 0x0f, 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4a, 0x19, + 0x01, 0x4a, 0x40, 0x40, 0x46, 0x16, 0x11, 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x30, 0x27, + 0x16, 0x01, 0x15, 0x13, 0x0f, 0x46, 0x24, 0x16, 0x0c, 0x46, 0x07, 0x13, 0x0a, 0x09, 0x07, + 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4e, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x44, 0x40, 0x40, 0x40, 0x44, 0x13, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09, 0x09, 0x4a, 0x4e, 0x07, 0x09, 0x0b, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x42, 0x42, 0x03, 0x44, 0x46, 0x07, 0x44, 0x54, 0x46, + 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x09, 0x0d, 0x0b, 0x13, 0x42, 0x42, 0x03, 0x44, 0x46, 0x07, + 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x09, 0x0d, 0x0b, 0x13, 0x1f, 0x52, 0x29, + 0x4e, 0x4a, 0x4a, 0x44, 0x42, 0x42, 0x07, 0x03, 0x0d, 0x03, 0x15, 0x44, 0x4e, 0x23, 0x07, + 0x44, 0x15, 0x44, 0x4e, 0x23, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x23, 0x07, 0x44, 0x46, 0x01, + 0x0b, 0x0b, 0x0f, 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4a, 0x19, 0x01, 0x4a, 0x40, 0x40, + 0x46, 0x17, 0x11, 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x31, 0x27, 0x17, 0x01, 0x15, 0x13, + 0x0f, 0x46, 0x23, 0x15, 0x0b, 0x46, 0x05, 0x13, 0x09, 0x09, 0x07, 0x19, 0x0d, 0x0f, 0x0f, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x4e, 0x02, 0x4d, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x40, + 0x40, 0x40, 0x44, 0x15, 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x07, 0x0a, 0x0a, 0x49, 0x4d, 0x09, 0x0a, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x02, 0x02, 0x41, 0x41, 0x04, 0x43, 0x45, 0x07, 0x43, 0x53, 0x45, 0x06, 0x49, 0x5d, 0x53, + 0x49, 0x0b, 0x0e, 0x0c, 0x15, 0x41, 0x41, 0x04, 0x43, 0x45, 0x07, 0x43, 0x53, 0x45, 0x06, + 0x49, 0x5d, 0x53, 0x49, 0x0b, 0x0e, 0x0c, 0x15, 0x21, 0x52, 0x2a, 0x4d, 0x49, 0x49, 0x43, + 0x41, 0x41, 0x09, 0x04, 0x0e, 0x04, 0x16, 0x43, 0x4d, 0x23, 0x07, 0x43, 0x16, 0x43, 0x4d, + 0x23, 0x07, 0x43, 0x16, 0x43, 0x4d, 0x23, 0x07, 0x43, 0x45, 0x02, 0x0b, 0x0b, 0x0f, 0x1a, + 0x0f, 0x1a, 0x07, 0x1a, 0x02, 0x49, 0x1a, 0x02, 0x49, 0x40, 0x40, 0x45, 0x19, 0x12, 0x0a, + 0x45, 0x0f, 0x0a, 0x02, 0x07, 0x33, 0x27, 0x19, 0x02, 0x16, 0x14, 0x0f, 0x45, 0x23, 0x15, + 0x0b, 0x45, 0x04, 0x14, 0x09, 0x0a, 0x07, 0x1a, 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x4f, 0x02, 0x4d, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x40, 0x40, 0x40, 0x45, 0x17, + 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a, + 0x48, 0x4d, 0x0a, 0x0a, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x40, 0x40, + 0x05, 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x07, 0x48, 0x5d, 0x52, 0x48, 0x0d, 0x0f, 0x0d, + 0x17, 0x40, 0x40, 0x05, 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x07, 0x48, 0x5d, 0x52, 0x48, + 0x0d, 0x0f, 0x0d, 0x17, 0x22, 0x52, 0x2a, 0x4d, 0x48, 0x48, 0x42, 0x40, 0x40, 0x0a, 0x05, + 0x0f, 0x05, 0x17, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x17, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x17, + 0x42, 0x4d, 0x22, 0x07, 0x42, 0x45, 0x02, 0x0a, 0x0a, 0x0f, 0x1a, 0x0f, 0x1a, 0x07, 0x1a, + 0x02, 0x48, 0x1a, 0x02, 0x48, 0x40, 0x40, 0x45, 0x1a, 0x12, 0x0a, 0x45, 0x0f, 0x0a, 0x02, + 0x07, 0x35, 0x27, 0x1a, 0x02, 0x17, 0x15, 0x0f, 0x45, 0x22, 0x15, 0x0a, 0x45, 0x02, 0x15, + 0x08, 0x0a, 0x07, 0x1a, 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x50, 0x02, 0x4d, 0x58, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x40, 0x40, 0x40, 0x45, 0x19, 0x02, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a, 0x47, 0x4d, 0x0b, 0x0a, + 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x00, 0x00, 0x05, 0x42, 0x45, 0x07, + 0x42, 0x52, 0x45, 0x08, 0x47, 0x5d, 0x52, 0x47, 0x0e, 0x10, 0x0d, 0x19, 0x00, 0x00, 0x05, + 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x08, 0x47, 0x5d, 0x52, 0x47, 0x0e, 0x10, 0x0d, 0x19, + 0x23, 0x52, 0x2a, 0x4d, 0x47, 0x47, 0x42, 0x00, 0x00, 0x0b, 0x05, 0x10, 0x05, 0x18, 0x42, + 0x4d, 0x22, 0x07, 0x42, 0x18, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x18, 0x42, 0x4d, 0x22, 0x07, + 0x42, 0x45, 0x02, 0x0a, 0x0a, 0x0f, 0x1a, 0x0f, 0x1a, 0x07, 0x1a, 0x02, 0x47, 0x1a, 0x02, + 0x47, 0x40, 0x40, 0x45, 0x1b, 0x12, 0x0a, 0x45, 0x0f, 0x0a, 0x02, 0x07, 0x36, 0x27, 0x1b, + 0x02, 0x18, 0x15, 0x0f, 0x45, 0x22, 0x15, 0x0a, 0x45, 0x01, 0x15, 0x07, 0x0a, 0x07, 0x1a, + 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x51, 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x46, 0x40, 0x40, 0x40, 0x46, 0x1b, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x07, 0x0b, 0x0b, 0x46, 0x4c, 0x0c, 0x0b, 0x09, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x03, 0x03, 0x01, 0x01, 0x06, 0x41, 0x44, 0x07, 0x41, 0x51, 0x44, 0x09, + 0x46, 0x5c, 0x51, 0x46, 0x10, 0x11, 0x0e, 0x1b, 0x01, 0x01, 0x06, 0x41, 0x44, 0x07, 0x41, + 0x51, 0x44, 0x09, 0x46, 0x5c, 0x51, 0x46, 0x10, 0x11, 0x0e, 0x1b, 0x24, 0x53, 0x2b, 0x4c, + 0x46, 0x46, 0x41, 0x01, 0x01, 0x0c, 0x06, 0x11, 0x06, 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41, + 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41, 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41, 0x44, 0x03, 0x09, + 0x09, 0x0f, 0x1b, 0x0f, 0x1b, 0x07, 0x1b, 0x03, 0x46, 0x1b, 0x03, 0x46, 0x40, 0x40, 0x44, + 0x1c, 0x13, 0x0b, 0x44, 0x0f, 0x0b, 0x03, 0x07, 0x38, 0x27, 0x1c, 0x03, 0x19, 0x16, 0x0f, + 0x44, 0x21, 0x14, 0x09, 0x44, 0x40, 0x16, 0x06, 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x52, 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x40, 0x40, + 0x40, 0x47, 0x1d, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x07, 0x0b, 0x0b, 0x45, 0x4c, 0x0e, 0x0b, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x03, + 0x03, 0x02, 0x02, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0a, 0x45, 0x5c, 0x50, 0x45, + 0x11, 0x12, 0x0f, 0x1d, 0x02, 0x02, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0a, 0x45, + 0x5c, 0x50, 0x45, 0x11, 0x12, 0x0f, 0x1d, 0x26, 0x53, 0x2b, 0x4c, 0x45, 0x45, 0x40, 0x02, + 0x02, 0x0e, 0x07, 0x12, 0x07, 0x1a, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1a, 0x40, 0x4c, 0x20, + 0x07, 0x40, 0x1a, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x44, 0x03, 0x08, 0x08, 0x0f, 0x1b, 0x0f, + 0x1b, 0x07, 0x1b, 0x03, 0x45, 0x1b, 0x03, 0x45, 0x40, 0x40, 0x44, 0x1e, 0x13, 0x0b, 0x44, + 0x0f, 0x0b, 0x03, 0x07, 0x39, 0x27, 0x1e, 0x03, 0x1a, 0x17, 0x0f, 0x44, 0x20, 0x14, 0x08, + 0x44, 0x41, 0x17, 0x05, 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x53, + 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x40, 0x40, 0x40, 0x47, 0x1f, 0x03, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0b, 0x0b, 0x44, + 0x4c, 0x0f, 0x0b, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x03, 0x03, 0x03, 0x03, 0x07, + 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0b, 0x44, 0x5c, 0x50, 0x44, 0x13, 0x13, 0x0f, 0x1f, + 0x03, 0x03, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0b, 0x44, 0x5c, 0x50, 0x44, 0x13, + 0x13, 0x0f, 0x1f, 0x27, 0x53, 0x2b, 0x4c, 0x44, 0x44, 0x40, 0x03, 0x03, 0x0f, 0x07, 0x13, + 0x07, 0x1b, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1b, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1b, 0x40, + 0x4c, 0x20, 0x07, 0x40, 0x44, 0x03, 0x08, 0x08, 0x0f, 0x1b, 0x0f, 0x1b, 0x07, 0x1b, 0x03, + 0x44, 0x1b, 0x03, 0x44, 0x40, 0x40, 0x44, 0x1f, 0x13, 0x0b, 0x44, 0x0f, 0x0b, 0x03, 0x07, + 0x3b, 0x27, 0x1f, 0x03, 0x1b, 0x17, 0x0f, 0x44, 0x20, 0x14, 0x08, 0x44, 0x43, 0x17, 0x04, + 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x54, 0x04, 0x4b, 0x58, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x48, 0x40, 0x40, 0x40, 0x48, 0x21, 0x04, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0c, 0x0c, 0x43, 0x4b, 0x10, 0x0c, 0x07, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x00, 0x43, 0x07, 0x00, + 0x4f, 0x43, 0x0c, 0x43, 0x5b, 0x4f, 0x43, 0x14, 0x14, 0x10, 0x21, 0x04, 0x04, 0x08, 0x00, + 0x43, 0x07, 0x00, 0x4f, 0x43, 0x0c, 0x43, 0x5b, 0x4f, 0x43, 0x14, 0x14, 0x10, 0x21, 0x28, + 0x54, 0x2c, 0x4b, 0x43, 0x43, 0x00, 0x04, 0x04, 0x10, 0x08, 0x14, 0x08, 0x1c, 0x00, 0x4b, + 0x1f, 0x07, 0x00, 0x1c, 0x00, 0x4b, 0x1f, 0x07, 0x00, 0x1c, 0x00, 0x4b, 0x1f, 0x07, 0x00, + 0x43, 0x04, 0x07, 0x07, 0x0f, 0x1c, 0x0f, 0x1c, 0x07, 0x1c, 0x04, 0x43, 0x1c, 0x04, 0x43, + 0x40, 0x40, 0x43, 0x20, 0x14, 0x0c, 0x43, 0x0f, 0x0c, 0x04, 0x07, 0x3c, 0x27, 0x20, 0x04, + 0x1c, 0x18, 0x0f, 0x43, 0x1f, 0x13, 0x07, 0x43, 0x44, 0x18, 0x03, 0x0c, 0x07, 0x1c, 0x0b, + 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x04, 0x4b, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x49, 0x40, 0x40, 0x40, 0x49, 0x22, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x07, 0x0c, 0x0c, 0x42, 0x4b, 0x11, 0x0c, 0x06, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x04, 0x04, 0x05, 0x05, 0x08, 0x00, 0x43, 0x07, 0x00, 0x4f, 0x43, 0x0d, 0x42, + 0x5b, 0x4f, 0x42, 0x16, 0x15, 0x10, 0x22, 0x05, 0x05, 0x08, 0x00, 0x43, 0x07, 0x00, 0x4f, + 0x43, 0x0d, 0x42, 0x5b, 0x4f, 0x42, 0x16, 0x15, 0x10, 0x22, 0x29, 0x54, 0x2c, 0x4b, 0x42, + 0x42, 0x00, 0x05, 0x05, 0x11, 0x08, 0x15, 0x08, 0x1d, 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x1d, + 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x1d, 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x43, 0x04, 0x06, 0x06, + 0x0f, 0x1c, 0x0f, 0x1c, 0x07, 0x1c, 0x04, 0x42, 0x1c, 0x04, 0x42, 0x40, 0x40, 0x43, 0x21, + 0x14, 0x0c, 0x43, 0x0f, 0x0c, 0x04, 0x07, 0x3e, 0x27, 0x21, 0x04, 0x1d, 0x18, 0x0f, 0x43, + 0x1e, 0x13, 0x06, 0x43, 0x46, 0x18, 0x02, 0x0c, 0x07, 0x1c, 0x0b, 0x0f, 0x0f, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x56, 0x04, 0x4b, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x40, 0x40, 0x40, + 0x49, 0x24, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, + 0x0c, 0x0c, 0x41, 0x4b, 0x13, 0x0c, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x04, + 0x06, 0x06, 0x09, 0x01, 0x43, 0x07, 0x01, 0x4e, 0x43, 0x0e, 0x41, 0x5b, 0x4e, 0x41, 0x18, + 0x16, 0x11, 0x24, 0x06, 0x06, 0x09, 0x01, 0x43, 0x07, 0x01, 0x4e, 0x43, 0x0e, 0x41, 0x5b, + 0x4e, 0x41, 0x18, 0x16, 0x11, 0x24, 0x2b, 0x54, 0x2c, 0x4b, 0x41, 0x41, 0x01, 0x06, 0x06, + 0x13, 0x09, 0x16, 0x09, 0x1e, 0x01, 0x4b, 0x1e, 0x07, 0x01, 0x1e, 0x01, 0x4b, 0x1e, 0x07, + 0x01, 0x1e, 0x01, 0x4b, 0x1e, 0x07, 0x01, 0x43, 0x04, 0x06, 0x06, 0x0f, 0x1c, 0x0f, 0x1c, + 0x07, 0x1c, 0x04, 0x41, 0x1c, 0x04, 0x41, 0x40, 0x40, 0x43, 0x23, 0x14, 0x0c, 0x43, 0x0f, + 0x0c, 0x04, 0x07, 0x3e, 0x27, 0x23, 0x04, 0x1e, 0x19, 0x0f, 0x43, 0x1e, 0x13, 0x06, 0x43, + 0x48, 0x19, 0x01, 0x0c, 0x07, 0x1c, 0x0b, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x05, + 0x4a, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x40, 0x40, 0x40, 0x4a, 0x26, 0x05, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x40, 0x4a, + 0x14, 0x0d, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x05, 0x07, 0x07, 0x0a, 0x02, + 0x42, 0x07, 0x02, 0x4d, 0x42, 0x0f, 0x40, 0x5a, 0x4d, 0x40, 0x19, 0x17, 0x12, 0x26, 0x07, + 0x07, 0x0a, 0x02, 0x42, 0x07, 0x02, 0x4d, 0x42, 0x0f, 0x40, 0x5a, 0x4d, 0x40, 0x19, 0x17, + 0x12, 0x26, 0x2c, 0x55, 0x2d, 0x4a, 0x40, 0x40, 0x02, 0x07, 0x07, 0x14, 0x0a, 0x17, 0x0a, + 0x1f, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x1f, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x1f, 0x02, 0x4a, + 0x1d, 0x07, 0x02, 0x42, 0x05, 0x05, 0x05, 0x0f, 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x40, + 0x1d, 0x05, 0x40, 0x40, 0x40, 0x42, 0x24, 0x15, 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e, + 0x27, 0x24, 0x05, 0x1f, 0x1a, 0x0f, 0x42, 0x1d, 0x12, 0x05, 0x42, 0x49, 0x1a, 0x00, 0x0d, + 0x07, 0x1d, 0x0a, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x58, 0x05, 0x4a, 0x58, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x4a, 0x40, 0x40, 0x40, 0x4a, 0x28, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x00, 0x4a, 0x15, 0x0d, 0x05, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x05, 0x08, 0x08, 0x0a, 0x02, 0x42, 0x07, 0x02, 0x4d, + 0x42, 0x10, 0x00, 0x5a, 0x4d, 0x00, 0x1b, 0x18, 0x12, 0x28, 0x08, 0x08, 0x0a, 0x02, 0x42, + 0x07, 0x02, 0x4d, 0x42, 0x10, 0x00, 0x5a, 0x4d, 0x00, 0x1b, 0x18, 0x12, 0x28, 0x2d, 0x55, + 0x2d, 0x4a, 0x00, 0x00, 0x02, 0x08, 0x08, 0x15, 0x0a, 0x18, 0x0a, 0x20, 0x02, 0x4a, 0x1d, + 0x07, 0x02, 0x20, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x20, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x42, + 0x05, 0x05, 0x05, 0x0f, 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x00, 0x1d, 0x05, 0x00, 0x40, + 0x40, 0x42, 0x25, 0x15, 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e, 0x27, 0x25, 0x05, 0x20, + 0x1a, 0x0f, 0x42, 0x1d, 0x12, 0x05, 0x42, 0x4b, 0x1a, 0x40, 0x0d, 0x07, 0x1d, 0x0a, 0x0f, + 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x59, 0x05, 0x4a, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4b, + 0x40, 0x40, 0x40, 0x4b, 0x2a, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x01, 0x4a, 0x16, 0x0d, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x05, 0x05, 0x09, 0x09, 0x0b, 0x03, 0x42, 0x07, 0x03, 0x4c, 0x42, 0x11, 0x01, 0x5a, + 0x4c, 0x01, 0x1c, 0x19, 0x13, 0x2a, 0x09, 0x09, 0x0b, 0x03, 0x42, 0x07, 0x03, 0x4c, 0x42, + 0x11, 0x01, 0x5a, 0x4c, 0x01, 0x1c, 0x19, 0x13, 0x2a, 0x2e, 0x55, 0x2d, 0x4a, 0x01, 0x01, + 0x03, 0x09, 0x09, 0x16, 0x0b, 0x19, 0x0b, 0x21, 0x03, 0x4a, 0x1c, 0x07, 0x03, 0x21, 0x03, + 0x4a, 0x1c, 0x07, 0x03, 0x21, 0x03, 0x4a, 0x1c, 0x07, 0x03, 0x42, 0x05, 0x04, 0x04, 0x0f, + 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x01, 0x1d, 0x05, 0x01, 0x40, 0x40, 0x42, 0x26, 0x15, + 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e, 0x27, 0x26, 0x05, 0x21, 0x1b, 0x0f, 0x42, 0x1c, + 0x12, 0x04, 0x42, 0x4c, 0x1b, 0x41, 0x0d, 0x07, 0x1d, 0x0a, 0x0f, 0x0f, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x5a, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x4c, + 0x2c, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e, + 0x0e, 0x02, 0x49, 0x18, 0x0e, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0a, + 0x0a, 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x12, 0x02, 0x59, 0x4b, 0x02, 0x1e, 0x1a, + 0x14, 0x2c, 0x0a, 0x0a, 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x12, 0x02, 0x59, 0x4b, + 0x02, 0x1e, 0x1a, 0x14, 0x2c, 0x30, 0x56, 0x2e, 0x49, 0x02, 0x02, 0x04, 0x0a, 0x0a, 0x18, + 0x0c, 0x1a, 0x0c, 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04, + 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x41, 0x06, 0x03, 0x03, 0x0f, 0x1e, 0x0f, 0x1e, 0x07, + 0x1e, 0x06, 0x02, 0x1e, 0x06, 0x02, 0x40, 0x40, 0x41, 0x28, 0x16, 0x0e, 0x41, 0x0f, 0x0e, + 0x06, 0x07, 0x3e, 0x27, 0x28, 0x06, 0x22, 0x1c, 0x0f, 0x41, 0x1b, 0x11, 0x03, 0x41, 0x4e, + 0x1c, 0x42, 0x0e, 0x07, 0x1e, 0x09, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5b, 0x06, 0x49, + 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x4c, 0x2e, 0x06, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e, 0x0e, 0x03, 0x49, 0x19, + 0x0e, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0b, 0x0b, 0x0c, 0x04, 0x41, + 0x07, 0x04, 0x4b, 0x41, 0x13, 0x03, 0x59, 0x4b, 0x03, 0x1f, 0x1b, 0x14, 0x2e, 0x0b, 0x0b, + 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x13, 0x03, 0x59, 0x4b, 0x03, 0x1f, 0x1b, 0x14, + 0x2e, 0x31, 0x56, 0x2e, 0x49, 0x03, 0x03, 0x04, 0x0b, 0x0b, 0x19, 0x0c, 0x1b, 0x0c, 0x23, + 0x04, 0x49, 0x1b, 0x07, 0x04, 0x23, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x23, 0x04, 0x49, 0x1b, + 0x07, 0x04, 0x41, 0x06, 0x03, 0x03, 0x0f, 0x1e, 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x03, 0x1e, + 0x06, 0x03, 0x40, 0x40, 0x41, 0x29, 0x16, 0x0e, 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27, + 0x29, 0x06, 0x23, 0x1c, 0x0f, 0x41, 0x1b, 0x11, 0x03, 0x41, 0x4f, 0x1c, 0x43, 0x0e, 0x07, + 0x1e, 0x09, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5c, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x4d, 0x40, 0x40, 0x40, 0x4d, 0x30, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e, 0x0e, 0x04, 0x49, 0x1a, 0x0e, 0x02, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41, + 0x14, 0x04, 0x59, 0x4a, 0x04, 0x21, 0x1c, 0x15, 0x30, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, + 0x05, 0x4a, 0x41, 0x14, 0x04, 0x59, 0x4a, 0x04, 0x21, 0x1c, 0x15, 0x30, 0x32, 0x56, 0x2e, + 0x49, 0x04, 0x04, 0x05, 0x0c, 0x0c, 0x1a, 0x0d, 0x1c, 0x0d, 0x24, 0x05, 0x49, 0x1a, 0x07, + 0x05, 0x24, 0x05, 0x49, 0x1a, 0x07, 0x05, 0x24, 0x05, 0x49, 0x1a, 0x07, 0x05, 0x41, 0x06, + 0x02, 0x02, 0x0f, 0x1e, 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x04, 0x1e, 0x06, 0x04, 0x40, 0x40, + 0x41, 0x2a, 0x16, 0x0e, 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27, 0x2a, 0x06, 0x24, 0x1d, + 0x0f, 0x41, 0x1a, 0x11, 0x02, 0x41, 0x51, 0x1d, 0x44, 0x0e, 0x07, 0x1e, 0x09, 0x0f, 0x0f, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x5d, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4e, 0x40, + 0x40, 0x40, 0x4e, 0x31, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x07, 0x0e, 0x0e, 0x04, 0x49, 0x1b, 0x0e, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x06, 0x06, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41, 0x14, 0x04, 0x59, 0x4a, + 0x04, 0x22, 0x1c, 0x15, 0x31, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41, 0x14, + 0x04, 0x59, 0x4a, 0x04, 0x22, 0x1c, 0x15, 0x31, 0x33, 0x57, 0x2e, 0x49, 0x04, 0x04, 0x05, + 0x0c, 0x0c, 0x1b, 0x0d, 0x1c, 0x0d, 0x24, 0x05, 0x49, 0x19, 0x07, 0x05, 0x24, 0x05, 0x49, + 0x19, 0x07, 0x05, 0x24, 0x05, 0x49, 0x19, 0x07, 0x05, 0x41, 0x06, 0x01, 0x01, 0x0f, 0x1e, + 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x04, 0x1e, 0x06, 0x04, 0x40, 0x40, 0x41, 0x2b, 0x16, 0x0e, + 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27, 0x2b, 0x06, 0x24, 0x1d, 0x0f, 0x41, 0x19, 0x10, + 0x01, 0x41, 0x53, 0x1d, 0x45, 0x0e, 0x07, 0x1e, 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x5d, 0x07, 0x48, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4e, 0x40, 0x40, 0x40, 0x4e, 0x33, + 0x07, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f, + 0x05, 0x48, 0x1d, 0x0f, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0d, 0x0d, + 0x0e, 0x06, 0x40, 0x07, 0x06, 0x49, 0x40, 0x15, 0x05, 0x58, 0x49, 0x05, 0x24, 0x1d, 0x16, + 0x33, 0x0d, 0x0d, 0x0e, 0x06, 0x40, 0x07, 0x06, 0x49, 0x40, 0x15, 0x05, 0x58, 0x49, 0x05, + 0x24, 0x1d, 0x16, 0x33, 0x35, 0x57, 0x2f, 0x48, 0x05, 0x05, 0x06, 0x0d, 0x0d, 0x1d, 0x0e, + 0x1d, 0x0e, 0x25, 0x06, 0x48, 0x19, 0x07, 0x06, 0x25, 0x06, 0x48, 0x19, 0x07, 0x06, 0x25, + 0x06, 0x48, 0x19, 0x07, 0x06, 0x40, 0x07, 0x01, 0x01, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f, + 0x07, 0x05, 0x1f, 0x07, 0x05, 0x40, 0x40, 0x40, 0x2d, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07, + 0x07, 0x3e, 0x27, 0x2d, 0x07, 0x25, 0x1e, 0x0f, 0x40, 0x19, 0x10, 0x01, 0x40, 0x54, 0x1e, + 0x45, 0x0f, 0x07, 0x1f, 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5e, 0x07, 0x48, 0x58, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x4f, 0x40, 0x40, 0x40, 0x4f, 0x35, 0x07, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f, 0x06, 0x48, 0x1e, 0x0f, + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0e, 0x0e, 0x0f, 0x07, 0x40, 0x07, + 0x07, 0x48, 0x40, 0x16, 0x06, 0x58, 0x48, 0x06, 0x26, 0x1e, 0x17, 0x35, 0x0e, 0x0e, 0x0f, + 0x07, 0x40, 0x07, 0x07, 0x48, 0x40, 0x16, 0x06, 0x58, 0x48, 0x06, 0x26, 0x1e, 0x17, 0x35, + 0x36, 0x57, 0x2f, 0x48, 0x06, 0x06, 0x07, 0x0e, 0x0e, 0x1e, 0x0f, 0x1e, 0x0f, 0x26, 0x07, + 0x48, 0x18, 0x07, 0x07, 0x26, 0x07, 0x48, 0x18, 0x07, 0x07, 0x26, 0x07, 0x48, 0x18, 0x07, + 0x07, 0x40, 0x07, 0x00, 0x00, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f, 0x07, 0x06, 0x1f, 0x07, + 0x06, 0x40, 0x40, 0x40, 0x2e, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07, 0x07, 0x3e, 0x27, 0x2e, + 0x07, 0x26, 0x1f, 0x0f, 0x40, 0x18, 0x10, 0x00, 0x40, 0x56, 0x1f, 0x46, 0x0f, 0x07, 0x1f, + 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5f, 0x07, 0x48, 0x58, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x4f, 0x40, 0x40, 0x40, 0x4f, 0x37, 0x07, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f, 0x07, 0x48, 0x1f, 0x0f, 0x00, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x07, 0x40, 0x07, 0x07, 0x48, 0x40, 0x17, + 0x07, 0x58, 0x48, 0x07, 0x27, 0x1f, 0x17, 0x37, 0x0f, 0x0f, 0x0f, 0x07, 0x40, 0x07, 0x07, + 0x48, 0x40, 0x17, 0x07, 0x58, 0x48, 0x07, 0x27, 0x1f, 0x17, 0x37, 0x37, 0x57, 0x2f, 0x48, + 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x1f, 0x0f, 0x1f, 0x0f, 0x27, 0x07, 0x48, 0x18, 0x07, 0x07, + 0x27, 0x07, 0x48, 0x18, 0x07, 0x07, 0x27, 0x07, 0x48, 0x18, 0x07, 0x07, 0x40, 0x07, 0x00, + 0x00, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f, 0x07, 0x07, 0x1f, 0x07, 0x07, 0x40, 0x40, 0x40, + 0x2f, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07, 0x07, 0x3e, 0x27, 0x2f, 0x07, 0x27, 0x1f, 0x0f, + 0x40, 0x18, 0x10, 0x00, 0x40, 0x57, 0x1f, 0x47, 0x0f, 0x07, 0x1f, 0x08, 0x0f, 0x0f, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x07, 0x48, 0x48, 0x60, 0x40, 0x27, 0x07, 0x07, 0x27, 0x40, 0x48, 0x40, + 0x40, 0x40, 0x0f, 0x48, 0x68, 0x60, 0x40, 0x68, 0x68, 0x68, 0x68, 0x68, 0x07, 0x07, 0x0f, + 0x50, 0x40, 0x60, 0x07, 0x68, 0x27, 0x48, 0x17, 0x40, 0x50, 0x1f, 0x40, 0x40, 0x40, 0x48, + 0x48, 0x58, 0x60, 0x60, 0x60, 0x68, 0x68, 0x58, 0x68, 0x60, 0x60, 0x60, 0x68, 0x68, 0x68, + 0x60, 0x50, 0x48, 0x50, 0x58, 0x60, 0x60, 0x60, 0x68, 0x68, 0x58, 0x68, 0x60, 0x60, 0x60, + 0x68, 0x68, 0x68, 0x60, 0x50, 0x48, 0x50, 0x07, 0x50, 0x58, 0x40, 0x48, 0x40, 0x48, 0x07, + 0x48, 0x48, 0x48, 0x68, 0x07, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f, + 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x40, 0x07, 0x48, 0x48, 0x48, 0x07, 0x48, + 0x07, 0x17, 0x17, 0x17, 0x50, 0x17, 0x17, 0x50, 0x40, 0x40, 0x40, 0x2f, 0x2f, 0x17, 0x40, + 0x0f, 0x17, 0x1f, 0x1f, 0x1f, 0x27, 0x0f, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x3e, 0x1f, 0x17, + 0x40, 0x17, 0x07, 0x1f, 0x48, 0x17, 0x48, 0x40, 0x48, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, + 0x47, 0x47, 0x5f, 0x40, 0x27, 0x07, 0x07, 0x27, 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47, + 0x66, 0x5f, 0x00, 0x66, 0x66, 0x66, 0x65, 0x65, 0x07, 0x07, 0x0f, 0x4f, 0x00, 0x5e, 0x07, + 0x67, 0x27, 0x47, 0x17, 0x40, 0x4f, 0x1f, 0x40, 0x40, 0x40, 0x47, 0x47, 0x57, 0x5f, 0x5e, + 0x5f, 0x66, 0x66, 0x57, 0x67, 0x5f, 0x5e, 0x5f, 0x67, 0x67, 0x66, 0x5e, 0x4f, 0x47, 0x4f, + 0x57, 0x5f, 0x5e, 0x5f, 0x66, 0x66, 0x57, 0x67, 0x5f, 0x5e, 0x5f, 0x67, 0x67, 0x66, 0x5e, + 0x4f, 0x47, 0x4f, 0x08, 0x4f, 0x56, 0x40, 0x48, 0x40, 0x47, 0x07, 0x47, 0x47, 0x47, 0x66, + 0x07, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17, + 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x47, 0x47, 0x47, 0x08, 0x47, 0x08, 0x17, 0x17, 0x17, + 0x4f, 0x17, 0x17, 0x4f, 0x40, 0x40, 0x40, 0x2f, 0x2f, 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x1f, + 0x20, 0x27, 0x10, 0x07, 0x08, 0x10, 0x08, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1f, + 0x47, 0x17, 0x46, 0x00, 0x47, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x46, 0x47, 0x5e, 0x40, + 0x26, 0x06, 0x06, 0x27, 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47, 0x64, 0x5e, 0x01, 0x65, + 0x64, 0x64, 0x63, 0x63, 0x07, 0x07, 0x0f, 0x4e, 0x00, 0x5d, 0x07, 0x66, 0x27, 0x46, 0x17, + 0x40, 0x4f, 0x1e, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5e, 0x5d, 0x5e, 0x65, 0x64, 0x56, + 0x66, 0x5e, 0x5c, 0x5e, 0x66, 0x66, 0x65, 0x5d, 0x4e, 0x46, 0x4e, 0x56, 0x5e, 0x5d, 0x5e, + 0x65, 0x64, 0x56, 0x66, 0x5e, 0x5c, 0x5e, 0x66, 0x66, 0x65, 0x5d, 0x4e, 0x46, 0x4e, 0x09, + 0x4f, 0x54, 0x40, 0x48, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x64, 0x07, 0x1f, 0x16, 0x4f, + 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, + 0x40, 0x07, 0x46, 0x46, 0x46, 0x09, 0x46, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f, + 0x40, 0x40, 0x40, 0x2e, 0x2e, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07, + 0x09, 0x10, 0x08, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1e, 0x46, 0x17, 0x45, 0x01, + 0x46, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x45, 0x47, 0x5e, 0x40, 0x25, 0x06, 0x05, 0x27, + 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47, 0x63, 0x5d, 0x01, 0x64, 0x63, 0x62, 0x60, 0x60, + 0x07, 0x07, 0x0f, 0x4e, 0x00, 0x5c, 0x07, 0x65, 0x27, 0x45, 0x17, 0x40, 0x4f, 0x1d, 0x40, + 0x40, 0x40, 0x47, 0x47, 0x56, 0x5d, 0x5c, 0x5d, 0x64, 0x63, 0x56, 0x65, 0x5d, 0x5b, 0x5d, + 0x65, 0x65, 0x64, 0x5c, 0x4d, 0x46, 0x4d, 0x56, 0x5d, 0x5c, 0x5d, 0x64, 0x63, 0x56, 0x65, + 0x5d, 0x5b, 0x5d, 0x65, 0x65, 0x64, 0x5c, 0x4d, 0x46, 0x4d, 0x09, 0x4f, 0x52, 0x40, 0x48, + 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x62, 0x07, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, + 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x46, 0x46, + 0x45, 0x09, 0x45, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f, 0x40, 0x40, 0x40, 0x2d, + 0x2d, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07, 0x09, 0x10, 0x08, 0x07, + 0x3d, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1e, 0x45, 0x17, 0x44, 0x01, 0x45, 0x17, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x05, 0x44, 0x46, 0x5d, 0x40, 0x24, 0x05, 0x04, 0x27, 0x40, 0x46, 0x40, 0x40, + 0x40, 0x0f, 0x46, 0x61, 0x5c, 0x02, 0x63, 0x61, 0x60, 0x5e, 0x5e, 0x07, 0x07, 0x0e, 0x4d, + 0x01, 0x5b, 0x07, 0x64, 0x27, 0x44, 0x16, 0x40, 0x4e, 0x1c, 0x40, 0x40, 0x40, 0x46, 0x46, + 0x55, 0x5c, 0x5b, 0x5c, 0x63, 0x61, 0x55, 0x64, 0x5c, 0x59, 0x5c, 0x64, 0x64, 0x63, 0x5b, + 0x4c, 0x45, 0x4c, 0x55, 0x5c, 0x5b, 0x5c, 0x63, 0x61, 0x55, 0x64, 0x5c, 0x59, 0x5c, 0x64, + 0x64, 0x63, 0x5b, 0x4c, 0x45, 0x4c, 0x0a, 0x4e, 0x50, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46, + 0x45, 0x45, 0x60, 0x07, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07, + 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x45, 0x45, 0x44, 0x0a, 0x44, 0x0a, + 0x16, 0x17, 0x15, 0x4e, 0x17, 0x15, 0x4e, 0x40, 0x40, 0x40, 0x2c, 0x2c, 0x16, 0x40, 0x0f, + 0x16, 0x1d, 0x1d, 0x21, 0x27, 0x11, 0x07, 0x0a, 0x11, 0x09, 0x06, 0x3c, 0x1e, 0x16, 0x40, + 0x16, 0x09, 0x1d, 0x44, 0x16, 0x43, 0x02, 0x44, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x43, + 0x46, 0x5c, 0x40, 0x23, 0x04, 0x03, 0x27, 0x40, 0x46, 0x40, 0x40, 0x40, 0x0f, 0x46, 0x60, + 0x5b, 0x03, 0x61, 0x60, 0x5e, 0x5b, 0x5b, 0x07, 0x07, 0x0e, 0x4c, 0x01, 0x59, 0x07, 0x63, + 0x27, 0x43, 0x16, 0x40, 0x4e, 0x1b, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5b, 0x59, 0x5b, + 0x61, 0x60, 0x54, 0x63, 0x5b, 0x58, 0x5b, 0x63, 0x63, 0x61, 0x59, 0x4b, 0x44, 0x4b, 0x54, + 0x5b, 0x59, 0x5b, 0x61, 0x60, 0x54, 0x63, 0x5b, 0x58, 0x5b, 0x63, 0x63, 0x61, 0x59, 0x4b, + 0x44, 0x4b, 0x0b, 0x4e, 0x4e, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5e, 0x07, + 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, + 0x11, 0x07, 0x40, 0x41, 0x07, 0x44, 0x44, 0x43, 0x0b, 0x43, 0x0b, 0x16, 0x17, 0x14, 0x4e, + 0x17, 0x14, 0x4e, 0x40, 0x40, 0x40, 0x2b, 0x2b, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21, + 0x27, 0x11, 0x07, 0x0b, 0x11, 0x09, 0x06, 0x3b, 0x1e, 0x16, 0x40, 0x16, 0x09, 0x1c, 0x43, + 0x16, 0x41, 0x03, 0x43, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x42, 0x46, 0x5c, 0x40, 0x22, + 0x04, 0x02, 0x27, 0x40, 0x46, 0x40, 0x40, 0x40, 0x0f, 0x46, 0x5e, 0x5a, 0x03, 0x60, 0x5e, + 0x5c, 0x59, 0x59, 0x07, 0x07, 0x0e, 0x4c, 0x01, 0x58, 0x07, 0x62, 0x27, 0x42, 0x16, 0x40, + 0x4e, 0x1a, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5a, 0x58, 0x5a, 0x60, 0x5e, 0x54, 0x62, + 0x5a, 0x56, 0x5a, 0x62, 0x62, 0x60, 0x58, 0x4a, 0x44, 0x4a, 0x54, 0x5a, 0x58, 0x5a, 0x60, + 0x5e, 0x54, 0x62, 0x5a, 0x56, 0x5a, 0x62, 0x62, 0x60, 0x58, 0x4a, 0x44, 0x4a, 0x0b, 0x4e, + 0x4c, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5c, 0x07, 0x1e, 0x14, 0x4e, 0x11, + 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x41, + 0x07, 0x44, 0x44, 0x42, 0x0b, 0x42, 0x0b, 0x16, 0x17, 0x14, 0x4e, 0x17, 0x14, 0x4e, 0x40, + 0x40, 0x40, 0x2a, 0x2a, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21, 0x27, 0x11, 0x07, 0x0b, + 0x11, 0x09, 0x06, 0x3a, 0x1e, 0x16, 0x40, 0x16, 0x09, 0x1c, 0x42, 0x16, 0x40, 0x03, 0x42, + 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x41, 0x45, 0x5b, 0x40, 0x21, 0x03, 0x01, 0x27, 0x40, + 0x45, 0x40, 0x40, 0x40, 0x0f, 0x45, 0x5d, 0x59, 0x04, 0x5f, 0x5d, 0x5a, 0x56, 0x56, 0x07, + 0x07, 0x0d, 0x4b, 0x02, 0x57, 0x07, 0x61, 0x27, 0x41, 0x15, 0x40, 0x4d, 0x19, 0x40, 0x40, + 0x40, 0x45, 0x45, 0x53, 0x59, 0x57, 0x59, 0x5f, 0x5d, 0x53, 0x61, 0x59, 0x55, 0x59, 0x61, + 0x61, 0x5f, 0x57, 0x49, 0x43, 0x49, 0x53, 0x59, 0x57, 0x59, 0x5f, 0x5d, 0x53, 0x61, 0x59, + 0x55, 0x59, 0x61, 0x61, 0x5f, 0x57, 0x49, 0x43, 0x49, 0x0c, 0x4d, 0x4a, 0x40, 0x48, 0x40, + 0x45, 0x07, 0x45, 0x43, 0x43, 0x5a, 0x07, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13, + 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x43, 0x43, 0x41, + 0x0c, 0x41, 0x0c, 0x15, 0x17, 0x13, 0x4d, 0x17, 0x13, 0x4d, 0x40, 0x40, 0x40, 0x29, 0x29, + 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x1b, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x0a, 0x05, 0x39, + 0x1d, 0x15, 0x40, 0x15, 0x0a, 0x1b, 0x41, 0x15, 0x00, 0x04, 0x41, 0x15, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x02, 0x40, 0x45, 0x5b, 0x40, 0x20, 0x02, 0x00, 0x27, 0x40, 0x45, 0x40, 0x40, 0x40, + 0x0f, 0x45, 0x5b, 0x58, 0x04, 0x5e, 0x5b, 0x59, 0x54, 0x54, 0x07, 0x07, 0x0d, 0x4b, 0x02, + 0x56, 0x07, 0x60, 0x27, 0x40, 0x15, 0x40, 0x4d, 0x18, 0x40, 0x40, 0x40, 0x45, 0x45, 0x53, + 0x58, 0x56, 0x58, 0x5e, 0x5b, 0x53, 0x60, 0x58, 0x53, 0x58, 0x60, 0x60, 0x5e, 0x56, 0x48, + 0x43, 0x48, 0x53, 0x58, 0x56, 0x58, 0x5e, 0x5b, 0x53, 0x60, 0x58, 0x53, 0x58, 0x60, 0x60, + 0x5e, 0x56, 0x48, 0x43, 0x48, 0x0c, 0x4d, 0x49, 0x40, 0x48, 0x40, 0x45, 0x07, 0x45, 0x43, + 0x43, 0x59, 0x07, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, + 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x43, 0x43, 0x40, 0x0c, 0x40, 0x0c, 0x15, + 0x17, 0x12, 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x28, 0x28, 0x15, 0x40, 0x0f, 0x15, + 0x1a, 0x1a, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x0a, 0x05, 0x38, 0x1d, 0x15, 0x40, 0x15, + 0x0a, 0x1a, 0x40, 0x15, 0x01, 0x04, 0x40, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x45, + 0x5a, 0x40, 0x1f, 0x02, 0x40, 0x27, 0x40, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x45, 0x59, 0x57, + 0x05, 0x5c, 0x59, 0x57, 0x51, 0x51, 0x07, 0x07, 0x0d, 0x4a, 0x02, 0x54, 0x07, 0x5f, 0x27, + 0x00, 0x15, 0x40, 0x4d, 0x17, 0x40, 0x40, 0x40, 0x45, 0x45, 0x52, 0x57, 0x54, 0x57, 0x5c, + 0x59, 0x52, 0x5f, 0x57, 0x51, 0x57, 0x5f, 0x5f, 0x5c, 0x54, 0x47, 0x42, 0x47, 0x52, 0x57, + 0x54, 0x57, 0x5c, 0x59, 0x52, 0x5f, 0x57, 0x51, 0x57, 0x5f, 0x5f, 0x5c, 0x54, 0x47, 0x42, + 0x47, 0x0d, 0x4d, 0x47, 0x40, 0x48, 0x40, 0x45, 0x07, 0x45, 0x42, 0x42, 0x57, 0x07, 0x1d, + 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, + 0x07, 0x40, 0x42, 0x07, 0x42, 0x42, 0x00, 0x0d, 0x00, 0x0d, 0x15, 0x17, 0x12, 0x4d, 0x17, + 0x12, 0x4d, 0x40, 0x40, 0x40, 0x27, 0x27, 0x15, 0x40, 0x0f, 0x15, 0x1a, 0x1a, 0x22, 0x27, + 0x12, 0x07, 0x0d, 0x12, 0x0a, 0x05, 0x37, 0x1d, 0x15, 0x40, 0x15, 0x0a, 0x1a, 0x00, 0x15, + 0x03, 0x05, 0x00, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x01, 0x44, 0x59, 0x40, 0x1e, 0x01, + 0x41, 0x27, 0x40, 0x44, 0x40, 0x40, 0x40, 0x0f, 0x44, 0x58, 0x56, 0x06, 0x5b, 0x58, 0x55, + 0x4f, 0x4f, 0x07, 0x07, 0x0c, 0x49, 0x03, 0x53, 0x07, 0x5e, 0x27, 0x01, 0x14, 0x40, 0x4c, + 0x16, 0x40, 0x40, 0x40, 0x44, 0x44, 0x51, 0x56, 0x53, 0x56, 0x5b, 0x58, 0x51, 0x5e, 0x56, + 0x50, 0x56, 0x5e, 0x5e, 0x5b, 0x53, 0x46, 0x41, 0x46, 0x51, 0x56, 0x53, 0x56, 0x5b, 0x58, + 0x51, 0x5e, 0x56, 0x50, 0x56, 0x5e, 0x5e, 0x5b, 0x53, 0x46, 0x41, 0x46, 0x0e, 0x4c, 0x45, + 0x40, 0x48, 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x55, 0x07, 0x1c, 0x11, 0x4c, 0x13, 0x07, + 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, + 0x41, 0x41, 0x01, 0x0e, 0x01, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40, + 0x40, 0x26, 0x26, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13, + 0x0b, 0x04, 0x36, 0x1c, 0x14, 0x40, 0x14, 0x0b, 0x19, 0x01, 0x14, 0x04, 0x06, 0x01, 0x14, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x01, 0x02, 0x44, 0x59, 0x40, 0x1d, 0x01, 0x42, 0x27, 0x40, 0x44, + 0x40, 0x40, 0x40, 0x0f, 0x44, 0x56, 0x55, 0x06, 0x5a, 0x56, 0x53, 0x4c, 0x4c, 0x07, 0x07, + 0x0c, 0x49, 0x03, 0x52, 0x07, 0x5d, 0x27, 0x02, 0x14, 0x40, 0x4c, 0x15, 0x40, 0x40, 0x40, + 0x44, 0x44, 0x51, 0x55, 0x52, 0x55, 0x5a, 0x56, 0x51, 0x5d, 0x55, 0x4e, 0x55, 0x5d, 0x5d, + 0x5a, 0x52, 0x45, 0x41, 0x45, 0x51, 0x55, 0x52, 0x55, 0x5a, 0x56, 0x51, 0x5d, 0x55, 0x4e, + 0x55, 0x5d, 0x5d, 0x5a, 0x52, 0x45, 0x41, 0x45, 0x0e, 0x4c, 0x43, 0x40, 0x48, 0x40, 0x44, + 0x07, 0x44, 0x41, 0x41, 0x53, 0x07, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, + 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x41, 0x41, 0x02, 0x0e, + 0x02, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40, 0x40, 0x25, 0x25, 0x14, + 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13, 0x0b, 0x04, 0x35, 0x1c, + 0x14, 0x40, 0x14, 0x0b, 0x19, 0x02, 0x14, 0x05, 0x06, 0x02, 0x14, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x03, 0x44, 0x58, 0x40, 0x1c, 0x00, 0x43, 0x27, 0x40, 0x44, 0x40, 0x40, 0x40, 0x0f, + 0x44, 0x55, 0x54, 0x07, 0x59, 0x55, 0x51, 0x4a, 0x4a, 0x07, 0x07, 0x0c, 0x48, 0x03, 0x51, + 0x07, 0x5c, 0x27, 0x03, 0x14, 0x40, 0x4c, 0x14, 0x40, 0x40, 0x40, 0x44, 0x44, 0x50, 0x54, + 0x51, 0x54, 0x59, 0x55, 0x50, 0x5c, 0x54, 0x4d, 0x54, 0x5c, 0x5c, 0x59, 0x51, 0x44, 0x40, + 0x44, 0x50, 0x54, 0x51, 0x54, 0x59, 0x55, 0x50, 0x5c, 0x54, 0x4d, 0x54, 0x5c, 0x5c, 0x59, + 0x51, 0x44, 0x40, 0x44, 0x0f, 0x4c, 0x41, 0x40, 0x48, 0x40, 0x44, 0x07, 0x44, 0x40, 0x40, + 0x51, 0x07, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c, + 0x10, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x40, 0x40, 0x03, 0x0f, 0x03, 0x0f, 0x14, 0x17, + 0x10, 0x4c, 0x17, 0x10, 0x4c, 0x40, 0x40, 0x40, 0x24, 0x24, 0x14, 0x40, 0x0f, 0x14, 0x18, + 0x18, 0x23, 0x27, 0x13, 0x07, 0x0f, 0x13, 0x0b, 0x04, 0x34, 0x1c, 0x14, 0x40, 0x14, 0x0b, + 0x18, 0x03, 0x14, 0x06, 0x07, 0x03, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 0x04, 0x43, 0x57, + 0x40, 0x1b, 0x40, 0x44, 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43, 0x53, 0x53, 0x08, + 0x57, 0x53, 0x4f, 0x47, 0x47, 0x07, 0x07, 0x0b, 0x47, 0x04, 0x4f, 0x07, 0x5b, 0x27, 0x04, + 0x13, 0x40, 0x4b, 0x13, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x53, 0x4f, 0x53, 0x57, 0x53, + 0x4f, 0x5b, 0x53, 0x4b, 0x53, 0x5b, 0x5b, 0x57, 0x4f, 0x43, 0x00, 0x43, 0x4f, 0x53, 0x4f, + 0x53, 0x57, 0x53, 0x4f, 0x5b, 0x53, 0x4b, 0x53, 0x5b, 0x5b, 0x57, 0x4f, 0x43, 0x00, 0x43, + 0x10, 0x4b, 0x00, 0x40, 0x48, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4f, 0x07, 0x1b, 0x0f, + 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, + 0x40, 0x44, 0x07, 0x00, 0x00, 0x04, 0x10, 0x04, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f, + 0x4b, 0x40, 0x40, 0x40, 0x23, 0x23, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14, + 0x07, 0x10, 0x14, 0x0c, 0x03, 0x33, 0x1b, 0x13, 0x40, 0x13, 0x0c, 0x17, 0x04, 0x13, 0x08, + 0x08, 0x04, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 0x05, 0x43, 0x57, 0x40, 0x1a, 0x40, 0x45, + 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43, 0x52, 0x52, 0x08, 0x56, 0x52, 0x4d, 0x45, + 0x45, 0x07, 0x07, 0x0b, 0x47, 0x04, 0x4e, 0x07, 0x5a, 0x27, 0x05, 0x13, 0x40, 0x4b, 0x12, + 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x52, 0x4e, 0x52, 0x56, 0x52, 0x4f, 0x5a, 0x52, 0x4a, + 0x52, 0x5a, 0x5a, 0x56, 0x4e, 0x42, 0x00, 0x42, 0x4f, 0x52, 0x4e, 0x52, 0x56, 0x52, 0x4f, + 0x5a, 0x52, 0x4a, 0x52, 0x5a, 0x5a, 0x56, 0x4e, 0x42, 0x00, 0x42, 0x10, 0x4b, 0x02, 0x40, + 0x48, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4d, 0x07, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, + 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x00, + 0x00, 0x05, 0x10, 0x05, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f, 0x4b, 0x40, 0x40, 0x40, + 0x22, 0x22, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14, 0x07, 0x10, 0x14, 0x0c, + 0x03, 0x32, 0x1b, 0x13, 0x40, 0x13, 0x0c, 0x17, 0x05, 0x13, 0x09, 0x08, 0x05, 0x13, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x41, 0x06, 0x43, 0x56, 0x40, 0x19, 0x41, 0x46, 0x27, 0x40, 0x43, 0x40, + 0x40, 0x40, 0x0f, 0x43, 0x50, 0x51, 0x09, 0x55, 0x50, 0x4b, 0x42, 0x42, 0x07, 0x07, 0x0b, + 0x46, 0x04, 0x4d, 0x07, 0x59, 0x27, 0x06, 0x13, 0x40, 0x4b, 0x11, 0x40, 0x40, 0x40, 0x43, + 0x43, 0x4e, 0x51, 0x4d, 0x51, 0x55, 0x50, 0x4e, 0x59, 0x51, 0x48, 0x51, 0x59, 0x59, 0x55, + 0x4d, 0x41, 0x01, 0x41, 0x4e, 0x51, 0x4d, 0x51, 0x55, 0x50, 0x4e, 0x59, 0x51, 0x48, 0x51, + 0x59, 0x59, 0x55, 0x4d, 0x41, 0x01, 0x41, 0x11, 0x4b, 0x04, 0x40, 0x48, 0x40, 0x43, 0x07, + 0x43, 0x01, 0x01, 0x4b, 0x07, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14, + 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x01, 0x01, 0x06, 0x11, 0x06, + 0x11, 0x13, 0x17, 0x0e, 0x4b, 0x17, 0x0e, 0x4b, 0x40, 0x40, 0x40, 0x21, 0x21, 0x13, 0x40, + 0x0f, 0x13, 0x16, 0x16, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x0c, 0x03, 0x31, 0x1b, 0x13, + 0x40, 0x13, 0x0c, 0x16, 0x06, 0x13, 0x0a, 0x09, 0x06, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, + 0x06, 0x43, 0x56, 0x40, 0x18, 0x42, 0x47, 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43, + 0x4f, 0x51, 0x09, 0x54, 0x4f, 0x4a, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x46, 0x04, 0x4c, 0x07, + 0x59, 0x27, 0x06, 0x12, 0x40, 0x4b, 0x10, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4e, 0x51, 0x4c, + 0x51, 0x54, 0x4f, 0x4e, 0x59, 0x51, 0x47, 0x51, 0x59, 0x59, 0x54, 0x4c, 0x41, 0x01, 0x41, + 0x4e, 0x51, 0x4c, 0x51, 0x54, 0x4f, 0x4e, 0x59, 0x51, 0x47, 0x51, 0x59, 0x59, 0x54, 0x4c, + 0x41, 0x01, 0x41, 0x11, 0x4b, 0x05, 0x40, 0x48, 0x40, 0x43, 0x07, 0x43, 0x01, 0x01, 0x4a, + 0x07, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d, + 0x4b, 0x14, 0x07, 0x40, 0x45, 0x07, 0x01, 0x01, 0x06, 0x11, 0x06, 0x11, 0x12, 0x17, 0x0d, + 0x4b, 0x17, 0x0d, 0x4b, 0x40, 0x40, 0x40, 0x20, 0x20, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15, + 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x0c, 0x02, 0x30, 0x1a, 0x12, 0x40, 0x12, 0x0c, 0x15, + 0x06, 0x12, 0x0b, 0x09, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x07, 0x42, 0x55, 0x40, + 0x18, 0x42, 0x47, 0x27, 0x40, 0x42, 0x40, 0x40, 0x40, 0x0f, 0x42, 0x4d, 0x50, 0x0a, 0x52, + 0x4d, 0x48, 0x02, 0x02, 0x07, 0x07, 0x0a, 0x45, 0x05, 0x4a, 0x07, 0x58, 0x27, 0x07, 0x12, + 0x40, 0x4a, 0x10, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4d, 0x50, 0x4a, 0x50, 0x52, 0x4d, 0x4d, + 0x58, 0x50, 0x45, 0x50, 0x58, 0x58, 0x52, 0x4a, 0x40, 0x02, 0x40, 0x4d, 0x50, 0x4a, 0x50, + 0x52, 0x4d, 0x4d, 0x58, 0x50, 0x45, 0x50, 0x58, 0x58, 0x52, 0x4a, 0x40, 0x02, 0x40, 0x12, + 0x4a, 0x07, 0x40, 0x48, 0x40, 0x42, 0x07, 0x42, 0x02, 0x02, 0x48, 0x07, 0x1a, 0x0d, 0x4a, + 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40, + 0x45, 0x07, 0x02, 0x02, 0x07, 0x12, 0x07, 0x12, 0x12, 0x17, 0x0d, 0x4a, 0x17, 0x0d, 0x4a, + 0x40, 0x40, 0x40, 0x20, 0x20, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15, 0x25, 0x27, 0x15, 0x07, + 0x12, 0x15, 0x0d, 0x02, 0x30, 0x1a, 0x12, 0x40, 0x12, 0x0d, 0x15, 0x07, 0x12, 0x0d, 0x0a, + 0x07, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x43, 0x08, 0x42, 0x54, 0x40, 0x17, 0x43, 0x48, 0x27, + 0x40, 0x42, 0x40, 0x40, 0x40, 0x0f, 0x42, 0x4b, 0x4f, 0x0b, 0x51, 0x4b, 0x46, 0x04, 0x04, + 0x07, 0x07, 0x0a, 0x44, 0x05, 0x49, 0x07, 0x57, 0x27, 0x08, 0x12, 0x40, 0x4a, 0x0f, 0x40, + 0x40, 0x40, 0x42, 0x42, 0x4c, 0x4f, 0x49, 0x4f, 0x51, 0x4b, 0x4c, 0x57, 0x4f, 0x43, 0x4f, + 0x57, 0x57, 0x51, 0x49, 0x00, 0x03, 0x00, 0x4c, 0x4f, 0x49, 0x4f, 0x51, 0x4b, 0x4c, 0x57, + 0x4f, 0x43, 0x4f, 0x57, 0x57, 0x51, 0x49, 0x00, 0x03, 0x00, 0x13, 0x4a, 0x09, 0x40, 0x48, + 0x40, 0x42, 0x07, 0x42, 0x03, 0x03, 0x46, 0x07, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, + 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x03, 0x03, + 0x08, 0x13, 0x08, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1f, + 0x1f, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0d, 0x02, + 0x2f, 0x1a, 0x12, 0x40, 0x12, 0x0d, 0x14, 0x08, 0x12, 0x0e, 0x0b, 0x08, 0x12, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x43, 0x09, 0x42, 0x54, 0x40, 0x16, 0x43, 0x49, 0x27, 0x40, 0x42, 0x40, 0x40, + 0x40, 0x0f, 0x42, 0x4a, 0x4e, 0x0b, 0x50, 0x4a, 0x44, 0x07, 0x07, 0x07, 0x07, 0x0a, 0x44, + 0x05, 0x48, 0x07, 0x56, 0x27, 0x09, 0x12, 0x40, 0x4a, 0x0e, 0x40, 0x40, 0x40, 0x42, 0x42, + 0x4c, 0x4e, 0x48, 0x4e, 0x50, 0x4a, 0x4c, 0x56, 0x4e, 0x42, 0x4e, 0x56, 0x56, 0x50, 0x48, + 0x01, 0x03, 0x01, 0x4c, 0x4e, 0x48, 0x4e, 0x50, 0x4a, 0x4c, 0x56, 0x4e, 0x42, 0x4e, 0x56, + 0x56, 0x50, 0x48, 0x01, 0x03, 0x01, 0x13, 0x4a, 0x0b, 0x40, 0x48, 0x40, 0x42, 0x07, 0x42, + 0x03, 0x03, 0x44, 0x07, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, + 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x03, 0x03, 0x09, 0x13, 0x09, 0x13, + 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1e, 0x1e, 0x12, 0x40, 0x0f, + 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0d, 0x02, 0x2e, 0x1a, 0x12, 0x40, + 0x12, 0x0d, 0x14, 0x09, 0x12, 0x0f, 0x0b, 0x09, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0x0a, + 0x41, 0x53, 0x40, 0x15, 0x44, 0x4a, 0x27, 0x40, 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x48, + 0x4d, 0x0c, 0x4f, 0x48, 0x42, 0x09, 0x09, 0x07, 0x07, 0x09, 0x43, 0x06, 0x47, 0x07, 0x55, + 0x27, 0x0a, 0x11, 0x40, 0x49, 0x0d, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4b, 0x4d, 0x47, 0x4d, + 0x4f, 0x48, 0x4b, 0x55, 0x4d, 0x40, 0x4d, 0x55, 0x55, 0x4f, 0x47, 0x02, 0x04, 0x02, 0x4b, + 0x4d, 0x47, 0x4d, 0x4f, 0x48, 0x4b, 0x55, 0x4d, 0x40, 0x4d, 0x55, 0x55, 0x4f, 0x47, 0x02, + 0x04, 0x02, 0x14, 0x49, 0x0d, 0x40, 0x48, 0x40, 0x41, 0x07, 0x41, 0x04, 0x04, 0x42, 0x07, + 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49, + 0x16, 0x07, 0x40, 0x46, 0x07, 0x04, 0x04, 0x0a, 0x14, 0x0a, 0x14, 0x11, 0x17, 0x0b, 0x49, + 0x17, 0x0b, 0x49, 0x40, 0x40, 0x40, 0x1d, 0x1d, 0x11, 0x40, 0x0f, 0x11, 0x13, 0x13, 0x26, + 0x27, 0x16, 0x07, 0x14, 0x16, 0x0e, 0x01, 0x2d, 0x19, 0x11, 0x40, 0x11, 0x0e, 0x13, 0x0a, + 0x11, 0x10, 0x0c, 0x0a, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x45, 0x0b, 0x41, 0x52, 0x40, 0x14, + 0x45, 0x4b, 0x27, 0x40, 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x47, 0x4c, 0x0d, 0x4d, 0x47, + 0x40, 0x0c, 0x0c, 0x07, 0x07, 0x09, 0x42, 0x06, 0x45, 0x07, 0x54, 0x27, 0x0b, 0x11, 0x40, + 0x49, 0x0c, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4c, 0x45, 0x4c, 0x4d, 0x47, 0x4a, 0x54, + 0x4c, 0x00, 0x4c, 0x54, 0x54, 0x4d, 0x45, 0x03, 0x05, 0x03, 0x4a, 0x4c, 0x45, 0x4c, 0x4d, + 0x47, 0x4a, 0x54, 0x4c, 0x00, 0x4c, 0x54, 0x54, 0x4d, 0x45, 0x03, 0x05, 0x03, 0x15, 0x49, + 0x0f, 0x40, 0x48, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x40, 0x07, 0x19, 0x0a, 0x49, 0x16, + 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46, + 0x07, 0x05, 0x05, 0x0b, 0x15, 0x0b, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40, + 0x40, 0x40, 0x1c, 0x1c, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15, + 0x16, 0x0e, 0x01, 0x2c, 0x19, 0x11, 0x40, 0x11, 0x0e, 0x12, 0x0b, 0x11, 0x12, 0x0d, 0x0b, + 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x45, 0x0c, 0x41, 0x52, 0x40, 0x13, 0x45, 0x4c, 0x27, 0x40, + 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x45, 0x4b, 0x0d, 0x4c, 0x45, 0x01, 0x0e, 0x0e, 0x07, + 0x07, 0x09, 0x42, 0x06, 0x44, 0x07, 0x53, 0x27, 0x0c, 0x11, 0x40, 0x49, 0x0b, 0x40, 0x40, + 0x40, 0x41, 0x41, 0x4a, 0x4b, 0x44, 0x4b, 0x4c, 0x45, 0x4a, 0x53, 0x4b, 0x02, 0x4b, 0x53, + 0x53, 0x4c, 0x44, 0x04, 0x05, 0x04, 0x4a, 0x4b, 0x44, 0x4b, 0x4c, 0x45, 0x4a, 0x53, 0x4b, + 0x02, 0x4b, 0x53, 0x53, 0x4c, 0x44, 0x04, 0x05, 0x04, 0x15, 0x49, 0x11, 0x40, 0x48, 0x40, + 0x41, 0x07, 0x41, 0x05, 0x05, 0x01, 0x07, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, + 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x05, 0x05, 0x0c, + 0x15, 0x0c, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40, 0x40, 0x40, 0x1b, 0x1b, + 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15, 0x16, 0x0e, 0x01, 0x2b, + 0x19, 0x11, 0x40, 0x11, 0x0e, 0x12, 0x0c, 0x11, 0x13, 0x0d, 0x0c, 0x11, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x46, 0x0d, 0x40, 0x51, 0x40, 0x12, 0x46, 0x4d, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x0f, 0x40, 0x44, 0x4a, 0x0e, 0x4b, 0x44, 0x03, 0x11, 0x11, 0x07, 0x07, 0x08, 0x41, 0x07, + 0x43, 0x07, 0x52, 0x27, 0x0d, 0x10, 0x40, 0x48, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, + 0x4a, 0x43, 0x4a, 0x4b, 0x44, 0x49, 0x52, 0x4a, 0x03, 0x4a, 0x52, 0x52, 0x4b, 0x43, 0x05, + 0x06, 0x05, 0x49, 0x4a, 0x43, 0x4a, 0x4b, 0x44, 0x49, 0x52, 0x4a, 0x03, 0x4a, 0x52, 0x52, + 0x4b, 0x43, 0x05, 0x06, 0x05, 0x16, 0x48, 0x13, 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x06, + 0x06, 0x03, 0x07, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, + 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x06, 0x06, 0x0d, 0x16, 0x0d, 0x16, 0x10, + 0x17, 0x09, 0x48, 0x17, 0x09, 0x48, 0x40, 0x40, 0x40, 0x1a, 0x1a, 0x10, 0x40, 0x0f, 0x10, + 0x11, 0x11, 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0f, 0x00, 0x2a, 0x18, 0x10, 0x40, 0x10, + 0x0f, 0x11, 0x0d, 0x10, 0x14, 0x0e, 0x0d, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x0e, 0x40, + 0x51, 0x40, 0x11, 0x47, 0x4e, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x42, 0x49, + 0x0e, 0x4a, 0x42, 0x04, 0x13, 0x13, 0x07, 0x07, 0x08, 0x41, 0x07, 0x42, 0x07, 0x51, 0x27, + 0x0e, 0x10, 0x40, 0x48, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x42, 0x49, 0x4a, + 0x42, 0x49, 0x51, 0x49, 0x05, 0x49, 0x51, 0x51, 0x4a, 0x42, 0x06, 0x06, 0x06, 0x49, 0x49, + 0x42, 0x49, 0x4a, 0x42, 0x49, 0x51, 0x49, 0x05, 0x49, 0x51, 0x51, 0x4a, 0x42, 0x06, 0x06, + 0x06, 0x16, 0x48, 0x14, 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x06, 0x06, 0x04, 0x07, 0x18, + 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, + 0x07, 0x40, 0x47, 0x07, 0x06, 0x06, 0x0e, 0x16, 0x0e, 0x16, 0x10, 0x17, 0x08, 0x48, 0x17, + 0x08, 0x48, 0x40, 0x40, 0x40, 0x19, 0x19, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27, + 0x17, 0x07, 0x16, 0x17, 0x0f, 0x00, 0x29, 0x18, 0x10, 0x40, 0x10, 0x0f, 0x10, 0x0e, 0x10, + 0x15, 0x0e, 0x0e, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x0f, 0x40, 0x50, 0x40, 0x10, 0x47, + 0x4f, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x48, 0x0f, 0x48, 0x40, 0x06, + 0x16, 0x16, 0x07, 0x07, 0x08, 0x40, 0x07, 0x40, 0x07, 0x50, 0x27, 0x0f, 0x10, 0x40, 0x48, + 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40, 0x48, 0x50, 0x48, + 0x07, 0x48, 0x50, 0x50, 0x48, 0x40, 0x07, 0x07, 0x07, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40, + 0x48, 0x50, 0x48, 0x07, 0x48, 0x50, 0x50, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x48, 0x16, + 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x07, 0x07, 0x06, 0x07, 0x18, 0x08, 0x48, 0x17, 0x07, + 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, + 0x07, 0x07, 0x0f, 0x17, 0x0f, 0x17, 0x10, 0x17, 0x08, 0x48, 0x17, 0x08, 0x48, 0x40, 0x40, + 0x40, 0x18, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27, 0x17, 0x07, 0x17, 0x17, + 0x0f, 0x00, 0x28, 0x18, 0x10, 0x40, 0x10, 0x0f, 0x10, 0x0f, 0x10, 0x17, 0x0f, 0x0f, 0x10, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x48, 0x10, 0x00, 0x4f, 0x40, 0x0f, 0x48, 0x50, 0x27, 0x40, 0x00, + 0x40, 0x40, 0x40, 0x0f, 0x00, 0x00, 0x47, 0x10, 0x47, 0x00, 0x08, 0x18, 0x18, 0x07, 0x07, + 0x07, 0x00, 0x08, 0x00, 0x07, 0x4f, 0x27, 0x10, 0x0f, 0x40, 0x47, 0x07, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x47, 0x08, 0x47, 0x4f, 0x4f, + 0x47, 0x00, 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x47, 0x08, + 0x47, 0x4f, 0x4f, 0x47, 0x00, 0x08, 0x08, 0x08, 0x18, 0x47, 0x18, 0x40, 0x48, 0x40, 0x00, + 0x07, 0x00, 0x08, 0x08, 0x08, 0x07, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, + 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x10, 0x18, + 0x10, 0x18, 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x17, 0x17, 0x0f, + 0x40, 0x0f, 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x27, 0x17, + 0x0f, 0x40, 0x0f, 0x10, 0x0f, 0x10, 0x0f, 0x18, 0x10, 0x10, 0x0f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x48, 0x11, 0x00, 0x4f, 0x40, 0x0e, 0x48, 0x51, 0x27, 0x40, 0x00, 0x40, 0x40, 0x40, 0x0f, + 0x00, 0x02, 0x46, 0x10, 0x46, 0x02, 0x0a, 0x1b, 0x1b, 0x07, 0x07, 0x07, 0x00, 0x08, 0x01, + 0x07, 0x4e, 0x27, 0x11, 0x0f, 0x40, 0x47, 0x06, 0x40, 0x40, 0x40, 0x00, 0x00, 0x47, 0x46, + 0x01, 0x46, 0x46, 0x02, 0x47, 0x4e, 0x46, 0x0a, 0x46, 0x4e, 0x4e, 0x46, 0x01, 0x09, 0x08, + 0x09, 0x47, 0x46, 0x01, 0x46, 0x46, 0x02, 0x47, 0x4e, 0x46, 0x0a, 0x46, 0x4e, 0x4e, 0x46, + 0x01, 0x09, 0x08, 0x09, 0x18, 0x47, 0x1a, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x08, 0x08, + 0x0a, 0x07, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, + 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x11, 0x18, 0x11, 0x18, 0x0f, 0x17, + 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x16, 0x16, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, + 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x26, 0x17, 0x0f, 0x40, 0x0f, 0x10, + 0x0f, 0x11, 0x0f, 0x19, 0x10, 0x11, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x49, 0x12, 0x00, 0x4e, + 0x40, 0x0d, 0x49, 0x52, 0x27, 0x40, 0x00, 0x40, 0x40, 0x40, 0x0f, 0x00, 0x03, 0x45, 0x11, + 0x45, 0x03, 0x0c, 0x1d, 0x1d, 0x07, 0x07, 0x07, 0x01, 0x08, 0x02, 0x07, 0x4d, 0x27, 0x12, + 0x0f, 0x40, 0x47, 0x05, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x45, 0x02, 0x45, 0x45, 0x03, + 0x46, 0x4d, 0x45, 0x0b, 0x45, 0x4d, 0x4d, 0x45, 0x02, 0x0a, 0x09, 0x0a, 0x46, 0x45, 0x02, + 0x45, 0x45, 0x03, 0x46, 0x4d, 0x45, 0x0b, 0x45, 0x4d, 0x4d, 0x45, 0x02, 0x0a, 0x09, 0x0a, + 0x19, 0x47, 0x1c, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x09, 0x09, 0x0c, 0x07, 0x17, 0x06, + 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07, + 0x40, 0x48, 0x07, 0x09, 0x09, 0x12, 0x19, 0x12, 0x19, 0x0f, 0x17, 0x06, 0x47, 0x17, 0x06, + 0x47, 0x40, 0x40, 0x40, 0x15, 0x15, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x0e, 0x28, 0x27, 0x18, + 0x07, 0x19, 0x18, 0x10, 0x40, 0x25, 0x17, 0x0f, 0x40, 0x0f, 0x10, 0x0e, 0x12, 0x0f, 0x1a, + 0x11, 0x12, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4a, 0x13, 0x01, 0x4d, 0x40, 0x0c, 0x4a, 0x53, + 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01, 0x05, 0x44, 0x12, 0x43, 0x05, 0x0e, 0x20, + 0x20, 0x07, 0x07, 0x06, 0x02, 0x09, 0x04, 0x07, 0x4c, 0x27, 0x13, 0x0e, 0x40, 0x46, 0x04, + 0x40, 0x40, 0x40, 0x01, 0x01, 0x45, 0x44, 0x04, 0x44, 0x43, 0x05, 0x45, 0x4c, 0x44, 0x0d, + 0x44, 0x4c, 0x4c, 0x43, 0x04, 0x0b, 0x0a, 0x0b, 0x45, 0x44, 0x04, 0x44, 0x43, 0x05, 0x45, + 0x4c, 0x44, 0x0d, 0x44, 0x4c, 0x4c, 0x43, 0x04, 0x0b, 0x0a, 0x0b, 0x1a, 0x46, 0x1e, 0x40, + 0x48, 0x40, 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x0e, 0x07, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, + 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0a, + 0x0a, 0x13, 0x1a, 0x13, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40, + 0x14, 0x14, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x11, + 0x41, 0x24, 0x16, 0x0e, 0x40, 0x0e, 0x11, 0x0d, 0x13, 0x0e, 0x1c, 0x12, 0x13, 0x0e, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x4a, 0x14, 0x01, 0x4d, 0x40, 0x0b, 0x4a, 0x54, 0x27, 0x40, 0x01, 0x40, + 0x40, 0x40, 0x0f, 0x01, 0x06, 0x43, 0x12, 0x42, 0x06, 0x10, 0x22, 0x22, 0x07, 0x07, 0x06, + 0x02, 0x09, 0x05, 0x07, 0x4b, 0x27, 0x14, 0x0e, 0x40, 0x46, 0x03, 0x40, 0x40, 0x40, 0x01, + 0x01, 0x45, 0x43, 0x05, 0x43, 0x42, 0x06, 0x45, 0x4b, 0x43, 0x0e, 0x43, 0x4b, 0x4b, 0x42, + 0x05, 0x0c, 0x0a, 0x0c, 0x45, 0x43, 0x05, 0x43, 0x42, 0x06, 0x45, 0x4b, 0x43, 0x0e, 0x43, + 0x4b, 0x4b, 0x42, 0x05, 0x0c, 0x0a, 0x0c, 0x1a, 0x46, 0x20, 0x40, 0x48, 0x40, 0x01, 0x07, + 0x01, 0x0a, 0x0a, 0x10, 0x07, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, + 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0a, 0x0a, 0x14, 0x1a, 0x14, + 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40, 0x13, 0x13, 0x0e, 0x40, + 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x11, 0x41, 0x23, 0x16, 0x0e, + 0x40, 0x0e, 0x11, 0x0d, 0x14, 0x0e, 0x1d, 0x12, 0x14, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4b, + 0x15, 0x01, 0x4c, 0x40, 0x0a, 0x4b, 0x55, 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01, + 0x08, 0x42, 0x13, 0x41, 0x08, 0x12, 0x25, 0x25, 0x07, 0x07, 0x06, 0x03, 0x09, 0x06, 0x07, + 0x4a, 0x27, 0x15, 0x0e, 0x40, 0x46, 0x02, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x06, + 0x42, 0x41, 0x08, 0x44, 0x4a, 0x42, 0x10, 0x42, 0x4a, 0x4a, 0x41, 0x06, 0x0d, 0x0b, 0x0d, + 0x44, 0x42, 0x06, 0x42, 0x41, 0x08, 0x44, 0x4a, 0x42, 0x10, 0x42, 0x4a, 0x4a, 0x41, 0x06, + 0x0d, 0x0b, 0x0d, 0x1b, 0x46, 0x22, 0x40, 0x48, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x12, + 0x07, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04, + 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0b, 0x0b, 0x15, 0x1b, 0x15, 0x1b, 0x0e, 0x17, 0x04, + 0x46, 0x17, 0x04, 0x46, 0x40, 0x40, 0x40, 0x12, 0x12, 0x0e, 0x40, 0x0f, 0x0e, 0x0c, 0x0c, + 0x29, 0x27, 0x19, 0x07, 0x1b, 0x19, 0x11, 0x41, 0x22, 0x16, 0x0e, 0x40, 0x0e, 0x11, 0x0c, + 0x15, 0x0e, 0x1e, 0x13, 0x15, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4c, 0x15, 0x01, 0x4c, 0x40, + 0x09, 0x4c, 0x56, 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01, 0x09, 0x42, 0x13, 0x40, + 0x09, 0x13, 0x27, 0x27, 0x07, 0x07, 0x05, 0x03, 0x09, 0x07, 0x07, 0x4a, 0x27, 0x15, 0x0d, + 0x40, 0x46, 0x01, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x07, 0x42, 0x40, 0x09, 0x44, + 0x4a, 0x42, 0x11, 0x42, 0x4a, 0x4a, 0x40, 0x07, 0x0d, 0x0b, 0x0d, 0x44, 0x42, 0x07, 0x42, + 0x40, 0x09, 0x44, 0x4a, 0x42, 0x11, 0x42, 0x4a, 0x4a, 0x40, 0x07, 0x0d, 0x0b, 0x0d, 0x1b, + 0x46, 0x23, 0x40, 0x48, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x13, 0x07, 0x15, 0x03, 0x46, + 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40, + 0x4a, 0x07, 0x0b, 0x0b, 0x15, 0x1b, 0x15, 0x1b, 0x0d, 0x17, 0x03, 0x46, 0x17, 0x03, 0x46, + 0x40, 0x40, 0x40, 0x11, 0x11, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x29, 0x27, 0x19, 0x07, + 0x1b, 0x19, 0x11, 0x42, 0x21, 0x15, 0x0d, 0x40, 0x0d, 0x11, 0x0b, 0x15, 0x0d, 0x1f, 0x13, + 0x15, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4c, 0x16, 0x02, 0x4b, 0x40, 0x09, 0x4c, 0x56, 0x27, + 0x40, 0x02, 0x40, 0x40, 0x40, 0x0f, 0x02, 0x0b, 0x41, 0x14, 0x01, 0x0b, 0x15, 0x2a, 0x2a, + 0x07, 0x07, 0x05, 0x04, 0x0a, 0x09, 0x07, 0x49, 0x27, 0x16, 0x0d, 0x40, 0x45, 0x01, 0x40, + 0x40, 0x40, 0x02, 0x02, 0x43, 0x41, 0x09, 0x41, 0x01, 0x0b, 0x43, 0x49, 0x41, 0x13, 0x41, + 0x49, 0x49, 0x01, 0x09, 0x0e, 0x0c, 0x0e, 0x43, 0x41, 0x09, 0x41, 0x01, 0x0b, 0x43, 0x49, + 0x41, 0x13, 0x41, 0x49, 0x49, 0x01, 0x09, 0x0e, 0x0c, 0x0e, 0x1c, 0x45, 0x25, 0x40, 0x48, + 0x40, 0x02, 0x07, 0x02, 0x0c, 0x0c, 0x15, 0x07, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15, + 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0c, 0x0c, + 0x16, 0x1c, 0x16, 0x1c, 0x0d, 0x17, 0x03, 0x45, 0x17, 0x03, 0x45, 0x40, 0x40, 0x40, 0x11, + 0x11, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x2a, 0x27, 0x1a, 0x07, 0x1c, 0x1a, 0x12, 0x42, + 0x21, 0x15, 0x0d, 0x40, 0x0d, 0x12, 0x0b, 0x16, 0x0d, 0x21, 0x14, 0x16, 0x0d, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x4d, 0x17, 0x02, 0x4a, 0x40, 0x08, 0x4d, 0x57, 0x27, 0x40, 0x02, 0x40, 0x40, + 0x40, 0x0f, 0x02, 0x0d, 0x40, 0x15, 0x02, 0x0d, 0x17, 0x2c, 0x2c, 0x07, 0x07, 0x05, 0x05, + 0x0a, 0x0a, 0x07, 0x48, 0x27, 0x17, 0x0d, 0x40, 0x45, 0x00, 0x40, 0x40, 0x40, 0x02, 0x02, + 0x42, 0x40, 0x0a, 0x40, 0x02, 0x0d, 0x42, 0x48, 0x40, 0x15, 0x40, 0x48, 0x48, 0x02, 0x0a, + 0x0f, 0x0d, 0x0f, 0x42, 0x40, 0x0a, 0x40, 0x02, 0x0d, 0x42, 0x48, 0x40, 0x15, 0x40, 0x48, + 0x48, 0x02, 0x0a, 0x0f, 0x0d, 0x0f, 0x1d, 0x45, 0x27, 0x40, 0x48, 0x40, 0x02, 0x07, 0x02, + 0x0d, 0x0d, 0x17, 0x07, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, + 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0d, 0x0d, 0x17, 0x1d, 0x17, 0x1d, + 0x0d, 0x17, 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x10, 0x10, 0x0d, 0x40, 0x0f, + 0x0d, 0x0a, 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x12, 0x42, 0x20, 0x15, 0x0d, 0x40, + 0x0d, 0x12, 0x0a, 0x17, 0x0d, 0x22, 0x15, 0x17, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4d, 0x18, + 0x02, 0x4a, 0x40, 0x07, 0x4d, 0x58, 0x27, 0x40, 0x02, 0x40, 0x40, 0x40, 0x0f, 0x02, 0x0e, + 0x00, 0x15, 0x03, 0x0e, 0x19, 0x2f, 0x2f, 0x07, 0x07, 0x05, 0x05, 0x0a, 0x0b, 0x07, 0x47, + 0x27, 0x18, 0x0d, 0x40, 0x45, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x42, 0x00, 0x0b, 0x00, + 0x03, 0x0e, 0x42, 0x47, 0x00, 0x16, 0x00, 0x47, 0x47, 0x03, 0x0b, 0x10, 0x0d, 0x10, 0x42, + 0x00, 0x0b, 0x00, 0x03, 0x0e, 0x42, 0x47, 0x00, 0x16, 0x00, 0x47, 0x47, 0x03, 0x0b, 0x10, + 0x0d, 0x10, 0x1d, 0x45, 0x29, 0x40, 0x48, 0x40, 0x02, 0x07, 0x02, 0x0d, 0x0d, 0x19, 0x07, + 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, + 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0d, 0x0d, 0x18, 0x1d, 0x18, 0x1d, 0x0d, 0x17, 0x02, 0x45, + 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x0f, 0x0d, 0x40, 0x0f, 0x0d, 0x0a, 0x0a, 0x2a, + 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x12, 0x42, 0x1f, 0x15, 0x0d, 0x40, 0x0d, 0x12, 0x0a, 0x18, + 0x0d, 0x23, 0x15, 0x18, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4e, 0x19, 0x03, 0x49, 0x40, 0x06, + 0x4e, 0x59, 0x27, 0x40, 0x03, 0x40, 0x40, 0x40, 0x0f, 0x03, 0x10, 0x01, 0x16, 0x04, 0x10, + 0x1b, 0x31, 0x31, 0x07, 0x07, 0x04, 0x06, 0x0b, 0x0c, 0x07, 0x46, 0x27, 0x19, 0x0c, 0x40, + 0x44, 0x41, 0x40, 0x40, 0x40, 0x03, 0x03, 0x41, 0x01, 0x0c, 0x01, 0x04, 0x10, 0x41, 0x46, + 0x01, 0x18, 0x01, 0x46, 0x46, 0x04, 0x0c, 0x11, 0x0e, 0x11, 0x41, 0x01, 0x0c, 0x01, 0x04, + 0x10, 0x41, 0x46, 0x01, 0x18, 0x01, 0x46, 0x46, 0x04, 0x0c, 0x11, 0x0e, 0x11, 0x1e, 0x44, + 0x2b, 0x40, 0x48, 0x40, 0x03, 0x07, 0x03, 0x0e, 0x0e, 0x1b, 0x07, 0x14, 0x01, 0x44, 0x1b, + 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x4b, + 0x07, 0x0e, 0x0e, 0x19, 0x1e, 0x19, 0x1e, 0x0c, 0x17, 0x01, 0x44, 0x17, 0x01, 0x44, 0x40, + 0x40, 0x40, 0x0e, 0x0e, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x09, 0x2b, 0x27, 0x1b, 0x07, 0x1e, + 0x1b, 0x13, 0x43, 0x1e, 0x14, 0x0c, 0x40, 0x0c, 0x13, 0x09, 0x19, 0x0c, 0x24, 0x16, 0x19, + 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x4f, 0x1a, 0x03, 0x48, 0x40, 0x05, 0x4f, 0x5a, 0x27, 0x40, + 0x03, 0x40, 0x40, 0x40, 0x0f, 0x03, 0x11, 0x02, 0x17, 0x06, 0x11, 0x1d, 0x34, 0x34, 0x07, + 0x07, 0x04, 0x07, 0x0b, 0x0e, 0x07, 0x45, 0x27, 0x1a, 0x0c, 0x40, 0x44, 0x42, 0x40, 0x40, + 0x40, 0x03, 0x03, 0x40, 0x02, 0x0e, 0x02, 0x06, 0x11, 0x40, 0x45, 0x02, 0x19, 0x02, 0x45, + 0x45, 0x06, 0x0e, 0x12, 0x0f, 0x12, 0x40, 0x02, 0x0e, 0x02, 0x06, 0x11, 0x40, 0x45, 0x02, + 0x19, 0x02, 0x45, 0x45, 0x06, 0x0e, 0x12, 0x0f, 0x12, 0x1f, 0x44, 0x2d, 0x40, 0x48, 0x40, + 0x03, 0x07, 0x03, 0x0f, 0x0f, 0x1d, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, + 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0f, 0x0f, 0x1a, + 0x1f, 0x1a, 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0d, 0x0d, + 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x13, 0x43, 0x1d, + 0x14, 0x0c, 0x40, 0x0c, 0x13, 0x08, 0x1a, 0x0c, 0x26, 0x17, 0x1a, 0x0c, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x4f, 0x1b, 0x03, 0x48, 0x40, 0x04, 0x4f, 0x5b, 0x27, 0x40, 0x03, 0x40, 0x40, 0x40, + 0x0f, 0x03, 0x13, 0x03, 0x17, 0x07, 0x13, 0x1f, 0x36, 0x36, 0x07, 0x07, 0x04, 0x07, 0x0b, + 0x0f, 0x07, 0x44, 0x27, 0x1b, 0x0c, 0x40, 0x44, 0x43, 0x40, 0x40, 0x40, 0x03, 0x03, 0x40, + 0x03, 0x0f, 0x03, 0x07, 0x13, 0x40, 0x44, 0x03, 0x1b, 0x03, 0x44, 0x44, 0x07, 0x0f, 0x13, + 0x0f, 0x13, 0x40, 0x03, 0x0f, 0x03, 0x07, 0x13, 0x40, 0x44, 0x03, 0x1b, 0x03, 0x44, 0x44, + 0x07, 0x0f, 0x13, 0x0f, 0x13, 0x1f, 0x44, 0x2f, 0x40, 0x48, 0x40, 0x03, 0x07, 0x03, 0x0f, + 0x0f, 0x1f, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, + 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0f, 0x0f, 0x1b, 0x1f, 0x1b, 0x1f, 0x0c, + 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0c, 0x0c, 0x0c, 0x40, 0x0f, 0x0c, + 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x13, 0x43, 0x1c, 0x14, 0x0c, 0x40, 0x0c, + 0x13, 0x08, 0x1b, 0x0c, 0x27, 0x17, 0x1b, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x50, 0x1c, 0x04, + 0x47, 0x40, 0x03, 0x50, 0x5c, 0x27, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04, 0x14, 0x04, + 0x18, 0x08, 0x14, 0x21, 0x39, 0x39, 0x07, 0x07, 0x03, 0x08, 0x0c, 0x10, 0x07, 0x43, 0x27, + 0x1c, 0x0b, 0x40, 0x43, 0x44, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x04, 0x10, 0x04, 0x08, + 0x14, 0x00, 0x43, 0x04, 0x1c, 0x04, 0x43, 0x43, 0x08, 0x10, 0x14, 0x10, 0x14, 0x00, 0x04, + 0x10, 0x04, 0x08, 0x14, 0x00, 0x43, 0x04, 0x1c, 0x04, 0x43, 0x43, 0x08, 0x10, 0x14, 0x10, + 0x14, 0x20, 0x43, 0x31, 0x40, 0x48, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x21, 0x07, 0x13, + 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c, + 0x07, 0x40, 0x4c, 0x07, 0x10, 0x10, 0x1c, 0x20, 0x1c, 0x20, 0x0b, 0x17, 0x40, 0x43, 0x17, + 0x40, 0x43, 0x40, 0x40, 0x40, 0x0b, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x07, 0x07, 0x2c, 0x27, + 0x1c, 0x07, 0x20, 0x1c, 0x14, 0x44, 0x1b, 0x13, 0x0b, 0x40, 0x0b, 0x14, 0x07, 0x1c, 0x0b, + 0x28, 0x18, 0x1c, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x51, 0x1d, 0x04, 0x47, 0x40, 0x02, 0x51, + 0x5d, 0x27, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04, 0x16, 0x05, 0x18, 0x09, 0x16, 0x22, + 0x3b, 0x3b, 0x07, 0x07, 0x03, 0x08, 0x0c, 0x11, 0x07, 0x42, 0x27, 0x1d, 0x0b, 0x40, 0x43, + 0x45, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x05, 0x11, 0x05, 0x09, 0x16, 0x00, 0x42, 0x05, + 0x1e, 0x05, 0x42, 0x42, 0x09, 0x11, 0x15, 0x10, 0x15, 0x00, 0x05, 0x11, 0x05, 0x09, 0x16, + 0x00, 0x42, 0x05, 0x1e, 0x05, 0x42, 0x42, 0x09, 0x11, 0x15, 0x10, 0x15, 0x20, 0x43, 0x32, + 0x40, 0x48, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x22, 0x07, 0x13, 0x41, 0x43, 0x1c, 0x07, + 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, + 0x10, 0x10, 0x1d, 0x20, 0x1d, 0x20, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40, + 0x40, 0x0a, 0x0a, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x20, 0x1c, + 0x14, 0x44, 0x1a, 0x13, 0x0b, 0x40, 0x0b, 0x14, 0x06, 0x1d, 0x0b, 0x29, 0x18, 0x1d, 0x0b, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x51, 0x1e, 0x04, 0x46, 0x40, 0x01, 0x51, 0x5e, 0x27, 0x40, 0x04, + 0x40, 0x40, 0x40, 0x0f, 0x04, 0x18, 0x06, 0x19, 0x0b, 0x18, 0x24, 0x3e, 0x3e, 0x07, 0x07, + 0x03, 0x09, 0x0c, 0x13, 0x07, 0x41, 0x27, 0x1e, 0x0b, 0x40, 0x43, 0x46, 0x40, 0x40, 0x40, + 0x04, 0x04, 0x01, 0x06, 0x13, 0x06, 0x0b, 0x18, 0x01, 0x41, 0x06, 0x20, 0x06, 0x41, 0x41, + 0x0b, 0x13, 0x16, 0x11, 0x16, 0x01, 0x06, 0x13, 0x06, 0x0b, 0x18, 0x01, 0x41, 0x06, 0x20, + 0x06, 0x41, 0x41, 0x0b, 0x13, 0x16, 0x11, 0x16, 0x21, 0x43, 0x34, 0x40, 0x48, 0x40, 0x04, + 0x07, 0x04, 0x11, 0x11, 0x24, 0x07, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, + 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x11, 0x11, 0x1e, 0x21, + 0x1e, 0x21, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40, 0x40, 0x09, 0x09, 0x0b, + 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x21, 0x1c, 0x14, 0x44, 0x19, 0x13, + 0x0b, 0x40, 0x0b, 0x14, 0x06, 0x1e, 0x0b, 0x2b, 0x19, 0x1e, 0x0b, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x52, 0x1f, 0x05, 0x45, 0x40, 0x00, 0x52, 0x5f, 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f, + 0x05, 0x19, 0x07, 0x1a, 0x0c, 0x19, 0x26, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0a, 0x0d, 0x14, + 0x07, 0x40, 0x27, 0x1f, 0x0a, 0x40, 0x42, 0x47, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x07, + 0x14, 0x07, 0x0c, 0x19, 0x02, 0x40, 0x07, 0x21, 0x07, 0x40, 0x40, 0x0c, 0x14, 0x17, 0x12, + 0x17, 0x02, 0x07, 0x14, 0x07, 0x0c, 0x19, 0x02, 0x40, 0x07, 0x21, 0x07, 0x40, 0x40, 0x0c, + 0x14, 0x17, 0x12, 0x17, 0x22, 0x42, 0x36, 0x40, 0x48, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12, + 0x26, 0x07, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, + 0x42, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x12, 0x12, 0x1f, 0x22, 0x1f, 0x22, 0x0a, 0x17, + 0x42, 0x42, 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x08, 0x08, 0x0a, 0x40, 0x0f, 0x0a, 0x05, + 0x05, 0x2d, 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x15, 0x45, 0x18, 0x12, 0x0a, 0x40, 0x0a, 0x15, + 0x05, 0x1f, 0x0a, 0x2c, 0x1a, 0x1f, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x52, 0x20, 0x05, 0x45, + 0x40, 0x40, 0x52, 0x60, 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f, 0x05, 0x1b, 0x08, 0x1a, + 0x0d, 0x1b, 0x28, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0a, 0x0d, 0x15, 0x07, 0x00, 0x27, 0x20, + 0x0a, 0x40, 0x42, 0x48, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x08, 0x15, 0x08, 0x0d, 0x1b, + 0x02, 0x00, 0x08, 0x23, 0x08, 0x00, 0x00, 0x0d, 0x15, 0x18, 0x12, 0x18, 0x02, 0x08, 0x15, + 0x08, 0x0d, 0x1b, 0x02, 0x00, 0x08, 0x23, 0x08, 0x00, 0x00, 0x0d, 0x15, 0x18, 0x12, 0x18, + 0x22, 0x42, 0x38, 0x40, 0x48, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12, 0x28, 0x07, 0x12, 0x42, + 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, + 0x40, 0x4d, 0x07, 0x12, 0x12, 0x20, 0x22, 0x20, 0x22, 0x0a, 0x17, 0x42, 0x42, 0x17, 0x42, + 0x42, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x05, 0x2d, 0x27, 0x1d, + 0x07, 0x22, 0x1d, 0x15, 0x45, 0x17, 0x12, 0x0a, 0x40, 0x0a, 0x15, 0x05, 0x20, 0x0a, 0x2d, + 0x1a, 0x20, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x53, 0x21, 0x05, 0x44, 0x40, 0x41, 0x53, 0x61, + 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f, 0x05, 0x1c, 0x09, 0x1b, 0x0e, 0x1c, 0x2a, 0x3e, + 0x3e, 0x07, 0x07, 0x02, 0x0b, 0x0d, 0x16, 0x07, 0x01, 0x27, 0x21, 0x0a, 0x40, 0x42, 0x49, + 0x40, 0x40, 0x40, 0x05, 0x05, 0x03, 0x09, 0x16, 0x09, 0x0e, 0x1c, 0x03, 0x01, 0x09, 0x24, + 0x09, 0x01, 0x01, 0x0e, 0x16, 0x19, 0x13, 0x19, 0x03, 0x09, 0x16, 0x09, 0x0e, 0x1c, 0x03, + 0x01, 0x09, 0x24, 0x09, 0x01, 0x01, 0x0e, 0x16, 0x19, 0x13, 0x19, 0x23, 0x42, 0x3a, 0x40, + 0x48, 0x40, 0x05, 0x07, 0x05, 0x13, 0x13, 0x2a, 0x07, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, + 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x13, + 0x13, 0x21, 0x23, 0x21, 0x23, 0x0a, 0x17, 0x43, 0x42, 0x17, 0x43, 0x42, 0x40, 0x40, 0x40, + 0x06, 0x06, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x04, 0x2d, 0x27, 0x1d, 0x07, 0x23, 0x1d, 0x15, + 0x45, 0x16, 0x12, 0x0a, 0x40, 0x0a, 0x15, 0x04, 0x21, 0x0a, 0x2e, 0x1b, 0x21, 0x0a, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x54, 0x22, 0x06, 0x43, 0x40, 0x42, 0x54, 0x62, 0x27, 0x40, 0x06, 0x40, + 0x40, 0x40, 0x0f, 0x06, 0x1e, 0x0a, 0x1c, 0x10, 0x1e, 0x2c, 0x3e, 0x3e, 0x07, 0x07, 0x01, + 0x0c, 0x0e, 0x18, 0x07, 0x02, 0x27, 0x22, 0x09, 0x40, 0x41, 0x4a, 0x40, 0x40, 0x40, 0x06, + 0x06, 0x04, 0x0a, 0x18, 0x0a, 0x10, 0x1e, 0x04, 0x02, 0x0a, 0x26, 0x0a, 0x02, 0x02, 0x10, + 0x18, 0x1a, 0x14, 0x1a, 0x04, 0x0a, 0x18, 0x0a, 0x10, 0x1e, 0x04, 0x02, 0x0a, 0x26, 0x0a, + 0x02, 0x02, 0x10, 0x18, 0x1a, 0x14, 0x1a, 0x24, 0x41, 0x3c, 0x40, 0x48, 0x40, 0x06, 0x07, + 0x06, 0x14, 0x14, 0x2c, 0x07, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, + 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x14, 0x14, 0x22, 0x24, 0x22, + 0x24, 0x09, 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x05, 0x05, 0x09, 0x40, + 0x0f, 0x09, 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x16, 0x46, 0x15, 0x11, 0x09, + 0x40, 0x09, 0x16, 0x03, 0x22, 0x09, 0x30, 0x1c, 0x22, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x54, + 0x23, 0x06, 0x43, 0x40, 0x43, 0x54, 0x63, 0x27, 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06, + 0x1f, 0x0b, 0x1c, 0x11, 0x1f, 0x2e, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x0c, 0x0e, 0x19, 0x07, + 0x03, 0x27, 0x23, 0x09, 0x40, 0x41, 0x4b, 0x40, 0x40, 0x40, 0x06, 0x06, 0x04, 0x0b, 0x19, + 0x0b, 0x11, 0x1f, 0x04, 0x03, 0x0b, 0x27, 0x0b, 0x03, 0x03, 0x11, 0x19, 0x1b, 0x14, 0x1b, + 0x04, 0x0b, 0x19, 0x0b, 0x11, 0x1f, 0x04, 0x03, 0x0b, 0x27, 0x0b, 0x03, 0x03, 0x11, 0x19, + 0x1b, 0x14, 0x1b, 0x24, 0x41, 0x3e, 0x40, 0x48, 0x40, 0x06, 0x07, 0x06, 0x14, 0x14, 0x2e, + 0x07, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, + 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x14, 0x14, 0x23, 0x24, 0x23, 0x24, 0x09, 0x17, 0x44, + 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x04, 0x04, 0x09, 0x40, 0x0f, 0x09, 0x03, 0x03, + 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x16, 0x46, 0x14, 0x11, 0x09, 0x40, 0x09, 0x16, 0x03, + 0x23, 0x09, 0x31, 0x1c, 0x23, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x24, 0x06, 0x42, 0x40, + 0x44, 0x55, 0x64, 0x27, 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06, 0x21, 0x0c, 0x1d, 0x12, + 0x21, 0x30, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x0d, 0x0e, 0x1a, 0x07, 0x04, 0x27, 0x24, 0x09, + 0x40, 0x41, 0x4c, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x1a, 0x0c, 0x12, 0x21, 0x05, + 0x04, 0x0c, 0x29, 0x0c, 0x04, 0x04, 0x12, 0x1a, 0x1c, 0x15, 0x1c, 0x05, 0x0c, 0x1a, 0x0c, + 0x12, 0x21, 0x05, 0x04, 0x0c, 0x29, 0x0c, 0x04, 0x04, 0x12, 0x1a, 0x1c, 0x15, 0x1c, 0x25, + 0x41, 0x3e, 0x40, 0x48, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x30, 0x07, 0x11, 0x45, 0x41, + 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40, + 0x4e, 0x07, 0x15, 0x15, 0x24, 0x25, 0x24, 0x25, 0x09, 0x17, 0x45, 0x41, 0x17, 0x45, 0x41, + 0x40, 0x40, 0x40, 0x03, 0x03, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x02, 0x2e, 0x27, 0x1e, 0x07, + 0x25, 0x1e, 0x16, 0x46, 0x13, 0x11, 0x09, 0x40, 0x09, 0x16, 0x02, 0x24, 0x09, 0x32, 0x1d, + 0x24, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x56, 0x24, 0x06, 0x42, 0x40, 0x45, 0x56, 0x65, 0x27, + 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06, 0x22, 0x0c, 0x1d, 0x13, 0x22, 0x31, 0x3e, 0x3e, + 0x07, 0x07, 0x00, 0x0d, 0x0e, 0x1b, 0x07, 0x04, 0x27, 0x24, 0x08, 0x40, 0x41, 0x4d, 0x40, + 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x1b, 0x0c, 0x13, 0x22, 0x05, 0x04, 0x0c, 0x2a, 0x0c, + 0x04, 0x04, 0x13, 0x1b, 0x1c, 0x15, 0x1c, 0x05, 0x0c, 0x1b, 0x0c, 0x13, 0x22, 0x05, 0x04, + 0x0c, 0x2a, 0x0c, 0x04, 0x04, 0x13, 0x1b, 0x1c, 0x15, 0x1c, 0x25, 0x41, 0x3e, 0x40, 0x48, + 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x31, 0x07, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10, + 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x4f, 0x07, 0x15, 0x15, + 0x24, 0x25, 0x24, 0x25, 0x08, 0x17, 0x46, 0x41, 0x17, 0x46, 0x41, 0x40, 0x40, 0x40, 0x02, + 0x02, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2e, 0x27, 0x1e, 0x07, 0x25, 0x1e, 0x16, 0x47, + 0x12, 0x10, 0x08, 0x40, 0x08, 0x16, 0x01, 0x24, 0x08, 0x33, 0x1d, 0x24, 0x08, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x56, 0x25, 0x07, 0x41, 0x40, 0x45, 0x56, 0x65, 0x27, 0x40, 0x07, 0x40, 0x40, + 0x40, 0x0f, 0x07, 0x24, 0x0d, 0x1e, 0x15, 0x24, 0x33, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0e, + 0x0f, 0x1d, 0x07, 0x05, 0x27, 0x25, 0x08, 0x40, 0x40, 0x4d, 0x40, 0x40, 0x40, 0x07, 0x07, + 0x06, 0x0d, 0x1d, 0x0d, 0x15, 0x24, 0x06, 0x05, 0x0d, 0x2c, 0x0d, 0x05, 0x05, 0x15, 0x1d, + 0x1d, 0x16, 0x1d, 0x06, 0x0d, 0x1d, 0x0d, 0x15, 0x24, 0x06, 0x05, 0x0d, 0x2c, 0x0d, 0x05, + 0x05, 0x15, 0x1d, 0x1d, 0x16, 0x1d, 0x26, 0x40, 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07, + 0x16, 0x16, 0x33, 0x07, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07, + 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x16, 0x16, 0x25, 0x26, 0x25, 0x26, + 0x08, 0x17, 0x46, 0x40, 0x17, 0x46, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x08, 0x40, 0x0f, + 0x08, 0x01, 0x01, 0x2f, 0x27, 0x1f, 0x07, 0x26, 0x1f, 0x17, 0x47, 0x12, 0x10, 0x08, 0x40, + 0x08, 0x17, 0x01, 0x25, 0x08, 0x35, 0x1e, 0x25, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x26, + 0x07, 0x40, 0x40, 0x46, 0x57, 0x66, 0x27, 0x40, 0x07, 0x40, 0x40, 0x40, 0x0f, 0x07, 0x26, + 0x0e, 0x1f, 0x16, 0x26, 0x35, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0f, 0x0f, 0x1e, 0x07, 0x06, + 0x27, 0x26, 0x08, 0x40, 0x40, 0x4e, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0e, 0x1e, 0x0e, + 0x16, 0x26, 0x07, 0x06, 0x0e, 0x2e, 0x0e, 0x06, 0x06, 0x16, 0x1e, 0x1e, 0x17, 0x1e, 0x07, + 0x0e, 0x1e, 0x0e, 0x16, 0x26, 0x07, 0x06, 0x0e, 0x2e, 0x0e, 0x06, 0x06, 0x16, 0x1e, 0x1e, + 0x17, 0x1e, 0x27, 0x40, 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x35, 0x07, + 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, + 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x17, 0x17, 0x26, 0x27, 0x26, 0x27, 0x08, 0x17, 0x47, 0x40, + 0x17, 0x47, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f, + 0x27, 0x1f, 0x07, 0x27, 0x1f, 0x17, 0x47, 0x11, 0x10, 0x08, 0x40, 0x08, 0x17, 0x00, 0x26, + 0x08, 0x36, 0x1f, 0x26, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x27, 0x07, 0x40, 0x40, 0x47, + 0x57, 0x67, 0x27, 0x40, 0x07, 0x40, 0x40, 0x40, 0x0f, 0x07, 0x27, 0x0f, 0x1f, 0x17, 0x27, + 0x37, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0f, 0x0f, 0x1f, 0x07, 0x07, 0x27, 0x27, 0x08, 0x40, + 0x40, 0x4f, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0f, 0x1f, 0x0f, 0x17, 0x27, 0x07, 0x07, + 0x0f, 0x2f, 0x0f, 0x07, 0x07, 0x17, 0x1f, 0x1f, 0x17, 0x1f, 0x07, 0x0f, 0x1f, 0x0f, 0x17, + 0x27, 0x07, 0x07, 0x0f, 0x2f, 0x0f, 0x07, 0x07, 0x17, 0x1f, 0x1f, 0x17, 0x1f, 0x27, 0x40, + 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x37, 0x07, 0x10, 0x47, 0x40, 0x1f, + 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x4f, + 0x07, 0x17, 0x17, 0x27, 0x27, 0x27, 0x27, 0x08, 0x17, 0x47, 0x40, 0x17, 0x47, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f, 0x27, 0x1f, 0x07, 0x27, + 0x1f, 0x17, 0x47, 0x10, 0x10, 0x08, 0x40, 0x08, 0x17, 0x00, 0x27, 0x08, 0x37, 0x1f, 0x27, + 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x48, 0x48, 0x60, 0x40, 0x27, 0x07, 0x07, 0x1f, 0x40, + 0x48, 0x40, 0x40, 0x17, 0x0f, 0x48, 0x68, 0x40, 0x07, 0x68, 0x68, 0x68, 0x68, 0x68, 0x07, + 0x07, 0x0f, 0x3e, 0x17, 0x40, 0x07, 0x68, 0x27, 0x50, 0x17, 0x40, 0x07, 0x1f, 0x40, 0x40, + 0x40, 0x48, 0x48, 0x58, 0x60, 0x50, 0x60, 0x68, 0x60, 0x58, 0x68, 0x68, 0x68, 0x58, 0x60, + 0x68, 0x68, 0x68, 0x50, 0x48, 0x58, 0x58, 0x60, 0x50, 0x60, 0x68, 0x60, 0x58, 0x68, 0x68, + 0x68, 0x58, 0x60, 0x68, 0x68, 0x68, 0x50, 0x48, 0x58, 0x07, 0x50, 0x58, 0x40, 0x40, 0x40, + 0x48, 0x07, 0x48, 0x48, 0x48, 0x68, 0x50, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17, + 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x40, 0x07, 0x40, 0x40, 0x40, + 0x07, 0x40, 0x07, 0x17, 0x17, 0x17, 0x50, 0x17, 0x17, 0x50, 0x40, 0x40, 0x40, 0x2f, 0x17, + 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x1f, 0x1f, 0x27, 0x0f, 0x07, 0x07, 0x0f, 0x40, 0x07, 0x3e, + 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x48, 0x17, 0x48, 0x48, 0x48, 0x17, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3e, 0x47, 0x47, 0x5f, 0x40, 0x27, 0x07, 0x07, 0x20, 0x40, 0x47, 0x40, 0x40, 0x17, + 0x0f, 0x47, 0x66, 0x40, 0x08, 0x66, 0x66, 0x66, 0x65, 0x65, 0x07, 0x07, 0x0f, 0x3e, 0x17, + 0x00, 0x07, 0x67, 0x27, 0x4e, 0x17, 0x40, 0x07, 0x1f, 0x40, 0x40, 0x40, 0x47, 0x47, 0x57, + 0x5f, 0x4f, 0x5f, 0x66, 0x5e, 0x57, 0x67, 0x67, 0x66, 0x57, 0x5f, 0x67, 0x67, 0x66, 0x4f, + 0x47, 0x56, 0x57, 0x5f, 0x4f, 0x5f, 0x66, 0x5e, 0x57, 0x67, 0x67, 0x66, 0x57, 0x5f, 0x67, + 0x67, 0x66, 0x4f, 0x47, 0x56, 0x08, 0x4f, 0x56, 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x47, + 0x47, 0x66, 0x4f, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, + 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x17, + 0x17, 0x17, 0x4f, 0x17, 0x17, 0x4f, 0x40, 0x40, 0x40, 0x2f, 0x17, 0x17, 0x40, 0x0f, 0x17, + 0x1f, 0x1f, 0x20, 0x27, 0x10, 0x07, 0x08, 0x10, 0x00, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f, + 0x17, 0x1f, 0x47, 0x17, 0x46, 0x47, 0x47, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x46, 0x47, + 0x5e, 0x40, 0x26, 0x06, 0x06, 0x20, 0x40, 0x47, 0x40, 0x40, 0x16, 0x0f, 0x47, 0x64, 0x40, + 0x08, 0x65, 0x64, 0x64, 0x63, 0x63, 0x07, 0x07, 0x0f, 0x3e, 0x17, 0x01, 0x07, 0x66, 0x27, + 0x4d, 0x17, 0x40, 0x07, 0x1e, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5e, 0x4e, 0x5e, 0x65, + 0x5d, 0x56, 0x66, 0x66, 0x64, 0x56, 0x5e, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x55, 0x56, 0x5e, + 0x4e, 0x5e, 0x65, 0x5d, 0x56, 0x66, 0x66, 0x64, 0x56, 0x5e, 0x66, 0x66, 0x64, 0x4e, 0x46, + 0x55, 0x09, 0x4f, 0x54, 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x64, 0x4e, 0x1f, + 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, + 0x07, 0x40, 0x40, 0x07, 0x00, 0x00, 0x01, 0x09, 0x01, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, + 0x16, 0x4f, 0x40, 0x40, 0x40, 0x2e, 0x17, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, + 0x10, 0x07, 0x09, 0x10, 0x01, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x46, 0x17, + 0x45, 0x46, 0x46, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x45, 0x47, 0x5e, 0x40, 0x25, 0x06, + 0x05, 0x20, 0x40, 0x47, 0x40, 0x40, 0x16, 0x0f, 0x47, 0x63, 0x40, 0x08, 0x64, 0x63, 0x62, + 0x60, 0x60, 0x07, 0x07, 0x0f, 0x3e, 0x17, 0x01, 0x07, 0x65, 0x27, 0x4c, 0x17, 0x40, 0x07, + 0x1d, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5d, 0x4e, 0x5d, 0x64, 0x5c, 0x56, 0x65, 0x65, + 0x63, 0x56, 0x5e, 0x65, 0x65, 0x63, 0x4d, 0x46, 0x54, 0x56, 0x5d, 0x4e, 0x5d, 0x64, 0x5c, + 0x56, 0x65, 0x65, 0x63, 0x56, 0x5e, 0x65, 0x65, 0x63, 0x4d, 0x46, 0x54, 0x09, 0x4f, 0x52, + 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x62, 0x4e, 0x1f, 0x16, 0x4f, 0x10, 0x07, + 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, + 0x00, 0x00, 0x01, 0x09, 0x01, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f, 0x40, 0x40, + 0x40, 0x2d, 0x17, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07, 0x09, 0x10, + 0x01, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x45, 0x17, 0x44, 0x45, 0x45, 0x17, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x3e, 0x44, 0x46, 0x5d, 0x40, 0x24, 0x05, 0x04, 0x21, 0x40, 0x46, + 0x40, 0x40, 0x15, 0x0f, 0x46, 0x61, 0x40, 0x09, 0x63, 0x61, 0x60, 0x5e, 0x5e, 0x07, 0x07, + 0x0e, 0x3e, 0x16, 0x02, 0x07, 0x64, 0x27, 0x4b, 0x16, 0x40, 0x06, 0x1c, 0x40, 0x40, 0x40, + 0x46, 0x46, 0x55, 0x5c, 0x4d, 0x5c, 0x63, 0x5b, 0x55, 0x64, 0x64, 0x61, 0x55, 0x5d, 0x64, + 0x64, 0x61, 0x4c, 0x45, 0x53, 0x55, 0x5c, 0x4d, 0x5c, 0x63, 0x5b, 0x55, 0x64, 0x64, 0x61, + 0x55, 0x5d, 0x64, 0x64, 0x61, 0x4c, 0x45, 0x53, 0x0a, 0x4e, 0x50, 0x40, 0x41, 0x40, 0x46, + 0x07, 0x46, 0x45, 0x45, 0x60, 0x4d, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e, + 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x01, 0x01, 0x02, 0x0a, + 0x02, 0x0a, 0x16, 0x17, 0x15, 0x4e, 0x17, 0x15, 0x4e, 0x40, 0x40, 0x40, 0x2c, 0x16, 0x16, + 0x40, 0x0f, 0x16, 0x1d, 0x1d, 0x21, 0x27, 0x11, 0x07, 0x0a, 0x11, 0x02, 0x06, 0x3e, 0x1e, + 0x16, 0x40, 0x0f, 0x16, 0x1d, 0x44, 0x16, 0x43, 0x44, 0x44, 0x16, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x3e, 0x43, 0x46, 0x5c, 0x40, 0x23, 0x04, 0x03, 0x21, 0x40, 0x46, 0x40, 0x40, 0x14, 0x0f, + 0x46, 0x60, 0x40, 0x09, 0x61, 0x60, 0x5e, 0x5b, 0x5b, 0x07, 0x07, 0x0e, 0x3e, 0x16, 0x03, + 0x07, 0x63, 0x27, 0x49, 0x16, 0x40, 0x06, 0x1b, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5b, + 0x4c, 0x5b, 0x61, 0x59, 0x54, 0x63, 0x63, 0x60, 0x54, 0x5c, 0x63, 0x63, 0x60, 0x4b, 0x44, + 0x51, 0x54, 0x5b, 0x4c, 0x5b, 0x61, 0x59, 0x54, 0x63, 0x63, 0x60, 0x54, 0x5c, 0x63, 0x63, + 0x60, 0x4b, 0x44, 0x51, 0x0b, 0x4e, 0x4e, 0x40, 0x41, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, + 0x5e, 0x4c, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, + 0x14, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x01, 0x01, 0x03, 0x0b, 0x03, 0x0b, 0x16, 0x17, + 0x14, 0x4e, 0x17, 0x14, 0x4e, 0x40, 0x40, 0x40, 0x2b, 0x16, 0x16, 0x40, 0x0f, 0x16, 0x1c, + 0x1c, 0x21, 0x27, 0x11, 0x07, 0x0b, 0x11, 0x03, 0x06, 0x3e, 0x1e, 0x16, 0x40, 0x0f, 0x16, + 0x1c, 0x43, 0x16, 0x41, 0x43, 0x43, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x42, 0x46, 0x5c, + 0x40, 0x22, 0x04, 0x02, 0x21, 0x40, 0x46, 0x40, 0x40, 0x14, 0x0f, 0x46, 0x5e, 0x40, 0x09, + 0x60, 0x5e, 0x5c, 0x59, 0x59, 0x07, 0x07, 0x0e, 0x3e, 0x16, 0x03, 0x07, 0x62, 0x27, 0x48, + 0x16, 0x40, 0x06, 0x1a, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5a, 0x4c, 0x5a, 0x60, 0x58, + 0x54, 0x62, 0x62, 0x5e, 0x54, 0x5c, 0x62, 0x62, 0x5e, 0x4a, 0x44, 0x50, 0x54, 0x5a, 0x4c, + 0x5a, 0x60, 0x58, 0x54, 0x62, 0x62, 0x5e, 0x54, 0x5c, 0x62, 0x62, 0x5e, 0x4a, 0x44, 0x50, + 0x0b, 0x4e, 0x4c, 0x40, 0x41, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5c, 0x4c, 0x1e, 0x14, + 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, + 0x40, 0x41, 0x07, 0x01, 0x01, 0x03, 0x0b, 0x03, 0x0b, 0x16, 0x17, 0x14, 0x4e, 0x17, 0x14, + 0x4e, 0x40, 0x40, 0x40, 0x2a, 0x16, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21, 0x27, 0x11, + 0x07, 0x0b, 0x11, 0x03, 0x06, 0x3e, 0x1e, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x42, 0x16, 0x40, + 0x42, 0x42, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x41, 0x45, 0x5b, 0x40, 0x21, 0x03, 0x01, + 0x22, 0x40, 0x45, 0x40, 0x40, 0x13, 0x0f, 0x45, 0x5d, 0x40, 0x0a, 0x5f, 0x5d, 0x5a, 0x56, + 0x56, 0x07, 0x07, 0x0d, 0x3e, 0x15, 0x04, 0x07, 0x61, 0x27, 0x47, 0x15, 0x40, 0x05, 0x19, + 0x40, 0x40, 0x40, 0x45, 0x45, 0x53, 0x59, 0x4b, 0x59, 0x5f, 0x57, 0x53, 0x61, 0x61, 0x5d, + 0x53, 0x5b, 0x61, 0x61, 0x5d, 0x49, 0x43, 0x4f, 0x53, 0x59, 0x4b, 0x59, 0x5f, 0x57, 0x53, + 0x61, 0x61, 0x5d, 0x53, 0x5b, 0x61, 0x61, 0x5d, 0x49, 0x43, 0x4f, 0x0c, 0x4d, 0x4a, 0x40, + 0x42, 0x40, 0x45, 0x07, 0x45, 0x43, 0x43, 0x5a, 0x4b, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, + 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02, + 0x02, 0x04, 0x0c, 0x04, 0x0c, 0x15, 0x17, 0x13, 0x4d, 0x17, 0x13, 0x4d, 0x40, 0x40, 0x40, + 0x29, 0x15, 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x1b, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x04, + 0x05, 0x3e, 0x1d, 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x41, 0x15, 0x00, 0x41, 0x41, 0x15, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x3e, 0x40, 0x45, 0x5b, 0x40, 0x20, 0x02, 0x00, 0x22, 0x40, 0x45, 0x40, + 0x40, 0x12, 0x0f, 0x45, 0x5b, 0x40, 0x0a, 0x5e, 0x5b, 0x59, 0x54, 0x54, 0x07, 0x07, 0x0d, + 0x3e, 0x15, 0x04, 0x07, 0x60, 0x27, 0x46, 0x15, 0x40, 0x05, 0x18, 0x40, 0x40, 0x40, 0x45, + 0x45, 0x53, 0x58, 0x4b, 0x58, 0x5e, 0x56, 0x53, 0x60, 0x60, 0x5b, 0x53, 0x5b, 0x60, 0x60, + 0x5b, 0x48, 0x43, 0x4e, 0x53, 0x58, 0x4b, 0x58, 0x5e, 0x56, 0x53, 0x60, 0x60, 0x5b, 0x53, + 0x5b, 0x60, 0x60, 0x5b, 0x48, 0x43, 0x4e, 0x0c, 0x4d, 0x49, 0x40, 0x42, 0x40, 0x45, 0x07, + 0x45, 0x43, 0x43, 0x59, 0x4b, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, + 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02, 0x02, 0x04, 0x0c, 0x04, + 0x0c, 0x15, 0x17, 0x12, 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x28, 0x15, 0x15, 0x40, + 0x0f, 0x15, 0x1a, 0x1a, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x04, 0x05, 0x3e, 0x1d, 0x15, + 0x40, 0x0f, 0x15, 0x1a, 0x40, 0x15, 0x01, 0x40, 0x40, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, + 0x00, 0x45, 0x5a, 0x40, 0x1f, 0x02, 0x40, 0x22, 0x40, 0x45, 0x40, 0x40, 0x12, 0x0f, 0x45, + 0x59, 0x40, 0x0a, 0x5c, 0x59, 0x57, 0x51, 0x51, 0x07, 0x07, 0x0d, 0x3e, 0x15, 0x05, 0x07, + 0x5f, 0x27, 0x44, 0x15, 0x40, 0x05, 0x17, 0x40, 0x40, 0x40, 0x45, 0x45, 0x52, 0x57, 0x4a, + 0x57, 0x5c, 0x54, 0x52, 0x5f, 0x5f, 0x59, 0x52, 0x5a, 0x5f, 0x5f, 0x59, 0x47, 0x42, 0x4c, + 0x52, 0x57, 0x4a, 0x57, 0x5c, 0x54, 0x52, 0x5f, 0x5f, 0x59, 0x52, 0x5a, 0x5f, 0x5f, 0x59, + 0x47, 0x42, 0x4c, 0x0d, 0x4d, 0x47, 0x40, 0x42, 0x40, 0x45, 0x07, 0x45, 0x42, 0x42, 0x57, + 0x4a, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, + 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02, 0x02, 0x05, 0x0d, 0x05, 0x0d, 0x15, 0x17, 0x12, + 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x27, 0x15, 0x15, 0x40, 0x0f, 0x15, 0x1a, 0x1a, + 0x22, 0x27, 0x12, 0x07, 0x0d, 0x12, 0x05, 0x05, 0x3e, 0x1d, 0x15, 0x40, 0x0f, 0x15, 0x1a, + 0x00, 0x15, 0x03, 0x00, 0x00, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x01, 0x44, 0x59, 0x40, + 0x1e, 0x01, 0x41, 0x23, 0x40, 0x44, 0x40, 0x40, 0x11, 0x0f, 0x44, 0x58, 0x40, 0x0b, 0x5b, + 0x58, 0x55, 0x4f, 0x4f, 0x07, 0x07, 0x0c, 0x3e, 0x14, 0x06, 0x07, 0x5e, 0x27, 0x43, 0x14, + 0x40, 0x04, 0x16, 0x40, 0x40, 0x40, 0x44, 0x44, 0x51, 0x56, 0x49, 0x56, 0x5b, 0x53, 0x51, + 0x5e, 0x5e, 0x58, 0x51, 0x59, 0x5e, 0x5e, 0x58, 0x46, 0x41, 0x4b, 0x51, 0x56, 0x49, 0x56, + 0x5b, 0x53, 0x51, 0x5e, 0x5e, 0x58, 0x51, 0x59, 0x5e, 0x5e, 0x58, 0x46, 0x41, 0x4b, 0x0e, + 0x4c, 0x45, 0x40, 0x43, 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x55, 0x49, 0x1c, 0x11, 0x4c, + 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, + 0x43, 0x07, 0x03, 0x03, 0x06, 0x0e, 0x06, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, + 0x40, 0x40, 0x40, 0x26, 0x14, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, + 0x0e, 0x13, 0x06, 0x04, 0x3e, 0x1c, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x01, 0x14, 0x04, 0x01, + 0x01, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x02, 0x44, 0x59, 0x40, 0x1d, 0x01, 0x42, 0x23, + 0x40, 0x44, 0x40, 0x40, 0x11, 0x0f, 0x44, 0x56, 0x40, 0x0b, 0x5a, 0x56, 0x53, 0x4c, 0x4c, + 0x07, 0x07, 0x0c, 0x3e, 0x14, 0x06, 0x07, 0x5d, 0x27, 0x42, 0x14, 0x40, 0x04, 0x15, 0x40, + 0x40, 0x40, 0x44, 0x44, 0x51, 0x55, 0x49, 0x55, 0x5a, 0x52, 0x51, 0x5d, 0x5d, 0x56, 0x51, + 0x59, 0x5d, 0x5d, 0x56, 0x45, 0x41, 0x4a, 0x51, 0x55, 0x49, 0x55, 0x5a, 0x52, 0x51, 0x5d, + 0x5d, 0x56, 0x51, 0x59, 0x5d, 0x5d, 0x56, 0x45, 0x41, 0x4a, 0x0e, 0x4c, 0x43, 0x40, 0x43, + 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x53, 0x49, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, + 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x03, 0x03, + 0x06, 0x0e, 0x06, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40, 0x40, 0x25, + 0x14, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13, 0x06, 0x04, + 0x3e, 0x1c, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x02, 0x14, 0x05, 0x02, 0x02, 0x14, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x3e, 0x03, 0x44, 0x58, 0x40, 0x1c, 0x00, 0x43, 0x23, 0x40, 0x44, 0x40, 0x40, + 0x10, 0x0f, 0x44, 0x55, 0x40, 0x0b, 0x59, 0x55, 0x51, 0x4a, 0x4a, 0x07, 0x07, 0x0c, 0x3d, + 0x14, 0x07, 0x07, 0x5c, 0x27, 0x41, 0x14, 0x40, 0x04, 0x14, 0x40, 0x40, 0x40, 0x44, 0x44, + 0x50, 0x54, 0x48, 0x54, 0x59, 0x51, 0x50, 0x5c, 0x5c, 0x55, 0x50, 0x58, 0x5c, 0x5c, 0x55, + 0x44, 0x40, 0x49, 0x50, 0x54, 0x48, 0x54, 0x59, 0x51, 0x50, 0x5c, 0x5c, 0x55, 0x50, 0x58, + 0x5c, 0x5c, 0x55, 0x44, 0x40, 0x49, 0x0f, 0x4c, 0x41, 0x40, 0x43, 0x40, 0x44, 0x07, 0x44, + 0x40, 0x40, 0x51, 0x48, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07, + 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x03, 0x03, 0x07, 0x0f, 0x07, 0x0f, + 0x14, 0x17, 0x10, 0x4c, 0x17, 0x10, 0x4c, 0x40, 0x40, 0x40, 0x24, 0x14, 0x14, 0x40, 0x0f, + 0x14, 0x18, 0x18, 0x23, 0x27, 0x13, 0x07, 0x0f, 0x13, 0x07, 0x04, 0x3e, 0x1c, 0x14, 0x40, + 0x0f, 0x14, 0x18, 0x03, 0x14, 0x06, 0x03, 0x03, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x04, + 0x43, 0x57, 0x40, 0x1b, 0x40, 0x44, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0f, 0x0f, 0x43, 0x53, + 0x40, 0x0c, 0x57, 0x53, 0x4f, 0x47, 0x47, 0x07, 0x07, 0x0b, 0x3b, 0x13, 0x08, 0x07, 0x5b, + 0x27, 0x00, 0x13, 0x40, 0x03, 0x13, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x53, 0x47, 0x53, + 0x57, 0x4f, 0x4f, 0x5b, 0x5b, 0x53, 0x4f, 0x57, 0x5b, 0x5b, 0x53, 0x43, 0x00, 0x47, 0x4f, + 0x53, 0x47, 0x53, 0x57, 0x4f, 0x4f, 0x5b, 0x5b, 0x53, 0x4f, 0x57, 0x5b, 0x5b, 0x53, 0x43, + 0x00, 0x47, 0x10, 0x4b, 0x00, 0x40, 0x44, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4f, 0x47, + 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, + 0x14, 0x07, 0x40, 0x44, 0x07, 0x04, 0x04, 0x08, 0x10, 0x08, 0x10, 0x13, 0x17, 0x0f, 0x4b, + 0x17, 0x0f, 0x4b, 0x40, 0x40, 0x40, 0x23, 0x13, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, + 0x27, 0x14, 0x07, 0x10, 0x14, 0x08, 0x03, 0x3e, 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x04, + 0x13, 0x08, 0x04, 0x04, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x05, 0x43, 0x57, 0x40, 0x1a, + 0x40, 0x45, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0f, 0x0f, 0x43, 0x52, 0x40, 0x0c, 0x56, 0x52, + 0x4d, 0x45, 0x45, 0x07, 0x07, 0x0b, 0x3a, 0x13, 0x08, 0x07, 0x5a, 0x27, 0x01, 0x13, 0x40, + 0x03, 0x12, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x52, 0x47, 0x52, 0x56, 0x4e, 0x4f, 0x5a, + 0x5a, 0x52, 0x4f, 0x57, 0x5a, 0x5a, 0x52, 0x42, 0x00, 0x46, 0x4f, 0x52, 0x47, 0x52, 0x56, + 0x4e, 0x4f, 0x5a, 0x5a, 0x52, 0x4f, 0x57, 0x5a, 0x5a, 0x52, 0x42, 0x00, 0x46, 0x10, 0x4b, + 0x02, 0x40, 0x44, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4d, 0x47, 0x1b, 0x0f, 0x4b, 0x14, + 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x44, + 0x07, 0x04, 0x04, 0x08, 0x10, 0x08, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f, 0x4b, 0x40, + 0x40, 0x40, 0x22, 0x13, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14, 0x07, 0x10, + 0x14, 0x08, 0x03, 0x3e, 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x05, 0x13, 0x09, 0x05, 0x05, + 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x06, 0x43, 0x56, 0x40, 0x19, 0x41, 0x46, 0x24, 0x40, + 0x43, 0x40, 0x40, 0x0e, 0x0f, 0x43, 0x50, 0x40, 0x0c, 0x55, 0x50, 0x4b, 0x42, 0x42, 0x07, + 0x07, 0x0b, 0x38, 0x13, 0x09, 0x07, 0x59, 0x27, 0x02, 0x13, 0x40, 0x03, 0x11, 0x40, 0x40, + 0x40, 0x43, 0x43, 0x4e, 0x51, 0x46, 0x51, 0x55, 0x4d, 0x4e, 0x59, 0x59, 0x50, 0x4e, 0x56, + 0x59, 0x59, 0x50, 0x41, 0x01, 0x45, 0x4e, 0x51, 0x46, 0x51, 0x55, 0x4d, 0x4e, 0x59, 0x59, + 0x50, 0x4e, 0x56, 0x59, 0x59, 0x50, 0x41, 0x01, 0x45, 0x11, 0x4b, 0x04, 0x40, 0x44, 0x40, + 0x43, 0x07, 0x43, 0x01, 0x01, 0x4b, 0x46, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e, + 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x04, 0x04, 0x09, + 0x11, 0x09, 0x11, 0x13, 0x17, 0x0e, 0x4b, 0x17, 0x0e, 0x4b, 0x40, 0x40, 0x40, 0x21, 0x13, + 0x13, 0x40, 0x0f, 0x13, 0x16, 0x16, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x09, 0x03, 0x3d, + 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x16, 0x06, 0x13, 0x0a, 0x06, 0x06, 0x13, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3e, 0x06, 0x43, 0x56, 0x40, 0x18, 0x42, 0x47, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0d, + 0x0f, 0x43, 0x4f, 0x40, 0x0c, 0x54, 0x4f, 0x4a, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x36, 0x12, + 0x09, 0x07, 0x59, 0x27, 0x03, 0x12, 0x40, 0x02, 0x10, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4e, + 0x51, 0x46, 0x51, 0x54, 0x4c, 0x4e, 0x59, 0x59, 0x4f, 0x4e, 0x56, 0x59, 0x59, 0x4f, 0x41, + 0x01, 0x44, 0x4e, 0x51, 0x46, 0x51, 0x54, 0x4c, 0x4e, 0x59, 0x59, 0x4f, 0x4e, 0x56, 0x59, + 0x59, 0x4f, 0x41, 0x01, 0x44, 0x11, 0x4b, 0x05, 0x40, 0x45, 0x40, 0x43, 0x07, 0x43, 0x01, + 0x01, 0x4a, 0x46, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, + 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x45, 0x07, 0x04, 0x04, 0x09, 0x11, 0x09, 0x11, 0x12, + 0x17, 0x0d, 0x4b, 0x17, 0x0d, 0x4b, 0x40, 0x40, 0x40, 0x20, 0x12, 0x12, 0x40, 0x0f, 0x12, + 0x15, 0x15, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x09, 0x02, 0x3b, 0x1a, 0x12, 0x40, 0x0f, + 0x12, 0x15, 0x06, 0x12, 0x0b, 0x06, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x07, 0x42, + 0x55, 0x40, 0x18, 0x42, 0x47, 0x25, 0x40, 0x42, 0x40, 0x40, 0x0d, 0x0f, 0x42, 0x4d, 0x40, + 0x0d, 0x52, 0x4d, 0x48, 0x02, 0x02, 0x07, 0x07, 0x0a, 0x35, 0x12, 0x0a, 0x07, 0x58, 0x27, + 0x05, 0x12, 0x40, 0x02, 0x10, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4d, 0x50, 0x45, 0x50, 0x52, + 0x4a, 0x4d, 0x58, 0x58, 0x4d, 0x4d, 0x55, 0x58, 0x58, 0x4d, 0x40, 0x02, 0x42, 0x4d, 0x50, + 0x45, 0x50, 0x52, 0x4a, 0x4d, 0x58, 0x58, 0x4d, 0x4d, 0x55, 0x58, 0x58, 0x4d, 0x40, 0x02, + 0x42, 0x12, 0x4a, 0x07, 0x40, 0x45, 0x40, 0x42, 0x07, 0x42, 0x02, 0x02, 0x48, 0x45, 0x1a, + 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, + 0x07, 0x40, 0x45, 0x07, 0x05, 0x05, 0x0a, 0x12, 0x0a, 0x12, 0x12, 0x17, 0x0d, 0x4a, 0x17, + 0x0d, 0x4a, 0x40, 0x40, 0x40, 0x20, 0x12, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15, 0x25, 0x27, + 0x15, 0x07, 0x12, 0x15, 0x0a, 0x02, 0x3a, 0x1a, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x07, 0x12, + 0x0d, 0x07, 0x07, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x08, 0x42, 0x54, 0x40, 0x17, 0x43, + 0x48, 0x25, 0x40, 0x42, 0x40, 0x40, 0x0c, 0x0f, 0x42, 0x4b, 0x40, 0x0d, 0x51, 0x4b, 0x46, + 0x04, 0x04, 0x07, 0x07, 0x0a, 0x33, 0x12, 0x0b, 0x07, 0x57, 0x27, 0x06, 0x12, 0x40, 0x02, + 0x0f, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4c, 0x4f, 0x44, 0x4f, 0x51, 0x49, 0x4c, 0x57, 0x57, + 0x4b, 0x4c, 0x54, 0x57, 0x57, 0x4b, 0x00, 0x03, 0x41, 0x4c, 0x4f, 0x44, 0x4f, 0x51, 0x49, + 0x4c, 0x57, 0x57, 0x4b, 0x4c, 0x54, 0x57, 0x57, 0x4b, 0x00, 0x03, 0x41, 0x13, 0x4a, 0x09, + 0x40, 0x45, 0x40, 0x42, 0x07, 0x42, 0x03, 0x03, 0x46, 0x44, 0x1a, 0x0c, 0x4a, 0x15, 0x07, + 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, + 0x05, 0x05, 0x0b, 0x13, 0x0b, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, + 0x40, 0x1f, 0x12, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, + 0x0b, 0x02, 0x39, 0x1a, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x08, 0x12, 0x0e, 0x08, 0x08, 0x12, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x3e, 0x09, 0x42, 0x54, 0x40, 0x16, 0x43, 0x49, 0x25, 0x40, 0x42, + 0x40, 0x40, 0x0c, 0x0f, 0x42, 0x4a, 0x40, 0x0d, 0x50, 0x4a, 0x44, 0x07, 0x07, 0x07, 0x07, + 0x0a, 0x32, 0x12, 0x0b, 0x07, 0x56, 0x27, 0x07, 0x12, 0x40, 0x02, 0x0e, 0x40, 0x40, 0x40, + 0x42, 0x42, 0x4c, 0x4e, 0x44, 0x4e, 0x50, 0x48, 0x4c, 0x56, 0x56, 0x4a, 0x4c, 0x54, 0x56, + 0x56, 0x4a, 0x01, 0x03, 0x40, 0x4c, 0x4e, 0x44, 0x4e, 0x50, 0x48, 0x4c, 0x56, 0x56, 0x4a, + 0x4c, 0x54, 0x56, 0x56, 0x4a, 0x01, 0x03, 0x40, 0x13, 0x4a, 0x0b, 0x40, 0x45, 0x40, 0x42, + 0x07, 0x42, 0x03, 0x03, 0x44, 0x44, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, + 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x05, 0x05, 0x0b, 0x13, + 0x0b, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1e, 0x12, 0x12, + 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0b, 0x02, 0x38, 0x1a, + 0x12, 0x40, 0x0f, 0x12, 0x14, 0x09, 0x12, 0x0f, 0x09, 0x09, 0x12, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x3e, 0x0a, 0x41, 0x53, 0x40, 0x15, 0x44, 0x4a, 0x26, 0x40, 0x41, 0x40, 0x40, 0x0b, 0x0f, + 0x41, 0x48, 0x40, 0x0e, 0x4f, 0x48, 0x42, 0x09, 0x09, 0x07, 0x07, 0x09, 0x30, 0x11, 0x0c, + 0x07, 0x55, 0x27, 0x08, 0x11, 0x40, 0x01, 0x0d, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4b, 0x4d, + 0x43, 0x4d, 0x4f, 0x47, 0x4b, 0x55, 0x55, 0x48, 0x4b, 0x53, 0x55, 0x55, 0x48, 0x02, 0x04, + 0x00, 0x4b, 0x4d, 0x43, 0x4d, 0x4f, 0x47, 0x4b, 0x55, 0x55, 0x48, 0x4b, 0x53, 0x55, 0x55, + 0x48, 0x02, 0x04, 0x00, 0x14, 0x49, 0x0d, 0x40, 0x46, 0x40, 0x41, 0x07, 0x41, 0x04, 0x04, + 0x42, 0x43, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, + 0x0b, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x06, 0x06, 0x0c, 0x14, 0x0c, 0x14, 0x11, 0x17, + 0x0b, 0x49, 0x17, 0x0b, 0x49, 0x40, 0x40, 0x40, 0x1d, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x13, + 0x13, 0x26, 0x27, 0x16, 0x07, 0x14, 0x16, 0x0c, 0x01, 0x36, 0x19, 0x11, 0x40, 0x0f, 0x11, + 0x13, 0x0a, 0x11, 0x10, 0x0a, 0x0a, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0b, 0x41, 0x52, + 0x40, 0x14, 0x45, 0x4b, 0x26, 0x40, 0x41, 0x40, 0x40, 0x0a, 0x0f, 0x41, 0x47, 0x40, 0x0e, + 0x4d, 0x47, 0x40, 0x0c, 0x0c, 0x07, 0x07, 0x09, 0x2f, 0x11, 0x0d, 0x07, 0x54, 0x27, 0x0a, + 0x11, 0x40, 0x01, 0x0c, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4c, 0x42, 0x4c, 0x4d, 0x45, + 0x4a, 0x54, 0x54, 0x47, 0x4a, 0x52, 0x54, 0x54, 0x47, 0x03, 0x05, 0x02, 0x4a, 0x4c, 0x42, + 0x4c, 0x4d, 0x45, 0x4a, 0x54, 0x54, 0x47, 0x4a, 0x52, 0x54, 0x54, 0x47, 0x03, 0x05, 0x02, + 0x15, 0x49, 0x0f, 0x40, 0x46, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x40, 0x42, 0x19, 0x0a, + 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, + 0x40, 0x46, 0x07, 0x06, 0x06, 0x0d, 0x15, 0x0d, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, + 0x49, 0x40, 0x40, 0x40, 0x1c, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, + 0x07, 0x15, 0x16, 0x0d, 0x01, 0x35, 0x19, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x0b, 0x11, 0x12, + 0x0b, 0x0b, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0c, 0x41, 0x52, 0x40, 0x13, 0x45, 0x4c, + 0x26, 0x40, 0x41, 0x40, 0x40, 0x0a, 0x0f, 0x41, 0x45, 0x40, 0x0e, 0x4c, 0x45, 0x01, 0x0e, + 0x0e, 0x07, 0x07, 0x09, 0x2d, 0x11, 0x0d, 0x07, 0x53, 0x27, 0x0b, 0x11, 0x40, 0x01, 0x0b, + 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4b, 0x42, 0x4b, 0x4c, 0x44, 0x4a, 0x53, 0x53, 0x45, + 0x4a, 0x52, 0x53, 0x53, 0x45, 0x04, 0x05, 0x03, 0x4a, 0x4b, 0x42, 0x4b, 0x4c, 0x44, 0x4a, + 0x53, 0x53, 0x45, 0x4a, 0x52, 0x53, 0x53, 0x45, 0x04, 0x05, 0x03, 0x15, 0x49, 0x11, 0x40, + 0x46, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x01, 0x42, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, + 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x06, + 0x06, 0x0d, 0x15, 0x0d, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40, 0x40, 0x40, + 0x1b, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15, 0x16, 0x0d, + 0x01, 0x34, 0x19, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x0c, 0x11, 0x13, 0x0c, 0x0c, 0x11, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x3e, 0x0d, 0x40, 0x51, 0x40, 0x12, 0x46, 0x4d, 0x27, 0x40, 0x40, 0x40, + 0x40, 0x09, 0x0f, 0x40, 0x44, 0x40, 0x0f, 0x4b, 0x44, 0x03, 0x11, 0x11, 0x07, 0x07, 0x08, + 0x2c, 0x10, 0x0e, 0x07, 0x52, 0x27, 0x0c, 0x10, 0x40, 0x00, 0x0a, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x49, 0x4a, 0x41, 0x4a, 0x4b, 0x43, 0x49, 0x52, 0x52, 0x44, 0x49, 0x51, 0x52, 0x52, + 0x44, 0x05, 0x06, 0x04, 0x49, 0x4a, 0x41, 0x4a, 0x4b, 0x43, 0x49, 0x52, 0x52, 0x44, 0x49, + 0x51, 0x52, 0x52, 0x44, 0x05, 0x06, 0x04, 0x16, 0x48, 0x13, 0x40, 0x47, 0x40, 0x40, 0x07, + 0x40, 0x06, 0x06, 0x03, 0x41, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x18, 0x09, 0x48, 0x17, + 0x07, 0x40, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x07, 0x07, 0x0e, 0x16, 0x0e, + 0x16, 0x10, 0x17, 0x09, 0x48, 0x17, 0x09, 0x48, 0x40, 0x40, 0x40, 0x1a, 0x10, 0x10, 0x40, + 0x0f, 0x10, 0x11, 0x11, 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0e, 0x00, 0x33, 0x18, 0x10, + 0x40, 0x0f, 0x10, 0x11, 0x0d, 0x10, 0x14, 0x0d, 0x0d, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, + 0x0e, 0x40, 0x51, 0x40, 0x11, 0x47, 0x4e, 0x27, 0x40, 0x40, 0x40, 0x40, 0x08, 0x0f, 0x40, + 0x42, 0x40, 0x0f, 0x4a, 0x42, 0x04, 0x13, 0x13, 0x07, 0x07, 0x08, 0x2a, 0x10, 0x0e, 0x07, + 0x51, 0x27, 0x0d, 0x10, 0x40, 0x00, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x41, + 0x49, 0x4a, 0x42, 0x49, 0x51, 0x51, 0x42, 0x49, 0x51, 0x51, 0x51, 0x42, 0x06, 0x06, 0x05, + 0x49, 0x49, 0x41, 0x49, 0x4a, 0x42, 0x49, 0x51, 0x51, 0x42, 0x49, 0x51, 0x51, 0x51, 0x42, + 0x06, 0x06, 0x05, 0x16, 0x48, 0x14, 0x40, 0x47, 0x40, 0x40, 0x07, 0x40, 0x06, 0x06, 0x04, + 0x41, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, + 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x07, 0x07, 0x0e, 0x16, 0x0e, 0x16, 0x10, 0x17, 0x08, + 0x48, 0x17, 0x08, 0x48, 0x40, 0x40, 0x40, 0x19, 0x10, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, + 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0e, 0x00, 0x31, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10, + 0x0e, 0x10, 0x15, 0x0e, 0x0e, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0f, 0x40, 0x50, 0x40, + 0x10, 0x47, 0x4f, 0x27, 0x40, 0x40, 0x40, 0x40, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x0f, 0x48, + 0x40, 0x06, 0x16, 0x16, 0x07, 0x07, 0x08, 0x28, 0x10, 0x0f, 0x07, 0x50, 0x27, 0x0f, 0x10, + 0x40, 0x00, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40, 0x48, + 0x50, 0x50, 0x40, 0x48, 0x50, 0x50, 0x50, 0x40, 0x07, 0x07, 0x07, 0x48, 0x48, 0x40, 0x48, + 0x48, 0x40, 0x48, 0x50, 0x50, 0x40, 0x48, 0x50, 0x50, 0x50, 0x40, 0x07, 0x07, 0x07, 0x17, + 0x48, 0x16, 0x40, 0x47, 0x40, 0x40, 0x07, 0x40, 0x07, 0x07, 0x06, 0x40, 0x18, 0x08, 0x48, + 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, + 0x47, 0x07, 0x07, 0x07, 0x0f, 0x17, 0x0f, 0x17, 0x10, 0x17, 0x08, 0x48, 0x17, 0x08, 0x48, + 0x40, 0x40, 0x40, 0x18, 0x10, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27, 0x17, 0x07, + 0x17, 0x17, 0x0f, 0x00, 0x30, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x0f, 0x10, 0x17, 0x0f, + 0x0f, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x10, 0x00, 0x4f, 0x40, 0x0f, 0x48, 0x50, 0x28, + 0x40, 0x00, 0x40, 0x40, 0x07, 0x0f, 0x00, 0x00, 0x40, 0x10, 0x47, 0x00, 0x08, 0x18, 0x18, + 0x07, 0x07, 0x07, 0x27, 0x0f, 0x10, 0x07, 0x4f, 0x27, 0x10, 0x0f, 0x40, 0x40, 0x07, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x4f, 0x00, 0x47, + 0x4f, 0x4f, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, + 0x4f, 0x00, 0x47, 0x4f, 0x4f, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x18, 0x47, 0x18, 0x40, 0x48, + 0x40, 0x00, 0x07, 0x00, 0x08, 0x08, 0x08, 0x00, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, + 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, + 0x10, 0x18, 0x10, 0x18, 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x17, + 0x0f, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, + 0x2f, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x10, 0x0f, 0x18, 0x10, 0x10, 0x0f, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x3e, 0x11, 0x00, 0x4f, 0x40, 0x0e, 0x48, 0x51, 0x28, 0x40, 0x00, 0x40, 0x40, + 0x07, 0x0f, 0x00, 0x02, 0x40, 0x10, 0x46, 0x02, 0x0a, 0x1b, 0x1b, 0x07, 0x07, 0x07, 0x25, + 0x0f, 0x10, 0x07, 0x4e, 0x27, 0x11, 0x0f, 0x40, 0x40, 0x06, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x47, 0x46, 0x00, 0x46, 0x46, 0x01, 0x47, 0x4e, 0x4e, 0x02, 0x47, 0x4f, 0x4e, 0x4e, 0x02, + 0x09, 0x08, 0x09, 0x47, 0x46, 0x00, 0x46, 0x46, 0x01, 0x47, 0x4e, 0x4e, 0x02, 0x47, 0x4f, + 0x4e, 0x4e, 0x02, 0x09, 0x08, 0x09, 0x18, 0x47, 0x1a, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, + 0x08, 0x08, 0x0a, 0x00, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, + 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x10, 0x18, 0x10, 0x18, + 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x16, 0x0f, 0x0f, 0x40, 0x0f, + 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x2e, 0x17, 0x0f, 0x40, + 0x0f, 0x0f, 0x0f, 0x11, 0x0f, 0x19, 0x11, 0x11, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x12, + 0x00, 0x4e, 0x40, 0x0d, 0x49, 0x52, 0x28, 0x40, 0x00, 0x40, 0x40, 0x06, 0x0f, 0x00, 0x03, + 0x40, 0x10, 0x45, 0x03, 0x0c, 0x1d, 0x1d, 0x07, 0x07, 0x07, 0x24, 0x0f, 0x11, 0x07, 0x4d, + 0x27, 0x12, 0x0f, 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x45, 0x01, 0x45, + 0x45, 0x02, 0x46, 0x4d, 0x4d, 0x03, 0x46, 0x4e, 0x4d, 0x4d, 0x03, 0x0a, 0x09, 0x0a, 0x46, + 0x45, 0x01, 0x45, 0x45, 0x02, 0x46, 0x4d, 0x4d, 0x03, 0x46, 0x4e, 0x4d, 0x4d, 0x03, 0x0a, + 0x09, 0x0a, 0x19, 0x47, 0x1c, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x09, 0x09, 0x0c, 0x01, + 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, + 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x11, 0x19, 0x11, 0x19, 0x0f, 0x17, 0x06, 0x47, + 0x17, 0x06, 0x47, 0x40, 0x40, 0x40, 0x15, 0x0f, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x0e, 0x28, + 0x27, 0x18, 0x07, 0x19, 0x18, 0x11, 0x40, 0x2c, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x12, + 0x0f, 0x1a, 0x12, 0x12, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x13, 0x01, 0x4d, 0x40, 0x0c, + 0x4a, 0x53, 0x29, 0x40, 0x01, 0x40, 0x40, 0x05, 0x0f, 0x01, 0x05, 0x40, 0x11, 0x43, 0x05, + 0x0e, 0x20, 0x20, 0x07, 0x07, 0x06, 0x22, 0x0e, 0x12, 0x07, 0x4c, 0x27, 0x14, 0x0e, 0x40, + 0x41, 0x04, 0x40, 0x40, 0x40, 0x01, 0x01, 0x45, 0x44, 0x02, 0x44, 0x43, 0x04, 0x45, 0x4c, + 0x4c, 0x05, 0x45, 0x4d, 0x4c, 0x4c, 0x05, 0x0b, 0x0a, 0x0c, 0x45, 0x44, 0x02, 0x44, 0x43, + 0x04, 0x45, 0x4c, 0x4c, 0x05, 0x45, 0x4d, 0x4c, 0x4c, 0x05, 0x0b, 0x0a, 0x0c, 0x1a, 0x46, + 0x1e, 0x40, 0x49, 0x40, 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x0e, 0x02, 0x16, 0x05, 0x46, 0x19, + 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, + 0x07, 0x09, 0x09, 0x12, 0x1a, 0x12, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, + 0x40, 0x40, 0x14, 0x0e, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, + 0x19, 0x12, 0x41, 0x2b, 0x16, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x13, 0x0e, 0x1c, 0x13, 0x13, + 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x14, 0x01, 0x4d, 0x40, 0x0b, 0x4a, 0x54, 0x29, 0x40, + 0x01, 0x40, 0x40, 0x05, 0x0f, 0x01, 0x06, 0x40, 0x11, 0x42, 0x06, 0x10, 0x22, 0x22, 0x07, + 0x07, 0x06, 0x21, 0x0e, 0x12, 0x07, 0x4b, 0x27, 0x15, 0x0e, 0x40, 0x41, 0x03, 0x40, 0x40, + 0x40, 0x01, 0x01, 0x45, 0x43, 0x02, 0x43, 0x42, 0x05, 0x45, 0x4b, 0x4b, 0x06, 0x45, 0x4d, + 0x4b, 0x4b, 0x06, 0x0c, 0x0a, 0x0d, 0x45, 0x43, 0x02, 0x43, 0x42, 0x05, 0x45, 0x4b, 0x4b, + 0x06, 0x45, 0x4d, 0x4b, 0x4b, 0x06, 0x0c, 0x0a, 0x0d, 0x1a, 0x46, 0x20, 0x40, 0x49, 0x40, + 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x10, 0x02, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, + 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x09, 0x09, 0x12, + 0x1a, 0x12, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40, 0x13, 0x0e, + 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x12, 0x41, 0x2a, + 0x16, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x14, 0x0e, 0x1d, 0x14, 0x14, 0x0e, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3e, 0x15, 0x01, 0x4c, 0x40, 0x0a, 0x4b, 0x55, 0x29, 0x40, 0x01, 0x40, 0x40, 0x04, + 0x0f, 0x01, 0x08, 0x40, 0x11, 0x41, 0x08, 0x12, 0x25, 0x25, 0x07, 0x07, 0x06, 0x1f, 0x0e, + 0x13, 0x07, 0x4a, 0x27, 0x16, 0x0e, 0x40, 0x41, 0x02, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, + 0x42, 0x03, 0x42, 0x41, 0x06, 0x44, 0x4a, 0x4a, 0x08, 0x44, 0x4c, 0x4a, 0x4a, 0x08, 0x0d, + 0x0b, 0x0e, 0x44, 0x42, 0x03, 0x42, 0x41, 0x06, 0x44, 0x4a, 0x4a, 0x08, 0x44, 0x4c, 0x4a, + 0x4a, 0x08, 0x0d, 0x0b, 0x0e, 0x1b, 0x46, 0x22, 0x40, 0x49, 0x40, 0x01, 0x07, 0x01, 0x0b, + 0x0b, 0x12, 0x03, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, + 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x09, 0x09, 0x13, 0x1b, 0x13, 0x1b, 0x0e, + 0x17, 0x04, 0x46, 0x17, 0x04, 0x46, 0x40, 0x40, 0x40, 0x12, 0x0e, 0x0e, 0x40, 0x0f, 0x0e, + 0x0c, 0x0c, 0x29, 0x27, 0x19, 0x07, 0x1b, 0x19, 0x13, 0x41, 0x29, 0x16, 0x0e, 0x40, 0x0f, + 0x0e, 0x0c, 0x15, 0x0e, 0x1e, 0x15, 0x15, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x15, 0x01, + 0x4c, 0x40, 0x09, 0x4c, 0x56, 0x29, 0x40, 0x01, 0x40, 0x40, 0x03, 0x0f, 0x01, 0x09, 0x40, + 0x11, 0x40, 0x09, 0x13, 0x27, 0x27, 0x07, 0x07, 0x05, 0x1d, 0x0d, 0x13, 0x07, 0x4a, 0x27, + 0x17, 0x0d, 0x40, 0x42, 0x01, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x03, 0x42, 0x40, + 0x07, 0x44, 0x4a, 0x4a, 0x09, 0x44, 0x4c, 0x4a, 0x4a, 0x09, 0x0d, 0x0b, 0x0f, 0x44, 0x42, + 0x03, 0x42, 0x40, 0x07, 0x44, 0x4a, 0x4a, 0x09, 0x44, 0x4c, 0x4a, 0x4a, 0x09, 0x0d, 0x0b, + 0x0f, 0x1b, 0x46, 0x23, 0x40, 0x4a, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x13, 0x03, 0x15, + 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, + 0x07, 0x40, 0x4a, 0x07, 0x09, 0x09, 0x13, 0x1b, 0x13, 0x1b, 0x0d, 0x17, 0x03, 0x46, 0x17, + 0x03, 0x46, 0x40, 0x40, 0x40, 0x11, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x29, 0x27, + 0x19, 0x07, 0x1b, 0x19, 0x13, 0x42, 0x27, 0x15, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x15, 0x0d, + 0x1f, 0x15, 0x15, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x16, 0x02, 0x4b, 0x40, 0x09, 0x4c, + 0x56, 0x2a, 0x40, 0x02, 0x40, 0x40, 0x03, 0x0f, 0x02, 0x0b, 0x40, 0x12, 0x01, 0x0b, 0x15, + 0x2a, 0x2a, 0x07, 0x07, 0x05, 0x1c, 0x0d, 0x14, 0x07, 0x49, 0x27, 0x19, 0x0d, 0x40, 0x42, + 0x01, 0x40, 0x40, 0x40, 0x02, 0x02, 0x43, 0x41, 0x04, 0x41, 0x01, 0x09, 0x43, 0x49, 0x49, + 0x0b, 0x43, 0x4b, 0x49, 0x49, 0x0b, 0x0e, 0x0c, 0x11, 0x43, 0x41, 0x04, 0x41, 0x01, 0x09, + 0x43, 0x49, 0x49, 0x0b, 0x43, 0x4b, 0x49, 0x49, 0x0b, 0x0e, 0x0c, 0x11, 0x1c, 0x45, 0x25, + 0x40, 0x4a, 0x40, 0x02, 0x07, 0x02, 0x0c, 0x0c, 0x15, 0x04, 0x15, 0x03, 0x45, 0x1a, 0x07, + 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, + 0x0a, 0x0a, 0x14, 0x1c, 0x14, 0x1c, 0x0d, 0x17, 0x03, 0x45, 0x17, 0x03, 0x45, 0x40, 0x40, + 0x40, 0x11, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x2a, 0x27, 0x1a, 0x07, 0x1c, 0x1a, + 0x14, 0x42, 0x26, 0x15, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x16, 0x0d, 0x21, 0x16, 0x16, 0x0d, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x3e, 0x17, 0x02, 0x4a, 0x40, 0x08, 0x4d, 0x57, 0x2a, 0x40, 0x02, + 0x40, 0x40, 0x02, 0x0f, 0x02, 0x0d, 0x40, 0x12, 0x02, 0x0d, 0x17, 0x2c, 0x2c, 0x07, 0x07, + 0x05, 0x1a, 0x0d, 0x15, 0x07, 0x48, 0x27, 0x1a, 0x0d, 0x40, 0x42, 0x00, 0x40, 0x40, 0x40, + 0x02, 0x02, 0x42, 0x40, 0x05, 0x40, 0x02, 0x0a, 0x42, 0x48, 0x48, 0x0d, 0x42, 0x4a, 0x48, + 0x48, 0x0d, 0x0f, 0x0d, 0x12, 0x42, 0x40, 0x05, 0x40, 0x02, 0x0a, 0x42, 0x48, 0x48, 0x0d, + 0x42, 0x4a, 0x48, 0x48, 0x0d, 0x0f, 0x0d, 0x12, 0x1d, 0x45, 0x27, 0x40, 0x4a, 0x40, 0x02, + 0x07, 0x02, 0x0d, 0x0d, 0x17, 0x05, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, + 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0a, 0x0a, 0x15, 0x1d, + 0x15, 0x1d, 0x0d, 0x17, 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x10, 0x0d, 0x0d, + 0x40, 0x0f, 0x0d, 0x0a, 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x15, 0x42, 0x25, 0x15, + 0x0d, 0x40, 0x0f, 0x0d, 0x0a, 0x17, 0x0d, 0x22, 0x17, 0x17, 0x0d, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x3e, 0x18, 0x02, 0x4a, 0x40, 0x07, 0x4d, 0x58, 0x2a, 0x40, 0x02, 0x40, 0x40, 0x02, 0x0f, + 0x02, 0x0e, 0x40, 0x12, 0x03, 0x0e, 0x19, 0x2f, 0x2f, 0x07, 0x07, 0x05, 0x19, 0x0d, 0x15, + 0x07, 0x47, 0x27, 0x1b, 0x0d, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x42, 0x00, + 0x05, 0x00, 0x03, 0x0b, 0x42, 0x47, 0x47, 0x0e, 0x42, 0x4a, 0x47, 0x47, 0x0e, 0x10, 0x0d, + 0x13, 0x42, 0x00, 0x05, 0x00, 0x03, 0x0b, 0x42, 0x47, 0x47, 0x0e, 0x42, 0x4a, 0x47, 0x47, + 0x0e, 0x10, 0x0d, 0x13, 0x1d, 0x45, 0x29, 0x40, 0x4a, 0x40, 0x02, 0x07, 0x02, 0x0d, 0x0d, + 0x19, 0x05, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, + 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0a, 0x0a, 0x15, 0x1d, 0x15, 0x1d, 0x0d, 0x17, + 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0a, + 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x15, 0x42, 0x24, 0x15, 0x0d, 0x40, 0x0f, 0x0d, + 0x0a, 0x18, 0x0d, 0x23, 0x18, 0x18, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x19, 0x03, 0x49, + 0x40, 0x06, 0x4e, 0x59, 0x2b, 0x40, 0x03, 0x40, 0x40, 0x01, 0x0f, 0x03, 0x10, 0x40, 0x13, + 0x04, 0x10, 0x1b, 0x31, 0x31, 0x07, 0x07, 0x04, 0x17, 0x0c, 0x16, 0x07, 0x46, 0x27, 0x1c, + 0x0c, 0x40, 0x43, 0x41, 0x40, 0x40, 0x40, 0x03, 0x03, 0x41, 0x01, 0x06, 0x01, 0x04, 0x0c, + 0x41, 0x46, 0x46, 0x10, 0x41, 0x49, 0x46, 0x46, 0x10, 0x11, 0x0e, 0x14, 0x41, 0x01, 0x06, + 0x01, 0x04, 0x0c, 0x41, 0x46, 0x46, 0x10, 0x41, 0x49, 0x46, 0x46, 0x10, 0x11, 0x0e, 0x14, + 0x1e, 0x44, 0x2b, 0x40, 0x4b, 0x40, 0x03, 0x07, 0x03, 0x0e, 0x0e, 0x1b, 0x06, 0x14, 0x01, + 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, + 0x40, 0x4b, 0x07, 0x0b, 0x0b, 0x16, 0x1e, 0x16, 0x1e, 0x0c, 0x17, 0x01, 0x44, 0x17, 0x01, + 0x44, 0x40, 0x40, 0x40, 0x0e, 0x0c, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x09, 0x2b, 0x27, 0x1b, + 0x07, 0x1e, 0x1b, 0x16, 0x43, 0x22, 0x14, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x19, 0x0c, 0x24, + 0x19, 0x19, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1a, 0x03, 0x48, 0x40, 0x05, 0x4f, 0x5a, + 0x2b, 0x40, 0x03, 0x40, 0x40, 0x00, 0x0f, 0x03, 0x11, 0x40, 0x13, 0x06, 0x11, 0x1d, 0x34, + 0x34, 0x07, 0x07, 0x04, 0x16, 0x0c, 0x17, 0x07, 0x45, 0x27, 0x1e, 0x0c, 0x40, 0x43, 0x42, + 0x40, 0x40, 0x40, 0x03, 0x03, 0x40, 0x02, 0x07, 0x02, 0x06, 0x0e, 0x40, 0x45, 0x45, 0x11, + 0x40, 0x48, 0x45, 0x45, 0x11, 0x12, 0x0f, 0x16, 0x40, 0x02, 0x07, 0x02, 0x06, 0x0e, 0x40, + 0x45, 0x45, 0x11, 0x40, 0x48, 0x45, 0x45, 0x11, 0x12, 0x0f, 0x16, 0x1f, 0x44, 0x2d, 0x40, + 0x4b, 0x40, 0x03, 0x07, 0x03, 0x0f, 0x0f, 0x1d, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, + 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0b, + 0x0b, 0x17, 0x1f, 0x17, 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, + 0x0d, 0x0c, 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x17, + 0x43, 0x21, 0x14, 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x1a, 0x0c, 0x26, 0x1a, 0x1a, 0x0c, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x3e, 0x1b, 0x03, 0x48, 0x40, 0x04, 0x4f, 0x5b, 0x2b, 0x40, 0x03, 0x40, + 0x40, 0x00, 0x0f, 0x03, 0x13, 0x40, 0x13, 0x07, 0x13, 0x1f, 0x36, 0x36, 0x07, 0x07, 0x04, + 0x14, 0x0c, 0x17, 0x07, 0x44, 0x27, 0x1f, 0x0c, 0x40, 0x43, 0x43, 0x40, 0x40, 0x40, 0x03, + 0x03, 0x40, 0x03, 0x07, 0x03, 0x07, 0x0f, 0x40, 0x44, 0x44, 0x13, 0x40, 0x48, 0x44, 0x44, + 0x13, 0x13, 0x0f, 0x17, 0x40, 0x03, 0x07, 0x03, 0x07, 0x0f, 0x40, 0x44, 0x44, 0x13, 0x40, + 0x48, 0x44, 0x44, 0x13, 0x13, 0x0f, 0x17, 0x1f, 0x44, 0x2f, 0x40, 0x4b, 0x40, 0x03, 0x07, + 0x03, 0x0f, 0x0f, 0x1f, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, + 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0b, 0x0b, 0x17, 0x1f, 0x17, + 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0c, 0x0c, 0x0c, 0x40, + 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x17, 0x43, 0x20, 0x14, 0x0c, + 0x40, 0x0f, 0x0c, 0x08, 0x1b, 0x0c, 0x27, 0x1b, 0x1b, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, + 0x1c, 0x04, 0x47, 0x40, 0x03, 0x50, 0x5c, 0x2c, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04, + 0x14, 0x40, 0x14, 0x08, 0x14, 0x21, 0x39, 0x39, 0x07, 0x07, 0x03, 0x13, 0x0b, 0x18, 0x07, + 0x43, 0x27, 0x20, 0x0b, 0x40, 0x44, 0x44, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x04, 0x08, + 0x04, 0x08, 0x10, 0x00, 0x43, 0x43, 0x14, 0x00, 0x47, 0x43, 0x43, 0x14, 0x14, 0x10, 0x18, + 0x00, 0x04, 0x08, 0x04, 0x08, 0x10, 0x00, 0x43, 0x43, 0x14, 0x00, 0x47, 0x43, 0x43, 0x14, + 0x14, 0x10, 0x18, 0x20, 0x43, 0x31, 0x40, 0x4c, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x21, + 0x08, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, + 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x0c, 0x0c, 0x18, 0x20, 0x18, 0x20, 0x0b, 0x17, 0x40, + 0x43, 0x17, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0b, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x07, 0x07, + 0x2c, 0x27, 0x1c, 0x07, 0x20, 0x1c, 0x18, 0x44, 0x1f, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x07, + 0x1c, 0x0b, 0x28, 0x1c, 0x1c, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1d, 0x04, 0x47, 0x40, + 0x02, 0x51, 0x5d, 0x2c, 0x40, 0x04, 0x40, 0x40, 0x41, 0x0f, 0x04, 0x16, 0x40, 0x14, 0x09, + 0x16, 0x22, 0x3b, 0x3b, 0x07, 0x07, 0x03, 0x11, 0x0b, 0x18, 0x07, 0x42, 0x27, 0x21, 0x0b, + 0x40, 0x44, 0x45, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x05, 0x08, 0x05, 0x09, 0x11, 0x00, + 0x42, 0x42, 0x16, 0x00, 0x47, 0x42, 0x42, 0x16, 0x15, 0x10, 0x19, 0x00, 0x05, 0x08, 0x05, + 0x09, 0x11, 0x00, 0x42, 0x42, 0x16, 0x00, 0x47, 0x42, 0x42, 0x16, 0x15, 0x10, 0x19, 0x20, + 0x43, 0x32, 0x40, 0x4c, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x22, 0x08, 0x13, 0x41, 0x43, + 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, + 0x4c, 0x07, 0x0c, 0x0c, 0x18, 0x20, 0x18, 0x20, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, + 0x40, 0x40, 0x40, 0x0a, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, + 0x20, 0x1c, 0x18, 0x44, 0x1d, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x1d, 0x0b, 0x29, 0x1d, + 0x1d, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1e, 0x04, 0x46, 0x40, 0x01, 0x51, 0x5e, 0x2c, + 0x40, 0x04, 0x40, 0x40, 0x41, 0x0f, 0x04, 0x18, 0x40, 0x14, 0x0b, 0x18, 0x24, 0x3e, 0x3e, + 0x07, 0x07, 0x03, 0x0f, 0x0b, 0x19, 0x07, 0x41, 0x27, 0x23, 0x0b, 0x40, 0x44, 0x46, 0x40, + 0x40, 0x40, 0x04, 0x04, 0x01, 0x06, 0x09, 0x06, 0x0b, 0x13, 0x01, 0x41, 0x41, 0x18, 0x01, + 0x46, 0x41, 0x41, 0x18, 0x16, 0x11, 0x1b, 0x01, 0x06, 0x09, 0x06, 0x0b, 0x13, 0x01, 0x41, + 0x41, 0x18, 0x01, 0x46, 0x41, 0x41, 0x18, 0x16, 0x11, 0x1b, 0x21, 0x43, 0x34, 0x40, 0x4c, + 0x40, 0x04, 0x07, 0x04, 0x11, 0x11, 0x24, 0x09, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, + 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x0c, 0x0c, + 0x19, 0x21, 0x19, 0x21, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40, 0x40, 0x09, + 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x21, 0x1c, 0x19, 0x44, + 0x1c, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x1e, 0x0b, 0x2b, 0x1e, 0x1e, 0x0b, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x3e, 0x1f, 0x05, 0x45, 0x40, 0x00, 0x52, 0x5f, 0x2d, 0x40, 0x05, 0x40, 0x40, + 0x42, 0x0f, 0x05, 0x19, 0x40, 0x15, 0x0c, 0x19, 0x26, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0e, + 0x0a, 0x1a, 0x07, 0x40, 0x27, 0x24, 0x0a, 0x40, 0x45, 0x47, 0x40, 0x40, 0x40, 0x05, 0x05, + 0x02, 0x07, 0x0a, 0x07, 0x0c, 0x14, 0x02, 0x40, 0x40, 0x19, 0x02, 0x45, 0x40, 0x40, 0x19, + 0x17, 0x12, 0x1c, 0x02, 0x07, 0x0a, 0x07, 0x0c, 0x14, 0x02, 0x40, 0x40, 0x19, 0x02, 0x45, + 0x40, 0x40, 0x19, 0x17, 0x12, 0x1c, 0x22, 0x42, 0x36, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05, + 0x12, 0x12, 0x26, 0x0a, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, + 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x0d, 0x0d, 0x1a, 0x22, 0x1a, 0x22, + 0x0a, 0x17, 0x42, 0x42, 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x08, 0x0a, 0x0a, 0x40, 0x0f, + 0x0a, 0x05, 0x05, 0x2d, 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x1a, 0x45, 0x1b, 0x12, 0x0a, 0x40, + 0x0f, 0x0a, 0x05, 0x1f, 0x0a, 0x2c, 0x1f, 0x1f, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x20, + 0x05, 0x45, 0x40, 0x40, 0x52, 0x60, 0x2d, 0x40, 0x05, 0x40, 0x40, 0x42, 0x0f, 0x05, 0x1b, + 0x40, 0x15, 0x0d, 0x1b, 0x28, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0c, 0x0a, 0x1a, 0x07, 0x00, + 0x27, 0x25, 0x0a, 0x40, 0x45, 0x48, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x08, 0x0a, 0x08, + 0x0d, 0x15, 0x02, 0x00, 0x00, 0x1b, 0x02, 0x45, 0x00, 0x00, 0x1b, 0x18, 0x12, 0x1d, 0x02, + 0x08, 0x0a, 0x08, 0x0d, 0x15, 0x02, 0x00, 0x00, 0x1b, 0x02, 0x45, 0x00, 0x00, 0x1b, 0x18, + 0x12, 0x1d, 0x22, 0x42, 0x38, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12, 0x28, 0x0a, + 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, + 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x0d, 0x0d, 0x1a, 0x22, 0x1a, 0x22, 0x0a, 0x17, 0x42, 0x42, + 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x05, 0x2d, + 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x1a, 0x45, 0x1a, 0x12, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x20, + 0x0a, 0x2d, 0x20, 0x20, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x21, 0x05, 0x44, 0x40, 0x41, + 0x53, 0x61, 0x2d, 0x40, 0x05, 0x40, 0x40, 0x43, 0x0f, 0x05, 0x1c, 0x40, 0x15, 0x0e, 0x1c, + 0x2a, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0b, 0x0a, 0x1b, 0x07, 0x01, 0x27, 0x26, 0x0a, 0x40, + 0x45, 0x49, 0x40, 0x40, 0x40, 0x05, 0x05, 0x03, 0x09, 0x0b, 0x09, 0x0e, 0x16, 0x03, 0x01, + 0x01, 0x1c, 0x03, 0x44, 0x01, 0x01, 0x1c, 0x19, 0x13, 0x1e, 0x03, 0x09, 0x0b, 0x09, 0x0e, + 0x16, 0x03, 0x01, 0x01, 0x1c, 0x03, 0x44, 0x01, 0x01, 0x1c, 0x19, 0x13, 0x1e, 0x23, 0x42, + 0x3a, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05, 0x13, 0x13, 0x2a, 0x0b, 0x12, 0x43, 0x42, 0x1d, + 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x4d, + 0x07, 0x0d, 0x0d, 0x1b, 0x23, 0x1b, 0x23, 0x0a, 0x17, 0x43, 0x42, 0x17, 0x43, 0x42, 0x40, + 0x40, 0x40, 0x06, 0x0a, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x04, 0x2d, 0x27, 0x1d, 0x07, 0x23, + 0x1d, 0x1b, 0x45, 0x18, 0x12, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x21, 0x0a, 0x2e, 0x21, 0x21, + 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x22, 0x06, 0x43, 0x40, 0x42, 0x54, 0x62, 0x2e, 0x40, + 0x06, 0x40, 0x40, 0x44, 0x0f, 0x06, 0x1e, 0x40, 0x16, 0x10, 0x1e, 0x2c, 0x3e, 0x3e, 0x07, + 0x07, 0x01, 0x09, 0x09, 0x1c, 0x07, 0x02, 0x27, 0x28, 0x09, 0x40, 0x46, 0x4a, 0x40, 0x40, + 0x40, 0x06, 0x06, 0x04, 0x0a, 0x0c, 0x0a, 0x10, 0x18, 0x04, 0x02, 0x02, 0x1e, 0x04, 0x43, + 0x02, 0x02, 0x1e, 0x1a, 0x14, 0x20, 0x04, 0x0a, 0x0c, 0x0a, 0x10, 0x18, 0x04, 0x02, 0x02, + 0x1e, 0x04, 0x43, 0x02, 0x02, 0x1e, 0x1a, 0x14, 0x20, 0x24, 0x41, 0x3c, 0x40, 0x4e, 0x40, + 0x06, 0x07, 0x06, 0x14, 0x14, 0x2c, 0x0c, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, + 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1c, + 0x24, 0x1c, 0x24, 0x09, 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x05, 0x09, + 0x09, 0x40, 0x0f, 0x09, 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x1c, 0x46, 0x17, + 0x11, 0x09, 0x40, 0x0f, 0x09, 0x03, 0x22, 0x09, 0x30, 0x22, 0x22, 0x09, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3e, 0x23, 0x06, 0x43, 0x40, 0x43, 0x54, 0x63, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x44, + 0x0f, 0x06, 0x1f, 0x40, 0x16, 0x11, 0x1f, 0x2e, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x08, 0x09, + 0x1c, 0x07, 0x03, 0x27, 0x29, 0x09, 0x40, 0x46, 0x4b, 0x40, 0x40, 0x40, 0x06, 0x06, 0x04, + 0x0b, 0x0c, 0x0b, 0x11, 0x19, 0x04, 0x03, 0x03, 0x1f, 0x04, 0x43, 0x03, 0x03, 0x1f, 0x1b, + 0x14, 0x21, 0x04, 0x0b, 0x0c, 0x0b, 0x11, 0x19, 0x04, 0x03, 0x03, 0x1f, 0x04, 0x43, 0x03, + 0x03, 0x1f, 0x1b, 0x14, 0x21, 0x24, 0x41, 0x3e, 0x40, 0x4e, 0x40, 0x06, 0x07, 0x06, 0x14, + 0x14, 0x2e, 0x0c, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, + 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1c, 0x24, 0x1c, 0x24, 0x09, + 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x04, 0x09, 0x09, 0x40, 0x0f, 0x09, + 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x1c, 0x46, 0x16, 0x11, 0x09, 0x40, 0x0f, + 0x09, 0x03, 0x23, 0x09, 0x31, 0x23, 0x23, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x24, 0x06, + 0x42, 0x40, 0x44, 0x55, 0x64, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x45, 0x0f, 0x06, 0x21, 0x40, + 0x16, 0x12, 0x21, 0x30, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x06, 0x09, 0x1d, 0x07, 0x04, 0x27, + 0x2a, 0x09, 0x40, 0x46, 0x4c, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x0d, 0x0c, 0x12, + 0x1a, 0x05, 0x04, 0x04, 0x21, 0x05, 0x42, 0x04, 0x04, 0x21, 0x1c, 0x15, 0x22, 0x05, 0x0c, + 0x0d, 0x0c, 0x12, 0x1a, 0x05, 0x04, 0x04, 0x21, 0x05, 0x42, 0x04, 0x04, 0x21, 0x1c, 0x15, + 0x22, 0x25, 0x41, 0x3e, 0x40, 0x4e, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x30, 0x0d, 0x11, + 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, + 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1d, 0x25, 0x1d, 0x25, 0x09, 0x17, 0x45, 0x41, 0x17, + 0x45, 0x41, 0x40, 0x40, 0x40, 0x03, 0x09, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x02, 0x2e, 0x27, + 0x1e, 0x07, 0x25, 0x1e, 0x1d, 0x46, 0x15, 0x11, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x24, 0x09, + 0x32, 0x24, 0x24, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x24, 0x06, 0x42, 0x40, 0x45, 0x56, + 0x65, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x46, 0x0f, 0x06, 0x22, 0x40, 0x16, 0x13, 0x22, 0x31, + 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x04, 0x08, 0x1d, 0x07, 0x04, 0x27, 0x2b, 0x08, 0x40, 0x47, + 0x4d, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x0d, 0x0c, 0x13, 0x1b, 0x05, 0x04, 0x04, + 0x22, 0x05, 0x42, 0x04, 0x04, 0x22, 0x1c, 0x15, 0x23, 0x05, 0x0c, 0x0d, 0x0c, 0x13, 0x1b, + 0x05, 0x04, 0x04, 0x22, 0x05, 0x42, 0x04, 0x04, 0x22, 0x1c, 0x15, 0x23, 0x25, 0x41, 0x3e, + 0x40, 0x4f, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x31, 0x0d, 0x10, 0x46, 0x41, 0x1e, 0x07, + 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x4f, 0x07, + 0x0e, 0x0e, 0x1d, 0x25, 0x1d, 0x25, 0x08, 0x17, 0x46, 0x41, 0x17, 0x46, 0x41, 0x40, 0x40, + 0x40, 0x02, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2e, 0x27, 0x1e, 0x07, 0x25, 0x1e, + 0x1d, 0x47, 0x13, 0x10, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x24, 0x08, 0x33, 0x24, 0x24, 0x08, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x3e, 0x25, 0x07, 0x41, 0x40, 0x45, 0x56, 0x65, 0x2f, 0x40, 0x07, + 0x40, 0x40, 0x46, 0x0f, 0x07, 0x24, 0x40, 0x17, 0x15, 0x24, 0x33, 0x3e, 0x3e, 0x07, 0x07, + 0x00, 0x03, 0x08, 0x1e, 0x07, 0x05, 0x27, 0x2d, 0x08, 0x40, 0x47, 0x4d, 0x40, 0x40, 0x40, + 0x07, 0x07, 0x06, 0x0d, 0x0e, 0x0d, 0x15, 0x1d, 0x06, 0x05, 0x05, 0x24, 0x06, 0x41, 0x05, + 0x05, 0x24, 0x1d, 0x16, 0x25, 0x06, 0x0d, 0x0e, 0x0d, 0x15, 0x1d, 0x06, 0x05, 0x05, 0x24, + 0x06, 0x41, 0x05, 0x05, 0x24, 0x1d, 0x16, 0x25, 0x26, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07, + 0x07, 0x07, 0x16, 0x16, 0x33, 0x0e, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40, + 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1e, 0x26, + 0x1e, 0x26, 0x08, 0x17, 0x46, 0x40, 0x17, 0x46, 0x40, 0x40, 0x40, 0x40, 0x02, 0x08, 0x08, + 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2f, 0x27, 0x1f, 0x07, 0x26, 0x1f, 0x1e, 0x47, 0x12, 0x10, + 0x08, 0x40, 0x0f, 0x08, 0x01, 0x25, 0x08, 0x35, 0x25, 0x25, 0x08, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x3e, 0x26, 0x07, 0x40, 0x40, 0x46, 0x57, 0x66, 0x2f, 0x40, 0x07, 0x40, 0x40, 0x47, 0x0f, + 0x07, 0x26, 0x40, 0x17, 0x16, 0x26, 0x35, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x01, 0x08, 0x1f, + 0x07, 0x06, 0x27, 0x2e, 0x08, 0x40, 0x47, 0x4e, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0e, + 0x0f, 0x0e, 0x16, 0x1e, 0x07, 0x06, 0x06, 0x26, 0x07, 0x40, 0x06, 0x06, 0x26, 0x1e, 0x17, + 0x26, 0x07, 0x0e, 0x0f, 0x0e, 0x16, 0x1e, 0x07, 0x06, 0x06, 0x26, 0x07, 0x40, 0x06, 0x06, + 0x26, 0x1e, 0x17, 0x26, 0x27, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, + 0x35, 0x0f, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, + 0x47, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1f, 0x27, 0x1f, 0x27, 0x08, 0x17, + 0x47, 0x40, 0x17, 0x47, 0x40, 0x40, 0x40, 0x40, 0x01, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x00, + 0x00, 0x2f, 0x27, 0x1f, 0x07, 0x27, 0x1f, 0x1f, 0x47, 0x11, 0x10, 0x08, 0x40, 0x0f, 0x08, + 0x00, 0x26, 0x08, 0x36, 0x26, 0x26, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x27, 0x07, 0x40, + 0x40, 0x47, 0x57, 0x67, 0x2f, 0x40, 0x07, 0x40, 0x40, 0x47, 0x0f, 0x07, 0x27, 0x40, 0x17, + 0x17, 0x27, 0x37, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x00, 0x08, 0x1f, 0x07, 0x07, 0x27, 0x2f, + 0x08, 0x40, 0x47, 0x4f, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x17, 0x1f, + 0x07, 0x07, 0x07, 0x27, 0x07, 0x40, 0x07, 0x07, 0x27, 0x1f, 0x17, 0x27, 0x07, 0x0f, 0x0f, + 0x0f, 0x17, 0x1f, 0x07, 0x07, 0x07, 0x27, 0x07, 0x40, 0x07, 0x07, 0x27, 0x1f, 0x17, 0x27, + 0x27, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x37, 0x0f, 0x10, 0x47, + 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, + 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1f, 0x27, 0x1f, 0x27, 0x08, 0x17, 0x47, 0x40, 0x17, 0x47, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f, 0x27, 0x1f, + 0x07, 0x27, 0x1f, 0x1f, 0x47, 0x10, 0x10, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x27, 0x08, 0x37, + 0x27, 0x27, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c new file mode 100644 index 000000000000..fc7e6a260b0a --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder HEVC backend + * + * Copyright (C) 2023 Collabora, Ltd. + * Sebastian Fricke <sebastian.fricke@collabora.com> + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-regs.h" +#include "rkvdec-hevc-data.c" + +/* Size in u8/u32 units. */ +#define RKV_SCALING_LIST_SIZE 1360 +#define RKV_PPS_SIZE (80 / 4) +#define RKV_PPS_LEN 64 +#define RKV_RPS_SIZE (32 / 4) +#define RKV_RPS_LEN 600 + +struct rkvdec_sps_pps_packet { + u32 info[RKV_PPS_SIZE]; +}; + +struct rkvdec_rps_packet { + u32 info[RKV_RPS_SIZE]; +}; + +struct rkvdec_ps_field { + u16 offset; + u8 len; +}; + +#define PS_FIELD(_offset, _len) \ + ((struct rkvdec_ps_field){ _offset, _len }) + +/* SPS */ +#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4) +#define CHROMA_FORMAT_IDC PS_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13) +#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13) +#define BIT_DEPTH_LUMA PS_FIELD(36, 4) +#define BIT_DEPTH_CHROMA PS_FIELD(40, 4) +#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3) +#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1) +#define AMP_ENABLED_FLAG PS_FIELD(66, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1) +#define PCM_ENABLED_FLAG PS_FIELD(68, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1) +#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1) +/* PPS */ +#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6) +#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1) +#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1) +#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4) +#define INIT_QP_MINUS26 PS_FIELD(163, 7) +#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1) +#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3) +#define PPS_CB_QP_OFFSET PS_FIELD(176, 5) +#define PPS_CR_QP_OFFSET PS_FIELD(181, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1) +#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1) +#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1) +#define TILES_ENABLED_FLAG PS_FIELD(190, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1) +#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4) +#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1) +#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1) +#define NUM_TILE_COLUMNS PS_FIELD(212, 5) +#define NUM_TILE_ROWS PS_FIELD(217, 5) +#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8) +#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8) +#define SCALING_LIST_ADDRESS PS_FIELD(592, 32) + +/* Data structure describing auxiliary buffer format. */ +struct rkvdec_hevc_priv_tbl { + u8 cabac_table[RKV_CABAC_TABLE_SIZE]; + u8 scaling_list[RKV_SCALING_LIST_SIZE]; + struct rkvdec_sps_pps_packet param_set[RKV_PPS_LEN]; + struct rkvdec_rps_packet rps[RKV_RPS_LEN]; +}; + +struct rkvdec_hevc_run { + struct rkvdec_run base; + const struct v4l2_ctrl_hevc_slice_params *slices_params; + const struct v4l2_ctrl_hevc_decode_params *decode_params; + const struct v4l2_ctrl_hevc_sps *sps; + const struct v4l2_ctrl_hevc_pps *pps; + const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix; + int num_slices; +}; + +struct rkvdec_hevc_ctx { + struct rkvdec_aux_buf priv_tbl; + struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache; +}; + +struct scaling_factor { + u8 scalingfactor0[1248]; + u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/ + u8 scalingdc[12]; /*N1005 Vienna Meeting*/ + u8 reserved[4]; /*16Bytes align*/ +}; + +static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) +{ + u8 bit = field.offset % 32, word = field.offset / 32; + u64 mask = GENMASK_ULL(bit + field.len - 1, bit); + u64 val = ((u64)value << bit) & mask; + + buf[word] &= ~mask; + buf[word] |= val; + if (bit + field.len > 32) { + buf[word + 1] &= ~(mask >> 32); + buf[word + 1] |= val >> 32; + } +} + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + const struct v4l2_ctrl_hevc_pps *pps = run->pps; + struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu; + struct rkvdec_sps_pps_packet *hw_ps; + u32 min_cb_log2_size_y, ctb_log2_size_y, ctb_size_y; + u32 log2_min_cu_qp_delta_size, scaling_distance; + dma_addr_t scaling_list_address; + int i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 80 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; + memset(hw_ps, 0, sizeof(*hw_ps)); + +#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) + /* write sps */ + WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID); + WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); + WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC); + WRITE_PPS(sps->pic_width_in_luma_samples, PIC_WIDTH_IN_LUMA_SAMPLES); + WRITE_PPS(sps->pic_height_in_luma_samples, PIC_HEIGHT_IN_LUMA_SAMPLES); + WRITE_PPS(sps->bit_depth_luma_minus8 + 8, BIT_DEPTH_LUMA); + WRITE_PPS(sps->bit_depth_chroma_minus8 + 8, BIT_DEPTH_CHROMA); + WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, + LOG2_MAX_PIC_ORDER_CNT_LSB); + WRITE_PPS(sps->log2_diff_max_min_luma_coding_block_size, + LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE); + WRITE_PPS(sps->log2_min_luma_coding_block_size_minus3 + 3, + LOG2_MIN_LUMA_CODING_BLOCK_SIZE); + WRITE_PPS(sps->log2_min_luma_transform_block_size_minus2 + 2, + LOG2_MIN_TRANSFORM_BLOCK_SIZE); + WRITE_PPS(sps->log2_diff_max_min_luma_transform_block_size, + LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE); + WRITE_PPS(sps->max_transform_hierarchy_depth_inter, + MAX_TRANSFORM_HIERARCHY_DEPTH_INTER); + WRITE_PPS(sps->max_transform_hierarchy_depth_intra, + MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED), + SCALING_LIST_ENABLED_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED), + AMP_ENABLED_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET), + SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG); + if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) { + WRITE_PPS(1, PCM_ENABLED_FLAG); + WRITE_PPS(sps->pcm_sample_bit_depth_luma_minus1 + 1, + PCM_SAMPLE_BIT_DEPTH_LUMA); + WRITE_PPS(sps->pcm_sample_bit_depth_chroma_minus1 + 1, + PCM_SAMPLE_BIT_DEPTH_CHROMA); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED), + PCM_LOOP_FILTER_DISABLED_FLAG); + WRITE_PPS(sps->log2_diff_max_min_pcm_luma_coding_block_size, + LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE); + WRITE_PPS(sps->log2_min_pcm_luma_coding_block_size_minus3 + 3, + LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE); + } + WRITE_PPS(sps->num_short_term_ref_pic_sets, NUM_SHORT_TERM_REF_PIC_SETS); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT), + LONG_TERM_REF_PICS_PRESENT_FLAG); + WRITE_PPS(sps->num_long_term_ref_pics_sps, NUM_LONG_TERM_REF_PICS_SPS); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED), + SPS_TEMPORAL_MVP_ENABLED_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED), + STRONG_INTRA_SMOOTHING_ENABLED_FLAG); + + /* write pps */ + WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID); + WRITE_PPS(sps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED), + DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT), + OUTPUT_FLAG_PRESENT_FLAG); + WRITE_PPS(pps->num_extra_slice_header_bits, NUM_EXTRA_SLICE_HEADER_BITS); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED), + SIGN_DATA_HIDING_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT), + CABAC_INIT_PRESENT_FLAG); + WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1 + 1, + NUM_REF_IDX_L0_DEFAULT_ACTIVE); + WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1 + 1, + NUM_REF_IDX_L1_DEFAULT_ACTIVE); + WRITE_PPS(pps->init_qp_minus26, INIT_QP_MINUS26); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED), + CONSTRAINED_INTRA_PRED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED), + TRANSFORM_SKIP_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED), + CU_QP_DELTA_ENABLED_FLAG); + + min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3; + ctb_log2_size_y = min_cb_log2_size_y + + sps->log2_diff_max_min_luma_coding_block_size; + ctb_size_y = 1 << ctb_log2_size_y; + log2_min_cu_qp_delta_size = ctb_log2_size_y - pps->diff_cu_qp_delta_depth; + WRITE_PPS(log2_min_cu_qp_delta_size, LOG2_MIN_CU_QP_DELTA_SIZE); + WRITE_PPS(pps->pps_cb_qp_offset, PPS_CB_QP_OFFSET); + WRITE_PPS(pps->pps_cr_qp_offset, PPS_CR_QP_OFFSET); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT), + PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED), + WEIGHTED_PRED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED), + WEIGHTED_BIPRED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED), + TRANSQUANT_BYPASS_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED), + TILES_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED), + ENTROPY_CODING_SYNC_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED), + PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED), + LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED), + DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER), + PPS_DEBLOCKING_FILTER_DISABLED_FLAG); + WRITE_PPS(pps->pps_beta_offset_div2, PPS_BETA_OFFSET_DIV2); + WRITE_PPS(pps->pps_tc_offset_div2, PPS_TC_OFFSET_DIV2); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT), + LISTS_MODIFICATION_PRESENT_FLAG); + WRITE_PPS(pps->log2_parallel_merge_level_minus2 + 2, LOG2_PARALLEL_MERGE_LEVEL); + WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT), + SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG); + WRITE_PPS(pps->num_tile_columns_minus1 + 1, NUM_TILE_COLUMNS); + WRITE_PPS(pps->num_tile_rows_minus1 + 1, NUM_TILE_ROWS); + + if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) { + /* Userspace also provide column width and row height for uniform spacing */ + for (i = 0; i <= pps->num_tile_columns_minus1; i++) + WRITE_PPS(pps->column_width_minus1[i], COLUMN_WIDTH(i)); + for (i = 0; i <= pps->num_tile_rows_minus1; i++) + WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i)); + } else { + WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1, + COLUMN_WIDTH(0)); + WRITE_PPS(((sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1, + ROW_HEIGHT(0)); + } + + scaling_distance = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list); + scaling_list_address = hevc_ctx->priv_tbl.dma + scaling_distance; + WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS); +} + +/* + * Creation of the Reference Picture Set memory blob for the hardware. + * The layout looks like this: + * [0] 32 bits for L0 (6 references + 2 bits of the 7th reference) + * [1] 32 bits for L0 (remaining 3 bits of the 7th reference + 5 references + * + 4 bits of the 13th reference) + * [2] 11 bits for L0 (remaining bit for 13 and 2 references) and + * 21 bits for L1 (4 references + first bit of 5) + * [3] 32 bits of padding with 0s + * [4] 32 bits for L1 (remaining 4 bits for 5 + 5 references + 3 bits of 11) + * [5] 22 bits for L1 (remaining 2 bits of 11 and 4 references) + * lowdelay flag (bit 23), rps bit offset long term (bit 24 - 32) + * [6] rps bit offset long term (bit 1 - 3), rps bit offset short term (bit 4 - 12) + * number of references (bit 13 - 16), remaining 16 bits of padding with 0s + * [7] 32 bits of padding with 0s + * + * Thus we have to set up padding in between reference 5 of the L1 list. + */ +static void assemble_sw_rps(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + const struct v4l2_ctrl_hevc_slice_params *sl_params; + const struct v4l2_hevc_dpb_entry *dpb; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu; + struct rkvdec_rps_packet *hw_ps; + int i, j; + unsigned int lowdelay; + +#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value) + +#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1) +#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4) +#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1) +#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4) + +#define LOWDELAY PS_FIELD(182, 1) +#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10) +#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9) +#define NUM_RPS_POC PS_FIELD(202, 4) + + for (j = 0; j < run->num_slices; j++) { + uint st_bit_offset = 0; + uint num_l0_refs = 0; + uint num_l1_refs = 0; + + sl_params = &run->slices_params[j]; + dpb = decode_params->dpb; + + if (sl_params->slice_type != V4L2_HEVC_SLICE_TYPE_I) { + num_l0_refs = sl_params->num_ref_idx_l0_active_minus1 + 1; + + if (sl_params->slice_type == V4L2_HEVC_SLICE_TYPE_B) + num_l1_refs = sl_params->num_ref_idx_l1_active_minus1 + 1; + + lowdelay = 1; + } else { + lowdelay = 0; + } + + hw_ps = &priv_tbl->rps[j]; + memset(hw_ps, 0, sizeof(*hw_ps)); + + for (i = 0; i < num_l0_refs; i++) { + const struct v4l2_hevc_dpb_entry dpb_l0 = dpb[sl_params->ref_idx_l0[i]]; + + WRITE_RPS(!!(dpb_l0.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE), + REF_PIC_LONG_TERM_L0(i)); + WRITE_RPS(sl_params->ref_idx_l0[i], REF_PIC_IDX_L0(i)); + + if (dpb_l0.pic_order_cnt_val > sl_params->slice_pic_order_cnt) + lowdelay = 0; + } + + for (i = 0; i < num_l1_refs; i++) { + const struct v4l2_hevc_dpb_entry dpb_l1 = dpb[sl_params->ref_idx_l1[i]]; + int is_long_term = + !!(dpb_l1.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE); + + WRITE_RPS(is_long_term, REF_PIC_LONG_TERM_L1(i)); + WRITE_RPS(sl_params->ref_idx_l1[i], REF_PIC_IDX_L1(i)); + + if (dpb_l1.pic_order_cnt_val > sl_params->slice_pic_order_cnt) + lowdelay = 0; + } + + WRITE_RPS(lowdelay, LOWDELAY); + + if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) { + if (sl_params->short_term_ref_pic_set_size) + st_bit_offset = sl_params->short_term_ref_pic_set_size; + else if (sps->num_short_term_ref_pic_sets > 1) + st_bit_offset = fls(sps->num_short_term_ref_pic_sets - 1); + } + + WRITE_RPS(st_bit_offset + sl_params->long_term_ref_pic_set_size, + LONG_TERM_RPS_BIT_OFFSET); + WRITE_RPS(sl_params->short_term_ref_pic_set_size, + SHORT_TERM_RPS_BIT_OFFSET); + + WRITE_RPS(decode_params->num_poc_st_curr_before + + decode_params->num_poc_st_curr_after + + decode_params->num_poc_lt_curr, + NUM_RPS_POC); + } +} + +/* + * Flip one or more matrices along their main diagonal and flatten them + * before writing it to the memory. + * Convert: + * ABCD AEIM + * EFGH => BFJN => AEIMBFJNCGKODHLP + * IJKL CGKO + * MNOP DHLP + */ +static void transpose_and_flatten_matrices(u8 *output, const u8 *input, + int matrices, int row_length) +{ + int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value; + + matrix_size = row_length * row_length; + for (i = 0; i < matrices; i++) { + row = 0; + x_offset = 0; + matrix_offset = i * matrix_size; + for (j = 0; j < matrix_size; j++) { + y_offset = j - (row * row_length); + rot_index = y_offset * row_length + x_offset; + new_value = *(input + i * matrix_size + j); + output[matrix_offset + rot_index] = new_value; + if ((j + 1) % row_length == 0) { + row += 1; + x_offset += 1; + } + } + } +} + +static void assemble_scalingfactor0(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + int offset = 0; + + transpose_and_flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4); + offset = 6 * 16 * sizeof(u8); + transpose_and_flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8); + offset += 6 * 64 * sizeof(u8); + transpose_and_flatten_matrices(output + offset, + (const u8 *)input->scaling_list_16x16, 6, 8); + offset += 6 * 64 * sizeof(u8); + /* Add a 128 byte padding with 0s between the two 32x32 matrices */ + transpose_and_flatten_matrices(output + offset, + (const u8 *)input->scaling_list_32x32, 1, 8); + offset += 64 * sizeof(u8); + memset(output + offset, 0, 128); + offset += 128 * sizeof(u8); + transpose_and_flatten_matrices(output + offset, + (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)), + 1, 8); + offset += 64 * sizeof(u8); + memset(output + offset, 0, 128); +} + +/* + * Required layout: + * A = scaling_list_dc_coef_16x16 + * B = scaling_list_dc_coef_32x32 + * 0 = Padding + * + * A, A, A, A, A, A, B, 0, 0, B, 0, 0 + */ +static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + u8 list_32x32[6] = {0}; + + memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8)); + list_32x32[0] = input->scaling_list_dc_coef_32x32[0]; + list_32x32[3] = input->scaling_list_dc_coef_32x32[1]; + memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8)); +} + +static void translate_scaling_list(struct scaling_factor *output, + const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + assemble_scalingfactor0(output->scalingfactor0, input); + memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96); + assemble_scalingdc(output->scalingdc, input); + memset(output->reserved, 0, 4 * sizeof(u8)); +} + +static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu; + u8 *dst; + + if (!memcmp((void *)&hevc_ctx->scaling_matrix_cache, scaling, + sizeof(struct v4l2_ctrl_hevc_scaling_matrix))) + return; + + dst = tbl->scaling_list; + translate_scaling_list((struct scaling_factor *)dst, scaling); + + memcpy((void *)&hevc_ctx->scaling_matrix_cache, scaling, + sizeof(struct v4l2_ctrl_hevc_scaling_matrix)); +} + +static struct vb2_buffer * +get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run, + unsigned int dpb_idx) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params; + const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf = NULL; + + if (dpb_idx < decode_params->num_active_dpb_entries) + buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp); + + /* + * If a DPB entry is unused or invalid, the address of current destination + * buffer is returned. + */ + if (!buf) + return &run->base.bufs.dst->vb2_buf; + + return buf; +} + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + const struct v4l2_ctrl_hevc_slice_params *sl_params = &run->slices_params[0]; + const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + dma_addr_t refer_addr; + u32 rlc_len; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; + u32 yuv_virstride = 0; + u32 offset; + dma_addr_t dst_addr; + u32 reg, i; + + reg = RKVDEC_MODE(RKVDEC_MODE_HEVC); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL); + + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + if (sps->chroma_format_idc == 0) + yuv_virstride = y_virstride; + else if (sps->chroma_format_idc == 1) + yuv_virstride = y_virstride + y_virstride / 2; + else if (sps->chroma_format_idc == 2) + yuv_virstride = 2 * y_virstride; + + reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | + RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | + RKVDEC_SLICE_NUM_LOWBITS(run->num_slices); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR); + + /* config rlc base address */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); + + rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + reg = RKVDEC_STRM_LEN(round_up(rlc_len, 16) + 64); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN); + + /* config cabac table */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); + + /* config output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + + reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); + + reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + + /* config ref pic address */ + for (i = 0; i < 15; i++) { + struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i); + + if (i < 4 && decode_params->num_active_dpb_entries) { + reg = GENMASK(decode_params->num_active_dpb_entries - 1, 0); + reg = (reg >> (i * 4)) & 0xf; + } else { + reg = 0; + } + + refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + writel_relaxed(refer_addr | reg, + rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i)); + + reg = RKVDEC_POC_REFER(i < decode_params->num_active_dpb_entries ? + dpb[i].pic_order_cnt_val : 0); + writel_relaxed(reg, + rkvdec->regs + RKVDEC_REG_H264_POC_REFER0(i)); + } + + reg = RKVDEC_CUR_POC(sl_params->slice_pic_order_cnt); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0); + + /* config hw pps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_PPS_BASE); + + /* config hw rps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, rps); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_RPS_BASE); + + reg = RKVDEC_AXI_DDR_RDATA(0); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA); + + reg = RKVDEC_AXI_DDR_WDATA(0); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA); +} + +#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2 + +static int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; + + fmt->num_planes = 1; + if (!fmt->plane_fmt[0].sizeimage) + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * + RKVDEC_HEVC_MAX_DEPTH_IN_BYTES; + return 0; +} + +static enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; + + if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS) + return RKVDEC_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_8BIT; + else + return RKVDEC_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_10BIT; + else + return RKVDEC_IMG_FMT_420_10BIT; + } + + return RKVDEC_IMG_FMT_ANY; +} + +static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_hevc_sps *sps) +{ + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit is supported */ + return -EINVAL; + + if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width || + sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height) + return -EINVAL; + + return 0; +} + +static int rkvdec_hevc_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_priv_tbl *priv_tbl; + struct rkvdec_hevc_ctx *hevc_ctx; + + hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL); + if (!hevc_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &hevc_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + kfree(hevc_ctx); + return -ENOMEM; + } + + hevc_ctx->priv_tbl.size = sizeof(*priv_tbl); + hevc_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table, + sizeof(rkvdec_hevc_cabac_table)); + + ctx->priv = hevc_ctx; + return 0; +} + +static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size, + hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma); + kfree(hevc_ctx); +} + +static void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SLICE_PARAMS); + run->slices_params = ctrl ? ctrl->p_cur.p : NULL; + run->num_slices = ctrl ? ctrl->new_elems : 0; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SPS); + run->sps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_PPS); + run->pps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); + run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; + + rkvdec_run_preamble(ctx, &run->base); +} + +static int rkvdec_hevc_run(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_run run; + u32 reg; + + rkvdec_hevc_run_preamble(ctx, &run); + + assemble_hw_scaling_list(ctx, &run); + assemble_hw_pps(ctx, &run); + assemble_sw_rps(ctx, &run); + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); + + writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); + writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E); + writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); + writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); + + if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS) + rkvdec_quirks_disable_qos(ctx); + + /* Start decoding! */ + reg = (run.pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) ? + 0 : RKVDEC_WR_DDR_ALIGN_EN; + writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | + RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E | reg, + rkvdec->regs + RKVDEC_REG_INTERRUPT); + + return 0; +} + +static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) + return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops = { + .adjust_fmt = rkvdec_hevc_adjust_fmt, + .start = rkvdec_hevc_start, + .stop = rkvdec_hevc_stop, + .run = rkvdec_hevc_run, + .try_ctrl = rkvdec_hevc_try_ctrl, + .get_image_fmt = rkvdec_hevc_get_image_fmt, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h index 15b9bee92016..c627b6b6f53a 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h @@ -28,6 +28,7 @@ #define RKVDEC_SOFTRST_EN_P BIT(20) #define RKVDEC_FORCE_SOFTRESET_VALID BIT(21) #define RKVDEC_SOFTRESET_RDY BIT(22) +#define RKVDEC_WR_DDR_ALIGN_EN BIT(23) #define RKVDEC_REG_SYSCTRL 0x008 #define RKVDEC_IN_ENDIAN BIT(0) @@ -43,6 +44,7 @@ #define RKVDEC_RLC_MODE BIT(11) #define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12) #define RKVDEC_MODE(x) (((x) & 0x03) << 20) +#define RKVDEC_MODE_HEVC 0 #define RKVDEC_MODE_H264 1 #define RKVDEC_MODE_VP9 2 #define RKVDEC_RPS_MODE BIT(24) @@ -217,6 +219,8 @@ #define RKVDEC_REG_H264_ERR_E 0x134 #define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff) +#define RKVDEC_REG_QOS_CTRL 0x18C + #define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410 #define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450 diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c index 0e7e16f20eeb..b4bf01e839ef 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c @@ -824,6 +824,10 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx) writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); + + if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS) + rkvdec_quirks_disable_qos(ctx); + /* Start decoding! */ writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 6e606d73ff51..5af9aa5ab353 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -14,6 +14,7 @@ #include <linux/iommu.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> @@ -158,6 +159,67 @@ static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { .s_ctrl = rkvdec_s_ctrl, }; +static const struct rkvdec_ctrl_desc rkvdec_hevc_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, + .cfg.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY, + .cfg.type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS, + .cfg.dims = { 600 }, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SPS, + .cfg.ops = &rkvdec_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_PPS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE, + .cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, + }, +}; + +static const struct rkvdec_ctrls rkvdec_hevc_ctrls = { + .ctrls = rkvdec_hevc_ctrl_descs, + .num_ctrls = ARRAY_SIZE(rkvdec_hevc_ctrl_descs), +}; + +static const struct rkvdec_decoded_fmt_desc rkvdec_hevc_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = RKVDEC_IMG_FMT_420_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV15, + .image_fmt = RKVDEC_IMG_FMT_420_10BIT, + }, +}; + static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, @@ -253,6 +315,22 @@ static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = { static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 64, + .max_height = 2304, + .step_height = 16, + }, + .ctrls = &rkvdec_hevc_ctrls, + .ops = &rkvdec_hevc_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts), + .decoded_fmts = rkvdec_hevc_decoded_fmts, + .capability = RKVDEC_CAPABILITY_HEVC, + }, + { .fourcc = V4L2_PIX_FMT_H264_SLICE, .frmsize = { .min_width = 64, @@ -267,6 +345,7 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), .decoded_fmts = rkvdec_h264_decoded_fmts, .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + .capability = RKVDEC_CAPABILITY_H264, }, { .fourcc = V4L2_PIX_FMT_VP9_FRAME, @@ -282,16 +361,40 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .ops = &rkvdec_vp9_fmt_ops, .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), .decoded_fmts = rkvdec_vp9_decoded_fmts, + .capability = RKVDEC_CAPABILITY_VP9, } }; +static bool rkvdec_is_capable(struct rkvdec_ctx *ctx, unsigned int capability) +{ + return (ctx->dev->variant->capabilities & capability) == capability; +} + +static const struct rkvdec_coded_fmt_desc * +rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index) +{ + int fmt_idx = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { + if (!rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) + continue; + fmt_idx++; + if (index == fmt_idx) + return &rkvdec_coded_fmts[i]; + } + + return NULL; +} + static const struct rkvdec_coded_fmt_desc * -rkvdec_find_coded_fmt_desc(u32 fourcc) +rkvdec_find_coded_fmt_desc(struct rkvdec_ctx *ctx, u32 fourcc) { unsigned int i; for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - if (rkvdec_coded_fmts[i].fourcc == fourcc) + if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability) && + rkvdec_coded_fmts[i].fourcc == fourcc) return &rkvdec_coded_fmts[i]; } @@ -302,7 +405,7 @@ static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) { struct v4l2_format *f = &ctx->coded_fmt; - ctx->coded_fmt_desc = &rkvdec_coded_fmts[0]; + ctx->coded_fmt_desc = rkvdec_enum_coded_fmt_desc(ctx, 0); rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -316,21 +419,22 @@ static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) static int rkvdec_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { - const struct rkvdec_coded_fmt_desc *fmt; + struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file); + const struct rkvdec_coded_fmt_desc *desc; if (fsize->index != 0) return -EINVAL; - fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format); - if (!fmt) + desc = rkvdec_find_coded_fmt_desc(ctx, fsize->pixel_format); + if (!desc) return -EINVAL; fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; fsize->stepwise.min_width = 1; - fsize->stepwise.max_width = fmt->frmsize.max_width; + fsize->stepwise.max_width = desc->frmsize.max_width; fsize->stepwise.step_width = 1; fsize->stepwise.min_height = 1; - fsize->stepwise.max_height = fmt->frmsize.max_height; + fsize->stepwise.max_height = desc->frmsize.max_height; fsize->stepwise.step_height = 1; return 0; @@ -390,10 +494,10 @@ static int rkvdec_try_output_fmt(struct file *file, void *priv, struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file); const struct rkvdec_coded_fmt_desc *desc; - desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat); + desc = rkvdec_find_coded_fmt_desc(ctx, pix_mp->pixelformat); if (!desc) { - pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc; - desc = &rkvdec_coded_fmts[0]; + desc = rkvdec_enum_coded_fmt_desc(ctx, 0); + pix_mp->pixelformat = desc->fourcc; } v4l2_apply_frmsize_constraints(&pix_mp->width, @@ -470,7 +574,7 @@ static int rkvdec_s_output_fmt(struct file *file, void *priv, if (ret) return ret; - desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat); + desc = rkvdec_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat); if (!desc) return -EINVAL; ctx->coded_fmt_desc = desc; @@ -522,10 +626,14 @@ static int rkvdec_g_capture_fmt(struct file *file, void *priv, static int rkvdec_enum_output_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts)) + struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file); + const struct rkvdec_coded_fmt_desc *desc; + + desc = rkvdec_enum_coded_fmt_desc(ctx, f->index); + if (!desc) return -EINVAL; - f->pixelformat = rkvdec_coded_fmts[f->index].fourcc; + f->pixelformat = desc->fourcc; return 0; } @@ -783,7 +891,7 @@ void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) if (src_req) v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl); - v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true); + v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst); } void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) @@ -794,6 +902,18 @@ void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl); } +void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + u32 reg; + + /* Set undocumented swreg_block_gating_e field */ + reg = readl(rkvdec->regs + RKVDEC_REG_QOS_CTRL); + reg &= GENMASK(31, 16); + reg |= 0xEFFF; + writel(reg, rkvdec->regs + RKVDEC_REG_QOS_CTRL); +} + static void rkvdec_device_run(void *priv) { struct rkvdec_ctx *ctx = priv; @@ -889,14 +1009,17 @@ static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx) int ret; for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) - nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; + if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) + nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); - if (ret) - goto err_free_handler; + if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) { + ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); + if (ret) + goto err_free_handler; + } } ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); @@ -1109,8 +1232,39 @@ static void rkvdec_watchdog_func(struct work_struct *work) } } +static const struct rkvdec_variant rk3288_rkvdec_variant = { + .num_regs = 68, + .capabilities = RKVDEC_CAPABILITY_HEVC, +}; + +static const struct rkvdec_variant rk3328_rkvdec_variant = { + .num_regs = 109, + .capabilities = RKVDEC_CAPABILITY_HEVC | + RKVDEC_CAPABILITY_H264 | + RKVDEC_CAPABILITY_VP9, + .quirks = RKVDEC_QUIRK_DISABLE_QOS, +}; + +static const struct rkvdec_variant rk3399_rkvdec_variant = { + .num_regs = 78, + .capabilities = RKVDEC_CAPABILITY_HEVC | + RKVDEC_CAPABILITY_H264 | + RKVDEC_CAPABILITY_VP9, +}; + static const struct of_device_id of_rkvdec_match[] = { - { .compatible = "rockchip,rk3399-vdec" }, + { + .compatible = "rockchip,rk3288-vdec", + .data = &rk3288_rkvdec_variant, + }, + { + .compatible = "rockchip,rk3328-vdec", + .data = &rk3328_rkvdec_variant, + }, + { + .compatible = "rockchip,rk3399-vdec", + .data = &rk3399_rkvdec_variant, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_rkvdec_match); @@ -1121,16 +1275,22 @@ static const char * const rkvdec_clk_names[] = { static int rkvdec_probe(struct platform_device *pdev) { + const struct rkvdec_variant *variant; struct rkvdec_dev *rkvdec; unsigned int i; int ret, irq; + variant = of_device_get_match_data(&pdev->dev); + if (!variant) + return -EINVAL; + rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL); if (!rkvdec) return -ENOMEM; platform_set_drvdata(pdev, rkvdec); rkvdec->dev = &pdev->dev; + rkvdec->variant = variant; mutex_init(&rkvdec->vdev_lock); INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h index 481aaa4bffe9..566e06fa2b1e 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h @@ -22,6 +22,12 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> +#define RKVDEC_CAPABILITY_HEVC BIT(0) +#define RKVDEC_CAPABILITY_H264 BIT(1) +#define RKVDEC_CAPABILITY_VP9 BIT(2) + +#define RKVDEC_QUIRK_DISABLE_QOS BIT(0) + struct rkvdec_ctx; struct rkvdec_ctrl_desc { @@ -63,6 +69,12 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) base.vb.vb2_buf); } +struct rkvdec_variant { + unsigned int num_regs; + unsigned int capabilities; + unsigned int quirks; +}; + struct rkvdec_coded_fmt_ops { int (*adjust_fmt)(struct rkvdec_ctx *ctx, struct v4l2_format *f); @@ -98,6 +110,7 @@ struct rkvdec_coded_fmt_desc { unsigned int num_decoded_fmts; const struct rkvdec_decoded_fmt_desc *decoded_fmts; u32 subsystem_flags; + unsigned int capability; }; struct rkvdec_dev { @@ -111,6 +124,7 @@ struct rkvdec_dev { struct mutex vdev_lock; /* serializes ioctls */ struct delayed_work watchdog_work; struct iommu_domain *empty_domain; + const struct rkvdec_variant *variant; }; struct rkvdec_ctx { @@ -138,7 +152,10 @@ struct rkvdec_aux_buf { void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); +void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx); + extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops; +extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops; extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops; #endif /* RKVDEC_H_ */ diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index ed7b7ca16f71..0827fdaf455a 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -996,7 +996,6 @@ static void fimc_is_module_exit(void) module_init(fimc_is_module_init); module_exit(fimc_is_module_exit); -MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index 0ce293b0718b..8be20fd32d1c 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -1662,4 +1662,3 @@ static struct platform_driver fimc_lite_driver = { module_platform_driver(fimc_lite_driver); MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index c781853586fd..bc7087eb761a 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -1333,8 +1333,8 @@ static int fimc_md_register_clk_provider(struct fimc_md *fmd) cp->clks[i] = clk_register(NULL, &camclk->hw); if (IS_ERR(cp->clks[i])) { - dev_err(dev, "failed to register clock: %s (%ld)\n", - init.name, PTR_ERR(cp->clks[i])); + dev_err(dev, "failed to register clock: %s (%pe)\n", + init.name, cp->clks[i]); ret = PTR_ERR(cp->clks[i]); goto err; } @@ -1399,12 +1399,14 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) mutex_lock(&fmd->media_dev.graph_mutex); ret = fimc_md_create_links(fmd); - if (ret < 0) - goto unlock; + if (ret < 0) { + mutex_unlock(&fmd->media_dev.graph_mutex); + return ret; + } - ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); -unlock: mutex_unlock(&fmd->media_dev.graph_mutex); + + ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret < 0) return ret; diff --git a/drivers/media/platform/samsung/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c index ffb9bee6cb9d..e765dfcc2830 100644 --- a/drivers/media/platform/samsung/s5p-g2d/g2d.c +++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c @@ -308,12 +308,8 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct g2d_ctx *ctx = file2ctx(file); - struct vb2_queue *vq; struct g2d_frame *frm; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; frm = get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c index 81792f7f8b16..ff28482759ec 100644 --- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c @@ -1332,15 +1332,10 @@ static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct vb2_queue *vq; struct s5p_jpeg_q_data *q_data = NULL; struct v4l2_pix_format *pix = &f->fmt.pix; struct s5p_jpeg_ctx *ct = file_to_ctx(file); - vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed) return -EINVAL; @@ -1593,8 +1588,6 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) unsigned int f_type; vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; q_data = get_q_data(ct, f->type); BUG_ON(q_data == NULL); diff --git a/drivers/media/platform/st/Makefile b/drivers/media/platform/st/Makefile index a1f75b2a8225..615a93d62662 100644 --- a/drivers/media/platform/st/Makefile +++ b/drivers/media/platform/st/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += sti/bdisp/ -obj-y += sti/c8sectpfe/ obj-y += sti/delta/ obj-y += sti/hva/ obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/Kconfig b/drivers/media/platform/st/sti/Kconfig index 60068e8b47b8..91ca0950ff73 100644 --- a/drivers/media/platform/st/sti/Kconfig +++ b/drivers/media/platform/st/sti/Kconfig @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only source "drivers/media/platform/st/sti/bdisp/Kconfig" -source "drivers/media/platform/st/sti/c8sectpfe/Kconfig" source "drivers/media/platform/st/sti/delta/Kconfig" source "drivers/media/platform/st/sti/hva/Kconfig" diff --git a/drivers/media/platform/st/sti/Makefile b/drivers/media/platform/st/sti/Makefile index f9ce8169b040..3328d50fb6cf 100644 --- a/drivers/media/platform/st/sti/Makefile +++ b/drivers/media/platform/st/sti/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += bdisp/ -obj-y += c8sectpfe/ obj-y += delta/ obj-y += hva/ obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig deleted file mode 100644 index 01c33d9c9ec3..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config DVB_C8SECTPFE - tristate "STMicroelectronics C8SECTPFE DVB support" - depends on DVB_PLATFORM_DRIVERS - depends on PINCTRL && DVB_CORE && I2C - depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST - select FW_LOADER - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT - - help - This adds support for DVB front-end cards connected - to TS inputs of STiH407/410 SoC. - - The driver currently supports C8SECTPFE's TS input block, - memdma engine, and HW PID filtering. - - Supported DVB front-end cards are: - - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212) - - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board - - To compile this driver as a module, choose M here: the - module will be called c8sectpfe. diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile deleted file mode 100644 index 99425137ee0a..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o - -ifneq ($(CONFIG_DEBUG_FS),) -c8sectpfe-y += c8sectpfe-debugfs.o -endif - -obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o - -ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ -ccflags-y += -I $(srctree)/drivers/media/tuners/ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c deleted file mode 100644 index 5df67da25525..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-common.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dvb/dmx.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/wait.h> - -#include <media/dmxdev.h> -#include <media/dvbdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-dvb.h" - -static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap, - void *start_feed, void *stop_feed, - struct c8sectpfei *fei) -{ - int result; - - demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - - demux->dvb_demux.priv = demux; - demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL; - demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL; - - demux->dvb_demux.start_feed = start_feed; - demux->dvb_demux.stop_feed = stop_feed; - demux->dvb_demux.write_to_decoder = NULL; - - result = dvb_dmx_init(&demux->dvb_demux); - if (result < 0) { - dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n", - result); - goto err_dmx; - } - - demux->dmxdev.filternum = demux->dvb_demux.filternum; - demux->dmxdev.demux = &demux->dvb_demux.dmx; - demux->dmxdev.capabilities = 0; - - result = dvb_dmxdev_init(&demux->dmxdev, adap); - if (result < 0) { - dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n", - result); - - goto err_dmxdev; - } - - demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index; - - result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - if (result < 0) { - dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result); - goto err_fe_hw; - } - - demux->mem_frontend.source = DMX_MEMORY_FE; - result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); - if (result < 0) { - dev_err(fei->dev, "add_frontend failed (%d)\n", result); - goto err_fe_mem; - } - - result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - if (result < 0) { - dev_err(fei->dev, "connect_frontend (%d)\n", result); - goto err_fe_con; - } - - return 0; - -err_fe_con: - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); -err_fe_mem: - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); -err_fe_hw: - dvb_dmxdev_release(&demux->dmxdev); -err_dmxdev: - dvb_dmx_release(&demux->dvb_demux); -err_dmx: - return result; - -} - -static void unregister_dvb(struct stdemux *demux) -{ - - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); - - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - - dvb_dmxdev_release(&demux->dmxdev); - - dvb_dmx_release(&demux->dvb_demux); -} - -static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei, - void *start_feed, - void *stop_feed) -{ - struct c8sectpfe *c8sectpfe; - int result; - int i, j; - - short int ids[] = { -1 }; - - c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL); - if (!c8sectpfe) - goto err1; - - mutex_init(&c8sectpfe->lock); - - c8sectpfe->device = fei->dev; - - result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe", - THIS_MODULE, fei->dev, ids); - if (result < 0) { - dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n", - result); - goto err2; - } - - c8sectpfe->adapter.priv = fei; - - for (i = 0; i < fei->tsin_count; i++) { - - c8sectpfe->demux[i].tsin_index = i; - c8sectpfe->demux[i].c8sectpfei = fei; - - result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter, - start_feed, stop_feed, fei); - if (result < 0) { - dev_err(fei->dev, - "register_dvb feed=%d failed (errno = %d)\n", - result, i); - - /* we take a all or nothing approach */ - for (j = 0; j < i; j++) - unregister_dvb(&c8sectpfe->demux[j]); - goto err3; - } - } - - c8sectpfe->num_feeds = fei->tsin_count; - - return c8sectpfe; -err3: - dvb_unregister_adapter(&c8sectpfe->adapter); -err2: - kfree(c8sectpfe); -err1: - return NULL; -}; - -static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe) -{ - int i; - - if (!c8sectpfe) - return; - - for (i = 0; i < c8sectpfe->num_feeds; i++) - unregister_dvb(&c8sectpfe->demux[i]); - - dvb_unregister_adapter(&c8sectpfe->adapter); - - kfree(c8sectpfe); -}; - -void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, - struct c8sectpfei *fei) -{ - int n; - struct channel_info *tsin; - - for (n = 0; n < fei->tsin_count; n++) { - - tsin = fei->channel_data[n]; - - if (tsin) { - if (tsin->frontend) { - dvb_unregister_frontend(tsin->frontend); - dvb_frontend_detach(tsin->frontend); - } - - i2c_put_adapter(tsin->i2c_adapter); - - if (tsin->i2c_client) { - module_put(tsin->i2c_client->dev.driver->owner); - i2c_unregister_device(tsin->i2c_client); - } - } - } - - c8sectpfe_delete(c8sectpfe); -}; - -int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, - struct c8sectpfei *fei, - void *start_feed, - void *stop_feed) -{ - struct channel_info *tsin; - struct dvb_frontend *frontend; - int n, res; - - *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed); - if (!*c8sectpfe) - return -ENOMEM; - - for (n = 0; n < fei->tsin_count; n++) { - tsin = fei->channel_data[n]; - - res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n); - if (res) - goto err; - - res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend); - if (res < 0) { - dev_err(fei->dev, "dvb_register_frontend failed (%d)\n", - res); - goto err; - } - - tsin->frontend = frontend; - } - - return 0; - -err: - c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei); - return res; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h deleted file mode 100644 index f8d97841f366..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-common.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_COMMON_H_ -#define _C8SECTPFE_COMMON_H_ - -#include <linux/dvb/dmx.h> -#include <linux/dvb/frontend.h> -#include <linux/gpio.h> - -#include <media/dmxdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -/* Maximum number of channels */ -#define C8SECTPFE_MAXADAPTER (4) -#define C8SECTPFE_MAXCHANNEL 64 -#define STPTI_MAXCHANNEL 64 - -#define MAX_INPUTBLOCKS 7 - -struct c8sectpfe; -struct stdemux; - -struct stdemux { - struct dvb_demux dvb_demux; - struct dmxdev dmxdev; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int tsin_index; - int running_feed_count; - struct c8sectpfei *c8sectpfei; -}; - -struct c8sectpfe { - struct stdemux demux[MAX_INPUTBLOCKS]; - struct mutex lock; - struct dvb_adapter adapter; - struct device *device; - int mapping; - int num_feeds; -}; - -/* Channel registration */ -int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, - struct c8sectpfei *fei, - void *start_feed, - void *stop_feed); - -void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, - struct c8sectpfei *fei); - -#endif diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c deleted file mode 100644 index 89bd15a4d26a..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ /dev/null @@ -1,1158 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-core.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author:Peter Bennett <peter.bennett@st.com> - * Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/atomic.h> -#include <linux/clk.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/dvb/dmx.h> -#include <linux/dvb/frontend.h> -#include <linux/err.h> -#include <linux/errno.h> -#include <linux/firmware.h> -#include <linux/gpio/consumer.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of_platform.h> -#include <linux/pinctrl/consumer.h> -#include <linux/pinctrl/pinctrl.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/usb.h> -#include <linux/wait.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-debugfs.h" - -#include <media/dmxdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -#define FIRMWARE_MEMDMA "pti_memdma_h407.elf" -MODULE_FIRMWARE(FIRMWARE_MEMDMA); - -#define PID_TABLE_SIZE 1024 -#define POLL_MSECS 50 - -static int load_c8sectpfe_fw(struct c8sectpfei *fei); - -#define TS_PKT_SIZE 188 -#define HEADER_SIZE (4) -#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE) - -#define FEI_ALIGNMENT (32) -/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */ -#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340) - -#define FIFO_LEN 1024 - -static void c8sectpfe_timer_interrupt(struct timer_list *t) -{ - struct c8sectpfei *fei = timer_container_of(fei, t, timer); - struct channel_info *channel; - int chan_num; - - /* iterate through input block channels */ - for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) { - channel = fei->channel_data[chan_num]; - - /* is this descriptor initialised and TP enabled */ - if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) - queue_work(system_bh_wq, &channel->bh_work); - } - - fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); - add_timer(&fei->timer); -} - -static void channel_swdemux_bh_work(struct work_struct *t) -{ - struct channel_info *channel = from_work(channel, t, bh_work); - struct c8sectpfei *fei; - unsigned long wp, rp; - int pos, num_packets, n, size; - u8 *buf; - - if (unlikely(!channel || !channel->irec)) - return; - - fei = channel->fei; - - wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0)); - rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0)); - - pos = rp - channel->back_buffer_busaddr; - - /* has it wrapped */ - if (wp < rp) - wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE; - - size = wp - rp; - num_packets = size / PACKET_SIZE; - - /* manage cache so data is visible to CPU */ - dma_sync_single_for_cpu(fei->dev, - rp, - size, - DMA_FROM_DEVICE); - - buf = channel->back_buffer_aligned; - - dev_dbg(fei->dev, - "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\trp=0x%lx, wp=0x%lx\n", - channel->tsin_id, channel, num_packets, buf, pos, rp, wp); - - for (n = 0; n < num_packets; n++) { - dvb_dmx_swfilter_packets( - &fei->c8sectpfe[0]-> - demux[channel->demux_mapping].dvb_demux, - &buf[pos], 1); - - pos += PACKET_SIZE; - } - - /* advance the read pointer */ - if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE)) - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSRP_TP(0)); - else - writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0)); -} - -static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *demux = dvbdmxfeed->demux; - struct stdemux *stdemux = demux->priv; - struct c8sectpfei *fei = stdemux->c8sectpfei; - struct channel_info *channel; - u32 tmp; - unsigned long *bitmap; - int ret; - - switch (dvbdmxfeed->type) { - case DMX_TYPE_TS: - break; - case DMX_TYPE_SEC: - break; - default: - dev_err(fei->dev, "%s:%d Error bailing\n" - , __func__, __LINE__); - return -EINVAL; - } - - if (dvbdmxfeed->type == DMX_TYPE_TS) { - switch (dvbdmxfeed->pes_type) { - case DMX_PES_VIDEO: - case DMX_PES_AUDIO: - case DMX_PES_TELETEXT: - case DMX_PES_PCR: - case DMX_PES_OTHER: - break; - default: - dev_err(fei->dev, "%s:%d Error bailing\n" - , __func__, __LINE__); - return -EINVAL; - } - } - - if (!atomic_read(&fei->fw_loaded)) { - ret = load_c8sectpfe_fw(fei); - if (ret) - return ret; - } - - mutex_lock(&fei->lock); - - channel = fei->channel_data[stdemux->tsin_index]; - - bitmap = channel->pid_buffer_aligned; - - /* 8192 is a special PID */ - if (dvbdmxfeed->pid == 8192) { - tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - tmp &= ~C8SECTPFE_PID_ENABLE; - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - - } else { - bitmap_set(bitmap, dvbdmxfeed->pid, 1); - } - - /* manage cache so PID bitmap is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - channel->active = 1; - - if (fei->global_feed_count == 0) { - fei->timer.expires = jiffies + - msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS)); - - add_timer(&fei->timer); - } - - if (stdemux->running_feed_count == 0) { - - dev_dbg(fei->dev, "Starting channel=%p\n", channel); - - INIT_WORK(&channel->bh_work, channel_swdemux_bh_work); - - /* Reset the internal inputblock sram pointers */ - writel(channel->fifo, - fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id)); - writel(channel->fifo + FIFO_LEN - 1, - fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id)); - - writel(channel->fifo, - fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id)); - writel(channel->fifo, - fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id)); - - - /* reset read / write memdma ptrs for this channel */ - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSBASE_TP(0)); - - tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSWP_TP(0)); - - /* Issue a reset and enable InputBlock */ - writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET - , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); - - /* and enable the tp */ - writel(0x1, channel->irec + DMA_PRDS_TPENABLE); - - dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n" - , __func__, __LINE__, stdemux); - } - - stdemux->running_feed_count++; - fei->global_feed_count++; - - mutex_unlock(&fei->lock); - - return 0; -} - -static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - - struct dvb_demux *demux = dvbdmxfeed->demux; - struct stdemux *stdemux = demux->priv; - struct c8sectpfei *fei = stdemux->c8sectpfei; - struct channel_info *channel; - int idlereq; - u32 tmp; - int ret; - unsigned long *bitmap; - - if (!atomic_read(&fei->fw_loaded)) { - ret = load_c8sectpfe_fw(fei); - if (ret) - return ret; - } - - mutex_lock(&fei->lock); - - channel = fei->channel_data[stdemux->tsin_index]; - - bitmap = channel->pid_buffer_aligned; - - if (dvbdmxfeed->pid == 8192) { - tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - tmp |= C8SECTPFE_PID_ENABLE; - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - } else { - bitmap_clear(bitmap, dvbdmxfeed->pid, 1); - } - - /* manage cache so data is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - if (--stdemux->running_feed_count == 0) { - - channel = fei->channel_data[stdemux->tsin_index]; - - /* TP re-configuration on page 168 of functional spec */ - - /* disable IB (prevents more TS data going to memdma) */ - writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); - - /* disable this channels descriptor */ - writel(0, channel->irec + DMA_PRDS_TPENABLE); - - disable_work_sync(&channel->bh_work); - - /* now request memdma channel goes idle */ - idlereq = (1 << channel->tsin_id) | IDLEREQ; - writel(idlereq, fei->io + DMA_IDLE_REQ); - - /* wait for idle irq handler to signal completion */ - ret = wait_for_completion_timeout(&channel->idle_completion, - msecs_to_jiffies(100)); - - if (ret == 0) - dev_warn(fei->dev, - "Timeout waiting for idle irq on tsin%d\n", - channel->tsin_id); - - reinit_completion(&channel->idle_completion); - - /* reset read / write ptrs for this channel */ - - writel(channel->back_buffer_busaddr, - channel->irec + DMA_PRDS_BUSBASE_TP(0)); - - tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(channel->back_buffer_busaddr, - channel->irec + DMA_PRDS_BUSWP_TP(0)); - - dev_dbg(fei->dev, - "%s:%d stopping DMA feed on stdemux=%p channel=%d\n", - __func__, __LINE__, stdemux, channel->tsin_id); - - /* turn off all PIDS in the bitmap */ - memset(channel->pid_buffer_aligned, 0, PID_TABLE_SIZE); - - /* manage cache so data is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - channel->active = 0; - } - - if (--fei->global_feed_count == 0) { - dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n" - , __func__, __LINE__, fei->global_feed_count); - - timer_delete(&fei->timer); - } - - mutex_unlock(&fei->lock); - - return 0; -} - -static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num) -{ - int i; - - for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) { - if (!fei->channel_data[i]) - continue; - - if (fei->channel_data[i]->tsin_id == tsin_num) - return fei->channel_data[i]; - } - - return NULL; -} - -static void c8sectpfe_getconfig(struct c8sectpfei *fei) -{ - struct c8sectpfe_hw *hw = &fei->hw_stats; - - hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB); - hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB); - hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS); - hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT); - hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC); - hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM); - hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP); - - dev_info(fei->dev, "C8SECTPFE hw supports the following:\n"); - dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib); - dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib); - dev_info(fei->dev, "Software Transport Stream Inputs: %d\n" - , hw->num_swts); - dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout); - dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc); - dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram); - dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n" - , hw->num_tp); -} - -static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv) -{ - struct c8sectpfei *fei = priv; - struct channel_info *chan; - int bit; - unsigned long tmp = readl(fei->io + DMA_IDLE_REQ); - - /* page 168 of functional spec: Clear the idle request - by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */ - - /* signal idle completion */ - for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) { - - chan = find_channel(fei, bit); - - if (chan) - complete(&chan->idle_completion); - } - - writel(0, fei->io + DMA_IDLE_REQ); - - return IRQ_HANDLED; -} - - -static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin) -{ - if (!fei || !tsin) - return; - - if (tsin->back_buffer_busaddr) - if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) - dma_unmap_single(fei->dev, tsin->back_buffer_busaddr, - FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL); - - kfree(tsin->back_buffer_start); - - if (tsin->pid_buffer_busaddr) - if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) - dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr, - PID_TABLE_SIZE, DMA_BIDIRECTIONAL); - - kfree(tsin->pid_buffer_start); -} - -#define MAX_NAME 20 - -static int configure_memdma_and_inputblock(struct c8sectpfei *fei, - struct channel_info *tsin) -{ - int ret; - u32 tmp; - char tsin_pin_name[MAX_NAME]; - - if (!fei || !tsin) - return -EINVAL; - - dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n" - , __func__, __LINE__, tsin, tsin->tsin_id); - - init_completion(&tsin->idle_completion); - - tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + FEI_ALIGNMENT, GFP_KERNEL); - if (!tsin->back_buffer_start) { - ret = -ENOMEM; - goto err_unmap; - } - - /* Ensure backbuffer is 32byte aligned */ - tsin->back_buffer_aligned = tsin->back_buffer_start + FEI_ALIGNMENT; - - tsin->back_buffer_aligned = PTR_ALIGN(tsin->back_buffer_aligned, FEI_ALIGNMENT); - - tsin->back_buffer_busaddr = dma_map_single(fei->dev, - tsin->back_buffer_aligned, - FEI_BUFFER_SIZE, - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) { - dev_err(fei->dev, "failed to map back_buffer\n"); - ret = -EFAULT; - goto err_unmap; - } - - /* - * The pid buffer can be configured (in hw) for byte or bit - * per pid. By powers of deduction we conclude stih407 family - * is configured (at SoC design stage) for bit per pid. - */ - tsin->pid_buffer_start = kzalloc(PID_TABLE_SIZE + PID_TABLE_SIZE, GFP_KERNEL); - if (!tsin->pid_buffer_start) { - ret = -ENOMEM; - goto err_unmap; - } - - /* - * PID buffer needs to be aligned to size of the pid table - * which at bit per pid is 1024 bytes (8192 pids / 8). - * PIDF_BASE register enforces this alignment when writing - * the register. - */ - - tsin->pid_buffer_aligned = tsin->pid_buffer_start + PID_TABLE_SIZE; - - tsin->pid_buffer_aligned = PTR_ALIGN(tsin->pid_buffer_aligned, PID_TABLE_SIZE); - - tsin->pid_buffer_busaddr = dma_map_single(fei->dev, - tsin->pid_buffer_aligned, - PID_TABLE_SIZE, - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) { - dev_err(fei->dev, "failed to map pid_bitmap\n"); - ret = -EFAULT; - goto err_unmap; - } - - /* manage cache so pid bitmap is visible to HW */ - dma_sync_single_for_device(fei->dev, - tsin->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id, - (tsin->serial_not_parallel ? "serial" : "parallel")); - - tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name); - if (IS_ERR(tsin->pstate)) { - dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n" - , __func__, tsin_pin_name); - ret = PTR_ERR(tsin->pstate); - goto err_unmap; - } - - ret = pinctrl_select_state(fei->pinctrl, tsin->pstate); - - if (ret) { - dev_err(fei->dev, "%s: pinctrl_select_state failed\n" - , __func__); - goto err_unmap; - } - - /* Enable this input block */ - tmp = readl(fei->io + SYS_INPUT_CLKEN); - tmp |= BIT(tsin->tsin_id); - writel(tmp, fei->io + SYS_INPUT_CLKEN); - - if (tsin->serial_not_parallel) - tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL; - - if (tsin->invert_ts_clk) - tmp |= C8SECTPFE_INVERT_TSCLK; - - if (tsin->async_not_sync) - tmp |= C8SECTPFE_ASYNC_NOT_SYNC; - - tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB; - - writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id)); - - writel(C8SECTPFE_SYNC(0x9) | - C8SECTPFE_DROP(0x9) | - C8SECTPFE_TOKEN(0x47), - fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id)); - - writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id)); - - /* Place the FIFO's at the end of the irec descriptors */ - - tsin->fifo = (tsin->tsin_id * FIFO_LEN); - - writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)); - writel(tsin->fifo + FIFO_LEN - 1, - fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)); - - writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)); - writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)); - - writel(tsin->pid_buffer_busaddr, - fei->io + PIDF_BASE(tsin->tsin_id)); - - dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", - tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), - &tsin->pid_buffer_busaddr); - - /* Configure and enable HW PID filtering */ - - /* - * The PID value is created by assembling the first 8 bytes of - * the TS packet into a 64-bit word in big-endian format. A - * slice of that 64-bit word is taken from - * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET. - */ - tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13) - | C8SECTPFE_PID_OFFSET(40)); - - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id)); - - dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n", - tsin->tsin_id, - readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id))); - - /* Get base addpress of pointer record block from DMEM */ - tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + - readl(fei->io + DMA_PTRREC_BASE); - - /* fill out pointer record data structure */ - - /* advance pointer record block to our channel */ - tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE); - - writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE); - - writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP); - - writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE); - - writel(0x1, tsin->irec + DMA_PRDS_TPENABLE); - - /* read/write pointers with physical bus address */ - - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0)); - - tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); - - /* initialize bh work */ - INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work); - - return 0; - -err_unmap: - free_input_block(fei, tsin); - return ret; -} - -static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv) -{ - struct c8sectpfei *fei = priv; - - dev_err(fei->dev, "%s: error handling not yet implemented\n" - , __func__); - - /* - * TODO FIXME we should detect some error conditions here - * and ideally do something about them! - */ - - return IRQ_HANDLED; -} - -static int c8sectpfe_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct c8sectpfei *fei; - struct resource *res; - int ret, index = 0; - struct channel_info *tsin; - - /* Allocate the c8sectpfei structure */ - fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL); - if (!fei) - return -ENOMEM; - - fei->dev = dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe"); - fei->io = devm_ioremap_resource(dev, res); - if (IS_ERR(fei->io)) - return PTR_ERR(fei->io); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "c8sectpfe-ram"); - fei->sram = devm_ioremap_resource(dev, res); - if (IS_ERR(fei->sram)) - return PTR_ERR(fei->sram); - - fei->sram_size = resource_size(res); - - fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq"); - if (fei->idle_irq < 0) - return fei->idle_irq; - - fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq"); - if (fei->error_irq < 0) - return fei->error_irq; - - platform_set_drvdata(pdev, fei); - - fei->c8sectpfeclk = devm_clk_get_enabled(dev, "c8sectpfe"); - if (IS_ERR(fei->c8sectpfeclk)) { - dev_err(dev, "Failed to enable c8sectpfe clock\n"); - return PTR_ERR(fei->c8sectpfeclk); - } - - /* to save power disable all IP's (on by default) */ - writel(0, fei->io + SYS_INPUT_CLKEN); - - /* Enable memdma clock */ - writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN); - - /* clear internal sram */ - memset_io(fei->sram, 0x0, fei->sram_size); - - c8sectpfe_getconfig(fei); - - ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler, - 0, "c8sectpfe-idle-irq", fei); - if (ret) { - dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n"); - return ret; - } - - ret = devm_request_irq(dev, fei->error_irq, - c8sectpfe_error_irq_handler, 0, - "c8sectpfe-error-irq", fei); - if (ret) { - dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n"); - return ret; - } - - fei->tsin_count = of_get_child_count(np); - - if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN || - fei->tsin_count > fei->hw_stats.num_ib) { - - dev_err(dev, "More tsin declared than exist on SoC!\n"); - return -EINVAL; - } - - fei->pinctrl = devm_pinctrl_get(dev); - - if (IS_ERR(fei->pinctrl)) { - dev_err(dev, "Error getting tsin pins\n"); - return PTR_ERR(fei->pinctrl); - } - - for_each_child_of_node_scoped(np, child) { - struct device_node *i2c_bus; - - fei->channel_data[index] = devm_kzalloc(dev, - sizeof(struct channel_info), - GFP_KERNEL); - - if (!fei->channel_data[index]) - return -ENOMEM; - - tsin = fei->channel_data[index]; - - tsin->fei = fei; - - ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); - if (ret) { - dev_err(&pdev->dev, "No tsin_num found\n"); - return ret; - } - - /* sanity check value */ - if (tsin->tsin_id > fei->hw_stats.num_ib) { - dev_err(&pdev->dev, - "tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)", - tsin->tsin_id, fei->hw_stats.num_ib); - return -EINVAL; - } - - tsin->invert_ts_clk = of_property_read_bool(child, - "invert-ts-clk"); - - tsin->serial_not_parallel = of_property_read_bool(child, - "serial-not-parallel"); - - tsin->async_not_sync = of_property_read_bool(child, - "async-not-sync"); - - ret = of_property_read_u32(child, "dvb-card", - &tsin->dvb_card); - if (ret) { - dev_err(&pdev->dev, "No dvb-card found\n"); - return ret; - } - - i2c_bus = of_parse_phandle(child, "i2c-bus", 0); - if (!i2c_bus) { - dev_err(&pdev->dev, "No i2c-bus found\n"); - return -ENODEV; - } - tsin->i2c_adapter = - of_find_i2c_adapter_by_node(i2c_bus); - of_node_put(i2c_bus); - if (!tsin->i2c_adapter) { - dev_err(&pdev->dev, "No i2c adapter found\n"); - return -ENODEV; - } - - /* Acquire reset GPIO and activate it */ - tsin->rst_gpio = devm_fwnode_gpiod_get(dev, - of_fwnode_handle(child), - "reset", GPIOD_OUT_HIGH, - "NIM reset"); - ret = PTR_ERR_OR_ZERO(tsin->rst_gpio); - if (ret && ret != -EBUSY) { - dev_err(dev, "Can't request tsin%d reset gpio\n", - fei->channel_data[index]->tsin_id); - return ret; - } - - if (!ret) { - /* wait for the chip to reset */ - usleep_range(3500, 5000); - /* release the reset line */ - gpiod_set_value_cansleep(tsin->rst_gpio, 0); - usleep_range(3000, 5000); - } - - tsin->demux_mapping = index; - - dev_dbg(fei->dev, - "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\tserial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n", - fei->channel_data[index], index, - tsin->tsin_id, tsin->invert_ts_clk, - tsin->serial_not_parallel, tsin->async_not_sync, - tsin->dvb_card); - - index++; - } - - /* Setup timer interrupt */ - timer_setup(&fei->timer, c8sectpfe_timer_interrupt, 0); - - mutex_init(&fei->lock); - - /* Get the configuration information about the tuners */ - ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0], - (void *)fei, - c8sectpfe_start_feed, - c8sectpfe_stop_feed); - if (ret) { - dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n", - ret); - return ret; - } - - c8sectpfe_debugfs_init(fei); - - return 0; -} - -static void c8sectpfe_remove(struct platform_device *pdev) -{ - struct c8sectpfei *fei = platform_get_drvdata(pdev); - struct channel_info *channel; - int i; - - wait_for_completion(&fei->fw_ack); - - c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei); - - /* - * Now loop through and un-configure each of the InputBlock resources - */ - for (i = 0; i < fei->tsin_count; i++) { - channel = fei->channel_data[i]; - free_input_block(fei, channel); - } - - c8sectpfe_debugfs_exit(fei); - - dev_info(fei->dev, "Stopping memdma SLIM core\n"); - if (readl(fei->io + DMA_CPU_RUN)) - writel(0x0, fei->io + DMA_CPU_RUN); - - /* unclock all internal IP's */ - if (readl(fei->io + SYS_INPUT_CLKEN)) - writel(0, fei->io + SYS_INPUT_CLKEN); - - if (readl(fei->io + SYS_OTHER_CLKEN)) - writel(0, fei->io + SYS_OTHER_CLKEN); -} - - -static int configure_channels(struct c8sectpfei *fei) -{ - int index = 0, ret; - struct device_node *np = fei->dev->of_node; - - /* iterate round each tsin and configure memdma descriptor and IB hw */ - for_each_child_of_node_scoped(np, child) { - ret = configure_memdma_and_inputblock(fei, - fei->channel_data[index]); - if (ret) { - dev_err(fei->dev, - "configure_memdma_and_inputblock failed\n"); - goto err_unmap; - } - index++; - } - - return 0; - -err_unmap: - while (--index >= 0) - free_input_block(fei, fei->channel_data[index]); - - return ret; -} - -static int -c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw) -{ - struct elf32_hdr *ehdr; - char class; - - if (!fw) { - dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA); - return -EINVAL; - } - - if (fw->size < sizeof(struct elf32_hdr)) { - dev_err(fei->dev, "Image is too small\n"); - return -EINVAL; - } - - ehdr = (struct elf32_hdr *)fw->data; - - /* We only support ELF32 at this point */ - class = ehdr->e_ident[EI_CLASS]; - if (class != ELFCLASS32) { - dev_err(fei->dev, "Unsupported class: %d\n", class); - return -EINVAL; - } - - if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { - dev_err(fei->dev, "Unsupported firmware endianness\n"); - return -EINVAL; - } - - if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { - dev_err(fei->dev, "Image is too small\n"); - return -EINVAL; - } - - if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { - dev_err(fei->dev, "Image is corrupted (bad magic)\n"); - return -EINVAL; - } - - /* Check ELF magic */ - ehdr = (Elf32_Ehdr *)fw->data; - if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || - ehdr->e_ident[EI_MAG1] != ELFMAG1 || - ehdr->e_ident[EI_MAG2] != ELFMAG2 || - ehdr->e_ident[EI_MAG3] != ELFMAG3) { - dev_err(fei->dev, "Invalid ELF magic\n"); - return -EINVAL; - } - - if (ehdr->e_type != ET_EXEC) { - dev_err(fei->dev, "Unsupported ELF header type\n"); - return -EINVAL; - } - - if (ehdr->e_phoff > fw->size) { - dev_err(fei->dev, "Firmware size is too small\n"); - return -EINVAL; - } - - return 0; -} - - -static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, - const struct firmware *fw, u8 __iomem *dest, - int seg_num) -{ - const u8 *imem_src = fw->data + phdr->p_offset; - int i; - - /* - * For IMEM segments, the segment contains 24-bit - * instructions which must be padded to 32-bit - * instructions before being written. The written - * segment is padded with NOP instructions. - */ - - dev_dbg(fei->dev, - "Loading IMEM segment %d 0x%08x\n\t (0x%x bytes) -> 0x%p (0x%x bytes)\n", - seg_num, phdr->p_paddr, phdr->p_filesz, dest, - phdr->p_memsz + phdr->p_memsz / 3); - - for (i = 0; i < phdr->p_filesz; i++) { - - writeb(readb((void __iomem *)imem_src), (void __iomem *)dest); - - /* Every 3 bytes, add an additional - * padding zero in destination */ - if (i % 3 == 2) { - dest++; - writeb(0x00, (void __iomem *)dest); - } - - dest++; - imem_src++; - } -} - -static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, - const struct firmware *fw, u8 __iomem *dst, int seg_num) -{ - /* - * For DMEM segments copy the segment data from the ELF - * file and pad segment with zeroes - */ - - dev_dbg(fei->dev, - "Loading DMEM segment %d 0x%08x\n\t(0x%x bytes) -> 0x%p (0x%x bytes)\n", - seg_num, phdr->p_paddr, phdr->p_filesz, - dst, phdr->p_memsz); - - memcpy((void __force *)dst, (void *)fw->data + phdr->p_offset, - phdr->p_filesz); - - memset((void __force *)dst + phdr->p_filesz, 0, - phdr->p_memsz - phdr->p_filesz); -} - -static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei) -{ - Elf32_Ehdr *ehdr; - Elf32_Phdr *phdr; - u8 __iomem *dst; - int err = 0, i; - - if (!fw || !fei) - return -EINVAL; - - ehdr = (Elf32_Ehdr *)fw->data; - phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); - - /* go through the available ELF segments */ - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - - /* Only consider LOAD segments */ - if (phdr->p_type != PT_LOAD) - continue; - - /* - * Check segment is contained within the fw->data buffer - */ - if (phdr->p_offset + phdr->p_filesz > fw->size) { - dev_err(fei->dev, - "Segment %d is outside of firmware file\n", i); - err = -EINVAL; - break; - } - - /* - * MEMDMA IMEM has executable flag set, otherwise load - * this segment into DMEM. - * - */ - - if (phdr->p_flags & PF_X) { - dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM; - /* - * The Slim ELF file uses 32-bit word addressing for - * load offsets. - */ - dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); - load_imem_segment(fei, phdr, fw, dst, i); - } else { - dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM; - /* - * The Slim ELF file uses 32-bit word addressing for - * load offsets. - */ - dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); - load_dmem_segment(fei, phdr, fw, dst, i); - } - } - - return err; -} - -static int load_c8sectpfe_fw(struct c8sectpfei *fei) -{ - const struct firmware *fw; - int err; - - dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); - - err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev); - if (err) - return err; - - err = c8sectpfe_elf_sanity_check(fei, fw); - if (err) { - dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" - , err); - release_firmware(fw); - return err; - } - - err = load_slim_core_fw(fw, fei); - release_firmware(fw); - if (err) { - dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); - return err; - } - - /* now the firmware is loaded configure the input blocks */ - err = configure_channels(fei); - if (err) { - dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); - return err; - } - - /* - * STBus target port can access IMEM and DMEM ports - * without waiting for CPU - */ - writel(0x1, fei->io + DMA_PER_STBUS_SYNC); - - dev_info(fei->dev, "Boot the memdma SLIM core\n"); - writel(0x1, fei->io + DMA_CPU_RUN); - - atomic_set(&fei->fw_loaded, 1); - - return 0; -} - -static const struct of_device_id c8sectpfe_match[] = { - { .compatible = "st,stih407-c8sectpfe" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, c8sectpfe_match); - -static struct platform_driver c8sectpfe_driver = { - .driver = { - .name = "c8sectpfe", - .of_match_table = c8sectpfe_match, - }, - .probe = c8sectpfe_probe, - .remove = c8sectpfe_remove, -}; - -module_platform_driver(c8sectpfe_driver); - -MODULE_AUTHOR("Peter Bennett <peter.bennett@st.com>"); -MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); -MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h deleted file mode 100644 index c1b124c6ef12..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h +++ /dev/null @@ -1,287 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-core.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author:Peter Bennett <peter.bennett@st.com> - * Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_CORE_H_ -#define _C8SECTPFE_CORE_H_ - -#define C8SECTPFEI_MAXCHANNEL 16 -#define C8SECTPFEI_MAXADAPTER 3 - -#define C8SECTPFE_MAX_TSIN_CHAN 8 - -struct gpio_desc; - -struct channel_info { - - int tsin_id; - bool invert_ts_clk; - bool serial_not_parallel; - bool async_not_sync; - int i2c; - int dvb_card; - - struct gpio_desc *rst_gpio; - - struct i2c_adapter *i2c_adapter; - struct i2c_adapter *tuner_i2c; - struct i2c_adapter *lnb_i2c; - struct i2c_client *i2c_client; - struct dvb_frontend *frontend; - - struct pinctrl_state *pstate; - - int demux_mapping; - int active; - - void *back_buffer_start; - void *back_buffer_aligned; - dma_addr_t back_buffer_busaddr; - - void *pid_buffer_start; - void *pid_buffer_aligned; - dma_addr_t pid_buffer_busaddr; - - unsigned long fifo; - - struct completion idle_completion; - struct work_struct bh_work; - - struct c8sectpfei *fei; - void __iomem *irec; - -}; - -struct c8sectpfe_hw { - int num_ib; - int num_mib; - int num_swts; - int num_tsout; - int num_ccsc; - int num_ram; - int num_tp; -}; - -struct c8sectpfei { - - struct device *dev; - struct pinctrl *pinctrl; - - struct dentry *root; - struct debugfs_regset32 *regset; - struct completion fw_ack; - atomic_t fw_loaded; - - int tsin_count; - - struct c8sectpfe_hw hw_stats; - - struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER]; - - int mapping[C8SECTPFEI_MAXCHANNEL]; - - struct mutex lock; - - struct timer_list timer; /* timer interrupts for outputs */ - - void __iomem *io; - void __iomem *sram; - - unsigned long sram_size; - - struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN]; - - struct clk *c8sectpfeclk; - int nima_rst_gpio; - int nimb_rst_gpio; - - int idle_irq; - int error_irq; - - int global_feed_count; -}; - -/* C8SECTPFE SYS Regs list */ - -#define SYS_INPUT_ERR_STATUS 0x0 -#define SYS_OTHER_ERR_STATUS 0x8 -#define SYS_INPUT_ERR_MASK 0x10 -#define SYS_OTHER_ERR_MASK 0x18 -#define SYS_DMA_ROUTE 0x20 -#define SYS_INPUT_CLKEN 0x30 -#define IBENABLE_MASK 0x7F - -#define SYS_OTHER_CLKEN 0x38 -#define TSDMAENABLE BIT(1) -#define MEMDMAENABLE BIT(0) - -#define SYS_CFG_NUM_IB 0x200 -#define SYS_CFG_NUM_MIB 0x204 -#define SYS_CFG_NUM_SWTS 0x208 -#define SYS_CFG_NUM_TSOUT 0x20C -#define SYS_CFG_NUM_CCSC 0x210 -#define SYS_CFG_NUM_RAM 0x214 -#define SYS_CFG_NUM_TP 0x218 - -/* Input Block Regs */ - -#define C8SECTPFE_INPUTBLK_OFFSET 0x1000 -#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET) - -#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00) -#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7) -#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6) -#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5) -#define C8SECTPFE_INVERT_TSCLK BIT(4) -#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3) -#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2) -#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1) -#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0) - -#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04) -#define C8SECTPFE_SYNC(x) (x & 0xf) -#define C8SECTPFE_DROP(x) ((x<<4) & 0xf) -#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00) -#define C8SECTPFE_SLDENDIANNESS BIT(16) - -#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08) -#define C8SECTPFE_TAG_HEADER(x) (x << 16) -#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff) -#define C8SECTPFE_TAG_ENABLE BIT(0) - -#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C) -#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f) -#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff) -#define C8SECTPFE_PID_ENABLE BIT(31) - -#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10) - -#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14) -#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18) -#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C) -#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20) - -#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24) -#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff) -#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24) -#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28) - -#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28) -#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1) -#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2) -#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4) -#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8) -#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10) -#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf) -#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf) - -#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C) -#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0) -#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1) -#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2) -#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3) -#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4) -#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8) -#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12) - -#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30) -#define C8SECTPFE_SYS_RESET BIT(1) -#define C8SECTPFE_SYS_ENABLE BIT(0) - -/* - * Pointer record data structure required for each input block - * see Table 82 on page 167 of functional specification. - */ - -#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */ -#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */ - -/* - * TS packet size, including tag bytes added by input block, - * rounded up to the next multiple of 8 bytes. The packet size, - * including any tagging bytes and rounded up to the nearest - * multiple of 8 bytes must be less than 255 bytes. - */ -#define DMA_PRDS_PKTSIZE 0x8 -#define DMA_PRDS_TPENABLE 0xc - -#define TP0_OFFSET 0x10 -#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET) -#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4) -#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8) -#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc) - -#define DMA_PRDS_SIZE (0x20) - -#define DMA_MEMDMA_OFFSET 0x4000 -#define DMA_IMEM_OFFSET 0x0 -#define DMA_DMEM_OFFSET 0x4000 -#define DMA_CPU 0x8000 -#define DMA_PER_OFFSET 0xb000 - -#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET) -#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET) - -/* XP70 Slim core regs */ -#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0) -#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4) -#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8) -#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc) -#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20) - -/* Enable Interrupt for a IB */ -#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00) -/* Ack interrupt by setting corresponding bit */ -#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80) -#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00) -#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80) -#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80) -#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88) -#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c) -#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90) -#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8) -#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac) -#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0) -#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4) -#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc) -#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0) -#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8) -#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0) -#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8) -#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0) -#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4) -#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8) -#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec) -#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0) -#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4) -#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8) -#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc) -/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */ - -/* The following are from DMA_DMEM_BaseAddress */ -#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0) -#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4) -#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8) -#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc) -#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4) -#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10) -#define IDLEREQ BIT(31) - -#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14) - -/* Regs for PID Filter */ - -#define PIDF_OFFSET 0x2800 -#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET) -#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100) -#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108) -#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110) -#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114) - -#endif /* _C8SECTPFE_CORE_H_ */ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c deleted file mode 100644 index 301fa10f419b..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/types.h> - -#include "c8sectpfe-debugfs.h" - -#define dump_register(nm ...) \ -{ \ - .name = #nm, \ - .offset = nm, \ -} - -static const struct debugfs_reg32 fei_sys_regs[] = { - dump_register(SYS_INPUT_ERR_STATUS), - dump_register(SYS_OTHER_ERR_STATUS), - dump_register(SYS_INPUT_ERR_MASK), - dump_register(SYS_DMA_ROUTE), - dump_register(SYS_INPUT_CLKEN), - dump_register(IBENABLE_MASK), - dump_register(SYS_OTHER_CLKEN), - dump_register(SYS_CFG_NUM_IB), - dump_register(SYS_CFG_NUM_MIB), - dump_register(SYS_CFG_NUM_SWTS), - dump_register(SYS_CFG_NUM_TSOUT), - dump_register(SYS_CFG_NUM_CCSC), - dump_register(SYS_CFG_NUM_RAM), - dump_register(SYS_CFG_NUM_TP), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)), - dump_register(C8SECTPFE_IB_PID_SET(0)), - dump_register(C8SECTPFE_IB_PKT_LEN(0)), - dump_register(C8SECTPFE_IB_BUFF_STRT(0)), - dump_register(C8SECTPFE_IB_BUFF_END(0)), - dump_register(C8SECTPFE_IB_READ_PNT(0)), - dump_register(C8SECTPFE_IB_WRT_PNT(0)), - dump_register(C8SECTPFE_IB_PRI_THRLD(0)), - dump_register(C8SECTPFE_IB_STAT(0)), - dump_register(C8SECTPFE_IB_MASK(0)), - dump_register(C8SECTPFE_IB_SYS(0)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)), - dump_register(C8SECTPFE_IB_PID_SET(1)), - dump_register(C8SECTPFE_IB_PKT_LEN(1)), - dump_register(C8SECTPFE_IB_BUFF_STRT(1)), - dump_register(C8SECTPFE_IB_BUFF_END(1)), - dump_register(C8SECTPFE_IB_READ_PNT(1)), - dump_register(C8SECTPFE_IB_WRT_PNT(1)), - dump_register(C8SECTPFE_IB_PRI_THRLD(1)), - dump_register(C8SECTPFE_IB_STAT(1)), - dump_register(C8SECTPFE_IB_MASK(1)), - dump_register(C8SECTPFE_IB_SYS(1)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)), - dump_register(C8SECTPFE_IB_PID_SET(2)), - dump_register(C8SECTPFE_IB_PKT_LEN(2)), - dump_register(C8SECTPFE_IB_BUFF_STRT(2)), - dump_register(C8SECTPFE_IB_BUFF_END(2)), - dump_register(C8SECTPFE_IB_READ_PNT(2)), - dump_register(C8SECTPFE_IB_WRT_PNT(2)), - dump_register(C8SECTPFE_IB_PRI_THRLD(2)), - dump_register(C8SECTPFE_IB_STAT(2)), - dump_register(C8SECTPFE_IB_MASK(2)), - dump_register(C8SECTPFE_IB_SYS(2)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)), - dump_register(C8SECTPFE_IB_PID_SET(3)), - dump_register(C8SECTPFE_IB_PKT_LEN(3)), - dump_register(C8SECTPFE_IB_BUFF_STRT(3)), - dump_register(C8SECTPFE_IB_BUFF_END(3)), - dump_register(C8SECTPFE_IB_READ_PNT(3)), - dump_register(C8SECTPFE_IB_WRT_PNT(3)), - dump_register(C8SECTPFE_IB_PRI_THRLD(3)), - dump_register(C8SECTPFE_IB_STAT(3)), - dump_register(C8SECTPFE_IB_MASK(3)), - dump_register(C8SECTPFE_IB_SYS(3)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)), - dump_register(C8SECTPFE_IB_PID_SET(4)), - dump_register(C8SECTPFE_IB_PKT_LEN(4)), - dump_register(C8SECTPFE_IB_BUFF_STRT(4)), - dump_register(C8SECTPFE_IB_BUFF_END(4)), - dump_register(C8SECTPFE_IB_READ_PNT(4)), - dump_register(C8SECTPFE_IB_WRT_PNT(4)), - dump_register(C8SECTPFE_IB_PRI_THRLD(4)), - dump_register(C8SECTPFE_IB_STAT(4)), - dump_register(C8SECTPFE_IB_MASK(4)), - dump_register(C8SECTPFE_IB_SYS(4)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)), - dump_register(C8SECTPFE_IB_PID_SET(5)), - dump_register(C8SECTPFE_IB_PKT_LEN(5)), - dump_register(C8SECTPFE_IB_BUFF_STRT(5)), - dump_register(C8SECTPFE_IB_BUFF_END(5)), - dump_register(C8SECTPFE_IB_READ_PNT(5)), - dump_register(C8SECTPFE_IB_WRT_PNT(5)), - dump_register(C8SECTPFE_IB_PRI_THRLD(5)), - dump_register(C8SECTPFE_IB_STAT(5)), - dump_register(C8SECTPFE_IB_MASK(5)), - dump_register(C8SECTPFE_IB_SYS(5)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)), - dump_register(C8SECTPFE_IB_PID_SET(6)), - dump_register(C8SECTPFE_IB_PKT_LEN(6)), - dump_register(C8SECTPFE_IB_BUFF_STRT(6)), - dump_register(C8SECTPFE_IB_BUFF_END(6)), - dump_register(C8SECTPFE_IB_READ_PNT(6)), - dump_register(C8SECTPFE_IB_WRT_PNT(6)), - dump_register(C8SECTPFE_IB_PRI_THRLD(6)), - dump_register(C8SECTPFE_IB_STAT(6)), - dump_register(C8SECTPFE_IB_MASK(6)), - dump_register(C8SECTPFE_IB_SYS(6)), - - dump_register(DMA_CPU_ID), - dump_register(DMA_CPU_VCR), - dump_register(DMA_CPU_RUN), - dump_register(DMA_CPU_PC), - - dump_register(DMA_PER_TPn_DREQ_MASK), - dump_register(DMA_PER_TPn_DACK_SET), - dump_register(DMA_PER_TPn_DREQ), - dump_register(DMA_PER_TPn_DACK), - dump_register(DMA_PER_DREQ_MODE), - dump_register(DMA_PER_STBUS_SYNC), - dump_register(DMA_PER_STBUS_ACCESS), - dump_register(DMA_PER_STBUS_ADDRESS), - dump_register(DMA_PER_IDLE_INT), - dump_register(DMA_PER_PRIORITY), - dump_register(DMA_PER_MAX_OPCODE), - dump_register(DMA_PER_MAX_CHUNK), - dump_register(DMA_PER_PAGE_SIZE), - dump_register(DMA_PER_MBOX_STATUS), - dump_register(DMA_PER_MBOX_SET), - dump_register(DMA_PER_MBOX_CLEAR), - dump_register(DMA_PER_MBOX_MASK), - dump_register(DMA_PER_INJECT_PKT_SRC), - dump_register(DMA_PER_INJECT_PKT_DEST), - dump_register(DMA_PER_INJECT_PKT_ADDR), - dump_register(DMA_PER_INJECT_PKT), - dump_register(DMA_PER_PAT_PTR_INIT), - dump_register(DMA_PER_PAT_PTR), - dump_register(DMA_PER_SLEEP_MASK), - dump_register(DMA_PER_SLEEP_COUNTER), - - dump_register(DMA_FIRMWARE_VERSION), - dump_register(DMA_PTRREC_BASE), - dump_register(DMA_PTRREC_INPUT_OFFSET), - dump_register(DMA_ERRREC_BASE), - - dump_register(DMA_ERROR_RECORD(0)), - dump_register(DMA_ERROR_RECORD(1)), - dump_register(DMA_ERROR_RECORD(2)), - dump_register(DMA_ERROR_RECORD(3)), - dump_register(DMA_ERROR_RECORD(4)), - dump_register(DMA_ERROR_RECORD(5)), - dump_register(DMA_ERROR_RECORD(6)), - dump_register(DMA_ERROR_RECORD(7)), - dump_register(DMA_ERROR_RECORD(8)), - dump_register(DMA_ERROR_RECORD(9)), - dump_register(DMA_ERROR_RECORD(10)), - dump_register(DMA_ERROR_RECORD(11)), - dump_register(DMA_ERROR_RECORD(12)), - dump_register(DMA_ERROR_RECORD(13)), - dump_register(DMA_ERROR_RECORD(14)), - dump_register(DMA_ERROR_RECORD(15)), - dump_register(DMA_ERROR_RECORD(16)), - dump_register(DMA_ERROR_RECORD(17)), - dump_register(DMA_ERROR_RECORD(18)), - dump_register(DMA_ERROR_RECORD(19)), - dump_register(DMA_ERROR_RECORD(20)), - dump_register(DMA_ERROR_RECORD(21)), - dump_register(DMA_ERROR_RECORD(22)), - - dump_register(DMA_IDLE_REQ), - dump_register(DMA_FIRMWARE_CONFIG), - - dump_register(PIDF_BASE(0)), - dump_register(PIDF_BASE(1)), - dump_register(PIDF_BASE(2)), - dump_register(PIDF_BASE(3)), - dump_register(PIDF_BASE(4)), - dump_register(PIDF_BASE(5)), - dump_register(PIDF_BASE(6)), - dump_register(PIDF_BASE(7)), - dump_register(PIDF_BASE(8)), - dump_register(PIDF_BASE(9)), - dump_register(PIDF_BASE(10)), - dump_register(PIDF_BASE(11)), - dump_register(PIDF_BASE(12)), - dump_register(PIDF_BASE(13)), - dump_register(PIDF_BASE(14)), - dump_register(PIDF_BASE(15)), - dump_register(PIDF_BASE(16)), - dump_register(PIDF_BASE(17)), - dump_register(PIDF_BASE(18)), - dump_register(PIDF_BASE(19)), - dump_register(PIDF_BASE(20)), - dump_register(PIDF_BASE(21)), - dump_register(PIDF_BASE(22)), - dump_register(PIDF_LEAK_ENABLE), - dump_register(PIDF_LEAK_STATUS), - dump_register(PIDF_LEAK_COUNT_RESET), - dump_register(PIDF_LEAK_COUNTER), -}; - -void c8sectpfe_debugfs_init(struct c8sectpfei *fei) -{ - fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL); - if (!fei->regset) - return; - - fei->regset->regs = fei_sys_regs; - fei->regset->nregs = ARRAY_SIZE(fei_sys_regs); - fei->regset->base = fei->io; - - fei->root = debugfs_create_dir("c8sectpfe", NULL); - debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset); -} - -void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) -{ - debugfs_remove_recursive(fei->root); - fei->root = NULL; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h deleted file mode 100644 index 3fe177b59b16..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header - * - * Copyright (c) STMicroelectronics 2015 - * - * Authors: Peter Griffin <peter.griffin@linaro.org> - */ - -#ifndef __C8SECTPFE_DEBUG_H -#define __C8SECTPFE_DEBUG_H - -#include "c8sectpfe-core.h" - -#if defined(CONFIG_DEBUG_FS) -void c8sectpfe_debugfs_init(struct c8sectpfei *); -void c8sectpfe_debugfs_exit(struct c8sectpfei *); -#else -static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {}; -static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {}; -#endif - -#endif /* __C8SECTPFE_DEBUG_H */ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c deleted file mode 100644 index feb48cb546d7..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> - -#include <dt-bindings/media/c8sectpfe.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-dvb.h" - -#include "dvb-pll.h" -#include "lnbh24.h" -#include "stv0367.h" -#include "stv0367_priv.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "tda18212.h" - -static inline const char *dvb_card_str(unsigned int c) -{ - switch (c) { - case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1"; - case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2"; - case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1"; - case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2"; - case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA"; - case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB"; - default: return "unknown dvb frontend card"; - } -} - -static struct stv090x_config stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - .xtal = 16000000, - .address = 0x69, - - .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - - .repeater_level = STV090x_RPTLEVEL_64, - - .tuner_init = NULL, - .tuner_set_mode = NULL, - .tuner_set_frequency = NULL, - .tuner_get_frequency = NULL, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = NULL, - .tuner_set_bbgain = NULL, - .tuner_get_bbgain = NULL, - .tuner_set_refclk = NULL, - .tuner_get_status = NULL, -}; - -static struct stv6110x_config stv6110x_config = { - .addr = 0x60, - .refclk = 16000000, -}; - -#define NIMA 0 -#define NIMB 1 - -static struct stv0367_config stv0367_tda18212_config[] = { - { - .demod_address = 0x1c, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, { - .demod_address = 0x1d, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, { - .demod_address = 0x1e, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, -}; - -static struct tda18212_config tda18212_conf = { - .if_dvbt_6 = 4150, - .if_dvbt_7 = 4150, - .if_dvbt_8 = 4500, - .if_dvbc = 5000, -}; - -int c8sectpfe_frontend_attach(struct dvb_frontend **fe, - struct c8sectpfe *c8sectpfe, - struct channel_info *tsin, int chan_num) -{ - struct tda18212_config *tda18212; - const struct stv6110x_devctl *fe2; - struct i2c_client *client; - struct i2c_board_info tda18212_info = { - .type = "tda18212", - .addr = 0x60, - }; - - if (!tsin) - return -EINVAL; - - switch (tsin->dvb_card) { - - case STV0367_TDA18212_NIMA_1: - case STV0367_TDA18212_NIMA_2: - case STV0367_TDA18212_NIMB_1: - case STV0367_TDA18212_NIMB_2: - if (tsin->dvb_card == STV0367_TDA18212_NIMA_1) - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[0], - tsin->i2c_adapter); - else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1) - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[1], - tsin->i2c_adapter); - else - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[2], - tsin->i2c_adapter); - - if (!*fe) { - dev_err(c8sectpfe->device, - "%s: stv0367ter_attach failed for NIM card %s\n" - , __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - /* - * init the demod so that i2c gate_ctrl - * to the tuner works correctly - */ - (*fe)->ops.init(*fe); - - /* Allocate the tda18212 structure */ - tda18212 = devm_kzalloc(c8sectpfe->device, - sizeof(struct tda18212_config), - GFP_KERNEL); - if (!tda18212) { - dev_err(c8sectpfe->device, - "%s: devm_kzalloc failed\n", __func__); - return -ENOMEM; - } - - memcpy(tda18212, &tda18212_conf, - sizeof(struct tda18212_config)); - - tda18212->fe = (*fe); - - tda18212_info.platform_data = tda18212; - - /* attach tuner */ - request_module("tda18212"); - client = i2c_new_client_device(tsin->i2c_adapter, - &tda18212_info); - if (!i2c_client_has_driver(client)) { - dvb_frontend_detach(*fe); - return -ENODEV; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(*fe); - return -ENODEV; - } - - tsin->i2c_client = client; - - break; - - case STV0903_6110_LNB24_NIMA: - *fe = dvb_attach(stv090x_attach, &stv090x_config, - tsin->i2c_adapter, STV090x_DEMODULATOR_0); - if (!*fe) { - dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n" - "\tfor NIM card %s\n", - __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - fe2 = dvb_attach(stv6110x_attach, *fe, - &stv6110x_config, tsin->i2c_adapter); - if (!fe2) { - dev_err(c8sectpfe->device, - "%s: stv6110x_attach failed for NIM card %s\n" - , __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - stv090x_config.tuner_init = fe2->tuner_init; - stv090x_config.tuner_set_mode = fe2->tuner_set_mode; - stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency; - stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency; - stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth; - stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth; - stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain; - stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain; - stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk; - stv090x_config.tuner_get_status = fe2->tuner_get_status; - - dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9); - break; - - default: - dev_err(c8sectpfe->device, - "%s: DVB frontend card %s not yet supported\n", - __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - (*fe)->id = chan_num; - - dev_info(c8sectpfe->device, - "DVB frontend card %s successfully attached", - dvb_card_str(tsin->dvb_card)); - return 0; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h deleted file mode 100644 index 3d87a9ae8702..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-common.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_DVB_H_ -#define _C8SECTPFE_DVB_H_ - -int c8sectpfe_frontend_attach(struct dvb_frontend **fe, - struct c8sectpfe *c8sectpfe, struct channel_info *tsin, - int chan_num); - -#endif diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c index 468c247ba328..72488aa922fc 100644 --- a/drivers/media/platform/st/stm32/dma2d/dma2d.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c @@ -355,13 +355,8 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct dma2d_ctx *ctx = file2ctx(file); - struct vb2_queue *vq; struct dma2d_frame *frm; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - frm = get_frame(ctx, f->type); f->fmt.pix.width = frm->width; f->fmt.pix.height = frm->height; @@ -490,7 +485,7 @@ static void device_run(void *prv) src->sequence = frm_out->sequence++; dst->sequence = frm_cap->sequence++; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); if (clk_enable(dev->gate)) goto end; diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index eb519afb30ca..7c4dd1ac772d 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -71,7 +71,7 @@ static void deinterlace_device_run(void *priv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); deinterlace_write(dev, DEINTERLACE_MOD_ENABLE, DEINTERLACE_MOD_ENABLE_EN); diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index 89992feaab60..2deab920884a 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -70,7 +70,7 @@ static void rotate_device_run(void *priv) src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); val = ROTATE_GLB_CTL_MODE(ROTATE_MODE_COPY_ROTATE); if (ctx->hflip) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index b7d278b3889f..c3007e09bc9f 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -237,7 +237,7 @@ static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev) break; } - ret = (cnt >= detection_threshold) ? true : false; + ret = cnt >= detection_threshold; v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret); return ret; diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index b644ed890412..3e25ce0c3c3b 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -1107,8 +1107,7 @@ static int cal_init_camerarx_regmap(struct cal_dev *cal) return 0; } - dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); + dev_warn(cal->dev, "failed to get ti,camerrx-control: %pe\n", syscon); /* * Backward DTS compatibility. If syscon entry is not present then diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index d053972888d1..243c6196b024 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1600,7 +1600,7 @@ err_cleanup: * This creates device entries by register itself to the V4L2 driver and * initializes fields of each channel objects */ -static __init int vpif_probe(struct platform_device *pdev) +static int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; struct i2c_adapter *i2c_adap; @@ -1807,7 +1807,7 @@ static int vpif_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); -static __refdata struct platform_driver vpif_driver = { +static struct platform_driver vpif_driver = { .driver = { .name = VPIF_DRIVER_NAME, .pm = &vpif_pm_ops, diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c index 70c89549f4b6..1e7815e9f8e0 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.c +++ b/drivers/media/platform/ti/davinci/vpif_display.c @@ -1214,7 +1214,7 @@ probe_out: * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects */ -static __init int vpif_probe(struct platform_device *pdev) +static int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; struct i2c_adapter *i2c_adap; @@ -1390,7 +1390,7 @@ static int vpif_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); -static __refdata struct platform_driver vpif_driver = { +static struct platform_driver vpif_driver = { .driver = { .name = VPIF_DRIVER_NAME, .pm = &vpif_pm_ops, diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c index f51cf6119e97..8ac2bdcdf87b 100644 --- a/drivers/media/platform/ti/omap3isp/isp.c +++ b/drivers/media/platform/ti/omap3isp/isp.c @@ -240,11 +240,11 @@ static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) return divider; } -static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int isp_xclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - isp_xclk_calc_divider(&rate, *parent_rate); - return rate; + isp_xclk_calc_divider(&req->rate, req->best_parent_rate); + return 0; } static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -275,7 +275,7 @@ static const struct clk_ops isp_xclk_ops = { .enable = isp_xclk_enable, .disable = isp_xclk_disable, .recalc_rate = isp_xclk_recalc_rate, - .round_rate = isp_xclk_round_rate, + .determine_rate = isp_xclk_determine_rate, .set_rate = isp_xclk_set_rate, }; diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index 6029d4e8e0bd..1a549775cabe 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -1567,13 +1567,8 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct vpe_ctx *ctx = to_vpe_ctx(file); - struct vb2_queue *vq; struct vpe_q_data *q_data; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - q_data = get_q_data(ctx, f->type); if (!q_data) return -EINVAL; @@ -1740,8 +1735,6 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; if (vb2_is_busy(vq)) { vpe_err(ctx->dev, "queue busy\n"); diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index e0c11fe8b55c..60b95b5d8565 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -183,7 +183,7 @@ static void device_run(void *priv) if (ret) goto err_cancel_job; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); if (ctx->codec_ops->run(ctx)) goto err_cancel_job; diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index aae0b562fabb..318673b66da8 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -5,43 +5,93 @@ * Copyright (C) 2021 Collabora Ltd, Andrzej Pietrasiewicz <andrzej.p@collabora.com> */ +#include <linux/delay.h> #include "hantro_hw.h" #include "hantro_g2_regs.h" #define G2_ALIGN 16 -void hantro_g2_check_idle(struct hantro_dev *vpu) +static bool hantro_g2_active(struct hantro_ctx *ctx) { - int i; - - for (i = 0; i < 3; i++) { - u32 status; - - /* Make sure the VPU is idle */ - status = vdpu_read(vpu, G2_REG_INTERRUPT); - if (status & G2_REG_INTERRUPT_DEC_E) { - dev_warn(vpu->dev, "device still running, aborting"); - status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS; - vdpu_write(vpu, status, G2_REG_INTERRUPT); - } + struct hantro_dev *vpu = ctx->dev; + u32 status; + + status = vdpu_read(vpu, G2_REG_INTERRUPT); + + return (status & G2_REG_INTERRUPT_DEC_E); +} + +/** + * hantro_g2_reset: + * @ctx: the hantro context + * + * Emulates a reset using Hantro abort function. Failing this procedure would + * results in programming a running IP which leads to CPU hang. + * + * Using a hard reset procedure instead is prefferred. + */ +void hantro_g2_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 status; + + status = vdpu_read(vpu, G2_REG_INTERRUPT); + if (status & G2_REG_INTERRUPT_DEC_E) { + dev_warn_ratelimited(vpu->dev, "device still running, aborting"); + status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS; + vdpu_write(vpu, status, G2_REG_INTERRUPT); + + do { + mdelay(1); + } while (hantro_g2_active(ctx)); } } irqreturn_t hantro_g2_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; u32 status; status = vdpu_read(vpu, G2_REG_INTERRUPT); - state = (status & G2_REG_INTERRUPT_DEC_RDY_INT) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - vdpu_write(vpu, 0, G2_REG_INTERRUPT); - vdpu_write(vpu, G2_REG_CONFIG_DEC_CLK_GATE_E, G2_REG_CONFIG); + if (!(status & G2_REG_INTERRUPT_DEC_IRQ)) + return IRQ_NONE; + + hantro_reg_write(vpu, &g2_dec_irq, 0); + hantro_reg_write(vpu, &g2_dec_int_stat, 0); + hantro_reg_write(vpu, &g2_clk_gate_e, 1); + + if (status & G2_REG_INTERRUPT_DEC_RDY_INT) { + hantro_irq_done(vpu, VB2_BUF_STATE_DONE); + return IRQ_HANDLED; + } + + if (status & G2_REG_INTERRUPT_DEC_ABORT_INT) { + /* disabled on abort, though lets be safe and handle it */ + dev_warn_ratelimited(vpu->dev, "decode operation aborted."); + return IRQ_HANDLED; + } + + if (status & G2_REG_INTERRUPT_DEC_LAST_SLICE_INT) + dev_warn_ratelimited(vpu->dev, "not all macroblocks were decoded."); + + if (status & G2_REG_INTERRUPT_DEC_BUS_INT) + dev_warn_ratelimited(vpu->dev, "bus error detected."); + + if (status & G2_REG_INTERRUPT_DEC_ERROR_INT) + dev_warn_ratelimited(vpu->dev, "decode error detected."); + + if (status & G2_REG_INTERRUPT_DEC_TIMEOUT) + dev_warn_ratelimited(vpu->dev, "frame decode timed out."); - hantro_irq_done(vpu, state); + /** + * If the decoding haven't stopped, let it continue. The hardware timeout + * will trigger if it is trully stuck. + */ + if (status & G2_REG_INTERRUPT_DEC_E) + return IRQ_HANDLED; + hantro_irq_done(vpu, VB2_BUF_STATE_ERROR); return IRQ_HANDLED; } diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index 0e212198dd65..e8c2e83379de 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -283,6 +283,15 @@ static void set_params(struct hantro_ctx *ctx) hantro_reg_write(vpu, &g2_apf_threshold, 8); } +static u32 get_dpb_index(const struct v4l2_ctrl_hevc_decode_params *decode_params, + const u32 index) +{ + if (index > decode_params->num_active_dpb_entries) + return 0; + + return index; +} + static void set_ref_pic_list(struct hantro_ctx *ctx) { const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; @@ -355,8 +364,10 @@ static void set_ref_pic_list(struct hantro_ctx *ctx) list1[j++] = list1[i++]; for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - hantro_reg_write(vpu, &ref_pic_regs0[i], list0[i]); - hantro_reg_write(vpu, &ref_pic_regs1[i], list1[i]); + hantro_reg_write(vpu, &ref_pic_regs0[i], + get_dpb_index(decode_params, list0[i])); + hantro_reg_write(vpu, &ref_pic_regs1[i], + get_dpb_index(decode_params, list1[i])); } } @@ -582,8 +593,6 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) struct hantro_dev *vpu = ctx->dev; int ret; - hantro_g2_check_idle(vpu); - /* Prepare HEVC decoder context. */ ret = hantro_hevc_dec_prepare_run(ctx); if (ret) diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h index b943b1816db7..c614951121c7 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_regs.h +++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h @@ -22,7 +22,14 @@ #define G2_REG_VERSION G2_SWREG(0) #define G2_REG_INTERRUPT G2_SWREG(1) +#define G2_REG_INTERRUPT_DEC_LAST_SLICE_INT BIT(19) +#define G2_REG_INTERRUPT_DEC_TIMEOUT BIT(18) +#define G2_REG_INTERRUPT_DEC_ERROR_INT BIT(16) +#define G2_REG_INTERRUPT_DEC_BUF_INT BIT(14) +#define G2_REG_INTERRUPT_DEC_BUS_INT BIT(13) #define G2_REG_INTERRUPT_DEC_RDY_INT BIT(12) +#define G2_REG_INTERRUPT_DEC_ABORT_INT BIT(11) +#define G2_REG_INTERRUPT_DEC_IRQ BIT(8) #define G2_REG_INTERRUPT_DEC_ABORT_E BIT(5) #define G2_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) #define G2_REG_INTERRUPT_DEC_E BIT(0) @@ -35,6 +42,9 @@ #define BUS_WIDTH_128 2 #define BUS_WIDTH_256 3 +#define g2_dec_int_stat G2_DEC_REG(1, 11, 0xf) +#define g2_dec_irq G2_DEC_REG(1, 8, 0x1) + #define g2_strm_swap G2_DEC_REG(2, 28, 0xf) #define g2_strm_swap_old G2_DEC_REG(2, 27, 0x1f) #define g2_pic_swap G2_DEC_REG(2, 22, 0x1f) @@ -225,6 +235,9 @@ #define vp9_filt_level_seg5 G2_DEC_REG(19, 8, 0x3f) #define vp9_quant_seg5 G2_DEC_REG(19, 0, 0xff) +#define g2_timemout_override_e G2_DEC_REG(45, 31, 0x1) +#define g2_timemout_cycles G2_DEC_REG(45, 0, 0x7fffffff) + #define hevc_cur_poc_00 G2_DEC_REG(46, 24, 0xff) #define hevc_cur_poc_01 G2_DEC_REG(46, 16, 0xff) #define hevc_cur_poc_02 G2_DEC_REG(46, 8, 0xff) diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c index 82a478ac645e..56c79e339030 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c @@ -893,8 +893,6 @@ int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx) struct vb2_v4l2_buffer *dst; int ret; - hantro_g2_check_idle(ctx->dev); - ret = start_prepare_run(ctx, &decode_params); if (ret) { hantro_end_prepare_run(ctx); diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index c9b6556f8b2b..5f2011529f02 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -583,6 +583,7 @@ void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx); int hantro_vp9_dec_init(struct hantro_ctx *ctx); void hantro_vp9_dec_exit(struct hantro_ctx *ctx); void hantro_g2_check_idle(struct hantro_dev *vpu); +void hantro_g2_reset(struct hantro_ctx *ctx); irqreturn_t hantro_g2_irq(int irq, void *dev_id); #endif /* HANTRO_HW_H_ */ diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c index f9f276385c11..5be0e2e76882 100644 --- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -294,11 +294,13 @@ static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = { static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = { [HANTRO_MODE_HEVC_DEC] = { .run = hantro_g2_hevc_dec_run, + .reset = hantro_g2_reset, .init = hantro_hevc_dec_init, .exit = hantro_hevc_dec_exit, }, [HANTRO_MODE_VP9_DEC] = { .run = hantro_g2_vp9_dec_run, + .reset = hantro_g2_reset, .done = hantro_g2_vp9_dec_done, .init = hantro_vp9_dec_init, .exit = hantro_vp9_dec_exit, |
