| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. | |
| */ | |
| #include <media/v4l2-event.h> | |
| #include <media/v4l2-mem2mem.h> | |
| #include "iris_buffer.h" | |
| #include "iris_common.h" | |
| #include "iris_ctrls.h" | |
| #include "iris_instance.h" | |
| #include "iris_power.h" | |
| #include "iris_vdec.h" | |
| #include "iris_vpu_buffer.h" | |
| #define DEFAULT_CODEC_ALIGNMENT 16 | |
| int iris_vdec_inst_init(struct iris_inst *inst) | |
| { | |
| struct iris_core *core = inst->core; | |
| struct v4l2_format *f; | |
| inst->fmt_src = kzalloc_obj(*inst->fmt_src); | |
| inst->fmt_dst = kzalloc_obj(*inst->fmt_dst); | |
| inst->fw_min_count = 0; | |
| f = inst->fmt_src; | |
| f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
| f->fmt.pix_mp.width = DEFAULT_WIDTH; | |
| f->fmt.pix_mp.height = DEFAULT_HEIGHT; | |
| f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; | |
| inst->codec = f->fmt.pix_mp.pixelformat; | |
| f->fmt.pix_mp.num_planes = 1; | |
| f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; | |
| f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT); | |
| f->fmt.pix_mp.field = V4L2_FIELD_NONE; | |
| inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT); | |
| inst->buffers[BUF_INPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage; | |
| f = inst->fmt_dst; | |
| f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
| f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; | |
| f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128); | |
| f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32); | |
| f->fmt.pix_mp.num_planes = 1; | |
| f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128); | |
| f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); | |
| f->fmt.pix_mp.field = V4L2_FIELD_NONE; | |
| f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT; | |
| f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; | |
| f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; | |
| f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; | |
| inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); | |
| inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage; | |
| inst->frame_rate = MAXIMUM_FPS; | |
| memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0], | |
| INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap)); | |
| return iris_ctrls_init(inst); | |
| } | |
| static const u32 iris_vdec_formats_cap[] = { | |
| [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, | |
| [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, | |
| [IRIS_FMT_TP10] = V4L2_PIX_FMT_P010, | |
| [IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C, | |
| }; | |
| static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) | |
| { | |
| unsigned int size, i; | |
| const u32 *fmt; | |
| switch (type) { | |
| case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
| fmt = inst->core->iris_platform_data->inst_iris_fmts; | |
| size = inst->core->iris_platform_data->inst_iris_fmts_size; | |
| break; | |
| case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
| fmt = iris_vdec_formats_cap; | |
| size = ARRAY_SIZE(iris_vdec_formats_cap); | |
| break; | |
| default: | |
| return false; | |
| } | |
| for (i = 0; i < size; i++) { | |
| if (fmt[i] == pixfmt) | |
| break; | |
| } | |
| if (i == size) | |
| return false; | |
| return true; | |
| } | |
| static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) | |
| { | |
| unsigned int size; | |
| const u32 *fmt; | |
| switch (type) { | |
| case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
| fmt = inst->core->iris_platform_data->inst_iris_fmts; | |
| size = inst->core->iris_platform_data->inst_iris_fmts_size; | |
| break; | |
| case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
| fmt = iris_vdec_formats_cap; | |
| size = ARRAY_SIZE(iris_vdec_formats_cap); | |
| break; | |
| default: | |
| return 0; | |
| } | |
| if (index >= size) | |
| return 0; | |
| return fmt[index]; | |
| } | |
| int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) | |
| { | |
| u32 fmt; | |
| switch (f->type) { | |
| case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
| fmt = find_format_by_index(inst, f->index, f->type); | |
| if (!fmt) | |
| return -EINVAL; | |
| f->pixelformat = fmt; | |
| f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; | |
| break; | |
| case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
| fmt = find_format_by_index(inst, f->index, f->type); | |
| if (!fmt) | |
| return -EINVAL; | |
| f->pixelformat = fmt; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) | |
| { | |
| struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; | |
| struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; | |
| struct v4l2_format *f_inst; | |
| struct vb2_queue *src_q; | |
| bool supported; | |
| memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); | |
| supported = check_format(inst, pixmp->pixelformat, f->type); | |
| switch (f->type) { | |
| case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
| if (!supported) { | |
| 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; | |
| f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; | |
| } | |
| break; | |
| case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
| if (!supported) { | |
| 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; | |
| f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; | |
| } | |
| src_q = v4l2_m2m_get_src_vq(m2m_ctx); | |
| if (vb2_is_streaming(src_q)) { | |
| f_inst = inst->fmt_src; | |
| f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; | |
| f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; | |
| } | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| if (pixmp->field == V4L2_FIELD_ANY) | |
| pixmp->field = V4L2_FIELD_NONE; | |
| pixmp->num_planes = 1; | |
| return 0; | |
| } | |
| int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) | |
| { | |
| struct v4l2_format *fmt, *output_fmt; | |
| struct vb2_queue *q; | |
| u32 codec_align; | |
| q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type); | |
| if (vb2_is_busy(q)) | |
| return -EBUSY; | |
| /* Width and height are optional, so fall back to a valid placeholder | |
| * resolution until the real one is decoded from the bitstream. | |
| */ | |
| if (f->fmt.pix_mp.width == 0 && f->fmt.pix_mp.height == 0) { | |
| f->fmt.pix_mp.width = DEFAULT_WIDTH; | |
| f->fmt.pix_mp.height = DEFAULT_HEIGHT; | |
| } | |
| iris_vdec_try_fmt(inst, f); | |
| switch (f->type) { | |
| case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | |
| if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) | |
| return -EINVAL; | |
| fmt = inst->fmt_src; | |
| fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
| fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; | |
| inst->codec = fmt->fmt.pix_mp.pixelformat; | |
| codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; | |
| fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align); | |
| fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align); | |
| fmt->fmt.pix_mp.num_planes = 1; | |
| fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0; | |
| fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT); | |
| inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT); | |
| inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; | |
| fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; | |
| fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; | |
| fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; | |
| fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; | |
| output_fmt = inst->fmt_dst; | |
| output_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; | |
| output_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; | |
| output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; | |
| output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; | |
| /* Update capture format based on new ip w/h */ | |
| output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); | |
| output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); | |
| inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); | |
| inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT); | |
| inst->crop.left = 0; | |
| inst->crop.top = 0; | |
| inst->crop.width = f->fmt.pix_mp.width; | |
| inst->crop.height = f->fmt.pix_mp.height; | |
| break; | |
| case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: | |
| if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) | |
| return -EINVAL; | |
| fmt = inst->fmt_dst; | |
| fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
| fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; | |
| fmt->fmt.pix_mp.num_planes = 1; | |
| switch (f->fmt.pix_mp.pixelformat) { | |
| case V4L2_PIX_FMT_P010: | |
| fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); | |
| fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); | |
| fmt->fmt.pix_mp.plane_fmt[0].bytesperline = | |
| ALIGN(f->fmt.pix_mp.width * 2, 256); | |
| break; | |
| case V4L2_PIX_FMT_QC10C: | |
| fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192); | |
| fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16); | |
| fmt->fmt.pix_mp.plane_fmt[0].bytesperline = | |
| ALIGN(f->fmt.pix_mp.width * 4 / 3, 256); | |
| break; | |
| case V4L2_PIX_FMT_NV12: | |
| case V4L2_PIX_FMT_QC08C: | |
| fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); | |
| fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); | |
| fmt->fmt.pix_mp.plane_fmt[0].bytesperline = | |
| ALIGN(f->fmt.pix_mp.width, 128); | |
| break; | |
| } | |
| fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); | |
| inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); | |
| inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; | |
| inst->crop.top = 0; | |
| inst->crop.left = 0; | |
| inst->crop.width = f->fmt.pix_mp.width; | |
| inst->crop.height = f->fmt.pix_mp.height; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| memcpy(f, fmt, sizeof(*fmt)); | |
| return 0; | |
| } | |
| int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat) | |
| { | |
| bool supported; | |
| supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (!supported) | |
| supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| return supported ? 0 : -EINVAL; | |
| } | |
| int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub) | |
| { | |
| int ret = 0; | |
| switch (sub->type) { | |
| case V4L2_EVENT_EOS: | |
| ret = v4l2_event_subscribe(&inst->fh, sub, 0, NULL); | |
| break; | |
| case V4L2_EVENT_SOURCE_CHANGE: | |
| ret = v4l2_src_change_event_subscribe(&inst->fh, sub); | |
| break; | |
| case V4L2_EVENT_CTRL: | |
| ret = v4l2_ctrl_subscribe_event(&inst->fh, sub); | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return ret; | |
| } | |
| void iris_vdec_src_change(struct iris_inst *inst) | |
| { | |
| struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; | |
| struct v4l2_event event = {0}; | |
| struct vb2_queue *src_q; | |
| src_q = v4l2_m2m_get_src_vq(m2m_ctx); | |
| if (!vb2_is_streaming(src_q)) | |
| return; | |
| event.type = V4L2_EVENT_SOURCE_CHANGE; | |
| event.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION; | |
| v4l2_event_queue_fh(&inst->fh, &event); | |
| } | |
| int iris_vdec_streamon_input(struct iris_inst *inst) | |
| { | |
| int ret; | |
| ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| ret = iris_alloc_and_queue_persist_bufs(inst, BUF_PERSIST); | |
| if (ret) | |
| return ret; | |
| iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| inst->frame_counter = 0; | |
| return iris_process_streamon_input(inst); | |
| } | |
| int iris_vdec_streamon_output(struct iris_inst *inst) | |
| { | |
| const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; | |
| int ret; | |
| ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| return ret; | |
| iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| return ret; | |
| ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| return ret; | |
| ret = iris_process_streamon_output(inst); | |
| if (ret) | |
| goto error; | |
| ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| goto error; | |
| return ret; | |
| error: | |
| iris_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| return ret; | |
| } | |
| int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) | |
| { | |
| struct iris_buffer *buf = to_iris_buffer(vbuf); | |
| struct vb2_buffer *vb2 = &vbuf->vb2_buf; | |
| u64 cur_buf_ns, delta_ns; | |
| struct vb2_queue *q; | |
| int ret; | |
| ret = iris_vb2_buffer_to_driver(vb2, buf); | |
| if (ret) | |
| return ret; | |
| if (buf->type == BUF_INPUT) | |
| iris_set_ts_metadata(inst, vbuf); | |
| q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type); | |
| if (!vb2_is_streaming(q)) { | |
| buf->attr |= BUF_ATTR_DEFERRED; | |
| return 0; | |
| } | |
| if (buf->type == BUF_INPUT) { | |
| cur_buf_ns = ktime_get_ns(); | |
| if (!inst->frame_counter) | |
| inst->last_buf_ns = cur_buf_ns; | |
| inst->frame_counter++; | |
| delta_ns = cur_buf_ns - inst->last_buf_ns; | |
| if (delta_ns >= NSEC_PER_SEC) { | |
| inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS, | |
| MAXIMUM_FPS); | |
| inst->frame_counter = 0; | |
| } | |
| } | |
| iris_scale_power(inst); | |
| return iris_queue_buffer(inst, buf); | |
| } | |
| int iris_vdec_start_cmd(struct iris_inst *inst) | |
| { | |
| const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; | |
| enum iris_inst_sub_state clear_sub_state = 0; | |
| struct vb2_queue *dst_vq; | |
| int ret; | |
| dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); | |
| if (inst->sub_state & IRIS_INST_SUB_DRC && | |
| inst->sub_state & IRIS_INST_SUB_DRC_LAST) { | |
| vb2_clear_last_buffer_dequeued(dst_vq); | |
| clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST; | |
| if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { | |
| ret = hfi_ops->session_resume_drc(inst, | |
| V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; | |
| } | |
| if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { | |
| ret = hfi_ops->session_resume_drc(inst, | |
| V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| return ret; | |
| clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; | |
| } | |
| } else if (inst->sub_state & IRIS_INST_SUB_DRAIN && | |
| inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) { | |
| vb2_clear_last_buffer_dequeued(dst_vq); | |
| clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST; | |
| if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) { | |
| if (hfi_ops->session_resume_drain) { | |
| ret = | |
| hfi_ops->session_resume_drain(inst, | |
| V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| } | |
| clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE; | |
| } | |
| if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) { | |
| if (hfi_ops->session_resume_drain) { | |
| ret = | |
| hfi_ops->session_resume_drain(inst, | |
| V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
| if (ret) | |
| return ret; | |
| } | |
| clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE; | |
| } | |
| } else { | |
| dev_err(inst->core->dev, "start called before receiving last_flag\n"); | |
| iris_inst_change_state(inst, IRIS_INST_ERROR); | |
| return -EBUSY; | |
| } | |
| return iris_inst_change_sub_state(inst, clear_sub_state, 0); | |
| } | |
| int iris_vdec_stop_cmd(struct iris_inst *inst) | |
| { | |
| const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; | |
| int ret; | |
| ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); | |
| if (ret) | |
| return ret; | |
| return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN); | |
| } |