/* * sunxi_scaler.c for scaler and osd v4l2 subdev * * Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com * * Authors: Zhao Wei * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "../vin.h" #include "../platform/platform_cfg.h" #include "sunxi_scaler.h" #include "../vin_video/vin_core.h" struct scaler_dev *global_scaler[VIN_MAX_SCALER]; bool scaler_probe_flag[VIN_MAX_SCALER]; #define VIPP_ISR_EN #define ALIGN_4B(x) (((x) + (3)) & ~(3)) #define ALIGN_2B(x) (((x) + (1)) & ~(1)) #define MIN_IN_WIDTH 192 #define MIN_IN_HEIGHT 128 #define MAX_IN_WIDTH 4224 #define MAX_IN_HEIGHT 4224 #define MIN_OUT_WIDTH 16 #define MIN_OUT_HEIGHT 10 #define MAX_OUT_WIDTH 4224 #define MAX_OUT_HEIGHT 4224 #define MIN_RATIO 256 #define MAX_RATIO 4096 static int __scaler_w_shift(int x_ratio, int y_ratio) { int m, n; int sum_weight = 0; int weight_shift; int xr = (x_ratio >> 8) + 1; int yr = (y_ratio >> 8) + 1; int weight_shift_bak = 0; weight_shift = -9; for (weight_shift = 17; weight_shift > 0; weight_shift--) { sum_weight = 0; for (m = 0; m <= xr; m++) { for (n = 0; n <= yr; n++) { sum_weight += (y_ratio - abs((n << 8) - (yr << 7)))* (x_ratio - abs((m << 8) - (yr << 7))) >> (weight_shift + 8); } } if (sum_weight > 0 && sum_weight < 256) weight_shift_bak = weight_shift; if (sum_weight > 255 && sum_weight < 486) break; } if (weight_shift == 0) weight_shift = weight_shift_bak; return weight_shift; } static void __scaler_calc_ratios(unsigned int id, struct v4l2_rect *input, struct scaler_para *para) { unsigned int width; unsigned int height; unsigned int r_min; global_video[id].o_width = clamp(global_video[id].o_width, MIN_IN_WIDTH, input->width); global_video[id].o_height = clamp(global_video[id].o_height, MIN_IN_HEIGHT, input->height); para->xratio = 256 * input->width / global_video[id].o_width; para->yratio = 256 * input->height / global_video[id].o_height; para->xratio = clamp(para->xratio, MIN_RATIO, MAX_RATIO); para->yratio = clamp(para->yratio, MIN_RATIO, MAX_RATIO); r_min = min(para->xratio, para->yratio); if (para->xratio == para->yratio) { width = min(ALIGN(global_video[id].o_width * r_min / 256, 2), input->width); height = min(ALIGN(global_video[id].o_height * r_min / 256, 2), input->height); para->width = global_video[id].o_width; para->height = global_video[id].o_height; vin_log(VIN_LOG_SCALER, "para: xr = %ld, yr = %ld, w = %ld, h = %ld\n", para->xratio, para->yratio, para->width, para->height); /* Center the new crop rectangle. * crop is before scaler */ input->left += (input->width - width) / 2; input->top += (input->height - height) / 2; input->left = ALIGN(input->left, 2); input->top = ALIGN(input->top, 1); input->width = width; input->height = height; } else { width = ALIGN_4B(global_video[id].o_width * r_min / 256); height = ALIGN_2B(global_video[id].o_height * r_min / 256); para->xratio = 256 * width / global_video[id].o_width; para->yratio = 256 * height / global_video[id].o_height; para->xratio = clamp(para->xratio, MIN_RATIO, MAX_RATIO); para->yratio = clamp(para->yratio, MIN_RATIO, MAX_RATIO); para->width = global_video[id].o_width; para->height = global_video[id].o_height; vin_log(VIN_LOG_SCALER, "para: xr = %ld, yr = %ld, w = %ld, h = %ld\n", para->xratio, para->yratio, para->width, para->height); /* Center the new crop rectangle. * crop is before scaler */ input->left += (input->width - width) / 2; input->top += (input->height - height) / 2; input->left = ALIGN_4B(input->left); input->top = ALIGN_2B(input->top); input->width = width; input->height = height; } vin_log(VIN_LOG_SCALER, "crop: left = %ld, top = %ld, w = %ld, h = %ld\n", input->left, input->top, input->width, input->height); } #ifdef VIPP_REG_MODE static int sunxi_scaler_subdev_init(unsigned int id) { struct scaler_dev *scaler = global_scaler[id]; memset((void *)scaler->vipp_reg.phy_addr, 0, VIPP_REG_SIZE); scaler->load_select = true; vipp_set_reg_load_addr(scaler->id, (unsigned long)scaler->load_para[0].phy_addr); vipp_set_osd_cv_update(scaler->id, NOT_UPDATED); vipp_set_osd_ov_update(scaler->id, NOT_UPDATED); vipp_set_para_ready(scaler->id, NOT_READY); return 0; } #endif static int sunxi_scaler_logic_s_stream(unsigned int virtual_id, int on) { unsigned char logic_id = vipp_virtual_find_logic[virtual_id]; struct scaler_dev *logic_scaler = global_scaler[logic_id]; int i; if (logic_scaler->work_mode == VIPP_ONLINE && virtual_id != logic_id) { vin_err("vipp%d work on online mode, vipp%d cannot to work!!\n", logic_id, virtual_id); return -1; } if (on && (logic_scaler->logic_top_stream_count)++ > 0) return 0; else if (!on && (logic_scaler->logic_top_stream_count == 0 || --(logic_scaler->logic_top_stream_count) > 0)) return 0; if (on) { vipp_cap_disable(logic_scaler->id); for (i = 0; i < VIPP_VIRT_NUM; i++) vipp_chn_cap_disable(logic_scaler->id); vipp_work_mode(logic_scaler->id, logic_scaler->work_mode); vipp_top_clk_en(logic_scaler->id, on); vipp_clear_status(logic_scaler->id, VIPP_STATUS_ALL); #ifdef VIPP_ISR_EN #if 0 vipp_irq_enable(logic_scaler->id, ID_LOST_EN | AHB_MBUS_W_EN | CHN0_REG_LOAD_EN | CHN0_FRAME_LOST_EN | CHN0_HBLANK_SHORT_EN | CHN0_PARA_NOT_READY_EN | CHN1_REG_LOAD_EN | CHN1_FRAME_LOST_EN | CHN1_HBLANK_SHORT_EN | CHN1_PARA_NOT_READY_EN | CHN2_REG_LOAD_EN | CHN2_FRAME_LOST_EN | CHN2_HBLANK_SHORT_EN | CHN2_PARA_NOT_READY_EN | CHN3_REG_LOAD_EN | CHN3_FRAME_LOST_EN | CHN3_HBLANK_SHORT_EN | CHN3_PARA_NOT_READY_EN); #else vipp_irq_enable(logic_scaler->id, ID_LOST_EN | AHB_MBUS_W_EN | CHN0_FRAME_LOST_EN | CHN0_HBLANK_SHORT_EN | CHN0_PARA_NOT_READY_EN | CHN1_FRAME_LOST_EN | CHN1_HBLANK_SHORT_EN | CHN1_PARA_NOT_READY_EN | CHN2_FRAME_LOST_EN | CHN2_HBLANK_SHORT_EN | CHN2_PARA_NOT_READY_EN | CHN3_FRAME_LOST_EN | CHN3_HBLANK_SHORT_EN | CHN3_PARA_NOT_READY_EN); hal_enable_irq(logic_scaler->irq); #endif #endif vipp_cap_enable(logic_scaler->id); } else { hal_disable_irq(logic_scaler->irq); vipp_cap_disable(logic_scaler->id); vipp_clear_status(logic_scaler->id, VIPP_STATUS_ALL); vipp_irq_disable(logic_scaler->id, VIPP_EN_ALL); vipp_top_clk_en(logic_scaler->id, on); } return 0; } int sunxi_scaler_subdev_s_stream(unsigned int id, int enable) { struct scaler_dev *scaler = global_scaler[id]; struct vipp_scaler_config scaler_cfg; struct vipp_scaler_size scaler_size; struct vipp_crop crop; enum vipp_format out_fmt; enum vipp_format sc_fmt; struct sensor_format_struct *sensor_format; int sensor_id = global_video[scaler->id].rear_sensor; int isp_sel = global_video[scaler->id].isp_sel; int *stream_count; stream_count = &scaler->stream_count; if (enable && (*stream_count)++ > 0) return 0; else if (!enable && (*stream_count == 0 || --(*stream_count) > 0)) return 0; switch (global_video[id].fourcc) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10: case V4L2_PIX_FMT_SRGGB10: case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SGBRG12: case V4L2_PIX_FMT_SGRBG12: case V4L2_PIX_FMT_SRGGB12: vin_log(VIN_LOG_FMT, "%s output fmt is raw, return directly\n", __func__); return 0; default: break; } if (global_sensors[sensor_id].sensor_core->sensor_g_format) sensor_format = global_sensors[sensor_id].sensor_core->sensor_g_format(sensor_id, isp_sel); else return -1; if (sensor_format->field == V4L2_FIELD_INTERLACED || sensor_format->field == V4L2_FIELD_TOP || sensor_format->field == V4L2_FIELD_BOTTOM) { vin_log(VIN_LOG_SCALER, "Scaler not support field mode, return directly!\n"); return 0; } if (enable) { if (sunxi_scaler_logic_s_stream(scaler->id, enable)) return -1; crop.hor = scaler->crop.active.left; crop.ver = scaler->crop.active.top; crop.width = scaler->crop.active.width; crop.height = scaler->crop.active.height; vipp_set_crop(scaler->id, &crop); scaler_size.sc_width = scaler->para.width; scaler_size.sc_height = scaler->para.height; vipp_scaler_output_size(scaler->id, &scaler_size); switch (global_video[id].fourcc) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420M: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YVU420M: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV21M: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12M: case V4L2_PIX_FMT_FBC: case V4L2_PIX_FMT_LBC_2_0X: case V4L2_PIX_FMT_LBC_2_5X: case V4L2_PIX_FMT_LBC_1_0X: if (scaler->id < MAX_OSD_NUM) { sc_fmt = YUV422; out_fmt = YUV420; vipp_chroma_ds_en(scaler->id, 1); } else { sc_fmt = YUV420; out_fmt = YUV420; } break; case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV61M: case V4L2_PIX_FMT_NV16M: sc_fmt = YUV422; out_fmt = YUV422; break; default: sc_fmt = YUV420; out_fmt = YUV420; break; } scaler_cfg.sc_out_fmt = sc_fmt; scaler_cfg.sc_x_ratio = scaler->para.xratio; scaler_cfg.sc_y_ratio = scaler->para.yratio; scaler_cfg.sc_w_shift = __scaler_w_shift(scaler->para.xratio, scaler->para.yratio); vipp_scaler_cfg(scaler->id, &scaler_cfg); vipp_output_fmt_cfg(scaler->id, out_fmt); vipp_scaler_en(scaler->id, 1); vipp_osd_en(scaler->id, 1); vipp_set_osd_ov_update(scaler->id, HAS_UPDATED); vipp_set_osd_cv_update(scaler->id, HAS_UPDATED); memcpy(scaler->load_para[0].phy_addr, scaler->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_para_ready(scaler->id, HAS_READY); vipp_chn_cap_enable(scaler->id); } else { vipp_chn_cap_disable(scaler->id); vipp_set_para_ready(scaler->id, NOT_READY); vipp_chroma_ds_en(scaler->id, 0); vipp_osd_en(scaler->id, 0); vipp_scaler_en(scaler->id, 0); vipp_set_osd_ov_update(scaler->id, NOT_UPDATED); vipp_set_osd_cv_update(scaler->id, NOT_UPDATED); sunxi_scaler_logic_s_stream(scaler->id, enable); } vin_log(VIN_LOG_FMT, "vipp%d %s, %ld*%ld hoff: %ld voff: %ld xr: %ld yr: %ld\n", scaler->id, enable ? "stream on" : "stream off", scaler->para.width, scaler->para.height, scaler->crop.active.left, scaler->crop.active.top, scaler->para.xratio, scaler->para.yratio); return 0; } #ifdef VIPP_ISR_EN static hal_irqreturn_t scaler_isr(void *priv) { uint32_t __cpsr; struct scaler_dev *scaler = (struct scaler_dev *)priv; struct vipp_status vipp_status; memset(&vipp_status, 0, sizeof(struct vipp_status)); vipp_get_status(scaler->id, &vipp_status); __cpsr = hal_spin_lock_irqsave(&scaler->slock); if (vipp_status.id_lost_pd) { vipp_clear_status(scaler->id, ID_LOST_PD); vin_err("scaler%d channel ID nember is lost!!!\n", scaler->id); } if (vipp_status.ahb_mbus_w_pd) { vipp_clear_status(scaler->id, VIPPAHB_MBUS_W_PD); vin_err("scaler%d AHB and MBUS write conflict!!!\n", scaler->id); } if (vipp_status.chn0_frame_lost_pd) { vipp_clear_status(scaler->id, CHN0_FRAME_LOST_PD); vin_err("scaler%d frame lost!!!\n", scaler->id + 0); } if (vipp_status.chn1_frame_lost_pd) { vipp_clear_status(scaler->id, CHN1_FRAME_LOST_PD); vin_err("scaler%d frame lost!!!\n", scaler->id + 1); } if (vipp_status.chn2_frame_lost_pd) { vipp_clear_status(scaler->id, CHN2_FRAME_LOST_PD); vin_err("scaler%d frame lost!!!\n", scaler->id + 2); } if (vipp_status.chn3_frame_lost_pd) { vipp_clear_status(scaler->id, CHN3_FRAME_LOST_PD); vin_err("scaler%d frame lost!!!\n", scaler->id + 3); } if (vipp_status.chn0_hblank_short_pd) { vipp_clear_status(scaler->id, CHN0_HBLANK_SHORT_PD); vin_err("scaler%d Hblank short!!!\n", scaler->id + 0); } if (vipp_status.chn1_hblank_short_pd) { vipp_clear_status(scaler->id, CHN1_HBLANK_SHORT_PD); vin_err("scaler%d Hblank short!!!\n", scaler->id + 1); } if (vipp_status.chn2_hblank_short_pd) { vipp_clear_status(scaler->id, CHN2_HBLANK_SHORT_PD); vin_err("scaler%d Hblank short!!!\n", scaler->id + 2); } if (vipp_status.chn3_hblank_short_pd) { vipp_clear_status(scaler->id, CHN3_HBLANK_SHORT_PD); vin_err("scaler%d Hblank short!!!\n", scaler->id + 3); } if (vipp_status.chn0_para_not_ready_pd) { vipp_clear_status(scaler->id, CHN0_PARA_NOT_READY_PD); vin_err("scaler%d param not ready!!!\n", scaler->id + 0); } if (vipp_status.chn1_para_not_ready_pd) { vipp_clear_status(scaler->id, CHN1_PARA_NOT_READY_PD); vin_err("scaler%d param not ready!!!\n", scaler->id + 1); } if (vipp_status.chn2_para_not_ready_pd) { vipp_clear_status(scaler->id, CHN2_PARA_NOT_READY_PD); vin_err("scaler%d param not ready!!!\n", scaler->id + 2); } if (vipp_status.chn3_para_not_ready_pd) { vipp_clear_status(scaler->id, CHN3_PARA_NOT_READY_PD); vin_err("scaler%d param not ready!!!\n", scaler->id + 3); } if (vipp_status.chn0_reg_load_pd) { vipp_clear_status(scaler->id, CHN0_REG_LOAD_PD); if (!vipp_get_irq_en(scaler->id, CHN0_REG_LOAD_EN)) { hal_spin_unlock_irqrestore(&scaler->slock, __cpsr); return 0; } vin_print("vipp%d change to load reg\n", scaler->id); if (scaler->load_select) { memcpy(scaler->load_para[1].phy_addr, scaler->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(scaler->id, (unsigned long)scaler->load_para[1].phy_addr); scaler->load_select = false; } else { memcpy(scaler->load_para[0].phy_addr, scaler->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(scaler->id, (unsigned long)scaler->load_para[0].phy_addr); scaler->load_select = true; } vipp_irq_disable(scaler->id, CHN0_REG_LOAD_EN); } if (vipp_status.chn1_reg_load_pd) { vipp_clear_status(scaler->id, CHN1_REG_LOAD_PD); if (!vipp_get_irq_en(scaler->id, CHN1_REG_LOAD_EN)) { hal_spin_unlock_irqrestore(&scaler->slock, __cpsr); return 0; } vin_print("vipp%d change to load reg\n", scaler->id + 1); if (global_scaler[scaler->id + 1]->load_select) { memcpy(global_scaler[scaler->id + 1]->load_para[1].phy_addr, global_scaler[scaler->id + 1]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 1]->id, (unsigned long)global_scaler[scaler->id + 1]->load_para[1].phy_addr); scaler->load_select = false; } else { memcpy(global_scaler[scaler->id + 1]->load_para[0].phy_addr, global_scaler[scaler->id + 1]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 1]->id, (unsigned long)global_scaler[scaler->id + 1]->load_para[0].phy_addr); scaler->load_select = true; } vipp_irq_disable(scaler->id, CHN1_REG_LOAD_EN); } if (vipp_status.chn2_reg_load_pd) { vipp_clear_status(scaler->id, CHN2_REG_LOAD_PD); if (!vipp_get_irq_en(scaler->id, CHN2_REG_LOAD_EN)) { hal_spin_unlock_irqrestore(&scaler->slock, __cpsr); return 0; } vin_print("vipp%d change to load reg\n", scaler->id + 2); if (global_scaler[scaler->id + 2]->load_select) { memcpy(global_scaler[scaler->id + 2]->load_para[1].phy_addr, global_scaler[scaler->id + 2]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 2]->id, (unsigned long)global_scaler[scaler->id + 2]->load_para[1].phy_addr); scaler->load_select = false; } else { memcpy(global_scaler[scaler->id + 2]->load_para[0].phy_addr, global_scaler[scaler->id + 2]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 2]->id, (unsigned long)global_scaler[scaler->id + 2]->load_para[0].phy_addr); scaler->load_select = true; } vipp_irq_disable(scaler->id, CHN2_REG_LOAD_EN); } if (vipp_status.chn3_reg_load_pd) { vipp_clear_status(scaler->id, CHN3_REG_LOAD_PD); if (!vipp_get_irq_en(scaler->id, CHN3_REG_LOAD_EN)) { hal_spin_unlock_irqrestore(&scaler->slock, __cpsr); return 0; } vin_print("vipp%d change to load reg\n", scaler->id + 3); if (global_scaler[scaler->id + 3]->load_select) { memcpy(global_scaler[scaler->id + 3]->load_para[1].phy_addr, global_scaler[scaler->id + 3]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 3]->id, (unsigned long)global_scaler[scaler->id + 3]->load_para[1].phy_addr); scaler->load_select = false; } else { memcpy(global_scaler[scaler->id + 3]->load_para[0].phy_addr, global_scaler[scaler->id + 3]->vipp_reg.phy_addr, VIPP_REG_SIZE); vipp_set_reg_load_addr(global_scaler[scaler->id + 3]->id, (unsigned long)global_scaler[scaler->id + 3]->load_para[0].phy_addr); scaler->load_select = true; } vipp_irq_disable(scaler->id, CHN3_REG_LOAD_EN); } hal_spin_unlock_irqrestore(&scaler->slock, __cpsr); return 0; } #endif #ifdef VIPP_REG_MODE static int scaler_resource_alloc(unsigned int id) { struct scaler_dev *scaler = global_scaler[id]; scaler->vipp_reg.size = 3 * VIPP_REG_SIZE; scaler->vipp_reg.phy_addr = rt_memheap_alloc_align(&isp_mempool, scaler->vipp_reg.size, 0x1000); if (scaler->vipp_reg.phy_addr == NULL) { vin_err("%s:vipp%d:alloc vipp buffer error\n", __func__, id); return -1; } memset(scaler->vipp_reg.phy_addr, 0, scaler->vipp_reg.size); //vin_print("%s:vipp_reg phy_addr is 0x%x\n", __func__, (unsigned long)scaler->vipp_reg.phy_addr); scaler->load_para[0].phy_addr = scaler->vipp_reg.phy_addr + VIPP_REG_SIZE; scaler->load_para[1].phy_addr = scaler->vipp_reg.phy_addr + VIPP_REG_SIZE * 2; return 0; } static void scaler_resource_free(unsigned int id) { //struct scaler_dev *scaler = global_scaler[id]; } #endif int scaler_probe(unsigned int id) { struct scaler_dev *scaler = NULL; struct scaler_dev *logic_scaler = NULL; struct sensor_format_struct *sensor_format; int sensor_id = global_video[id].rear_sensor; int isp_sel = global_video[id].isp_sel; int ret = 0; if (scaler_probe_flag[id]) return 0; else scaler_probe_flag[id] = true; if (id > VIN_MAX_SCALER) { vin_err("vipp%d is not existing, max is %d\n", id, VIN_MAX_SCALER); return -1; } scaler = hal_malloc(sizeof(struct scaler_dev)); if (!scaler) { ret = -1; goto ekzalloc; } memset(scaler, 0, sizeof(struct scaler_dev)); scaler->id = id; if (scaler->id == vipp_virtual_find_logic[scaler->id]) { scaler->work_mode = vin_work_mode;//clamp(scaler->work_mode, VIPP_ONLINE, VIPP_OFFLINE); } else { scaler->work_mode = 0xff; logic_scaler = global_scaler[vipp_virtual_find_logic[scaler->id]]; if (logic_scaler->work_mode == VIPP_ONLINE) { /*logic vipp work in online*/ vin_log(VIN_LOG_VIDEO, "scaler%d work in online mode, scaler%d cannot to work!\n", logic_scaler->id, scaler->id); global_scaler[scaler->id] = scaler; scaler->noneed_register = 1; vin_log(VIN_LOG_SCALER, "scaler%d probe end\n", scaler->id); return 0; } } scaler->base = sunxi_vin_get_scaler_base(scaler->id); if (!scaler->base) { vin_err("Fail to get VIPP base addr!\n"); ret = -1; goto freedev; } vin_log(VIN_LOG_MD, "vipp%d base reg is 0x%lx\n", scaler->id, scaler->base); global_scaler[id] = scaler; #ifdef VIPP_REG_MODE scaler_resource_alloc(id); #endif vipp_set_base_addr(scaler->id, scaler->base); #ifdef VIPP_REG_MODE vipp_map_reg_load_addr(scaler->id, (unsigned long)scaler->vipp_reg.phy_addr); sunxi_scaler_subdev_init(id); #endif #ifdef VIPP_ISR_EN /*get irq resource */ if (scaler->id == vipp_virtual_find_logic[scaler->id]) { scaler->irq = sunxi_vin_get_vipp_irq(scaler->id); if (scaler->irq <= 0) { scaler->is_irq_empty = 1; if (scaler->id == vipp_virtual_find_logic[scaler->id]) vin_err("failed to get vipp%d IRQ resource\n", scaler->id); } else { ret = hal_request_irq(scaler->irq, scaler_isr, "sunxi_scaler", scaler); if (ret != scaler->irq) { vin_err("vipp%d request irq failed\n", scaler->id); ret = -1; goto freedev; } } } #endif if (global_sensors[sensor_id].sensor_core->sensor_g_format) { sensor_format = global_sensors[sensor_id].sensor_core->sensor_g_format(sensor_id, isp_sel); } else { vin_err("%s sensor_g_format failed!!!!\n", global_sensors[sensor_id].sensor_name); return -1; } scaler->crop.active.width = sensor_format->width; scaler->crop.active.height = sensor_format->height; __scaler_calc_ratios(id, &scaler->crop.active, &scaler->para); vin_log(VIN_LOG_SCALER, "scaler%d probe end\n", scaler->id); return 0; freedev: hal_free(scaler); ekzalloc: vin_err("scaler%d probe err!\n", scaler->id); return ret; } int scaler_remove(unsigned int id) { struct scaler_dev *scaler = global_scaler[id]; if (!scaler_probe_flag[id]) return 0; else scaler_probe_flag[id] = false; if (scaler->noneed_register == 1) { hal_free(scaler); return 0; } #ifdef VIPP_REG_MODE scaler_resource_free(id); #endif #ifdef VIPP_ISR_EN if (scaler->id == vipp_virtual_find_logic[scaler->id]) { if (!scaler->is_irq_empty) hal_free_irq(scaler->irq); } #endif hal_free(scaler); return 0; }