diff options
Diffstat (limited to 'drivers/media/platform/allegro-dvt/allegro-core.c')
| -rw-r--r-- | drivers/media/platform/allegro-dvt/allegro-core.c | 505 |
1 files changed, 409 insertions, 96 deletions
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 887b492e4ad1..eec0b8b30b7f 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -6,16 +6,19 @@ */ #include <linux/bits.h> +#include <linux/clk.h> #include <linux/firmware.h> #include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/log2.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/xlnx-vcu.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -101,6 +104,12 @@ #define BETA_OFFSET_DIV_2 -1 #define TC_OFFSET_DIV_2 -1 +/* + * This control allows applications to explicitly disable the encoder buffer. + * This value is Allegro specific. + */ +#define V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER (V4L2_CID_USER_ALLEGRO_BASE + 0) + static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-2)"); @@ -125,6 +134,13 @@ struct allegro_mbox { struct mutex lock; }; +struct allegro_encoder_buffer { + unsigned int size; + unsigned int color_depth; + unsigned int num_cores; + unsigned int clk_rate; +}; + struct allegro_dev { struct v4l2_device v4l2_dev; struct video_device video_dev; @@ -136,12 +152,19 @@ struct allegro_dev { struct regmap *regmap; struct regmap *sram; + struct regmap *settings; + + struct clk *clk_core; + struct clk *clk_mcu; const struct fw_info *fw_info; struct allegro_buffer firmware; struct allegro_buffer suballocator; + bool has_encoder_buffer; + struct allegro_encoder_buffer encoder_buffer; struct completion init_complete; + bool initialized; /* The mailbox interface */ struct allegro_mbox *mbox_command; @@ -154,9 +177,10 @@ struct allegro_dev { */ unsigned long channel_user_ids; struct list_head channels; + struct mutex channels_lock; }; -static struct regmap_config allegro_regmap_config = { +static const struct regmap_config allegro_regmap_config = { .name = "regmap", .reg_bits = 32, .val_bits = 32, @@ -165,7 +189,7 @@ static struct regmap_config allegro_regmap_config = { .cache_type = REGCACHE_NONE, }; -static struct regmap_config allegro_sram_config = { +static const struct regmap_config allegro_sram_config = { .name = "sram", .reg_bits = 32, .val_bits = 32, @@ -174,9 +198,8 @@ static struct regmap_config allegro_sram_config = { .cache_type = REGCACHE_NONE, }; -#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) - struct allegro_channel { + struct kref ref; struct allegro_dev *dev; struct v4l2_fh fh; struct v4l2_ctrl_handler ctrl_handler; @@ -257,6 +280,8 @@ struct allegro_channel { struct v4l2_ctrl *mpeg_video_cpb_size; struct v4l2_ctrl *mpeg_video_gop_size; + struct v4l2_ctrl *encoder_buffer; + /* user_id is used to identify the channel during CREATE_CHANNEL */ /* not sure, what to set here and if this is actually required */ int user_id; @@ -277,6 +302,11 @@ struct allegro_channel { unsigned int error; }; +static inline struct allegro_channel *file_to_channel(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct allegro_channel, fh); +} + static inline int allegro_channel_get_i_frame_qp(struct allegro_channel *channel) { @@ -402,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; @@ -803,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) { @@ -811,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; @@ -829,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 @@ -888,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); @@ -919,6 +992,54 @@ 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, + struct allegro_encoder_buffer *buffer) +{ + int err; + struct regmap *settings = dev->settings; + unsigned int supports_10_bit; + unsigned int memory_depth; + unsigned int num_cores; + unsigned int color_depth; + unsigned long clk_rate; + + /* We don't support the encoder buffer pre Firmware version 2019.2 */ + if (dev->fw_info->mailbox_version < MCU_MSG_VERSION_2019_2) + return -ENODEV; + + if (!settings) + return -EINVAL; + + err = regmap_read(settings, VCU_ENC_COLOR_DEPTH, &supports_10_bit); + if (err < 0) + return err; + err = regmap_read(settings, VCU_MEMORY_DEPTH, &memory_depth); + if (err < 0) + return err; + err = regmap_read(settings, VCU_NUM_CORE, &num_cores); + if (err < 0) + return err; + + clk_rate = clk_get_rate(dev->clk_core); + if (clk_rate == 0) + return -EINVAL; + + color_depth = supports_10_bit ? 10 : 8; + /* The firmware expects the encoder buffer size in bits. */ + buffer->size = color_depth * 32 * memory_depth; + buffer->color_depth = color_depth; + buffer->num_cores = num_cores; + buffer->clk_rate = clk_rate; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "using %d bits encoder buffer with %d-bit color depth\n", + buffer->size, color_depth); + + return 0; } static void allegro_mcu_send_init(struct allegro_dev *dev, @@ -934,10 +1055,17 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); - /* disable L2 cache */ - msg.l2_cache[0] = -1; - msg.l2_cache[1] = -1; - msg.l2_cache[2] = -1; + if (dev->has_encoder_buffer) { + msg.encoder_buffer_size = dev->encoder_buffer.size; + msg.encoder_buffer_color_depth = dev->encoder_buffer.color_depth; + msg.num_cores = dev->encoder_buffer.num_cores; + msg.clk_rate = dev->encoder_buffer.clk_rate; + } else { + msg.encoder_buffer_size = -1; + msg.encoder_buffer_color_depth = -1; + msg.num_cores = -1; + msg.clk_rate = -1; + } allegro_mbox_send(dev->mbox_command, &msg); } @@ -1184,9 +1312,8 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->max_transfo_depth_intra = channel->max_transfo_depth_intra; param->max_transfo_depth_inter = channel->max_transfo_depth_inter; - param->prefetch_auto = 0; - param->prefetch_mem_offset = 0; - param->prefetch_mem_size = 0; + param->encoder_buffer_enabled = v4l2_ctrl_g_ctrl(channel->encoder_buffer); + param->encoder_buffer_offset = 0; param->rate_control_mode = channel->frame_rc_enable ? v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; @@ -1311,6 +1438,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, u64 src_handle) { struct mcu_msg_encode_frame msg; + bool use_encoder_buffer = v4l2_ctrl_g_ctrl(channel->encoder_buffer); memset(&msg, 0, sizeof(msg)); @@ -1319,6 +1447,8 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; + if (use_encoder_buffer) + msg.encoding_options |= AL_OPT_USE_L2; msg.pps_qp = 26; /* qp are relative to 26 */ msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ /* src_handle is copied to mcu_msg_encode_frame_response */ @@ -1326,8 +1456,6 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.src_y = to_codec_addr(dev, src_y); msg.src_uv = to_codec_addr(dev, src_uv); msg.stride = channel->stride; - msg.ep2 = 0x0; - msg.ep2_v = to_mcu_addr(dev, msg.ep2); allegro_mbox_send(dev->mbox_command, &msg); @@ -1337,11 +1465,11 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, unsigned long timeout_ms) { - unsigned long tmo; + unsigned long time_left; - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) + time_left = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (time_left == 0) return -ETIMEDOUT; reinit_completion(&dev->init_complete); @@ -1431,8 +1559,10 @@ static int allocate_buffers_internal(struct allegro_channel *channel, INIT_LIST_HEAD(&buffer->head); err = allegro_alloc_buffer(dev, buffer, size); - if (err) + if (err) { + kfree(buffer); goto err; + } list_add(&buffer->head, list); } @@ -1509,14 +1639,14 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_profile); level = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_level); - sps->profile_idc = nal_h264_profile_from_v4l2(profile); + sps->profile_idc = nal_h264_profile(profile); sps->constraint_set0_flag = 0; sps->constraint_set1_flag = 1; sps->constraint_set2_flag = 0; sps->constraint_set3_flag = 0; sps->constraint_set4_flag = 0; sps->constraint_set5_flag = 0; - sps->level_idc = nal_h264_level_from_v4l2(level); + sps->level_idc = nal_h264_level(level); sps->seq_parameter_set_id = 0; sps->log2_max_frame_num_minus4 = LOG2_MAX_FRAME_NUM - 4; sps->pic_order_cnt_type = 0; @@ -1541,13 +1671,17 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui_parameters_present_flag = 1; sps->vui.aspect_ratio_info_present_flag = 0; sps->vui.overscan_info_present_flag = 0; + sps->vui.video_signal_type_present_flag = 1; - sps->vui.video_format = 1; - sps->vui.video_full_range_flag = 0; + sps->vui.video_format = 5; /* unspecified */ + sps->vui.video_full_range_flag = nal_h264_full_range(channel->quantization); sps->vui.colour_description_present_flag = 1; - sps->vui.colour_primaries = 5; - sps->vui.transfer_characteristics = 5; - sps->vui.matrix_coefficients = 5; + sps->vui.colour_primaries = nal_h264_color_primaries(channel->colorspace); + sps->vui.transfer_characteristics = + nal_h264_transfer_characteristics(channel->colorspace, channel->xfer_func); + sps->vui.matrix_coefficients = + nal_h264_matrix_coeffs(channel->colorspace, channel->ycbcr_enc); + sps->vui.chroma_loc_info_present_flag = 1; sps->vui.chroma_sample_loc_type_top_field = 0; sps->vui.chroma_sample_loc_type_bottom_field = 0; @@ -1560,8 +1694,9 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui.nal_hrd_parameters_present_flag = 0; sps->vui.vcl_hrd_parameters_present_flag = 1; sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; - sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ + sps->vui.vcl_hrd_parameters.bit_rate_scale = + ffs(channel->bitrate_peak) - 6; sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ @@ -1654,12 +1789,12 @@ static ssize_t allegro_hevc_write_vps(struct allegro_channel *channel, vps->temporal_id_nesting_flag = 1; ptl = &vps->profile_tier_level; - ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_idc = nal_hevc_profile(profile); ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; - ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_tier_flag = nal_hevc_tier(tier); ptl->general_progressive_source_flag = 1; ptl->general_frame_only_constraint_flag = 1; - ptl->general_level_idc = nal_hevc_level_from_v4l2(level); + ptl->general_level_idc = nal_hevc_level(level); vps->sub_layer_ordering_info_present_flag = 0; vps->max_dec_pic_buffering_minus1[0] = num_ref_frames; @@ -1678,7 +1813,10 @@ static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel, struct allegro_dev *dev = channel->dev; struct nal_hevc_sps *sps; struct nal_hevc_profile_tier_level *ptl; + struct nal_hevc_vui_parameters *vui; + struct nal_hevc_hrd_parameters *hrd; ssize_t size; + unsigned int cpb_size; unsigned int num_ref_frames = channel->num_ref_idx_l0; s32 profile = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_profile); s32 level = v4l2_ctrl_g_ctrl(channel->mpeg_video_hevc_level); @@ -1691,12 +1829,12 @@ static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel, sps->temporal_id_nesting_flag = 1; ptl = &sps->profile_tier_level; - ptl->general_profile_idc = nal_hevc_profile_from_v4l2(profile); + ptl->general_profile_idc = nal_hevc_profile(profile); ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; - ptl->general_tier_flag = nal_hevc_tier_from_v4l2(tier); + ptl->general_tier_flag = nal_hevc_tier(tier); ptl->general_progressive_source_flag = 1; ptl->general_frame_only_constraint_flag = 1; - ptl->general_level_idc = nal_hevc_level_from_v4l2(level); + ptl->general_level_idc = nal_hevc_level(level); sps->seq_parameter_set_id = 0; sps->chroma_format_idc = 1; /* Only 4:2:0 sampling supported */ @@ -1731,6 +1869,50 @@ static ssize_t allegro_hevc_write_sps(struct allegro_channel *channel, sps->sps_temporal_mvp_enabled_flag = channel->temporal_mvp_enable; sps->strong_intra_smoothing_enabled_flag = channel->max_cu_size > 4; + sps->vui_parameters_present_flag = 1; + vui = &sps->vui; + + vui->video_signal_type_present_flag = 1; + vui->video_format = 5; /* unspecified */ + vui->video_full_range_flag = nal_hevc_full_range(channel->quantization); + vui->colour_description_present_flag = 1; + vui->colour_primaries = nal_hevc_color_primaries(channel->colorspace); + vui->transfer_characteristics = nal_hevc_transfer_characteristics(channel->colorspace, + channel->xfer_func); + vui->matrix_coeffs = nal_hevc_matrix_coeffs(channel->colorspace, channel->ycbcr_enc); + + vui->chroma_loc_info_present_flag = 1; + vui->chroma_sample_loc_type_top_field = 0; + vui->chroma_sample_loc_type_bottom_field = 0; + + vui->vui_timing_info_present_flag = 1; + vui->vui_num_units_in_tick = channel->framerate.denominator; + vui->vui_time_scale = channel->framerate.numerator; + + vui->bitstream_restriction_flag = 1; + vui->motion_vectors_over_pic_boundaries_flag = 1; + vui->restricted_ref_pic_lists_flag = 1; + vui->log2_max_mv_length_horizontal = 15; + vui->log2_max_mv_length_vertical = 15; + + vui->vui_hrd_parameters_present_flag = 1; + hrd = &vui->nal_hrd_parameters; + hrd->vcl_hrd_parameters_present_flag = 1; + + hrd->initial_cpb_removal_delay_length_minus1 = 31; + hrd->au_cpb_removal_delay_length_minus1 = 30; + hrd->dpb_output_delay_length_minus1 = 30; + + hrd->bit_rate_scale = ffs(channel->bitrate_peak) - 6; + hrd->vcl_hrd[0].bit_rate_value_minus1[0] = + (channel->bitrate_peak >> (6 + hrd->bit_rate_scale)) - 1; + + cpb_size = v4l2_ctrl_g_ctrl(channel->mpeg_video_cpb_size) * 1000; + hrd->cpb_size_scale = ffs(cpb_size) - 4; + hrd->vcl_hrd[0].cpb_size_value_minus1[0] = (cpb_size >> (4 + hrd->cpb_size_scale)) - 1; + + hrd->vcl_hrd[0].cbr_flag[0] = !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); + size = nal_hevc_write_sps(&dev->plat_dev->dev, dest, n, sps); kfree(sps); @@ -1989,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 @@ -2028,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", @@ -2095,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; @@ -2106,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", @@ -2119,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; } @@ -2129,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", @@ -2139,6 +2323,7 @@ allegro_handle_encode_frame(struct allegro_dev *dev, } allegro_channel_finish_frame(channel, msg); + allegro_ref_put_channel(channel); return 0; } @@ -2185,7 +2370,19 @@ static irqreturn_t allegro_irq_thread(int irq, void *data) { struct allegro_dev *dev = data; - allegro_mbox_notify(dev->mbox_status); + /* + * The firmware is initialized after the mailbox is setup. We further + * check the AL5_ITC_CPU_IRQ_STA register, if the firmware actually + * triggered the interrupt. Although this should not happen, make sure + * that we ignore interrupts, if the mailbox is not initialized. + */ + if (!dev->mbox_status) + return IRQ_NONE; + + while (allegro_mbox_get_available(dev->mbox_status) > 0) { + if (allegro_mbox_notify(dev->mbox_status)) + break; + } return IRQ_HANDLED; } @@ -2342,14 +2539,14 @@ static void allegro_mcu_interrupt(struct allegro_dev *dev) static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { reinit_completion(&channel->completion); allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (time_left == 0) v4l2_warn(&dev->v4l2_dev, "channel %d: timeout while destroying\n", channel->mcu_channel_id); @@ -2384,6 +2581,8 @@ static void allegro_destroy_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); + v4l2_ctrl_grab(channel->encoder_buffer, false); + if (channel->user_id != -1) { clear_bit(channel->user_id, &dev->channel_user_ids); channel->user_id = -1; @@ -2403,7 +2602,7 @@ static void allegro_destroy_channel(struct allegro_channel *channel) static int allegro_create_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -2450,12 +2649,20 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); + v4l2_ctrl_grab(channel->encoder_buffer, true); + reinit_completion(&channel->completion); allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + 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; @@ -2671,7 +2878,7 @@ static void allegro_buf_queue(struct vb2_buffer *vb) unsigned int i; for (i = 0; i < vb->num_planes; i++) - vb->planes[i].bytesused = 0; + vb2_set_plane_payload(vb, i, 0); vbuf->field = V4L2_FIELD_NONE; vbuf->sequence = channel->csequence++; @@ -2752,8 +2959,6 @@ static const struct vb2_ops allegro_queue_ops = { .buf_queue = allegro_buf_queue, .start_streaming = allegro_start_streaming, .stop_streaming = allegro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int allegro_queue_init(void *priv, @@ -2833,6 +3038,10 @@ static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: allegro_clamp_bitrate(channel, ctrl); break; + case V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER: + if (!channel->dev->has_encoder_buffer) + ctrl->val = 0; + break; } return 0; @@ -2873,6 +3082,16 @@ static const struct v4l2_ctrl_ops allegro_ctrl_ops = { .s_ctrl = allegro_s_ctrl, }; +static const struct v4l2_ctrl_config allegro_encoder_buffer_ctrl_config = { + .id = V4L2_CID_USER_ALLEGRO_ENCODER_BUFFER, + .name = "Encoder Buffer Enable", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + static int allegro_open(struct file *file) { struct video_device *vdev = video_devdata(file); @@ -2890,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); @@ -3024,6 +3245,8 @@ static int allegro_open(struct file *file) V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, ALLEGRO_GOP_SIZE_MAX, 1, ALLEGRO_GOP_SIZE_DEFAULT); + channel->encoder_buffer = v4l2_ctrl_new_custom(handler, + &allegro_encoder_buffer_ctrl_config, NULL); v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, @@ -3054,9 +3277,11 @@ static int allegro_open(struct file *file) goto error; } - list_add(&channel->list, &dev->channels); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); + scoped_guard(mutex, &dev->channels_lock) { + list_add(&channel->list, &dev->channels); + } + + v4l2_fh_add(&channel->fh, file); allegro_channel_adjust(channel); @@ -3070,18 +3295,21 @@ error: static int allegro_release(struct file *file) { - struct allegro_channel *channel = fh_to_channel(file->private_data); + 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); + v4l2_fh_del(&channel->fh, file); v4l2_fh_exit(&channel->fh); - kfree(channel); + allegro_ref_put_channel(channel); return 0; } @@ -3089,13 +3317,8 @@ static int allegro_release(struct file *file) static int allegro_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); - struct allegro_dev *dev = video_get_drvdata(vdev); - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&dev->plat_dev->dev)); return 0; } @@ -3126,7 +3349,7 @@ static int allegro_enum_fmt_vid(struct file *file, void *fh, static int allegro_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = channel->width; @@ -3168,7 +3391,7 @@ static int allegro_try_fmt_vid_cap(struct file *file, void *fh, static int allegro_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); struct vb2_queue *vq; int err; @@ -3177,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; @@ -3192,7 +3413,7 @@ static int allegro_s_fmt_vid_cap(struct file *file, void *fh, static int allegro_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); f->fmt.pix.field = V4L2_FIELD_NONE; @@ -3239,7 +3460,7 @@ static int allegro_try_fmt_vid_out(struct file *file, void *fh, static int allegro_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); int err; err = allegro_try_fmt_vid_out(file, fh, f); @@ -3280,7 +3501,7 @@ static int allegro_channel_cmd_start(struct allegro_channel *channel) static int allegro_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); int err; err = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd); @@ -3329,8 +3550,7 @@ static int allegro_enum_framesizes(struct file *file, void *fh, static int allegro_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct v4l2_fh *fh = file->private_data; - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); int err; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -3339,13 +3559,13 @@ static int allegro_ioctl_streamon(struct file *file, void *priv, return err; } - return v4l2_m2m_streamon(file, fh->m2m_ctx, type); + return v4l2_m2m_streamon(file, channel->fh.m2m_ctx, type); } static int allegro_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); struct v4l2_fract *timeperframe; if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -3362,7 +3582,7 @@ static int allegro_g_parm(struct file *file, void *fh, static int allegro_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) { - struct allegro_channel *channel = fh_to_channel(fh); + struct allegro_channel *channel = file_to_channel(file); struct v4l2_fract *timeperframe; int div; @@ -3504,6 +3724,11 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, return -EIO; } + err = allegro_encoder_buffer_init(dev, &dev->encoder_buffer); + dev->has_encoder_buffer = (err == 0); + if (!dev->has_encoder_buffer) + v4l2_info(&dev->v4l2_dev, "encoder buffer not available\n"); + allegro_mcu_enable_interrupts(dev); /* The mcu sends INIT after reset. */ @@ -3591,11 +3816,16 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) v4l2_info(&dev->v4l2_dev, "using mcu firmware version '%s'\n", dev->fw_info->version); + pm_runtime_enable(&dev->plat_dev->dev); + err = pm_runtime_resume_and_get(&dev->plat_dev->dev); + if (err) + goto err_release_firmware_codec; + /* Ensure that the mcu is sleeping at the reset vector */ err = allegro_mcu_reset(dev); if (err) { v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); - goto err_release_firmware_codec; + goto err_suspend; } allegro_copy_firmware(dev, fw->data, fw->size); @@ -3623,6 +3853,8 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) "allegro codec registered as /dev/video%d\n", dev->video_dev.num); + dev->initialized = true; + release_firmware(fw_codec); release_firmware(fw); @@ -3635,6 +3867,9 @@ err_mcu_hw_deinit: allegro_mcu_hw_deinit(dev); err_free_fw_codec: allegro_free_fw_codec(dev); +err_suspend: + pm_runtime_put(&dev->plat_dev->dev); + pm_runtime_disable(&dev->plat_dev->dev); err_release_firmware_codec: release_firmware(fw_codec); err_release_firmware: @@ -3666,9 +3901,12 @@ 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); + dev->initialized = false; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); if (!res) { dev_err(&pdev->dev, @@ -3707,6 +3945,18 @@ static int allegro_probe(struct platform_device *pdev) return PTR_ERR(dev->sram); } + dev->settings = syscon_regmap_lookup_by_compatible("xlnx,vcu-settings"); + if (IS_ERR(dev->settings)) + dev_warn(&pdev->dev, "failed to open settings\n"); + + dev->clk_core = devm_clk_get(&pdev->dev, "core_clk"); + if (IS_ERR(dev->clk_core)) + return PTR_ERR(dev->clk_core); + + dev->clk_mcu = devm_clk_get(&pdev->dev, "mcu_clk"); + if (IS_ERR(dev->clk_mcu)) + return PTR_ERR(dev->clk_mcu); + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -3729,23 +3979,80 @@ static int allegro_probe(struct platform_device *pdev) if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to request firmware: %d\n", ret); + v4l2_device_unregister(&dev->v4l2_dev); return ret; } return 0; } -static int allegro_remove(struct platform_device *pdev) +static void allegro_remove(struct platform_device *pdev) { struct allegro_dev *dev = platform_get_drvdata(pdev); - video_unregister_device(&dev->video_dev); - if (dev->m2m_dev) - v4l2_m2m_release(dev->m2m_dev); - allegro_mcu_hw_deinit(dev); - allegro_free_fw_codec(dev); + if (dev->initialized) { + video_unregister_device(&dev->video_dev); + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + allegro_mcu_hw_deinit(dev); + allegro_free_fw_codec(dev); + } + + pm_runtime_put(&dev->plat_dev->dev); + pm_runtime_disable(&dev->plat_dev->dev); v4l2_device_unregister(&dev->v4l2_dev); +} + +static int allegro_runtime_resume(struct device *device) +{ + struct allegro_dev *dev = dev_get_drvdata(device); + struct regmap *settings = dev->settings; + unsigned int clk_mcu; + unsigned int clk_core; + int err; + + if (!settings) + return -EINVAL; + +#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000) + + err = regmap_read(settings, VCU_CORE_CLK, &clk_core); + if (err < 0) + return err; + err = clk_set_rate(dev->clk_core, MHZ_TO_HZ(clk_core)); + if (err < 0) + return err; + err = clk_prepare_enable(dev->clk_core); + if (err) + return err; + + err = regmap_read(settings, VCU_MCU_CLK, &clk_mcu); + if (err < 0) + goto disable_clk_core; + err = clk_set_rate(dev->clk_mcu, MHZ_TO_HZ(clk_mcu)); + if (err < 0) + goto disable_clk_core; + err = clk_prepare_enable(dev->clk_mcu); + if (err) + goto disable_clk_core; + +#undef MHZ_TO_HZ + + return 0; + +disable_clk_core: + clk_disable_unprepare(dev->clk_core); + + return err; +} + +static int allegro_runtime_suspend(struct device *device) +{ + struct allegro_dev *dev = dev_get_drvdata(device); + + clk_disable_unprepare(dev->clk_mcu); + clk_disable_unprepare(dev->clk_core); return 0; } @@ -3757,12 +4064,18 @@ static const struct of_device_id allegro_dt_ids[] = { MODULE_DEVICE_TABLE(of, allegro_dt_ids); +static const struct dev_pm_ops allegro_pm_ops = { + .runtime_resume = allegro_runtime_resume, + .runtime_suspend = allegro_runtime_suspend, +}; + static struct platform_driver allegro_driver = { .probe = allegro_probe, .remove = allegro_remove, .driver = { .name = "allegro", - .of_match_table = of_match_ptr(allegro_dt_ids), + .of_match_table = allegro_dt_ids, + .pm = &allegro_pm_ops, }, }; |
