示例#1
0
static int dpe_set_display_region(struct platform_device *pdev,
	struct dss_rect *dirty)
{
	int ret = 0;
	struct k3_fb_data_type *k3fd = NULL;
	char __iomem *addr = NULL;

	BUG_ON(pdev == NULL || dirty == NULL);
	k3fd = platform_get_drvdata(pdev);
	BUG_ON(k3fd == NULL);

	K3_FB_DEBUG("index=%d, enter!\n", k3fd->index);

	addr = k3fd->dss_base + DSS_DPE0_OFFSET + DFS_FRM_SIZE;
	outp32(addr, dirty->w * dirty->h);
	addr = k3fd->dss_base + DSS_DPE0_OFFSET + DFS_FRM_HSIZE;
	outp32(addr, DSS_WIDTH(dirty->w));

	addr = k3fd->dss_base + DSS_DPP_IFBC_OFFSET + IFBC_SIZE;
	outp32(addr, ((DSS_WIDTH(dirty->w) << 16) | DSS_HEIGHT(dirty->h)));

	addr = k3fd->dss_base + PDP_LDI_DPI0_HRZ_CTRL1;
	outp32(addr, DSS_WIDTH(dirty->w) | (DSS_WIDTH(k3fd->panel_info.ldi.h_pulse_width) << 16));
	addr = k3fd->dss_base + PDP_LDI_VRT_CTRL1;
	outp32(addr, DSS_HEIGHT(dirty->h) | (DSS_HEIGHT(k3fd->panel_info.ldi.v_pulse_width) << 16));

	ret = panel_next_set_display_region(pdev, dirty);

	K3_FB_DEBUG("index=%d, exit!\n", k3fd->index);

