| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> |
| * |
| * Helper functions for handling messages that are send via mailbox to the |
| * Allegro VCU firmware. |
| */ |
| |
| #include <linux/bitfield.h> |
| #include <linux/export.h> |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/videodev2.h> |
| |
| #include "allegro-mail.h" |
| |
| const char *msg_type_name(enum mcu_msg_type type) |
| { |
| static char buf[13]; |
| |
| switch (type) { |
| case MCU_MSG_TYPE_INIT: |
| return "INIT"; |
| case MCU_MSG_TYPE_CREATE_CHANNEL: |
| return "CREATE_CHANNEL"; |
| case MCU_MSG_TYPE_DESTROY_CHANNEL: |
| return "DESTROY_CHANNEL"; |
| case MCU_MSG_TYPE_ENCODE_FRAME: |
| return "ENCODE_FRAME"; |
| case MCU_MSG_TYPE_PUT_STREAM_BUFFER: |
| return "PUT_STREAM_BUFFER"; |
| case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: |
| return "PUSH_BUFFER_INTERMEDIATE"; |
| case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: |
| return "PUSH_BUFFER_REFERENCE"; |
| default: |
| snprintf(buf, sizeof(buf), "(0x%04x)", type); |
| return buf; |
| } |
| } |
| EXPORT_SYMBOL(msg_type_name); |
| |
| static ssize_t |
| allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) |
| { |
| unsigned int i = 0; |
| enum mcu_msg_version version = msg->header.version; |
| |
| dst[i++] = msg->reserved0; |
| dst[i++] = msg->suballoc_dma; |
| dst[i++] = msg->suballoc_size; |
| dst[i++] = msg->encoder_buffer_size; |
| dst[i++] = msg->encoder_buffer_color_depth; |
| dst[i++] = msg->num_cores; |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| dst[i++] = msg->clk_rate; |
| dst[i++] = 0; |
| } |
| |
| return i * sizeof(*dst); |
| } |
| |
| static inline u32 settings_get_mcu_codec(struct create_channel_param *param) |
| { |
| enum mcu_msg_version version = param->version; |
| u32 pixelformat = param->codec; |
| |
| if (version < MCU_MSG_VERSION_2019_2) { |
| switch (pixelformat) { |
| case V4L2_PIX_FMT_HEVC: |
| return 2; |
| case V4L2_PIX_FMT_H264: |
| default: |
| return 1; |
| } |
| } else { |
| switch (pixelformat) { |
| case V4L2_PIX_FMT_HEVC: |
| return 1; |
| case V4L2_PIX_FMT_H264: |
| default: |
| return 0; |
| } |
| } |
| } |
| |
| ssize_t |
| allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) |
| { |
| enum mcu_msg_version version = param->version; |
| unsigned int i = 0; |
| unsigned int j = 0; |
| u32 val; |
| unsigned int codec = settings_get_mcu_codec(param); |
| |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->layer_id; |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | |
| FIELD_PREP(GENMASK(15, 0), param->width); |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->videomode; |
| dst[i++] = param->format; |
| if (version < MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->colorspace; |
| dst[i++] = param->src_mode; |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->src_bit_depth; |
| dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | |
| FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | |
| FIELD_PREP(GENMASK(7, 0), param->profile); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | |
| FIELD_PREP(GENMASK(15, 0), param->level); |
| |
| val = 0; |
| val |= param->temporal_mvp_enable ? BIT(20) : 0; |
| val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num); |
| if (version >= MCU_MSG_VERSION_2019_2) |
| val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc - 1); |
| else |
| val |= FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); |
| dst[i++] = val; |
| |
| val = 0; |
| val |= param->enable_reordering ? BIT(0) : 0; |
| val |= param->dbf_ovr_en ? BIT(2) : 0; |
| val |= param->override_lf ? BIT(12) : 0; |
| dst[i++] = val; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| val = 0; |
| val |= param->custom_lda ? BIT(2) : 0; |
| val |= param->rdo_cost_mode ? BIT(20) : 0; |
| dst[i++] = val; |
| |
| val = 0; |
| val |= param->lf ? BIT(2) : 0; |
| val |= param->lf_x_tile ? BIT(3) : 0; |
| val |= param->lf_x_slice ? BIT(4) : 0; |
| dst[i++] = val; |
| } else { |
| val = 0; |
| dst[i++] = val; |
| } |
| |
| dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | |
| FIELD_PREP(GENMASK(7, 0), param->tc_offset); |
| dst[i++] = param->unknown11; |
| dst[i++] = param->unknown12; |
| dst[i++] = param->num_slices; |
| dst[i++] = param->encoder_buffer_offset; |
| dst[i++] = param->encoder_buffer_enabled; |
| |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | |
| FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | |
| FIELD_PREP(GENMASK(15, 0), param->me_range[0]); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | |
| FIELD_PREP(GENMASK(15, 0), param->me_range[2]); |
| dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | |
| FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | |
| FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | |
| FIELD_PREP(GENMASK(8, 0), param->max_cu_size); |
| dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | |
| FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); |
| dst[i++] = param->entropy_mode; |
| dst[i++] = param->wp_mode; |
| |
| dst[i++] = param->rate_control_mode; |
| dst[i++] = param->initial_rem_delay; |
| dst[i++] = param->cpb_size; |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | |
| FIELD_PREP(GENMASK(15, 0), param->framerate); |
| dst[i++] = param->target_bitrate; |
| dst[i++] = param->max_bitrate; |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | |
| FIELD_PREP(GENMASK(15, 0), param->initial_qp); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | |
| FIELD_PREP(GENMASK(15, 0), param->max_qp); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | |
| FIELD_PREP(GENMASK(15, 0), param->pb_delta); |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | |
| FIELD_PREP(GENMASK(15, 0), param->golden_delta); |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->rate_control_option; |
| else |
| dst[i++] = 0; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| dst[i++] = param->num_pixel; |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | |
| FIELD_PREP(GENMASK(15, 0), param->max_psnr); |
| for (j = 0; j < 3; j++) |
| dst[i++] = param->maxpicturesize[j]; |
| } |
| |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->gop_ctrl_mode; |
| else |
| dst[i++] = 0; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | |
| FIELD_PREP(GENMASK(23, 16), param->num_b) | |
| FIELD_PREP(GENMASK(15, 0), param->gop_length); |
| dst[i++] = param->freq_idr; |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->enable_lt; |
| dst[i++] = param->freq_lt; |
| dst[i++] = param->gdr_mode; |
| if (version < MCU_MSG_VERSION_2019_2) |
| dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | |
| FIELD_PREP(GENMASK(23, 16), param->num_b) | |
| FIELD_PREP(GENMASK(15, 0), param->gop_length); |
| |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->tmpdqp; |
| |
| dst[i++] = param->subframe_latency; |
| dst[i++] = param->lda_control_mode; |
| if (version < MCU_MSG_VERSION_2019_2) |
| dst[i++] = param->unknown41; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| for (j = 0; j < 6; j++) |
| dst[i++] = param->lda_factors[j]; |
| dst[i++] = param->max_num_merge_cand; |
| } |
| |
| return i * sizeof(*dst); |
| } |
| |
| static ssize_t |
| allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) |
| { |
| enum mcu_msg_version version = msg->header.version; |
| unsigned int i = 0; |
| |
| dst[i++] = msg->user_id; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| dst[i++] = msg->blob_mcu_addr; |
| } else { |
| memcpy(&dst[i], msg->blob, msg->blob_size); |
| i += msg->blob_size / sizeof(*dst); |
| } |
| |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = msg->ep1_addr; |
| |
| return i * sizeof(*dst); |
| } |
| |
| ssize_t allegro_decode_config_blob(struct create_channel_param *param, |
| struct mcu_msg_create_channel_response *msg, |
| u32 *src) |
| { |
| enum mcu_msg_version version = msg->header.version; |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); |
| param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); |
| } else { |
| param->num_ref_idx_l0 = msg->num_ref_idx_l0; |
| param->num_ref_idx_l1 = msg->num_ref_idx_l1; |
| } |
| |
| return 0; |
| } |
| |
| static ssize_t |
| allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) |
| { |
| unsigned int i = 0; |
| |
| dst[i++] = msg->channel_id; |
| |
| return i * sizeof(*dst); |
| } |
| |
| static ssize_t |
| allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) |
| { |
| unsigned int i = 0; |
| struct mcu_msg_push_buffers_internal_buffer *buffer; |
| unsigned int num_buffers = msg->num_buffers; |
| unsigned int j; |
| |
| dst[i++] = msg->channel_id; |
| |
| for (j = 0; j < num_buffers; j++) { |
| buffer = &msg->buffer[j]; |
| dst[i++] = buffer->dma_addr; |
| dst[i++] = buffer->mcu_addr; |
| dst[i++] = buffer->size; |
| } |
| |
| return i * sizeof(*dst); |
| } |
| |
| static ssize_t |
| allegro_enc_put_stream_buffer(u32 *dst, |
| struct mcu_msg_put_stream_buffer *msg) |
| { |
| unsigned int i = 0; |
| |
| dst[i++] = msg->channel_id; |
| dst[i++] = msg->dma_addr; |
| dst[i++] = msg->mcu_addr; |
| dst[i++] = msg->size; |
| dst[i++] = msg->offset; |
| dst[i++] = lower_32_bits(msg->dst_handle); |
| dst[i++] = upper_32_bits(msg->dst_handle); |
| |
| return i * sizeof(*dst); |
| } |
| |
| static ssize_t |
| allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) |
| { |
| enum mcu_msg_version version = msg->header.version; |
| unsigned int i = 0; |
| |
| dst[i++] = msg->channel_id; |
| |
| dst[i++] = msg->reserved; |
| dst[i++] = msg->encoding_options; |
| dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | |
| FIELD_PREP(GENMASK(15, 0), msg->pps_qp); |
| |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| dst[i++] = 0; |
| dst[i++] = 0; |
| dst[i++] = 0; |
| dst[i++] = 0; |
| } |
| |
| dst[i++] = lower_32_bits(msg->user_param); |
| dst[i++] = upper_32_bits(msg->user_param); |
| dst[i++] = lower_32_bits(msg->src_handle); |
| dst[i++] = upper_32_bits(msg->src_handle); |
| dst[i++] = msg->request_options; |
| dst[i++] = msg->src_y; |
| dst[i++] = msg->src_uv; |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = msg->is_10_bit; |
| dst[i++] = msg->stride; |
| if (version >= MCU_MSG_VERSION_2019_2) |
| dst[i++] = msg->format; |
| dst[i++] = msg->ep2; |
| dst[i++] = lower_32_bits(msg->ep2_v); |
| dst[i++] = upper_32_bits(msg->ep2_v); |
| |
| return i * sizeof(*dst); |
| } |
| |
| static ssize_t |
| allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) |
| { |
| unsigned int i = 0; |
| |
| msg->reserved0 = src[i++]; |
| |
| return i * sizeof(*src); |
| } |
| |
| static ssize_t |
| allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, |
| u32 *src) |
| { |
| enum mcu_msg_version version = msg->header.version; |
| unsigned int i = 0; |
| |
| msg->channel_id = src[i++]; |
| msg->user_id = src[i++]; |
| /* |
| * Version >= MCU_MSG_VERSION_2019_2 is handled in |
| * allegro_decode_config_blob(). |
| */ |
| if (version < MCU_MSG_VERSION_2019_2) { |
| msg->options = src[i++]; |
| msg->num_core = src[i++]; |
| msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); |
| msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); |
| } |
| msg->int_buffers_count = src[i++]; |
| msg->int_buffers_size = src[i++]; |
| msg->rec_buffers_count = src[i++]; |
| msg->rec_buffers_size = src[i++]; |
| msg->reserved = src[i++]; |
| msg->error_code = src[i++]; |
| |
| return i * sizeof(*src); |
| } |
| |
| static ssize_t |
| allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, |
| u32 *src) |
| { |
| unsigned int i = 0; |
| |
| msg->channel_id = src[i++]; |
| |
| return i * sizeof(*src); |
| } |
| |
| static ssize_t |
| allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) |
| { |
| enum mcu_msg_version version = msg->header.version; |
| unsigned int i = 0; |
| unsigned int j; |
| |
| msg->channel_id = src[i++]; |
| |
| msg->dst_handle = src[i++]; |
| msg->dst_handle |= (((u64)src[i++]) << 32); |
| msg->user_param = src[i++]; |
| msg->user_param |= (((u64)src[i++]) << 32); |
| msg->src_handle = src[i++]; |
| msg->src_handle |= (((u64)src[i++]) << 32); |
| msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); |
| msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); |
| msg->initial_removal_delay = src[i++]; |
| msg->dpb_output_delay = src[i++]; |
| msg->size = src[i++]; |
| msg->frame_tag_size = src[i++]; |
| msg->stuffing = src[i++]; |
| msg->filler = src[i++]; |
| msg->num_row = FIELD_GET(GENMASK(31, 16), src[i]); |
| msg->num_column = FIELD_GET(GENMASK(15, 0), src[i++]); |
| msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); |
| msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); |
| msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); |
| msg->partition_table_offset = src[i++]; |
| msg->partition_table_size = src[i++]; |
| msg->sum_complex = src[i++]; |
| for (j = 0; j < 4; j++) |
| msg->tile_width[j] = src[i++]; |
| for (j = 0; j < 22; j++) |
| msg->tile_height[j] = src[i++]; |
| msg->error_code = src[i++]; |
| msg->slice_type = src[i++]; |
| msg->pic_struct = src[i++]; |
| msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); |
| msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); |
| msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); |
| msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); |
| |
| msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); |
| msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); |
| |
| msg->reserved2 = src[i++]; |
| if (version >= MCU_MSG_VERSION_2019_2) { |
| msg->reserved3 = src[i++]; |
| msg->reserved4 = src[i++]; |
| msg->reserved5 = src[i++]; |
| msg->reserved6 = src[i++]; |
| } |
| |
| return i * sizeof(*src); |
| } |
| |
| /** |
| * allegro_encode_mail() - Encode allegro messages to firmware format |
| * @dst: Pointer to the memory that will be filled with data |
| * @msg: The allegro message that will be encoded |
| */ |
| ssize_t allegro_encode_mail(u32 *dst, void *msg) |
| { |
| const struct mcu_msg_header *header = msg; |
| ssize_t size; |
| |
| if (!msg || !dst) |
| return -EINVAL; |
| |
| switch (header->type) { |
| case MCU_MSG_TYPE_INIT: |
| size = allegro_enc_init(&dst[1], msg); |
| break; |
| case MCU_MSG_TYPE_CREATE_CHANNEL: |
| size = allegro_enc_create_channel(&dst[1], msg); |
| break; |
| case MCU_MSG_TYPE_DESTROY_CHANNEL: |
| size = allegro_enc_destroy_channel(&dst[1], msg); |
| break; |
| case MCU_MSG_TYPE_ENCODE_FRAME: |
| size = allegro_enc_encode_frame(&dst[1], msg); |
| break; |
| case MCU_MSG_TYPE_PUT_STREAM_BUFFER: |
| size = allegro_enc_put_stream_buffer(&dst[1], msg); |
| break; |
| case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: |
| case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: |
| size = allegro_enc_push_buffers(&dst[1], msg); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| /* |
| * The encoded messages might have different length depending on |
| * the firmware version or certain fields. Therefore, we have to |
| * set the body length after encoding the message. |
| */ |
| dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | |
| FIELD_PREP(GENMASK(15, 0), size); |
| |
| return size + sizeof(*dst); |
| } |
| |
| /** |
| * allegro_decode_mail() - Parse allegro messages from the firmware. |
| * @msg: The mcu_msg_response that will be filled with parsed values. |
| * @src: Pointer to the memory that will be parsed |
| * |
| * The message format in the mailbox depends on the firmware. Parse the |
| * different formats into a uniform message format that can be used without |
| * taking care of the firmware version. |
| */ |
| int allegro_decode_mail(void *msg, u32 *src) |
| { |
| struct mcu_msg_header *header; |
| |
| if (!src || !msg) |
| return -EINVAL; |
| |
| header = msg; |
| header->type = FIELD_GET(GENMASK(31, 16), src[0]); |
| |
| src++; |
| switch (header->type) { |
| case MCU_MSG_TYPE_INIT: |
| allegro_dec_init(msg, src); |
| break; |
| case MCU_MSG_TYPE_CREATE_CHANNEL: |
| allegro_dec_create_channel(msg, src); |
| break; |
| case MCU_MSG_TYPE_DESTROY_CHANNEL: |
| allegro_dec_destroy_channel(msg, src); |
| break; |
| case MCU_MSG_TYPE_ENCODE_FRAME: |
| allegro_dec_encode_frame(msg, src); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |