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_probe_helper.h> | ||||
| 
 | ||||
| #include "sun4i_crtc.h" | ||||
| #include "sun4i_drv.h" | ||||
| #include "sun4i_tcon.h" | ||||
| #include "sun6i_mipi_dsi.h" | ||||
| 
 | ||||
| #include <video/mipi_display.h> | ||||
|  | @ -33,6 +35,8 @@ | |||
| #define SUN6I_DSI_CTL_EN			BIT(0) | ||||
| 
 | ||||
| #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_HSA_HSE_DIS		BIT(1) | ||||
| #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_SYNC_POINT		40 | ||||
| 
 | ||||
| enum sun6i_dsi_start_inst { | ||||
| 	DSI_START_LPRX, | ||||
| 	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); | ||||
| } | ||||
| 
 | ||||
| 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, | ||||
| 				  struct drm_display_mode *mode) | ||||
| { | ||||
| 	struct mipi_dsi_device *device = dsi->device; | ||||
| 	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 */ | ||||
| 		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, | ||||
| 				      struct drm_display_mode *mode) | ||||
| { | ||||
| 	struct mipi_dsi_device *device = dsi->device; | ||||
| 	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), | ||||
| 		     SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | | ||||
| 		     SUN6I_DSI_INST_LOOP_NUM_N1(delay)); | ||||
|  | @ -457,45 +531,59 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, | |||
| { | ||||
| 	struct mipi_dsi_device *device = dsi->device; | ||||
| 	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; | ||||
| 	u8 *buffer; | ||||
| 
 | ||||
| 	/* Do all timing calculations up front to allocate buffer space */ | ||||
| 
 | ||||
| 	if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { | ||||
| 		hblk = mode->hdisplay * Bpp; | ||||
| 		basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST | | ||||
| 			    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 | ||||
| 		 * 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 | ||||
| 		hsa = max((unsigned int)HSA_PACKET_OVERHEAD, | ||||
| 			  (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); | ||||
| 
 | ||||
| 		/*
 | ||||
| 	 * The backporch is set using a blanking packet (4 bytes + | ||||
| 	 * payload + 2 bytes). Its minimal size is therefore 6 bytes | ||||
| 		 * The backporch is set using a blanking packet (4 | ||||
| 		 * bytes + payload + 2 bytes). Its minimal size is | ||||
| 		 * therefore 6 bytes | ||||
| 		 */ | ||||
| #define HBP_PACKET_OVERHEAD	6 | ||||
| 		hbp = max((unsigned int)HBP_PACKET_OVERHEAD, | ||||
| 			  (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); | ||||
| 
 | ||||
| 		/*
 | ||||
| 	 * The frontporch is set using a blanking packet (4 bytes + | ||||
| 	 * payload + 2 bytes). Its minimal size is therefore 6 bytes | ||||
| 		 * The frontporch is set using a blanking packet (4 | ||||
| 		 * bytes + payload + 2 bytes). Its minimal size is | ||||
| 		 * therefore 6 bytes | ||||
| 		 */ | ||||
| #define HFP_PACKET_OVERHEAD	6 | ||||
| 		hfp = max((unsigned int)HFP_PACKET_OVERHEAD, | ||||
| 			  (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); | ||||
| 
 | ||||
| 		/*
 | ||||
| 	 * The blanking is set using a sync event (4 bytes) and a | ||||
| 	 * blanking packet (4 bytes + payload + 2 bytes). Its minimal | ||||
| 	 * size is therefore 10 bytes. | ||||
| 		 * The blanking is set using a sync event (4 bytes) | ||||
| 		 * and a blanking packet (4 bytes + payload + 2 | ||||
| 		 * bytes). Its minimal size is therefore 10 bytes. | ||||
| 		 */ | ||||
| #define HBLK_PACKET_OVERHEAD	10 | ||||
| 		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 | ||||
|  | @ -504,6 +592,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, | |||
| 		 * case) even with a 4 lanes screen seems to work... | ||||
| 		 */ | ||||
| 		vblk = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* How many bytes do we need to send all payloads? */ | ||||
| 	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)) | ||||
| 		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, | ||||
| 		     sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Konstantin Sudakov
						Konstantin Sudakov