From 85a20d8027f947bb65fc346c8704b8b0cb67cc9d Mon Sep 17 00:00:00 2001
From: Troy Kisky <troy.kisky@boundarydevices.com>
Date: Mon, 6 Jul 2015 12:47:36 -0700
Subject: [PATCH] ipu_disp: add YUV modes

---
 drivers/video/ipu.h        |   4 +-
 drivers/video/ipu_common.c |   9 ++-
 drivers/video/ipu_disp.c   | 130 ++++++++++++++++++++++++++++++-------
 drivers/video/ipu_regs.h   |  14 +++-
 4 files changed, 128 insertions(+), 29 deletions(-)

diff --git a/drivers/video/ipu.h b/drivers/video/ipu.h
index 1e02c7ab6d5..ea4555264b5 100644
--- a/drivers/video/ipu.h
+++ b/drivers/video/ipu.h
@@ -151,6 +151,8 @@ typedef union {
 	struct {
 		uint32_t di;
 		unsigned char interlaced;
+		uint32_t in_pixel_fmt;
+		uint32_t out_pixel_fmt;
 	} mem_dc_sync;
 	struct {
 		uint32_t temp;
@@ -257,7 +259,7 @@ bool ipu_clk_enabled(void);
 void ipu_dmfc_init(int dmfc_type, int first);
 void ipu_init_dc_mappings(void);
 void ipu_dmfc_set_wait4eot(int dma_chan, int width);
-void ipu_dc_init(int dc_chan, int di, unsigned char interlaced);
+void ipu_dc_init(int dc_chan, int di, unsigned char interlaced, uint32_t pixel_fmt);
 void ipu_dc_uninit(int dc_chan);
 void ipu_dp_dc_enable(ipu_channel_t channel);
 int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
diff --git a/drivers/video/ipu_common.c b/drivers/video/ipu_common.c
index cbe1984e4f7..5cc7683fcbd 100644
--- a/drivers/video/ipu_common.c
+++ b/drivers/video/ipu_common.c
@@ -603,7 +603,8 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params)
 
 		g_dc_di_assignment[1] = params->mem_dc_sync.di;
 		ipu_dc_init(1, params->mem_dc_sync.di,
-			     params->mem_dc_sync.interlaced);
+			     params->mem_dc_sync.interlaced,
+			     params->mem_dc_sync.out_pixel_fmt);
 		ipu_di_use_count[params->mem_dc_sync.di]++;
 		ipu_dc_use_count++;
 		ipu_dmfc_use_count++;
@@ -618,7 +619,8 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params)
 		ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt,
 			     params->mem_dp_bg_sync.out_pixel_fmt);
 		ipu_dc_init(5, params->mem_dp_bg_sync.di,
-			     params->mem_dp_bg_sync.interlaced);
+			     params->mem_dp_bg_sync.interlaced,
+			     params->mem_dp_bg_sync.out_pixel_fmt);
 		ipu_di_use_count[params->mem_dp_bg_sync.di]++;
 		ipu_dc_use_count++;
 		ipu_dp_use_count++;
