forked from mirrors/linux
		
	drm/sun4i: dsi: Add burst support
The current driver doesn't support the DSI burst operation mode. Let's add the needed quirks to make it work. Signed-off-by: Konstantin Sudakov <k.sudakov@integrasources.com> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com> Link: https://patchwork.freedesktop.org/patch/msgid/1dcabf2b38d3f0d3387b1cf02575e3d14e3ecd4e.1549896081.git-series.maxime.ripard@bootlin.com
This commit is contained in:
		
							parent
							
								
									62e7511a4f
								
							
						
					
					
						commit
						1c1a7aa366
					
				
					 1 changed files with 125 additions and 36 deletions
				
			
		|  | @ -24,7 +24,9 @@ | ||||||
| #include <drm/drm_panel.h> | #include <drm/drm_panel.h> | ||||||
| #include <drm/drm_probe_helper.h> | #include <drm/drm_probe_helper.h> | ||||||
| 
 | 
 | ||||||
|  | #include "sun4i_crtc.h" | ||||||
| #include "sun4i_drv.h" | #include "sun4i_drv.h" | ||||||
|  | #include "sun4i_tcon.h" | ||||||
| #include "sun6i_mipi_dsi.h" | #include "sun6i_mipi_dsi.h" | ||||||
| 
 | 
 | ||||||
| #include <video/mipi_display.h> | #include <video/mipi_display.h> | ||||||
|  | @ -33,6 +35,8 @@ | ||||||
| #define SUN6I_DSI_CTL_EN			BIT(0) | #define SUN6I_DSI_CTL_EN			BIT(0) | ||||||
| 
 | 
 | ||||||
| #define SUN6I_DSI_BASIC_CTL_REG		0x00c | #define SUN6I_DSI_BASIC_CTL_REG		0x00c | ||||||
|  | #define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n)		(((n) & 0xf) << 4) | ||||||
|  | #define SUN6I_DSI_BASIC_CTL_TRAIL_FILL		BIT(3) | ||||||
| #define SUN6I_DSI_BASIC_CTL_HBP_DIS		BIT(2) | #define SUN6I_DSI_BASIC_CTL_HBP_DIS		BIT(2) | ||||||
| #define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS		BIT(1) | #define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS		BIT(1) | ||||||
| #define SUN6I_DSI_BASIC_CTL_VIDEO_BURST		BIT(0) | #define SUN6I_DSI_BASIC_CTL_VIDEO_BURST		BIT(0) | ||||||
|  | @ -153,6 +157,8 @@ | ||||||
| 
 | 
 | ||||||
| #define SUN6I_DSI_CMD_TX_REG(n)		(0x300 + (n) * 0x04) | #define SUN6I_DSI_CMD_TX_REG(n)		(0x300 + (n) * 0x04) | ||||||
| 
 | 
 | ||||||
|  | #define SUN6I_DSI_SYNC_POINT		40 | ||||||
|  | 
 | ||||||