	return ret;
}
示例#2
0
static int dpe_set_timing(struct platform_device *pdev)
{
	char __iomem *dss_base = 0;
	struct k3_fb_data_type *k3fd = NULL;
	struct k3_panel_info *pinfo = NULL;
	struct dss_clk_rate *pdss_clk_rate = NULL;
	int ret = 0;

	BUG_ON(pdev == NULL);
	k3fd = platform_get_drvdata(pdev);
	BUG_ON(k3fd == NULL);

	if (k3fd->index != 1) {
		K3_FB_ERR("fb%d not support set timing.\n", k3fd->index);
		return -EINVAL;
	}

	if (!k3fd->panel_power_on) {
		K3_FB_ERR("fb%d is not power on.\n", k3fd->index);
		return -EINVAL;
	}

	dss_base = k3fd->dss_base;
	BUG_ON(dss_base == NULL);

	pinfo = &(k3fd->panel_info);
	pdss_clk_rate = get_dss_clk_rate(k3fd);
	BUG_ON(pdss_clk_rate == NULL);

	init_dfs_sdp(k3fd);

	if (IS_ERR(k3fd->dss_pxl1_clk)) {
		ret = PTR_ERR(k3fd->dss_pxl1_clk);
		return ret;
	} else {
		ret = clk_set_rate(k3fd->dss_pxl1_clk, pinfo->pxl_clk_rate);
		if (ret < 0) {
			K3_FB_ERR("fb%d dss_pxl1_clk clk_set_rate(%lu) failed, error=%d!\n",
				k3fd->index, pinfo->pxl_clk_rate, ret);
			return -EINVAL;
		}
		K3_FB_INFO("dss_pxl1_clk:[%lu]->[%lu].\n",
			pinfo->pxl_clk_rate, clk_get_rate(k3fd->dss_pxl1_clk));
	}

	outp32(dss_base + SDP_LDI_HRZ_CTRL0,
		pinfo->ldi.h_front_porch | (pinfo->ldi.h_back_porch << 16));
	outp32(dss_base + SDP_LDI_HRZ_CTRL1,
		DSS_WIDTH(pinfo->xres) | (DSS_WIDTH(pinfo->ldi.h_pulse_width) << 16));
	outp32(dss_base + SDP_LDI_VRT_CTRL0,
		pinfo->ldi.v_front_porch | (pinfo->ldi.v_back_porch << 16));
	outp32(dss_base + SDP_LDI_VRT_CTRL1,
		DSS_HEIGHT(pinfo->yres) | (DSS_HEIGHT(pinfo->ldi.v_pulse_width) << 16));
	outp32(dss_base + SDP_LDI_PLR_CTRL,
		pinfo->ldi.vsync_plr | (pinfo->ldi.hsync_plr << 1) |
		(pinfo->ldi.pixelclk_plr << 2) | (pinfo->ldi.data_en_plr << 3));

	return 0;
}
int get_inc_hscl(dss_layer_t *layer_infos)
{
	int inc_hscl = INC_FACTOR;

	if (!(layer_infos->need_cap & CAP_SCL))
		return inc_hscl;

	if (layer_infos->need_cap & CAP_ROT)
		inc_hscl = DSS_HEIGHT(layer_infos->src_rect.h) * INC_FACTOR / DSS_HEIGHT(layer_infos->dst_rect.w);
	else
		inc_hscl = DSS_WIDTH(layer_infos->src_rect.w) * INC_FACTOR / DSS_WIDTH(layer_infos->dst_rect.w);

	return inc_hscl;
}
void init_ifbc(struct hisi_fb_data_type *hisifd)
{
	char __iomem *ifbc_base = NULL;

	BUG_ON(hisifd == NULL);

	if (hisifd->index == PRIMARY_PANEL_IDX) {
		ifbc_base = hisifd->dss_base + DSS_DPP_IFBC_OFFSET;
	} else {
		HISI_FB_ERR("fb%d, not support!", hisifd->index);
		return ;
	}

	set_reg(ifbc_base + IFBC_SIZE,
		((DSS_WIDTH(hisifd->panel_info.xres) << 16) |
		DSS_HEIGHT(hisifd->panel_info.yres)), 32, 0);
	set_reg(ifbc_base + IFBC_CTRL, hisifd->panel_info.ifbc_type, 10, 0);
	if (hisifd->panel_info.ifbc_type != IFBC_TYPE_NON) {
		set_reg(ifbc_base + IFBC_EN, 0x3, 2, 0);
	} else {
		set_reg(ifbc_base + IFBC_EN, 0x0, 2, 0);
	}

#if 0
	set_reg(ifbc_base + IFBC_Himax_CTRL0, 32, 0);
	set_reg(ifbc_base + IFBC_Himax_CTRL1, 32, 0);
	set_reg(ifbc_base + IFBC_Himax_CTRL2, 32, 0);
	set_reg(ifbc_base + IFBC_Himax_CTRL3, 32, 0);
	set_reg(ifbc_base + IFBC_PM_CTRL, 32, 0);
	set_reg(ifbc_base + IFBC_MEM_CTRL, 32, 0);
	set_reg(ifbc_base + IFBC_HIMAX_TEST_MODE, 32, 0);
	set_reg(ifbc_base + IFBC_CORE_GT, 32, 0);
#endif
}
void set_layer_cfg_info(dss_layer_t *layer_info, int need_rot,
	block_cfg_t *b_cfg, block_detail_t *b_detail)
{
	if (need_rot) {
		b_detail->inc_hscl = DSS_WIDTH(layer_info->src_rect.h) * SCF_CONST_FACTOR / DSS_WIDTH(layer_info->dst_rect.w);
		b_detail->inc_vscl = (DSS_HEIGHT(layer_info->src_rect.w) * SCF_CONST_FACTOR + SCF_CONST_FACTOR / 2 - b_detail->acc_hscl) /
					DSS_HEIGHT(layer_info->dst_rect.h);
	} else {
		b_detail->inc_hscl = DSS_WIDTH(layer_info->src_rect.w) * SCF_CONST_FACTOR / DSS_WIDTH(layer_info->dst_rect.w);
		b_detail->inc_vscl = (DSS_HEIGHT(layer_info->src_rect.h) * SCF_CONST_FACTOR + SCF_CONST_FACTOR / 2 - b_detail->acc_hscl) /
					DSS_HEIGHT(layer_info->dst_rect.h);
	}

	if (((need_rot) && (layer_info->src_rect.h != layer_info->dst_rect.w))
		|| ((!need_rot) && (layer_info->src_rect.w != layer_info->dst_rect.w))) {
		b_cfg->Scl_Input_Left_Ov = 3;
		b_cfg->Scl_Input_Right_Ov = 3;
		if (b_detail->inc_hscl > INC_FACTOR)
			b_detail->h_v_order = 0;
		else
			b_detail->h_v_order = 1;
	}
}
void init_dpp(struct hisi_fb_data_type *hisifd)
{
	char __iomem *dpp_base = NULL;

	BUG_ON(hisifd == NULL);

	if (hisifd->index == PRIMARY_PANEL_IDX) {
		dpp_base = hisifd->dss_base + DSS_DPP0_CTRL_OFFSET;
	} else {
		HISI_FB_ERR("fb%d, not support!", hisifd->index);
		return ;
	}

	set_reg(dpp_base + DPP_IMG_HRZ, DSS_WIDTH(hisifd->panel_info.xres), 13, 0);
	set_reg(dpp_base + DPP_IMG_VRT, DSS_HEIGHT(hisifd->panel_info.yres), 13, 0);

#ifdef CONFIG_FIX_DIRTY_REGION_UPDT
	if (is_mipi_cmd_panel(hisifd)) {
		outp32(dpp_base + DPP_CLK_GT, 0xA);
	}
#endif
}
void init_ldi(struct hisi_fb_data_type *hisifd)
{
	char __iomem *dss_base = 0;
	struct hisi_panel_info *pinfo = NULL;

	BUG_ON(hisifd == NULL);

	pinfo = &(hisifd->panel_info);
	dss_base = hisifd->dss_base;

	if (hisifd->index == PRIMARY_PANEL_IDX) {
		if (is_dual_mipi_panel(hisifd)) {
			outp32(dss_base + PDP_LDI_DPI0_HRZ_CTRL0,
				pinfo->ldi.h_front_porch | (pinfo->ldi.h_back_porch << 16));
			outp32(dss_base + PDP_LDI_DPI0_HRZ_CTRL1,
				DSS_WIDTH(pinfo->xres / 2) | (DSS_WIDTH(pinfo->ldi.h_pulse_width) << 16));

			outp32(dss_base + PDP_LDI_DPI1_HRZ_CTRL0,
				pinfo->ldi.h_back_porch << 16);
			outp32(dss_base + PDP_LDI_DPI1_HRZ_CTRL1,
				DSS_WIDTH(pinfo->xres / 2) | (DSS_WIDTH(pinfo->ldi.h_pulse_width) << 16));

			outp32(dss_base + PDP_LDI_OVERLAP_SIZE,
				pinfo->ldi.dpi0_overlap_size | (pinfo->ldi.dpi1_overlap_size << 16));

			/* dual_mode_en */
			set_reg(dss_base + PDP_LDI_CTRL, 1, 1, 5);
			/* split mode */
			set_reg(dss_base + PDP_LDI_CTRL, 0, 1, 16);
		} else {
			outp32(dss_base + PDP_LDI_DPI0_HRZ_CTRL0,
				pinfo->ldi.h_front_porch | (pinfo->ldi.h_back_porch << 16));
			outp32(dss_base + PDP_LDI_DPI0_HRZ_CTRL1,
				DSS_WIDTH(pinfo->xres) | (DSS_WIDTH(pinfo->ldi.h_pulse_width) << 16));
		}
		outp32(dss_base + PDP_LDI_VRT_CTRL0,
			pinfo->ldi.v_front_porch | (pinfo->ldi.v_back_porch << 16));
		outp32(dss_base + PDP_LDI_VRT_CTRL1,
			DSS_HEIGHT(pinfo->yres) | (DSS_HEIGHT(pinfo->ldi.v_pulse_width) << 16));
		outp32(dss_base + PDP_LDI_PLR_CTRL,
			pinfo->ldi.vsync_plr | (pinfo->ldi.hsync_plr << 1) |
			(pinfo->ldi.pixelclk_plr << 2) | (pinfo->ldi.data_en_plr << 3));

		/* ldi disable */
		set_reg(dss_base + PDP_LDI_CTRL, 0, 1, 0);
		/* data_gate_en: mipi command LCD open */
		if (is_mipi_cmd_panel(hisifd)) {
			//set_reg(dss_base + PDP_LDI_CTRL, 0x1, 1, 2);
			set_reg(dss_base + PDP_LDI_FRM_MSK, 0x1, 1, 0);
		}
		/* bpp */
		set_reg(dss_base + PDP_LDI_CTRL, pinfo->bpp, 2, 3);
		/* bgr */
		set_reg(dss_base + PDP_LDI_CTRL, pinfo->bgr_fmt, 1, 13);
		/* color mode: for mipi dsi */
		/*set_reg(dss_base + PDP_LDI_CTRL, 0, 1, 14);*/
		/* shutdown: for mipi dsi */
		/*set_reg(dss_base + PDP_LDI_CTRL, 0, 1, 15);*/

	#ifdef CONFIG_HISI_FB_COLORBAR_USED
		/* colorbar width */
		set_reg(dss_base + PDP_LDI_CTRL, DSS_WIDTH(0x3c), 7, 6);
		/* colorbar ort */
		set_reg(dss_base + PDP_LDI_WORK_MODE, 0, 1, 1);
		/* colorbar enable */
		set_reg(dss_base + PDP_LDI_WORK_MODE, 0, 1, 0);
	#else
		/* normal */
		outp32(dss_base + PDP_LDI_WORK_MODE, 0x1);
	#endif
		/* for ddr pmqos */
		outp32(dss_base + PDP_LDI_VINACT_MSK_LEN,
		pinfo->ldi.v_front_porch);
		outp32(dss_base + PDP_LDI_CG_CTRL, 0x0);
		/* use default value */
		/*outp32(dss_base + PDP_SRAM_LP_CTRL, );*/

		/* for 3D frame by frame */
		/*outp32(dss_base + PDP_LDI_3D_CTRL, );
		outp32(dss_base + PDP_LDI_DE_SPACE_LOW, );
		set_reg(dss_base + PDP_LDI_CTRL, 1, 1, 1);*/

		/* for 1Hz LCD and mipi command LCD */
		/*outp32(dss_base + PDP_LDI_FRM_MSK, );*/
		if (is_mipi_cmd_panel(hisifd)) {
			set_reg(dss_base + PDP_DSI_CMD_MOD_CTRL, 0x1, 1, 0);
			set_reg(dss_base + PDP_DSI_TE_CTRL, 0x4001, 32, 0);
			set_reg(dss_base + PDP_DSI_TE_HS_NUM, 0x0, 32, 0);
			set_reg(dss_base + PDP_DSI_TE_HS_WD, 0xF0F, 32, 0);
			set_reg(dss_base + PDP_DSI_TE_VS_WD, 0x3FC0FF, 32, 0);
		}

		/*
		** for test
		** 2'b00: reserved
		** 2'b01: LDI0->DSI0
		** 2'b10: LDI0->DSI1
		** 2'b11: LDI1
		*/
	#if defined(CONFIG_ARCH_HI3630FPGA) || defined(CONFIG_HISI_3635_FPGA) \
		|| defined(CONFIG_HISI_3650_FPGA)
		set_reg(dss_base + DSS_GLB_TEST_SEL, 0x1, 2, 0);
	#endif
	} else if (hisifd->index == EXTERNAL_PANEL_IDX) {
		outp32(dss_base + SDP_LDI_HRZ_CTRL0,
			pinfo->ldi.h_front_porch | (pinfo->ldi.h_back_porch << 16));
		outp32(dss_base + SDP_LDI_HRZ_CTRL1,
			DSS_WIDTH(pinfo->xres) | (DSS_WIDTH(pinfo->ldi.h_pulse_width) << 16));
		outp32(dss_base + SDP_LDI_VRT_CTRL0,
			pinfo->ldi.v_front_porch | (pinfo->ldi.v_back_porch << 16));
		outp32(dss_base + SDP_LDI_VRT_CTRL1,
			DSS_HEIGHT(pinfo->yres) | (DSS_HEIGHT(pinfo->ldi.v_pulse_width) << 16));
		outp32(dss_base + SDP_LDI_PLR_CTRL,
			pinfo->ldi.vsync_plr | (pinfo->ldi.hsync_plr << 1) |
			(pinfo->ldi.pixelclk_plr << 2) | (pinfo->ldi.data_en_plr << 3));

		/* ldi disable */
		set_reg(dss_base + SDP_LDI_CTRL, 0, 1, 0);
		/* data_gate_en */
		/*set_reg(dss_base + PDP_LDI_CTRL, 1, 1, 2);*/
		/* bpp */
		set_reg(dss_base + SDP_LDI_CTRL, pinfo->bpp, 2, 3);
		/* bgr */
		set_reg(dss_base + SDP_LDI_CTRL, pinfo->bgr_fmt, 1, 13);
		/* color mode */
		/*set_reg(dss_base + SDP_LDI_CTRL, 0, 1, 14);*/
		/* shutdown */
		/*set_reg(dss_base + SDP_LDI_CTRL, 0, 1, 15);*/

	#ifdef CONFIG_HISI_FB_COLORBAR_USED
		/* colorbar width */
		set_reg(dss_base + SDP_LDI_CTRL, DSS_WIDTH(0x3c), 7, 6);
		/* colorbar ort */
		set_reg(dss_base + SDP_LDI_WORK_MODE, 0, 1, 1);
		/* colorbar enable */
		set_reg(dss_base + SDP_LDI_WORK_MODE, 0, 1, 0);
	#else
		/* normal */
		outp32(dss_base + SDP_LDI_WORK_MODE, 0x1);
	#endif
		outp32(dss_base + SDP_LDI_VINACT_MSK_LEN,
			pinfo->ldi.v_front_porch);
	} else {
		HISI_FB_ERR("fb%d, not support!", hisifd->index);
		return ;
	}
}
void init_dbuf(struct hisi_fb_data_type *hisifd)
{
	char __iomem *dbuf_base = NULL;
	struct hisi_panel_info *pinfo = NULL;
	int sram_valid_num = 0;
	int sram_max_mem_depth = 0;

	uint32_t thd_rqos_in = 0;
	uint32_t thd_rqos_out = 0;
	uint32_t thd_wqos_in = 0;
	uint32_t thd_wqos_out = 0;
	uint32_t thd_cg_in = 0;
	uint32_t thd_cg_out = 0;
	uint32_t thd_wr_wait = 0;
	uint32_t thd_cg_hold = 0;
	uint32_t thd_fill_lev1 = 0;
	uint32_t thd_fill_lev2 = 0;
	uint32_t thd_fill_lev3 = 0;
	uint32_t thd_fill_lev4 = 0;

	BUG_ON(hisifd == NULL);
	pinfo = &(hisifd->panel_info);

	sram_valid_num = get_dfs_sram_valid_num(hisifd);
	sram_max_mem_depth = (sram_valid_num + 1) * DFS_SRAM_ONE_MEM_DEPTH;

	if (hisifd->index == PRIMARY_PANEL_IDX) {
		dbuf_base = hisifd->dss_base + DSS_DPE0_OFFSET;

	#if defined(CONFIG_ARCH_HI3630FPGA) || defined(CONFIG_HISI_3635_FPGA) \
		|| defined(CONFIG_HISI_3650_FPGA)
		thd_rqos_in = GET_THD_RQOS_IN(sram_max_mem_depth);
		thd_rqos_out = GET_THD_RQOS_OUT(sram_max_mem_depth);
		thd_wqos_in = GET_THD_WQOS_IN(sram_max_mem_depth);
		thd_wqos_out = GET_THD_WQOS_OUT(sram_max_mem_depth);
		#if 1
		thd_cg_in = GET_THD_CG_IN(sram_max_mem_depth);
		thd_cg_out = GET_THD_CG_OUT(sram_max_mem_depth);
		#else
		thd_cg_in = 0x3fff;
		thd_cg_out = 0x3fff;
		#endif
		thd_wr_wait = GET_THD_OTHER_WR_WAIT(sram_max_mem_depth);
		thd_cg_hold = GET_THD_OTHER_DFS_CG_HOLD(sram_max_mem_depth);
	#else
		if (sram_valid_num == 0) {
			thd_rqos_in = 0x30C;
			thd_rqos_out = 0x352;
			thd_wqos_in = 0x420;
			thd_wqos_out = 0x160;
			thd_cg_in = 0x57F;
			thd_cg_out = 0x352;
			thd_cg_hold = 0x20;
			thd_wr_wait = 0x352;
			thd_fill_lev1 = 0x11A;
			thd_fill_lev2 = 0x234;
			thd_fill_lev3 = 0x34D;
			thd_fill_lev4 = 0x467;
		} else if (sram_valid_num == 1) {
			thd_rqos_in = 0x898;
			thd_rqos_out = 0x9C0;
			thd_wqos_in = 0x840;
			thd_wqos_out = 0x2C0;
			thd_cg_in = 0xAFF;
			thd_cg_out = 0x9C0;
			thd_cg_hold = 0x20;
			thd_wr_wait = 0x8DA;
			thd_fill_lev1 = 0x234;
			thd_fill_lev2 = 0x467;
			thd_fill_lev3 = 0x69A;
			thd_fill_lev4 = 0x8CD;
		} else {
			thd_rqos_in = 0xD9F;
			thd_rqos_out = 0xF23;
			thd_wqos_in = 0xC7D;
			thd_wqos_out = 0x42A;
			thd_cg_in = 0x107F;
			thd_cg_out = 0xFFF;
			thd_cg_hold = 0x20;
			thd_wr_wait = 0xF23;
			thd_fill_lev1 = 0x34D;
			thd_fill_lev2 = 0x69A;
			thd_fill_lev3 = 0x9E7;
			thd_fill_lev4 = 0xD34;
		}
	#endif
	} else if (hisifd->index == EXTERNAL_PANEL_IDX) {
		dbuf_base = hisifd->dss_base + DSS_DPE1_OFFSET;

	#if defined(CONFIG_ARCH_HI3630FPGA) || defined(CONFIG_HISI_3635_FPGA) \
		|| defined(CONFIG_HISI_3650_FPGA)
		thd_rqos_in = GET_THD_RQOS_IN(sram_max_mem_depth);
		thd_rqos_out = GET_THD_RQOS_OUT(sram_max_mem_depth);
		thd_wqos_in = GET_THD_WQOS_IN(sram_max_mem_depth);
		thd_wqos_out = GET_THD_WQOS_OUT(sram_max_mem_depth);
		#if 1
		thd_cg_in = GET_THD_CG_IN(sram_max_mem_depth);
		thd_cg_out = GET_THD_CG_OUT(sram_max_mem_depth);
		#else
		thd_cg_in = 0x3fff;
		thd_cg_out = 0x3fff;
		#endif
		thd_wr_wait = GET_THD_OTHER_WR_WAIT(sram_max_mem_depth);
		thd_cg_hold = GET_THD_OTHER_DFS_CG_HOLD(sram_max_mem_depth);
	#else
		thd_rqos_in = 0x1B4;
		thd_rqos_out = 0x1E7;
		thd_wqos_in = 0x180;
		thd_wqos_out = 0x80;
		thd_cg_in = 0x3FF;
		thd_cg_out = 0x3FF;
		thd_wr_wait = 0x19A;
		thd_cg_hold = 0x20;
		thd_fill_lev1 = 0x67;
		thd_fill_lev2 = 0xCD;
		thd_fill_lev3 = 0x134;
		thd_fill_lev4 = 0x19A;
	#endif
	} else {
		HISI_FB_ERR("fb%d, not support!", hisifd->index);
		return ;
	}

	outp32(dbuf_base + DFS_FRM_SIZE, pinfo->xres * pinfo->yres);
	outp32(dbuf_base + DFS_FRM_HSIZE, DSS_WIDTH(pinfo->xres));
	outp32(dbuf_base + DFS_SRAM_VALID_NUM, sram_valid_num);

	outp32(dbuf_base + DFS_THD_RQOS, (thd_rqos_out<< 16) | thd_rqos_in);
	outp32(dbuf_base + DFS_THD_WQOS, (thd_wqos_out << 16) | thd_wqos_in);
	outp32(dbuf_base + DFS_THD_CG, (thd_cg_out << 16) | thd_cg_in);
	outp32(dbuf_base + DFS_THD_OTHER, (thd_cg_hold << 16) | thd_wr_wait);
	outp32(dbuf_base + DFS_THD_FILL_LEV0, (thd_fill_lev2 << 16) | thd_fill_lev1);
	outp32(dbuf_base + DFS_FILL_LEV1_CNT, (thd_fill_lev4 << 16) | thd_fill_lev3);

	/* dfs_core_gt: use default value */
	/*set_reg(dbuf_base + DFS_DFS_LP_CTRL, 0, 2, 26);*/
	/* ret_aft_prefetch: use default value */
	/*set_reg(dbuf_base + DFS_DFS_LP_CTRL, 1, 1, 25);*/
	/* pd_upon_frm_end: use default value */
	/*set_reg(dbuf_base + DFS_DFS_LP_CTRL, 1, 1, 24);*/
}