@@ -899,7 +901,7 @@ static void ipu_ch_param_init(int ch,
 	case IPU_PIX_FMT_YUYV:
 		ipu_ch_param_set_field(&params, 0, 107, 3, 3);	/* bits/pixel */
 		ipu_ch_param_set_field(&params, 1, 85, 4, 0x8);	/* pix format */
-		ipu_ch_param_set_field(&params, 1, 78, 7, 31);	/* burst size */
+		ipu_ch_param_set_field(&params, 1, 78, 7, 15);	/* burst size */
 		break;
 	case IPU_PIX_FMT_YUV420P2:
 	case IPU_PIX_FMT_YUV420P:
@@ -1016,6 +1018,7 @@ int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
 	uint32_t reg;
 	uint32_t dma_chan;
 
+	printf("%s: chan=0x%08x, pixel_fmt=%x\n", __func__, channel, pixel_fmt);
 	dma_chan = channel_2_dma(channel, type);
 	if (!idma_is_valid(dma_chan))
 		return -EINVAL;
diff --git a/drivers/video/ipu_disp.c b/drivers/video/ipu_disp.c
index ead8c479358..e154fe45a33 100644
--- a/drivers/video/ipu_disp.c
+++ b/drivers/video/ipu_disp.c
@@ -239,6 +239,24 @@ static void ipu_di_sync_config(int di, int wave_gen,
 	__raw_writel(reg, DI_STP_REP(di, wave_gen));
 }
 
+static void ipu_dc_map_link(int current_map,
+		int base_map_0, int buf_num_0,
+		int base_map_1, int buf_num_1,
+		int base_map_2, int buf_num_2)
+{
+	int ptr_0 = base_map_0 * 3 + buf_num_0;
+	int ptr_1 = base_map_1 * 3 + buf_num_1;
+	int ptr_2 = base_map_2 * 3 + buf_num_2;
+	int ptr;
+	u32 reg;
+	ptr = (ptr_2 << 10) +  (ptr_1 << 5) + ptr_0;
+
+	reg = __raw_readl(DC_MAP_CONF_PTR(current_map));
+	reg &= ~(0x1F << ((16 * (current_map & 0x1))));
+	reg |= ptr << ((16 * (current_map & 0x1)));
+	__raw_writel(reg, DC_MAP_CONF_PTR(current_map));
+}
+
 static void ipu_dc_map_config(int map, int byte_num, int offset, int mask)
 {
 	int ptr = map * 3 + byte_num;
@@ -284,11 +302,32 @@ static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
 static void ipu_dc_link_event(int chan, int event, int addr, int priority)
 {
 	u32 reg;
-
-	reg = __raw_readl(DC_RL_CH(chan, event));
-	reg &= ~(0xFFFF << (16 * (event & 0x1)));
-	reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
-	__raw_writel(reg, DC_RL_CH(chan, event));
+	u32 address_shift;
+	if (event < DC_EVEN_UGDE0) {
+		reg = __raw_readl(DC_RL_CH(chan, event));
+		reg &= ~(0xFFFF << (16 * (event & 0x1)));
+		reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+		__raw_writel(reg, DC_RL_CH(chan, event));
+	} else {
+		reg = __raw_readl(DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+		if ((event - DC_EVEN_UGDE0) & 0x1) {
+			/* DC_ODD_UGDEx */
+			reg &= ~(0x2FF << 16);
+			reg |= (addr << 16);
+			reg |= priority ? (2 << 24) : 0x0;
+		} else {
+			/* DC_EVEN_UGDEx */
+			reg &= ~0xFC00FFFF;
+			if (priority)
+				chan = (chan >> 1) +
+					((((chan & 0x1) + ((chan & 0x2) >> 1))) | (chan >> 3));
+			else
+				chan = 0x7;
+			address_shift = ((event - DC_EVEN_UGDE0) >> 1) ? 7 : 8;
+			reg |= (addr << address_shift) | (priority << 3) | chan;
+		}
+		__raw_writel(reg, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+	}
 }
 
 /* Y = R *  1.200 + G *  2.343 + B *  .453 + 0.250;
@@ -427,6 +466,7 @@ int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
 	int partial = 0;
 	uint32_t reg;
 
+	printf("%s: chan=0x%08x, infmt %x, outfmt=%x\n", __func__, channel, in_pixel_fmt, out_pixel_fmt);
 	if (channel == MEM_FG_SYNC) {
 		dp = DP_SYNC;
 		partial = 1;
@@ -443,6 +483,7 @@ int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
 	in_fmt = format_to_colorspace(in_pixel_fmt);
 	out_fmt = format_to_colorspace(out_pixel_fmt);
 
+	printf("%s: %s to %s\n", __func__, (in_fmt == RGB) ? "rgb" : "yuv", (out_fmt == RGB) ? "rgb" : "yuv");
 	if (partial) {
 		if (in_fmt == RGB) {
 			if (out_fmt == RGB)
@@ -532,26 +573,35 @@ void ipu_dp_uninit(ipu_channel_t channel)
 	ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 0);
 }
 
-void ipu_dc_init(int dc_chan, int di, unsigned char interlaced)
+void ipu_dc_init(int dc_chan, int di, unsigned char interlaced, uint32_t pixel_fmt)
 {
 	u32 reg = 0;
 
+	printf("%s: fmt %x, chan=%d, di=%d, interlaced=%d\n", __func__, pixel_fmt, dc_chan, di, interlaced);
 	if ((dc_chan == 1) || (dc_chan == 5)) {
 		if (interlaced) {
 			ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
 			ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
 			ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
 		} else {
-			if (di) {
-				ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
-				ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
-				ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA,
-					4, 1);
-			} else {
-				ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
-				ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
-				ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA,
-					7, 1);
+			int mc1 = di ? 2 : 5;
+			int mc2 = di ? 8 : 10;
+			int dc = di ? DC_EVEN_UGDE1 : DC_EVEN_UGDE0;
+#define MC_WORD_DI1_NL		2
+#define MC_WORD_DI1_EOL		3
+#define MC_WORD_DI1_NEW_DATA	4
+#define MC_WORD_DI0_NL		5
+#define MC_WORD_DI0_EOL		6
+#define MC_WORD_DI0_NEW_DATA	7
+			ipu_dc_link_event(dc_chan, DC_EVT_NL, mc1++, 3);
+			ipu_dc_link_event(dc_chan, DC_EVT_EOL, mc1++, 2);
+			ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, mc1, 1);
+
+			if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+			    (pixel_fmt == IPU_PIX_FMT_UYVY)) {
+				printf("%s: link %d %d\n", __func__, dc, mc2);
+				ipu_dc_link_event(dc_chan, dc++, mc2++, 5);
+				ipu_dc_link_event(dc_chan, dc, mc2, 5);
 			}
 		}
 		ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
@@ -771,6 +821,28 @@ void ipu_init_dc_mappings(void)
 	ipu_dc_map_config(4, 0, 5, 0xFC);
 	ipu_dc_map_config(4, 1, 13, 0xFC);
 	ipu_dc_map_config(4, 2, 21, 0xFC);
+
+	/* IPU_PIX_FMT_VYUY 16bit width */
+	ipu_dc_map_clear(5);
+	ipu_dc_map_config(5, 0, 7, 0xFF);
+	ipu_dc_map_config(5, 1, 0, 0x0);
+	ipu_dc_map_config(5, 2, 15, 0xFF);
+	ipu_dc_map_clear(6);
+	ipu_dc_map_config(6, 0, 0, 0x0);
+	ipu_dc_map_config(6, 1, 7, 0xFF);
+	ipu_dc_map_config(6, 2, 15, 0xFF);
+
+	/* IPU_PIX_FMT_UYVY 16bit width */
+	ipu_dc_map_clear(7);
+	ipu_dc_map_link(7, 6, 0, 6, 1, 6, 2);
+	ipu_dc_map_clear(8);
+	ipu_dc_map_link(8, 5, 0, 5, 1, 5, 2);
+
+	/* IPU_PIX_FMT_YUYV 16bit width */
+	ipu_dc_map_clear(9);
+	ipu_dc_map_link(9, 5, 1, 5, 2, 5, 0);
+	ipu_dc_map_clear(10);
+	ipu_dc_map_link(10, 5, 2, 5, 1, 5, 0);
 }
 
 static int ipu_pixfmt_to_map(uint32_t fmt)
@@ -787,6 +859,10 @@ static int ipu_pixfmt_to_map(uint32_t fmt)
 		return 3;
 	case IPU_PIX_FMT_LVDS666:
 		return 4;
+	case IPU_PIX_FMT_UYVY:
+		return 8;
+	case IPU_PIX_FMT_YUYV:
+		return 10;
 	}
 
 	return -1;
@@ -1077,6 +1153,8 @@ int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk,
 		di_gen |= DI_GEN_POLARITY_5;
 		di_gen |= DI_GEN_POLARITY_8;
 	} else {
+		int mc1, mc2;
+
 		/* Setup internal HSYNC waveform */
 		ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK,
 				0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
@@ -1124,15 +1202,19 @@ int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk,
 		__raw_writel(0, DI_STP_REP9(disp));
 
 		/* Init template microcode */
-		if (disp) {
-		   ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
-		   ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
-		   ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
-		} else {
-		   ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
-		   ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
-		   ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+		mc1 = disp ? 2 : 5;
+		mc2 = disp ? 8 : 10;
+		if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+		    (pixel_fmt == IPU_PIX_FMT_UYVY)) {
+			printf("%s: map %d %d, %d %d\n", __func__, map-1, mc2, map, mc2+1);
+			ipu_dc_write_tmpl(mc2++, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5);
+			ipu_dc_write_tmpl(mc2, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+			/* configure user events according to DISP NUM */
+			__raw_writel((width - 1), DC_UGDE_3(disp));
 		}
+		ipu_dc_write_tmpl(mc1++, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+		ipu_dc_write_tmpl(mc1++, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+		ipu_dc_write_tmpl(mc1, WROD(0), 0, map, SYNC_WAVE, 0, 5);
 
 		if (sig.Hsync_pol)
 			di_gen |= DI_GEN_POLARITY_2;
diff --git a/drivers/video/ipu_regs.h b/drivers/video/ipu_regs.h
index deb44002d75..e688aa0d931 100644
--- a/drivers/video/ipu_regs.h
+++ b/drivers/video/ipu_regs.h
@@ -73,6 +73,14 @@ extern u32 *ipu_dc_tmpl_reg;
 #define DC_EVT_NEW_CHAN_R_1	9
 #define DC_EVT_NEW_DATA_R_0	10
 #define DC_EVT_NEW_DATA_R_1	11
+#define DC_EVEN_UGDE0 		12
+#define DC_ODD_UGDE0 		13
+#define DC_EVEN_UGDE1 		14
+#define DC_ODD_UGDE1 		15
+#define DC_EVEN_UGDE2 		16
+#define DC_ODD_UGDE2 		17
+#define DC_EVEN_UGDE3 		18
+#define DC_ODD_UGDE3 		19
 
 /* Software reset for ipu */
 #define SW_IPU_RST	8
@@ -275,7 +283,7 @@ struct ipu_dc {
 	u32 di1_conf[2];
 	u32 dc_map_ptr[15];
 	u32 dc_map_val[12];
-	u32 udge[16];
+	u32 ugde[16];
 	u32 lla[2];
 	u32 r_lla[2];
 	u32 wr_ch_addr_5_alt;
@@ -392,6 +400,10 @@ static inline struct ipu_dc_ch *dc_ch_offset(int ch)
 #define DC_GEN			(&DC_REG->gen)
 #define DC_DISP_CONF2(disp)	(&DC_REG->disp_conf2[disp])
 #define DC_STAT			(&DC_REG->stat)
+#define DC_UGDE_0(evt)		(&DC_REG->ugde[0 + (4 * (evt))])
+#define DC_UGDE_1(evt)		(&DC_REG->ugde[1 + (4 * (evt))])
+#define DC_UGDE_2(evt)		(&DC_REG->ugde[2 + (4 * (evt))])
+#define DC_UGDE_3(evt)		(&DC_REG->ugde[3 + (4 * (evt))])
 
 #define DP_SYNC 0
 #define DP_ASYNC0 0x60
-- 
GitLab