| enum sun6i_dsi_start_inst { | enum sun6i_dsi_start_inst { | ||||||
| 	DSI_START_LPRX, | 	DSI_START_LPRX, | ||||||
| 	DSI_START_LPTX, | 	DSI_START_LPTX, | ||||||
|  | @ -367,13 +373,70 @@ static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi, | ||||||
| 	return max_t(u16, delay, 1); | 	return max_t(u16, delay, 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi, | ||||||
|  | 				  struct drm_display_mode *mode) | ||||||
|  | { | ||||||
|  | 	struct mipi_dsi_device *device = dsi->device; | ||||||
|  | 	unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; | ||||||
|  | 
 | ||||||
|  | 	return mode->htotal * Bpp / device->lanes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi, | ||||||
|  | 				   struct drm_display_mode *mode, | ||||||
|  | 				   u16 line_num, u16 edge1) | ||||||
|  | { | ||||||
|  | 	u16 edge0 = edge1; | ||||||
|  | 
 | ||||||
|  | 	edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8; | ||||||
|  | 
 | ||||||
|  | 	if (edge0 > line_num) | ||||||
|  | 		return edge0 - line_num; | ||||||
|  | 
 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi, | ||||||
|  | 				   struct drm_display_mode *mode, | ||||||
|  | 				   u16 line_num) | ||||||
|  | { | ||||||
|  | 	struct mipi_dsi_device *device = dsi->device; | ||||||
|  | 	unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; | ||||||
|  | 	unsigned int hbp = mode->htotal - mode->hsync_end; | ||||||
|  | 	u16 edge1; | ||||||
|  | 
 | ||||||
|  | 	edge1 = SUN6I_DSI_SYNC_POINT; | ||||||
|  | 	edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes; | ||||||
|  | 
 | ||||||
|  | 	if (edge1 > line_num) | ||||||
|  | 		return line_num; | ||||||
|  | 
 | ||||||
|  | 	return edge1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi, | static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi, | ||||||
| 				  struct drm_display_mode *mode) | 				  struct drm_display_mode *mode) | ||||||
| { | { | ||||||
| 	struct mipi_dsi_device *device = dsi->device; | 	struct mipi_dsi_device *device = dsi->device; | ||||||
| 	u32 val = 0; | 	u32 val = 0; | ||||||
| 
 | 
 | ||||||
| 	if ((mode->hsync_end - mode->hdisplay) > 20) { | 	if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { | ||||||
|  | 		u16 line_num = sun6i_dsi_get_line_num(dsi, mode); | ||||||
|  | 		u16 edge0, edge1; | ||||||
|  | 
 | ||||||
|  | 		edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num); | ||||||
|  | 		edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1); | ||||||
|  | 
 | ||||||
|  | 		regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG, | ||||||
|  | 			     SUN6I_DSI_BURST_DRQ_EDGE0(edge0) | | ||||||
|  | 			     SUN6I_DSI_BURST_DRQ_EDGE1(edge1)); | ||||||
|  | 
 | ||||||
|  | 		regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG, | ||||||
|  | 			     SUN6I_DSI_BURST_LINE_NUM(line_num) | | ||||||
|  | 			     SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT)); | ||||||
|  | 
 | ||||||
