diff options
Diffstat (limited to 'drivers/video/mxc_gis.c')
-rw-r--r-- | drivers/video/mxc_gis.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/drivers/video/mxc_gis.c b/drivers/video/mxc_gis.c new file mode 100644 index 0000000..fd7012e --- /dev/null +++ b/drivers/video/mxc_gis.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <video_fb.h> + +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/errno.h> +#include <asm/io.h> + +#include <linux/string.h> +#include <linux/list.h> +#include <linux/fb.h> +#include <gis.h> +#include <mxsfb.h> + +#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(); + + lcdif_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"); + + 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(); +} |