/* * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mxc_gis.h" #include "mxc_csi.h" #include "mxc_pxp.h" #include "mxc_vadc.h" #define CHANNEL_OFFSET 36 #define COMMAND_OFFSET 8 #define REG_OFFSET 4 #define COMMAND_OPCODE_SHIFT 8 enum { CMD_SET_ACC = 0, CMD_WR_DATA, CMD_WR_ACC, CMD_WR_ALU, CMD_MOV_ACC, CMD_RD_DATA, CMD_RD_ALU, CMD_WR_FB_CSI, CMD_WR_FB_PXP_IN, CMD_WR_FB_PXP_OUT, CMD_WR_FB_LCDIF, }; enum { ALU_AND = 0, ALU_OR, ALU_XOR, ALU_ADD, ALU_SUB, }; enum { CH_MAPPING_CSI_ISR = 0, CH_MAPPING_CSI_FB_UPDATE, CH_MAPPING_PXP_ISR, CH_MAPPING_LCDIF_FB_UPDATE, CH_MAPPING_PXP_KICK, CH_MAPPING_CHANNEL_UNUSED = 0xf, }; enum { LCDIF1_SEL = 0x10, LCDIF0_SEL = 0x8, PXP_SEL = 0x4, CSI1_SEL = 0x2, CSI0_SEL = 0x1, }; struct command_opcode { unsigned opcode:4; unsigned alu:3; unsigned acc_neg:1; }; struct command_param { union { struct command_opcode cmd_bits; u8 cmd_opc; }; u32 addr; u32 data; }; struct channel_param { u32 ch_num; u32 ch_map; u32 cmd_num; struct command_param cmd_data[4]; }; static void *csibuf0, *csibuf1, *fb0, *fb1; static struct mxs_gis_regs *gis_regs; static struct mxs_pxp_regs *pxp_regs; static struct mxs_csi_regs *csi_regs; static struct mxs_lcdif_regs *lcdif_regs; static u32 lcdif_sel; static bool gis_running; static void config_channel(struct channel_param *ch) { u32 val, i; u32 reg_offset; /* Config channel map and command */ switch (ch->ch_num) { case 0: val = readl(&gis_regs->hw_gis_config0); val &= ~(GIS_CONFIG0_CH0_MAPPING_MASK | GIS_CONFIG0_CH0_NUM_MASK); val |= ch->ch_map << GIS_CONFIG0_CH0_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG0_CH0_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config0); break; case 1: val = readl(&gis_regs->hw_gis_config0); val &= ~(GIS_CONFIG0_CH1_MAPPING_MASK | GIS_CONFIG0_CH1_NUM_MASK); val |= ch->ch_map << GIS_CONFIG0_CH1_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG0_CH1_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config0); break; case 2: val = readl(&gis_regs->hw_gis_config0); val &= ~(GIS_CONFIG0_CH2_MAPPING_MASK | GIS_CONFIG0_CH2_NUM_MASK); val |= ch->ch_map << GIS_CONFIG0_CH2_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG0_CH2_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config0); break; case 3: val = readl(&gis_regs->hw_gis_config0); val &= ~(GIS_CONFIG0_CH3_MAPPING_MASK | GIS_CONFIG0_CH3_NUM_MASK); val |= ch->ch_map << GIS_CONFIG0_CH3_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG0_CH3_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config0); break; case 4: val = readl(&gis_regs->hw_gis_config1); val &= ~(GIS_CONFIG1_CH4_MAPPING_MASK | GIS_CONFIG1_CH4_NUM_MASK); val |= ch->ch_map << GIS_CONFIG1_CH4_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG1_CH4_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config1); break; case 5: val = readl(&gis_regs->hw_gis_config1); val &= ~(GIS_CONFIG1_CH5_MAPPING_MASK | GIS_CONFIG1_CH5_NUM_MASK); val |= ch->ch_map << GIS_CONFIG1_CH5_MAPPING_SHIFT; val |= ch->cmd_num << GIS_CONFIG1_CH5_NUM_SHIFT; writel(val, &gis_regs->hw_gis_config1); break; default: printf("Error channel num\n"); } /* Config command */ for (i = 0; i < ch->cmd_num; i++) { val = readl(&gis_regs->hw_gis_ch0_ctrl + ch->ch_num * CHANNEL_OFFSET); val &= ~(0xFF << (COMMAND_OPCODE_SHIFT * i)); val |= ch->cmd_data[i].cmd_opc << (COMMAND_OPCODE_SHIFT * i); writel(val, &gis_regs->hw_gis_ch0_ctrl + ch->ch_num * CHANNEL_OFFSET); reg_offset = ch->ch_num * CHANNEL_OFFSET + i * COMMAND_OFFSET; writel(ch->cmd_data[i].addr, &gis_regs->hw_gis_ch0_addr0 + reg_offset); writel(ch->cmd_data[i].data, &gis_regs->hw_gis_ch0_data0 + reg_offset); } } static void gis_channel_init(void) { struct channel_param ch; int ret; u32 addr0, data0, addr1, data1; u32 val; /* Restart the GIS block */ ret = mxs_reset_block(&gis_regs->hw_gis_ctrl_reg); if (ret) { debug("MXS GIS: Block reset timeout\n"); return; } writel((u32)csibuf0, &gis_regs->hw_gis_fb0); writel((u32)csibuf1, &gis_regs->hw_gis_fb1); writel((u32)fb0, &gis_regs->hw_gis_pxp_fb0); writel((u32)fb1, &gis_regs->hw_gis_pxp_fb1); /* Config channel 0 -- CSI clean interrupt */ addr0 = (u32)&csi_regs->csi_csisr; data0 = BIT_DMA_TSF_DONE_FB1 | BIT_DMA_TSF_DONE_FB2 | BIT_SOF_INT; ch.ch_num = 0; ch.ch_map = CH_MAPPING_CSI_ISR; ch.cmd_num = 1; ch.cmd_data[0].cmd_bits.opcode = CMD_WR_DATA; ch.cmd_data[0].cmd_bits.alu = ALU_AND; ch.cmd_data[0].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[0].addr = CSI0_SEL << GIS_CH_ADDR_SEL_SHIFT | addr0; ch.cmd_data[0].data = data0; config_channel(&ch); /* Config channel 1 -- CSI set next framebuffer addr */ addr0 = (u32)&csi_regs->csi_csidmasa_fb1; data0 = (u32)&csi_regs->csi_csidmasa_fb2; ch.ch_num = 1; ch.ch_map = CH_MAPPING_CSI_FB_UPDATE; ch.cmd_num = 1; ch.cmd_data[0].cmd_bits.opcode = CMD_WR_FB_CSI; ch.cmd_data[0].cmd_bits.alu = ALU_AND; ch.cmd_data[0].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[0].addr = CSI0_SEL << GIS_CH_ADDR_SEL_SHIFT | addr0; ch.cmd_data[0].data = data0; config_channel(&ch); /* Config channel 2 -- PXP clear interrupt and set framebuffer */ addr0 = (u32)&pxp_regs->pxp_stat_clr; data0 = BM_PXP_STAT_IRQ; addr1 = (u32)&pxp_regs->pxp_out_buf; data1 = 0; ch.ch_num = 2; ch.ch_map = CH_MAPPING_PXP_ISR; ch.cmd_num = 2; ch.cmd_data[0].cmd_bits.opcode = CMD_WR_DATA; ch.cmd_data[0].cmd_bits.alu = ALU_AND; ch.cmd_data[0].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[0].addr = PXP_SEL << GIS_CH_ADDR_SEL_SHIFT | addr0; ch.cmd_data[0].data = data0; ch.cmd_data[1].cmd_bits.opcode = CMD_WR_FB_PXP_OUT; ch.cmd_data[1].cmd_bits.alu = ALU_AND; ch.cmd_data[1].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[1].addr = PXP_SEL << GIS_CH_ADDR_SEL_SHIFT | addr1; ch.cmd_data[1].data = data1; config_channel(&ch); /* Config channel 3 -- LCDIF set framebuffer to display */ addr0 = (u32)&lcdif_regs->hw_lcdif_next_buf; data0 = 0; ch.ch_num = 3; ch.ch_map = CH_MAPPING_LCDIF_FB_UPDATE; ch.cmd_num = 1; ch.cmd_data[0].cmd_bits.opcode = CMD_WR_FB_LCDIF; ch.cmd_data[0].cmd_bits.alu = ALU_AND; ch.cmd_data[0].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[0].addr = ((lcdif_sel == 0) ? LCDIF0_SEL : LCDIF1_SEL) << GIS_CH_ADDR_SEL_SHIFT | addr0; ch.cmd_data[0].data = data0; config_channel(&ch); /* Config channel 4 -- PXP kick to process next framebuffer */ addr0 = (u32)&pxp_regs->pxp_ps_buf; data0 = 0; addr1 = (u32)&pxp_regs->pxp_ctrl; data1 = BM_PXP_CTRL_IRQ_ENABLE | BM_PXP_CTRL_ENABLE; ch.ch_num = 4; ch.ch_map = CH_MAPPING_PXP_KICK; ch.cmd_num = 2; ch.cmd_data[0].cmd_bits.opcode = CMD_WR_FB_PXP_IN; ch.cmd_data[0].cmd_bits.alu = ALU_AND; ch.cmd_data[0].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[0].addr = PXP_SEL << GIS_CH_ADDR_SEL_SHIFT | addr0; ch.cmd_data[0].data = data0; ch.cmd_data[1].cmd_bits.opcode = CMD_WR_DATA; ch.cmd_data[1].cmd_bits.alu = ALU_AND; ch.cmd_data[1].cmd_bits.acc_neg = GIS_CH_CTRL_CMD_ACC_NO_NEGATE; ch.cmd_data[1].addr = PXP_SEL << GIS_CH_ADDR_SEL_SHIFT | addr1; ch.cmd_data[1].data = data1; config_channel(&ch); /* start gis */ val = readl(&gis_regs->hw_gis_ctrl); if (lcdif_sel == 1) val |= GIS_CTRL_ENABLE_SET | GIS_CTRL_LCDIF_SEL_LCDIF1; else val |= GIS_CTRL_ENABLE_SET | GIS_CTRL_LCDIF_SEL_LCDIF0; writel(val, &gis_regs->hw_gis_ctrl); } void mxc_disable_gis(void) { u32 val; if (!gis_running) return; /* Stop gis */ val = GIS_CTRL_SFTRST_SET | GIS_CTRL_CLK_GATE_SET; writel(val, &gis_regs->hw_gis_ctrl); /* Stop pxp */ mxs_reset_block(&pxp_regs->pxp_ctrl_reg); val = BM_PXP_CTRL_SFTRST | BM_PXP_CTRL_CLKGATE; writel(val , &pxp_regs->pxp_ctrl); csi_disable(); vadc_power_down(); } void mxc_enable_gis(void) { struct sensor_data sensor; struct csi_conf_param csi_conf; struct pxp_config_data pxp_conf; struct display_panel panel; u32 csimemsize, pxpmemsize; char const *gis_input = getenv("gis"); #ifdef CONFIG_MX6 if (check_module_fused(MX6_MODULE_CSI)) { printf("CSI@0x%x is fused, disable it\n", CSI1_BASE_ADDR); return; } #endif #ifdef CONFIG_MX6 if (check_module_fused(MX6_MODULE_PXP)) { printf("PXP@0x%x is fused, disable it\n", PXP_BASE_ADDR); return; } #endif gis_regs = (struct mxs_gis_regs *)GIS_BASE_ADDR; pxp_regs = (struct mxs_pxp_regs *)PXP_BASE_ADDR; csi_regs = (struct mxs_csi_regs *)CSI1_BASE_ADDR; gis_running = false; if (!strcmp(gis_input, "vadc")) { printf("gis input --- vadc\n"); /* vadc_in 0 */ vadc_config(0); /* Get vadc mode */ vadc_get_std(&sensor); } else { printf("gis input --- No input\n"); return; } /* Get display mode */ mxs_lcd_get_panel(&panel); lcdif_regs = (struct mxs_lcdif_regs *)panel.reg_base; if (panel.reg_base == LCDIF2_BASE_ADDR) lcdif_sel = 1; else lcdif_sel = 0; /* Allocate csi buffer */ if (sensor.pixel_fmt == FMT_YUV444) { csimemsize = sensor.width * sensor.height * 4; csi_conf.bpp = 32; } else { csimemsize = sensor.width * sensor.height * 2; csi_conf.bpp = 16; } pxpmemsize = panel.width * panel.height * panel.gdfbytespp; csibuf0 = malloc(csimemsize); csibuf1 = malloc(csimemsize); fb0 = malloc(pxpmemsize); fb1 = malloc(pxpmemsize); if (!csibuf0 || !csibuf1 || !fb0 || !fb1) { printf("MXSGIS: Error allocating csibuffer!\n"); return; } /* Wipe framebuffer */ memset(csibuf0, 0, csimemsize); memset(csibuf1, 0, csimemsize); memset(fb0, 0, pxpmemsize); memset(fb1, 0, pxpmemsize); /*config csi */ csi_conf.width = sensor.width; csi_conf.height = sensor.height; csi_conf.btvmode = true; csi_conf.std = sensor.std_id; csi_conf.fb0addr = csibuf0; csi_conf.fb1addr = csibuf1; csi_config(&csi_conf); /* config pxp */ pxp_conf.s0_param.pixel_fmt = sensor.pixel_fmt; pxp_conf.s0_param.width = sensor.width; pxp_conf.s0_param.height = sensor.height; pxp_conf.s0_param.stride = sensor.width * csi_conf.bpp/8; pxp_conf.s0_param.paddr = csibuf0; switch (panel.gdfindex) { case GDF_32BIT_X888RGB: pxp_conf.out_param.pixel_fmt = FMT_RGB888; break; case GDF_16BIT_565RGB: pxp_conf.out_param.pixel_fmt = FMT_RGB565; break; default: printf("GIS unsupported format!"); } pxp_conf.out_param.width = panel.width; pxp_conf.out_param.height = panel.height; pxp_conf.out_param.stride = pxp_conf.out_param.width * panel.gdfbytespp; pxp_conf.out_param.paddr = fb0; pxp_config(&pxp_conf); gis_running = true; /* Config gis */ gis_channel_init(); }