|  | 		val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE; | ||||||
|  | 	} else if ((mode->hsync_end - mode->hdisplay) > 20) { | ||||||
| 		/* Maaaaaagic */ | 		/* Maaaaaagic */ | ||||||
| 		u16 drq = (mode->hsync_end - mode->hdisplay) - 20; | 		u16 drq = (mode->hsync_end - mode->hdisplay) - 20; | ||||||
| 
 | 
 | ||||||
|  | @ -390,8 +453,19 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi, | ||||||
| static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi, | static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi, | ||||||
| 				      struct drm_display_mode *mode) | 				      struct drm_display_mode *mode) | ||||||
| { | { | ||||||
|  | 	struct mipi_dsi_device *device = dsi->device; | ||||||
| 	u16 delay = 50 - 1; | 	u16 delay = 50 - 1; | ||||||
| 
 | 
 | ||||||
|  | 	if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { | ||||||
|  | 		delay = (mode->htotal - mode->hdisplay) * 150; | ||||||
|  | 		delay /= (mode->clock / 1000) * 8; | ||||||
|  | 		delay -= 50; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG, | ||||||
|  | 		     2 << (4 * DSI_INST_ID_LP11) | | ||||||
|  | 		     3 << (4 * DSI_INST_ID_DLY)); | ||||||
|  | 
 | ||||||
| 	regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0), | 	regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0), | ||||||
| 		     SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | | 		     SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | | ||||||
| 		     SUN6I_DSI_INST_LOOP_NUM_N1(delay)); | 		     SUN6I_DSI_INST_LOOP_NUM_N1(delay)); | ||||||
|  | @ -457,53 +531,68 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, | ||||||
| { | { | ||||||
| 	struct mipi_dsi_device *device = dsi->device; | 	struct mipi_dsi_device *device = dsi->device; | ||||||
| 	unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; | 	unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; | ||||||
| 	u16 hbp, hfp, hsa, hblk, vblk; | 	u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0; | ||||||
|  | 	u32 basic_ctl = 0; | ||||||
| 	size_t bytes; | 	size_t bytes; | ||||||
| 	u8 *buffer; | 	u8 *buffer; | ||||||
| 
 | 
 | ||||||
| 	/* Do all timing calculations up front to allocate buffer space */ | 	/* Do all timing calculations up front to allocate buffer space */ | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { | ||||||
| 	 * A sync period is composed of a blanking packet (4 bytes + | 		hblk = mode->hdisplay * Bpp; | ||||||
| 	 * payload + 2 bytes) and a sync event packet (4 bytes). Its | 		basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST | | ||||||
| 	 * minimal size is therefore 10 bytes | 			    SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS | | ||||||
| 	 */ | 			    SUN6I_DSI_BASIC_CTL_HBP_DIS; | ||||||
|  | 
 | ||||||
|  | 		if (device->lanes == 4) | ||||||
|  | 			basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL | | ||||||
|  | 				     SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc); | ||||||
|  | 	} else { | ||||||
|  | 		/*
 | ||||||
|  | 		 * A sync period is composed of a blanking packet (4 | ||||||
|  | 		 * bytes + payload + 2 bytes) and a sync event packet | ||||||
|  | 		 * (4 bytes). Its minimal size is therefore 10 bytes | ||||||
|  | 		 */ | ||||||
| #define HSA_PACKET_OVERHEAD	10 | #define HSA_PACKET_OVERHEAD	10 | ||||||
| 	hsa = max((unsigned int)HSA_PACKET_OVERHEAD, | 		hsa = max((unsigned int)HSA_PACKET_OVERHEAD, | ||||||
| 		  (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); | 			  (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 		/*
 | ||||||
| 	 * The backporch is set using a blanking packet (4 bytes + | 		 * The backporch is set using a blanking packet (4 | ||||||
| 	 * payload + 2 bytes). Its minimal size is therefore 6 bytes | 		 * bytes + payload + 2 bytes). Its minimal size is | ||||||
| 	 */ | 		 * therefore 6 bytes | ||||||
|  | 		 */ | ||||||
| #define HBP_PACKET_OVERHEAD	6 | #define HBP_PACKET_OVERHEAD	6 | ||||||
| 	hbp = max((unsigned int)HBP_PACKET_OVERHEAD, | 		hbp = max((unsigned int)HBP_PACKET_OVERHEAD, | ||||||
| 		  (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); | 			  (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 		/*
 | ||||||
| 	 * The frontporch is set using a blanking packet (4 bytes + | 		 * The frontporch is set using a blanking packet (4 | ||||||
| 	 * payload + 2 bytes). Its minimal size is therefore 6 bytes | 		 * bytes + payload + 2 bytes). Its minimal size is | ||||||
| 	 */ | 		 * therefore 6 bytes | ||||||
|  | 		 */ | ||||||
| #define HFP_PACKET_OVERHEAD	6 | #define HFP_PACKET_OVERHEAD	6 | ||||||
| 	hfp = max((unsigned int)HFP_PACKET_OVERHEAD, | 		hfp = max((unsigned int)HFP_PACKET_OVERHEAD, | ||||||
| 		  (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); | 			  (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 		/*
 | ||||||
| 	 * The blanking is set using a sync event (4 bytes) and a | 		 * The blanking is set using a sync event (4 bytes) | ||||||
| 	 * blanking packet (4 bytes + payload + 2 bytes). Its minimal | 		 * and a blanking packet (4 bytes + payload + 2 | ||||||
| 	 * size is therefore 10 bytes. | 		 * bytes). Its minimal size is therefore 10 bytes. | ||||||
| 	 */ | 		 */ | ||||||
| #define HBLK_PACKET_OVERHEAD	10 | #define HBLK_PACKET_OVERHEAD	10 | ||||||
| 	hblk = max((unsigned int)HBLK_PACKET_OVERHEAD, | 		hblk = max((unsigned int)HBLK_PACKET_OVERHEAD, | ||||||
| 		   (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp - HBLK_PACKET_OVERHEAD); | 			   (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp - | ||||||
|  | 			   HBLK_PACKET_OVERHEAD); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 		/*
 | ||||||
| 	 * And I'm not entirely sure what vblk is about. The driver in | 		 * And I'm not entirely sure what vblk is about. The driver in | ||||||
| 	 * Allwinner BSP is using a rather convoluted calculation | 		 * Allwinner BSP is using a rather convoluted calculation | ||||||
| 	 * there only for 4 lanes. However, using 0 (the !4 lanes | 		 * there only for 4 lanes. However, using 0 (the !4 lanes | ||||||
| 	 * case) even with a 4 lanes screen seems to work... | 		 * case) even with a 4 lanes screen seems to work... | ||||||
| 	 */ | 		 */ | ||||||
| 	vblk = 0; | 		vblk = 0; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/* How many bytes do we need to send all payloads? */ | 	/* How many bytes do we need to send all payloads? */ | ||||||
| 	bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk); | 	bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk); | ||||||
|  | @ -511,7 +600,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, | ||||||
| 	if (WARN_ON(!buffer)) | 	if (WARN_ON(!buffer)) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0); | 	regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl); | ||||||
| 
 | 
 | ||||||
| 	regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG, | 	regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG, | ||||||
| 		     sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START, | 		     sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Konstantin Sudakov
						Konstantin Sudakov