mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	Fix RPI5 support
This commit is contained in:
		
							parent
							
								
									57beb2ec60
								
							
						
					
					
						commit
						3743692973
					
				
					 1017 changed files with 232368 additions and 4124 deletions
				
			
		|  | @ -0,0 +1,69 @@ | |||
| From 2b0d70ec4fba339947992252c949c8cbd9be04af Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Tue, 10 Oct 2023 14:49:50 +0100 | ||||
| Subject: [PATCH] Revert "Revert "xhci: add quirk for host controllers that | ||||
|  don't update endpoint DCS"" | ||||
| 
 | ||||
| This reverts commit 96a0b80eb1b02e1330d525d4c866ccdfa8c67434. | ||||
| ---
 | ||||
|  drivers/usb/host/xhci-pci.c  |  4 +++- | ||||
|  drivers/usb/host/xhci-ring.c | 25 ++++++++++++++++++++++++- | ||||
|  2 files changed, 27 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/usb/host/xhci-pci.c
 | ||||
| +++ b/drivers/usb/host/xhci-pci.c
 | ||||
| @@ -293,8 +293,10 @@ static void xhci_pci_quirks(struct devic
 | ||||
|  			pdev->device == 0x3432) | ||||
|  		xhci->quirks |= XHCI_BROKEN_STREAMS; | ||||
|   | ||||
| -	if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483)
 | ||||
| +	if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
 | ||||
|  		xhci->quirks |= XHCI_LPM_SUPPORT; | ||||
| +		xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
 | ||||
| +	}
 | ||||
|   | ||||
|  	if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && | ||||
|  		pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) { | ||||
| --- a/drivers/usb/host/xhci-ring.c
 | ||||
| +++ b/drivers/usb/host/xhci-ring.c
 | ||||
| @@ -592,8 +592,11 @@ static int xhci_move_dequeue_past_td(str
 | ||||
|  	struct xhci_ring *ep_ring; | ||||
|  	struct xhci_command *cmd; | ||||
|  	struct xhci_segment *new_seg; | ||||
| +	struct xhci_segment *halted_seg = NULL;
 | ||||
|  	union xhci_trb *new_deq; | ||||
|  	int new_cycle; | ||||
| +	union xhci_trb *halted_trb;
 | ||||
| +	int index = 0;
 | ||||
|  	dma_addr_t addr; | ||||
|  	u64 hw_dequeue; | ||||
|  	bool cycle_found = false; | ||||
| @@ -631,7 +634,27 @@ static int xhci_move_dequeue_past_td(str
 | ||||
|  	hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id); | ||||
|  	new_seg = ep_ring->deq_seg; | ||||
|  	new_deq = ep_ring->dequeue; | ||||
| -	new_cycle = hw_dequeue & 0x1;
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * Quirk: xHC write-back of the DCS field in the hardware dequeue
 | ||||
| +	 * pointer is wrong - use the cycle state of the TRB pointed to by
 | ||||
| +	 * the dequeue pointer.
 | ||||
| +	 */
 | ||||
| +	if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS &&
 | ||||
| +	    !(ep->ep_state & EP_HAS_STREAMS))
 | ||||
| +		halted_seg = trb_in_td(xhci, td->start_seg,
 | ||||
| +				       td->first_trb, td->last_trb,
 | ||||
| +				       hw_dequeue & ~0xf, false);
 | ||||
| +	if (halted_seg) {
 | ||||
| +		index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
 | ||||
| +			 sizeof(*halted_trb);
 | ||||
| +		halted_trb = &halted_seg->trbs[index];
 | ||||
| +		new_cycle = halted_trb->generic.field[3] & 0x1;
 | ||||
| +		xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
 | ||||
| +			 (u8)(hw_dequeue & 0x1), index, new_cycle);
 | ||||
| +	} else {
 | ||||
| +		new_cycle = hw_dequeue & 0x1;
 | ||||
| +	}
 | ||||
|   | ||||
|  	/* | ||||
|  	 * We want to find the pointer, segment and cycle state of the new trb | ||||
|  | @ -0,0 +1,32 @@ | |||
| From d290d16da3157fe9fa6fddff6153fd533109a3f3 Mon Sep 17 00:00:00 2001 | ||||
| From: Joerg Quinten <aBUGSworstnightmare@gmail.com> | ||||
| Date: Fri, 18 Jun 2021 13:02:29 +0200 | ||||
| Subject: [PATCH] Support RPi DPI interface in mode6 for 18-bit color | ||||
| 
 | ||||
| A matching media bus format was added and an overlay for using it, | ||||
| both with FB and VC4 was added as well. | ||||
| 
 | ||||
| Signed-off-by: Joerg Quinten <aBUGSworstnightmare@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dpi.c | 6 ++++++ | ||||
|  1 file changed, 6 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| @@ -170,10 +170,16 @@ static void vc4_dpi_encoder_enable(struc
 | ||||
|  				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, | ||||
|  						       DPI_ORDER); | ||||
|  				break; | ||||
| +			case MEDIA_BUS_FMT_BGR666_1X24_CPADHI:
 | ||||
| +				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
 | ||||
| +				fallthrough;
 | ||||
|  			case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: | ||||
|  				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2, | ||||
|  						       DPI_FORMAT); | ||||
|  				break; | ||||
| +			case MEDIA_BUS_FMT_BGR666_1X18:
 | ||||
| +				dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
 | ||||
| +				fallthrough;
 | ||||
|  			case MEDIA_BUS_FMT_RGB666_1X18: | ||||
|  				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1, | ||||
|  						       DPI_FORMAT); | ||||
|  | @ -0,0 +1,26 @@ | |||
| From 378c0fb11b0a3b2fd873728379fc276f20564770 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Tue, 19 May 2020 16:20:30 +0100 | ||||
| Subject: [PATCH] drm/vc4: Add FKMS as an acceptable node for dma ranges. | ||||
| 
 | ||||
| Under FKMS, the firmware (via FKMS) also requires the VideoCore cache | ||||
| aliases for image planes, as defined by the dma-ranges under /soc. | ||||
| 
 | ||||
| Add rpi-firmware-kms to the list of acceptable nodes to look for | ||||
| to copy dma config from. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_drv.c | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.c
 | ||||
| @@ -276,6 +276,7 @@ static void vc4_component_unbind_all(voi
 | ||||
|  static const struct of_device_id vc4_dma_range_matches[] = { | ||||
|  	{ .compatible = "brcm,bcm2711-hvs" }, | ||||
|  	{ .compatible = "brcm,bcm2835-hvs" }, | ||||
| +	{ .compatible = "raspberrypi,rpi-firmware-kms" },
 | ||||
|  	{ .compatible = "brcm,bcm2835-v3d" }, | ||||
|  	{ .compatible = "brcm,cygnus-v3d" }, | ||||
|  	{ .compatible = "brcm,vc4-v3d" }, | ||||
|  | @ -0,0 +1,24 @@ | |||
| From fdea56559f905b8397630fedd72833ee80e8503f Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 26 Oct 2020 12:38:27 +0000 | ||||
| Subject: [PATCH] drm/vc4: Add the 2711 HVS as a suitable DMA node | ||||
| 
 | ||||
| With vc4-drv node not being under /soc on Pi4, we need to | ||||
| adopt the correct DMA parameters from a suitable sub-component. | ||||
| Add "brcm,bcm2711-hvs" to that list of components. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_drv.c | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.c
 | ||||
| @@ -276,6 +276,7 @@ static void vc4_component_unbind_all(voi
 | ||||
|  static const struct of_device_id vc4_dma_range_matches[] = { | ||||
|  	{ .compatible = "brcm,bcm2711-hvs" }, | ||||
|  	{ .compatible = "brcm,bcm2835-hvs" }, | ||||
| +	{ .compatible = "brcm,bcm2711-hvs" },
 | ||||
|  	{ .compatible = "raspberrypi,rpi-firmware-kms" }, | ||||
|  	{ .compatible = "brcm,bcm2835-v3d" }, | ||||
|  	{ .compatible = "brcm,cygnus-v3d" }, | ||||
|  | @ -0,0 +1,34 @@ | |||
| From 5ed2ba8530cfb805fa494f4c8a8577e3239e9198 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 12 Feb 2021 17:31:37 +0000 | ||||
| Subject: [PATCH] drm/vc4: Change the default DPI format to being 18bpp, not | ||||
|  24. | ||||
| 
 | ||||
| DPI hasn't really been used up until now, so the default has | ||||
| been meaningless. | ||||
| In theory we should be able to pass the desired format for the | ||||
| adjacent bridge chip through, but framework seems to be missing | ||||
| for that. | ||||
| 
 | ||||
| As the main device to use DPI is the VGA666 or Adafruit Kippah, | ||||
| both of which use RGB666, change the default to being RGB666 instead | ||||
| of RGB888. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dpi.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| @@ -150,8 +150,8 @@ static void vc4_dpi_encoder_enable(struc
 | ||||
|  	} | ||||
|  	drm_connector_list_iter_end(&conn_iter); | ||||
|   | ||||
| -	/* Default to 24bit if no connector or format found. */
 | ||||
| -	dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, DPI_FORMAT);
 | ||||
| +	/* Default to 18bit if no connector or format found. */
 | ||||
| +	dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1, DPI_FORMAT);
 | ||||
|   | ||||
|  	if (connector) { | ||||
|  		if (connector->display_info.num_bus_formats) { | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 72b0f65ba954201286e99cf89583cbb0cfa2ecc6 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 7 Jan 2021 16:30:55 +0000 | ||||
| Subject: [PATCH] drm/atomic: Don't fixup modes that haven't been reset | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/drm_atomic_helper.c | 5 +++++ | ||||
|  1 file changed, 5 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/drm_atomic_helper.c
 | ||||
| +++ b/drivers/gpu/drm/drm_atomic_helper.c
 | ||||
| @@ -443,6 +443,11 @@ mode_fixup(struct drm_atomic_state *stat
 | ||||
|  		new_crtc_state = | ||||
|  			drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); | ||||
|   | ||||
| +		if (!new_crtc_state->mode_changed &&
 | ||||
| +		    !new_crtc_state->connectors_changed) {
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		/* | ||||
|  		 * Each encoder has at most one connector (since we always steal | ||||
|  		 * it away), so we won't call ->mode_fixup twice. | ||||
|  | @ -0,0 +1,127 @@ | |||
| From 30a8ce6736c11903c205b667d61e059b1510913f Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:07:30 +0200 | ||||
| Subject: [PATCH] drm/vc4: Fix timings for VEC modes | ||||
| 
 | ||||
| This commit fixes vertical timings of the VEC (composite output) modes | ||||
| to accurately represent the 525-line ("NTSC") and 625-line ("PAL") ITU-R | ||||
| standards. | ||||
| 
 | ||||
| Previous timings were actually defined as 502 and 601 lines, resulting | ||||
| in non-standard 62.69 Hz and 52 Hz signals being generated, | ||||
| respectively. | ||||
| 
 | ||||
| Changes to vc4_crtc.c have also been made, to make the PixelValve | ||||
| vertical timings accurately correspond to the DRM modeline in interlaced | ||||
| modes. The resulting VERTA/VERTB register values have been verified | ||||
| against the reference values set by the Raspberry Pi firmware. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 70 +++++++++++++++++++++------------- | ||||
|  1 file changed, 43 insertions(+), 27 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -326,8 +326,14 @@ static void vc4_crtc_config_pv(struct dr
 | ||||
|  	bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || | ||||
|  		       vc4_encoder->type == VC4_ENCODER_TYPE_DSI1); | ||||
|  	bool is_dsi1 = vc4_encoder->type == VC4_ENCODER_TYPE_DSI1; | ||||
| +	bool is_vec = vc4_encoder->type == VC4_ENCODER_TYPE_VEC;
 | ||||
|  	u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24; | ||||
|  	u8 ppc = pv_data->pixels_per_clock; | ||||
| +
 | ||||
| +	u16 vert_bp = mode->crtc_vtotal - mode->crtc_vsync_end;
 | ||||
| +	u16 vert_sync = mode->crtc_vsync_end - mode->crtc_vsync_start;
 | ||||
| +	u16 vert_fp = mode->crtc_vsync_start - mode->crtc_vdisplay;
 | ||||
| +
 | ||||
|  	bool debug_dump_regs = false; | ||||
|  	int idx; | ||||
|   | ||||
| @@ -355,49 +361,59 @@ static void vc4_crtc_config_pv(struct dr
 | ||||
|  		   VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc, | ||||
|  				 PV_HORZB_HACTIVE)); | ||||
|   | ||||
| -	CRTC_WRITE(PV_VERTA,
 | ||||
| -		   VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
 | ||||
| -				 interlace,
 | ||||
| -				 PV_VERTA_VBP) |
 | ||||
| -		   VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
 | ||||
| -				 PV_VERTA_VSYNC));
 | ||||
| -	CRTC_WRITE(PV_VERTB,
 | ||||
| -		   VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
 | ||||
| -				 PV_VERTB_VFP) |
 | ||||
| -		   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
 | ||||
| -
 | ||||
|  	if (interlace) { | ||||
| +		bool odd_field_first = false;
 | ||||
| +		u32 field_delay = mode->htotal * pixel_rep / (2 * ppc);
 | ||||
| +		u16 vert_bp_even = vert_bp;
 | ||||
| +		u16 vert_fp_even = vert_fp;
 | ||||
| +
 | ||||
| +		if (is_vec) {
 | ||||
| +			/* VEC (composite output) */
 | ||||
| +			++field_delay;
 | ||||
| +			if (mode->htotal == 858) {
 | ||||
| +				/* 525-line mode (NTSC or PAL-M) */
 | ||||
| +				odd_field_first = true;
 | ||||
| +			}
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (odd_field_first)
 | ||||
| +			++vert_fp_even;
 | ||||
| +		else
 | ||||
| +			++vert_bp;
 | ||||
| +
 | ||||
|  		CRTC_WRITE(PV_VERTA_EVEN, | ||||
| -			   VC4_SET_FIELD(mode->crtc_vtotal -
 | ||||
| -					 mode->crtc_vsync_end,
 | ||||
| -					 PV_VERTA_VBP) |
 | ||||
| -			   VC4_SET_FIELD(mode->crtc_vsync_end -
 | ||||
| -					 mode->crtc_vsync_start,
 | ||||
| -					 PV_VERTA_VSYNC));
 | ||||
| +			   VC4_SET_FIELD(vert_bp_even, PV_VERTA_VBP) |
 | ||||
| +			   VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
 | ||||
|  		CRTC_WRITE(PV_VERTB_EVEN, | ||||
| -			   VC4_SET_FIELD(mode->crtc_vsync_start -
 | ||||
| -					 mode->crtc_vdisplay,
 | ||||
| -					 PV_VERTB_VFP) |
 | ||||
| +			   VC4_SET_FIELD(vert_fp_even, PV_VERTB_VFP) |
 | ||||
|  			   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE)); | ||||
|   | ||||
| -		/* We set up first field even mode for HDMI.  VEC's
 | ||||
| -		 * NTSC mode would want first field odd instead, once
 | ||||
| -		 * we support it (to do so, set ODD_FIRST and put the
 | ||||
| -		 * delay in VSYNCD_EVEN instead).
 | ||||
| +		/* We set up first field even mode for HDMI and VEC's PAL.
 | ||||
| +		 * For NTSC, we need first field odd.
 | ||||
|  		 */ | ||||
|  		CRTC_WRITE(PV_V_CONTROL, | ||||
|  			   PV_VCONTROL_CONTINUOUS | | ||||
|  			   (is_dsi ? PV_VCONTROL_DSI : 0) | | ||||
|  			   PV_VCONTROL_INTERLACE | | ||||
| -			   VC4_SET_FIELD(mode->htotal * pixel_rep / (2 * ppc),
 | ||||
| -					 PV_VCONTROL_ODD_DELAY));
 | ||||
| -		CRTC_WRITE(PV_VSYNCD_EVEN, 0);
 | ||||
| +			   (odd_field_first
 | ||||
| +				   ? PV_VCONTROL_ODD_FIRST
 | ||||
| +				   : VC4_SET_FIELD(field_delay,
 | ||||
| +						   PV_VCONTROL_ODD_DELAY)));
 | ||||
| +		CRTC_WRITE(PV_VSYNCD_EVEN,
 | ||||
| +			   (odd_field_first ? field_delay : 0));
 | ||||
|  	} else { | ||||
|  		CRTC_WRITE(PV_V_CONTROL, | ||||
|  			   PV_VCONTROL_CONTINUOUS | | ||||
|  			   (is_dsi ? PV_VCONTROL_DSI : 0)); | ||||
|  	} | ||||
|   | ||||
| +	CRTC_WRITE(PV_VERTA,
 | ||||
| +		   VC4_SET_FIELD(vert_bp, PV_VERTA_VBP) |
 | ||||
| +		   VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
 | ||||
| +	CRTC_WRITE(PV_VERTB,
 | ||||
| +		   VC4_SET_FIELD(vert_fp, PV_VERTB_VFP) |
 | ||||
| +		   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
 | ||||
| +
 | ||||
|  	if (is_dsi) | ||||
|  		CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); | ||||
|   | ||||
|  | @ -0,0 +1,77 @@ | |||
| From b42ffab72e99a43ca50c1cbbd981d8e60e5973a7 Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:07:53 +0200 | ||||
| Subject: [PATCH] drm/vc4: Fix definition of PAL-M mode | ||||
| 
 | ||||
| PAL-M is a Brazilian analog TV standard that uses a PAL-style chroma | ||||
| subcarrier at 3.575611[888111] MHz on top of 525-line (480i60) timings. | ||||
| This commit makes the driver actually use the proper VEC preset for this | ||||
| mode instead of just changing PAL subcarrier frequency. | ||||
| 
 | ||||
| DRM mode constant names have also been changed, as they no longer | ||||
| correspond to the "NTSC" or "PAL" terms. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c | 18 +++++++++--------- | ||||
|  1 file changed, 9 insertions(+), 9 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -69,6 +69,7 @@
 | ||||
|  #define VEC_CONFIG0_STD_MASK		GENMASK(1, 0) | ||||
|  #define VEC_CONFIG0_NTSC_STD		0 | ||||
|  #define VEC_CONFIG0_PAL_BDGHI_STD	1 | ||||
| +#define VEC_CONFIG0_PAL_M_STD		2
 | ||||
|  #define VEC_CONFIG0_PAL_N_STD		3 | ||||
|   | ||||
|  #define VEC_SCHPH			0x108 | ||||
| @@ -224,14 +225,14 @@ static const struct debugfs_reg32 vec_re
 | ||||
|  	VC4_REG32(VEC_DAC_MISC), | ||||
|  }; | ||||
|   | ||||
| -static const struct drm_display_mode ntsc_mode = {
 | ||||
| +static const struct drm_display_mode drm_mode_480i = {
 | ||||
|  	DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, | ||||
|  		 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, | ||||
|  		 480, 480 + 7, 480 + 7 + 6, 525, 0, | ||||
|  		 DRM_MODE_FLAG_INTERLACE) | ||||
|  }; | ||||
|   | ||||
| -static const struct drm_display_mode pal_mode = {
 | ||||
| +static const struct drm_display_mode drm_mode_576i = {
 | ||||
|  	DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, | ||||
|  		 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, | ||||
|  		 576, 576 + 4, 576 + 4 + 6, 625, 0, | ||||
| @@ -240,25 +241,24 @@ static const struct drm_display_mode pal
 | ||||
|   | ||||
|  static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { | ||||
|  	[VC4_VEC_TV_MODE_NTSC] = { | ||||
| -		.mode = &ntsc_mode,
 | ||||
| +		.mode = &drm_mode_480i,
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_NTSC_J] = { | ||||
| -		.mode = &ntsc_mode,
 | ||||
| +		.mode = &drm_mode_480i,
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL] = { | ||||
| -		.mode = &pal_mode,
 | ||||
| +		.mode = &drm_mode_576i,
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_BDGHI_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL_M] = { | ||||
| -		.mode = &pal_mode,
 | ||||
| -		.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
 | ||||
| -		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
 | ||||
| -		.custom_freq = 0x223b61d1,
 | ||||
| +		.mode = &drm_mode_480i,
 | ||||
| +		.config0 = VEC_CONFIG0_PAL_M_STD,
 | ||||
| +		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
|  | @ -0,0 +1,148 @@ | |||
| From 80848aa95fcdffe65cb430b362e3be1910c2a6c6 Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:07:58 +0200 | ||||
| Subject: [PATCH] drm/vc4: Add support for more analog TV standards | ||||
| 
 | ||||
| Add support for the following composite output modes (all of them are | ||||
| somewhat more obscure than the previously defined ones): | ||||
| 
 | ||||
| - NTSC_443 - NTSC-style signal with the chroma subcarrier shifted to
 | ||||
|   4.43361875 MHz (the PAL subcarrier frequency). Never used for | ||||
|   broadcasting, but sometimes used as a hack to play NTSC content in PAL | ||||
|   regions (e.g. on VCRs). | ||||
| - PAL_N - PAL with alternative chroma subcarrier frequency,
 | ||||
|   3.58205625 MHz. Used as a broadcast standard in Argentina, Paraguay | ||||
|   and Uruguay to fit 576i50 with colour in 6 MHz channel raster. | ||||
| - PAL60 - 480i60 signal with PAL-style color at normal European PAL
 | ||||
|   frequency. Another non-standard, non-broadcast mode, used in similar | ||||
|   contexts as NTSC_443. Some displays support one but not the other. | ||||
| - SECAM - French frequency-modulated analog color standard; also have
 | ||||
|   been broadcast in Eastern Europe and various parts of Africa and Asia. | ||||
|   Uses the same 576i50 timings as PAL. | ||||
| 
 | ||||
| Also added some comments explaining color subcarrier frequency | ||||
| registers. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c | 63 +++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 63 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -46,6 +46,7 @@
 | ||||
|  #define VEC_CONFIG0_YDEL(x)		((x) << 26) | ||||
|  #define VEC_CONFIG0_CDEL_MASK		GENMASK(25, 24) | ||||
|  #define VEC_CONFIG0_CDEL(x)		((x) << 24) | ||||
| +#define VEC_CONFIG0_SECAM_STD		BIT(21)
 | ||||
|  #define VEC_CONFIG0_PBPR_FIL		BIT(18) | ||||
|  #define VEC_CONFIG0_CHROMA_GAIN_MASK	GENMASK(17, 16) | ||||
|  #define VEC_CONFIG0_CHROMA_GAIN_UNITY	(0 << 16) | ||||
| @@ -76,6 +77,27 @@
 | ||||
|  #define VEC_SOFT_RESET			0x10c | ||||
|  #define VEC_CLMP0_START			0x144 | ||||
|  #define VEC_CLMP0_END			0x148 | ||||
| +
 | ||||
| +/*
 | ||||
| + * These set the color subcarrier frequency
 | ||||
| + * if VEC_CONFIG1_CUSTOM_FREQ is enabled.
 | ||||
| + *
 | ||||
| + * VEC_FREQ1_0 contains the most significant 16-bit half-word,
 | ||||
| + * VEC_FREQ3_2 contains the least significant 16-bit half-word.
 | ||||
| + * 0x80000000 seems to be equivalent to the pixel clock
 | ||||
| + * (which itself is the VEC clock divided by 8).
 | ||||
| + *
 | ||||
| + * Reference values (with the default pixel clock of 13.5 MHz):
 | ||||
| + *
 | ||||
| + * NTSC  (3579545.[45] Hz)     - 0x21F07C1F
 | ||||
| + * PAL   (4433618.75 Hz)       - 0x2A098ACB
 | ||||
| + * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3
 | ||||
| + * PAL-N (3582056.25 Hz)       - 0x21F69446
 | ||||
| + *
 | ||||
| + * NOTE: For SECAM, it is used as the Dr center frequency,
 | ||||
| + * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;
 | ||||
| + * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.
 | ||||
| + */
 | ||||
|  #define VEC_FREQ3_2			0x180 | ||||
|  #define VEC_FREQ1_0			0x184 | ||||
|   | ||||
| @@ -118,6 +140,14 @@
 | ||||
|   | ||||
|  #define VEC_INTERRUPT_CONTROL		0x190 | ||||
|  #define VEC_INTERRUPT_STATUS		0x194 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Db center frequency for SECAM; the clock for this is the same as for
 | ||||
| + * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.
 | ||||
| + *
 | ||||
| + * This is specified as 4250000 Hz, which corresponds to 0x284BDA13.
 | ||||
| + * That is also the default value, so no need to set it explicitly.
 | ||||
| + */
 | ||||
|  #define VEC_FCW_SECAM_B			0x198 | ||||
|  #define VEC_SECAM_GAIN_VAL		0x19c | ||||
|   | ||||
| @@ -187,8 +217,12 @@ encoder_to_vc4_vec(struct drm_encoder *e
 | ||||
|  enum vc4_vec_tv_mode_id { | ||||
|  	VC4_VEC_TV_MODE_NTSC, | ||||
|  	VC4_VEC_TV_MODE_NTSC_J, | ||||
| +	VC4_VEC_TV_MODE_NTSC_443,
 | ||||
|  	VC4_VEC_TV_MODE_PAL, | ||||
|  	VC4_VEC_TV_MODE_PAL_M, | ||||
| +	VC4_VEC_TV_MODE_PAL_N,
 | ||||
| +	VC4_VEC_TV_MODE_PAL60,
 | ||||
| +	VC4_VEC_TV_MODE_SECAM,
 | ||||
|  }; | ||||
|   | ||||
|  struct vc4_vec_tv_mode { | ||||
| @@ -250,6 +284,13 @@ static const struct vc4_vec_tv_mode vc4_
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
| +	[VC4_VEC_TV_MODE_NTSC_443] = {
 | ||||
| +		/* NTSC with PAL chroma frequency */
 | ||||
| +		.mode = &drm_mode_480i,
 | ||||
| +		.config0 = VEC_CONFIG0_NTSC_STD,
 | ||||
| +		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
 | ||||
| +		.custom_freq = 0x2a098acb,
 | ||||
| +	},
 | ||||
|  	[VC4_VEC_TV_MODE_PAL] = { | ||||
|  		.mode = &drm_mode_576i, | ||||
|  		.config0 = VEC_CONFIG0_PAL_BDGHI_STD, | ||||
| @@ -260,6 +301,24 @@ static const struct vc4_vec_tv_mode vc4_
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_M_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
| +	[VC4_VEC_TV_MODE_PAL_N] = {
 | ||||
| +		.mode = &drm_mode_576i,
 | ||||
| +		.config0 = VEC_CONFIG0_PAL_N_STD,
 | ||||
| +		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
 | ||||
| +	},
 | ||||
| +	[VC4_VEC_TV_MODE_PAL60] = {
 | ||||
| +		/* PAL-M with chroma frequency of regular PAL */
 | ||||
| +		.mode = &drm_mode_480i,
 | ||||
| +		.config0 = VEC_CONFIG0_PAL_M_STD,
 | ||||
| +		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
 | ||||
| +		.custom_freq = 0x2a098acb,
 | ||||
| +	},
 | ||||
| +	[VC4_VEC_TV_MODE_SECAM] = {
 | ||||
| +		.mode = &drm_mode_576i,
 | ||||
| +		.config0 = VEC_CONFIG0_SECAM_STD,
 | ||||
| +		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
 | ||||
| +		.custom_freq = 0x29c71c72,
 | ||||
| +	},
 | ||||
|  }; | ||||
|   | ||||
|  static enum drm_connector_status | ||||
| @@ -503,8 +562,12 @@ static const struct of_device_id vc4_vec
 | ||||
|  static const char * const tv_mode_names[] = { | ||||
|  	[VC4_VEC_TV_MODE_NTSC] = "NTSC", | ||||
|  	[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J", | ||||
| +	[VC4_VEC_TV_MODE_NTSC_443] = "NTSC-443",
 | ||||
|  	[VC4_VEC_TV_MODE_PAL] = "PAL", | ||||
|  	[VC4_VEC_TV_MODE_PAL_M] = "PAL-M", | ||||
| +	[VC4_VEC_TV_MODE_PAL_N] = "PAL-N",
 | ||||
| +	[VC4_VEC_TV_MODE_PAL60] = "PAL60",
 | ||||
| +	[VC4_VEC_TV_MODE_SECAM] = "SECAM",
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_vec_bind(struct device *dev, struct device *master, void *data) | ||||
|  | @ -0,0 +1,142 @@ | |||
| From 29dc851079145c632f1d1b0edcefa107bd98e982 Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:08:01 +0200 | ||||
| Subject: [PATCH] drm/vc4: Allow setting the TV norm via module parameter | ||||
| 
 | ||||
| Similar to the ch7006 and nouveau drivers, introduce a "tv_mode" module | ||||
| parameter that allow setting the TV norm by specifying vc4.tv_norm= on | ||||
| the kernel command line. | ||||
| 
 | ||||
| If that is not specified, try inferring one of the most popular norms | ||||
| (PAL or NTSC) from the video mode specified on the command line. On | ||||
| Raspberry Pis, this causes the most common cases of the sdtv_mode | ||||
| setting in config.txt to be respected. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c | 72 ++++++++++++++++++++++++++++------- | ||||
|  1 file changed, 58 insertions(+), 14 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -67,7 +67,7 @@
 | ||||
|  #define VEC_CONFIG0_YCDELAY		BIT(4) | ||||
|  #define VEC_CONFIG0_RAMPEN		BIT(2) | ||||
|  #define VEC_CONFIG0_YCDIS		BIT(2) | ||||
| -#define VEC_CONFIG0_STD_MASK		GENMASK(1, 0)
 | ||||
| +#define VEC_CONFIG0_STD_MASK		(VEC_CONFIG0_SECAM_STD | GENMASK(1, 0))
 | ||||
|  #define VEC_CONFIG0_NTSC_STD		0 | ||||
|  #define VEC_CONFIG0_PAL_BDGHI_STD	1 | ||||
|  #define VEC_CONFIG0_PAL_M_STD		2 | ||||
| @@ -186,6 +186,8 @@
 | ||||
|  #define VEC_DAC_MISC_DAC_RST_N		BIT(0) | ||||
|   | ||||
|   | ||||
| +static char *vc4_vec_tv_norm;
 | ||||
| +
 | ||||
|  struct vc4_vec_variant { | ||||
|  	u32 dac_config; | ||||
|  }; | ||||
| @@ -321,6 +323,44 @@ static const struct vc4_vec_tv_mode vc4_
 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| +static const char * const tv_mode_names[] = {
 | ||||
| +	[VC4_VEC_TV_MODE_NTSC] = "NTSC",
 | ||||
| +	[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
 | ||||
| +	[VC4_VEC_TV_MODE_NTSC_443] = "NTSC-443",
 | ||||
| +	[VC4_VEC_TV_MODE_PAL] = "PAL",
 | ||||
| +	[VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
 | ||||
| +	[VC4_VEC_TV_MODE_PAL_N] = "PAL-N",
 | ||||
| +	[VC4_VEC_TV_MODE_PAL60] = "PAL60",
 | ||||
| +	[VC4_VEC_TV_MODE_SECAM] = "SECAM",
 | ||||
| +};
 | ||||
| +
 | ||||
| +enum vc4_vec_tv_mode_id
 | ||||
| +vc4_vec_get_default_mode(struct drm_connector *connector)
 | ||||
| +{
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	if (vc4_vec_tv_norm) {
 | ||||
| +		for (i = 0; i < ARRAY_SIZE(tv_mode_names); i++)
 | ||||
| +			if (strcmp(vc4_vec_tv_norm, tv_mode_names[i]) == 0)
 | ||||
| +				return (enum vc4_vec_tv_mode_id) i;
 | ||||
| +	} else if (connector->cmdline_mode.specified &&
 | ||||
| +		   ((connector->cmdline_mode.refresh_specified &&
 | ||||
| +		     (connector->cmdline_mode.refresh == 25 ||
 | ||||
| +		      connector->cmdline_mode.refresh == 50)) ||
 | ||||
| +		    (!connector->cmdline_mode.refresh_specified &&
 | ||||
| +		     (connector->cmdline_mode.yres == 288 ||
 | ||||
| +		      connector->cmdline_mode.yres == 576)))) {
 | ||||
| +		/*
 | ||||
| +		 * no explicitly specified TV norm; use PAL if a mode that
 | ||||
| +		 * looks like PAL has been specified on the command line
 | ||||
| +		 */
 | ||||
| +		return VC4_VEC_TV_MODE_PAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* in all other cases, default to NTSC */
 | ||||
| +	return VC4_VEC_TV_MODE_NTSC;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static enum drm_connector_status | ||||
|  vc4_vec_connector_detect(struct drm_connector *connector, bool force) | ||||
|  { | ||||
| @@ -344,10 +384,18 @@ static int vc4_vec_connector_get_modes(s
 | ||||
|  	return 1; | ||||
|  } | ||||
|   | ||||
| +static void vc4_vec_connector_reset(struct drm_connector *connector)
 | ||||
| +{
 | ||||
| +	drm_atomic_helper_connector_reset(connector);
 | ||||
| +	/* preserve TV standard */
 | ||||
| +	if (connector->state)
 | ||||
| +		connector->state->tv.mode = vc4_vec_get_default_mode(connector);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static const struct drm_connector_funcs vc4_vec_connector_funcs = { | ||||
|  	.detect = vc4_vec_connector_detect, | ||||
|  	.fill_modes = drm_helper_probe_single_connector_modes, | ||||
| -	.reset = drm_atomic_helper_connector_reset,
 | ||||
| +	.reset = vc4_vec_connector_reset,
 | ||||
|  	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||||
|  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||||
|  }; | ||||
| @@ -372,7 +420,7 @@ static int vc4_vec_connector_init(struct
 | ||||
|   | ||||
|  	drm_object_attach_property(&connector->base, | ||||
|  				   dev->mode_config.tv_mode_property, | ||||
| -				   VC4_VEC_TV_MODE_NTSC);
 | ||||
| +				   vc4_vec_get_default_mode(connector));
 | ||||
|   | ||||
|  	drm_connector_attach_encoder(connector, &vec->encoder.base); | ||||
|   | ||||
| @@ -559,17 +607,6 @@ static const struct of_device_id vc4_vec
 | ||||
|  	{ /* sentinel */ }, | ||||
|  }; | ||||
|   | ||||
| -static const char * const tv_mode_names[] = {
 | ||||
| -	[VC4_VEC_TV_MODE_NTSC] = "NTSC",
 | ||||
| -	[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
 | ||||
| -	[VC4_VEC_TV_MODE_NTSC_443] = "NTSC-443",
 | ||||
| -	[VC4_VEC_TV_MODE_PAL] = "PAL",
 | ||||
| -	[VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
 | ||||
| -	[VC4_VEC_TV_MODE_PAL_N] = "PAL-N",
 | ||||
| -	[VC4_VEC_TV_MODE_PAL60] = "PAL60",
 | ||||
| -	[VC4_VEC_TV_MODE_SECAM] = "SECAM",
 | ||||
| -};
 | ||||
| -
 | ||||
|  static int vc4_vec_bind(struct device *dev, struct device *master, void *data) | ||||
|  { | ||||
|  	struct platform_device *pdev = to_platform_device(dev); | ||||
| @@ -650,3 +687,10 @@ struct platform_driver vc4_vec_driver =
 | ||||
|  		.of_match_table = vc4_vec_dt_match, | ||||
|  	}, | ||||
|  }; | ||||
| +
 | ||||
| +module_param_named(tv_norm, vc4_vec_tv_norm, charp, 0600);
 | ||||
| +MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
 | ||||
| +		 "\t\tSupported: NTSC, NTSC-J, NTSC-443, PAL, PAL-M, PAL-N,\n"
 | ||||
| +		 "\t\t\tPAL60, SECAM.\n"
 | ||||
| +		 "\t\tDefault: PAL if a 50 Hz mode has been set via video=,\n"
 | ||||
| +		 "\t\t\tNTSC otherwise");
 | ||||
|  | @ -0,0 +1,82 @@ | |||
| From 51db525c675adb41eeae0d23a8e13cbc7ce76284 Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:08:05 +0200 | ||||
| Subject: [PATCH] drm/vc4: Refactor mode checking logic | ||||
| 
 | ||||
| Replace drm_encoder_helper_funcs::atomic_check with | ||||
| drm_connector_helper_funcs::atomic_check - the former is not called | ||||
| during drm_mode_obj_set_property_ioctl(). Set crtc_state->mode_changed | ||||
| if TV norm changes even without explicit mode change. This makes things | ||||
| like "xrandr --output Composite-1 --set mode PAL-M" work properly. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c | 42 ++++++++++++++++++++++------------- | ||||
|  1 file changed, 26 insertions(+), 16 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -392,6 +392,31 @@ static void vc4_vec_connector_reset(stru
 | ||||
|  		connector->state->tv.mode = vc4_vec_get_default_mode(connector); | ||||
|  } | ||||
|   | ||||
| +static int vc4_vec_connector_atomic_check(struct drm_connector *conn,
 | ||||
| +					  struct drm_atomic_state *state)
 | ||||
| +{
 | ||||
| +	struct drm_connector_state *old_state =
 | ||||
| +		drm_atomic_get_old_connector_state(state, conn);
 | ||||
| +	struct drm_connector_state *new_state =
 | ||||
| +		drm_atomic_get_new_connector_state(state, conn);
 | ||||
| +
 | ||||
| +	const struct vc4_vec_tv_mode *vec_mode =
 | ||||
| +		&vc4_vec_tv_modes[new_state->tv.mode];
 | ||||
| +
 | ||||
| +	if (new_state->crtc) {
 | ||||
| +		struct drm_crtc_state *crtc_state =
 | ||||
| +			drm_atomic_get_new_crtc_state(state, new_state->crtc);
 | ||||
| +
 | ||||
| +		if (!drm_mode_equal(vec_mode->mode, &crtc_state->mode))
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		if (old_state->tv.mode != new_state->tv.mode)
 | ||||
| +			crtc_state->mode_changed = true;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static const struct drm_connector_funcs vc4_vec_connector_funcs = { | ||||
|  	.detect = vc4_vec_connector_detect, | ||||
|  	.fill_modes = drm_helper_probe_single_connector_modes, | ||||
| @@ -402,6 +427,7 @@ static const struct drm_connector_funcs
 | ||||
|   | ||||
|  static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = { | ||||
|  	.get_modes = vc4_vec_connector_get_modes, | ||||
| +	.atomic_check = vc4_vec_connector_atomic_check,
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) | ||||
| @@ -550,23 +576,7 @@ err_dev_exit:
 | ||||
|  	drm_dev_exit(idx); | ||||
|  } | ||||
|   | ||||
| -static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
 | ||||
| -					struct drm_crtc_state *crtc_state,
 | ||||
| -					struct drm_connector_state *conn_state)
 | ||||
| -{
 | ||||
| -	const struct vc4_vec_tv_mode *vec_mode;
 | ||||
| -
 | ||||
| -	vec_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
 | ||||
| -
 | ||||
| -	if (conn_state->crtc &&
 | ||||
| -	    !drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode))
 | ||||
| -		return -EINVAL;
 | ||||
| -
 | ||||
| -	return 0;
 | ||||
| -}
 | ||||
| -
 | ||||
|  static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { | ||||
| -	.atomic_check = vc4_vec_encoder_atomic_check,
 | ||||
|  	.atomic_disable = vc4_vec_encoder_disable, | ||||
|  	.atomic_enable = vc4_vec_encoder_enable, | ||||
|  }; | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,276 @@ | |||
| From ef1315fc9e665d4ca9c8743ac43c7849dcd07605 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Tue, 27 Apr 2021 14:24:21 +0200 | ||||
| Subject: [PATCH] drm/vc4: Add support for gamma on BCM2711 | ||||
| 
 | ||||
| BCM2711 changes from a 256 entry lookup table to a 16 point | ||||
| piecewise linear function as the pipeline bitdepth has increased | ||||
| to make a LUT unwieldy. | ||||
| 
 | ||||
| Implement a simple conversion from a 256 entry LUT that userspace | ||||
| is likely to expect to 16 evenly spread points in the PWL. This | ||||
| could be improved with curve fitting at a later date. | ||||
| 
 | ||||
| Co-developed-by: Juerg Haefliger <juergh@canonical.com> | ||||
| Signed-off-by: Juerg Haefliger <juergh@canonical.com> | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 35 ++++++++++--- | ||||
|  drivers/gpu/drm/vc4/vc4_drv.h  | 28 +++++++++-- | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c  | 89 ++++++++++++++++++++++++++++++++-- | ||||
|  drivers/gpu/drm/vc4/vc4_regs.h | 22 +++++++++ | ||||
|  4 files changed, 162 insertions(+), 12 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -1326,19 +1326,42 @@ int vc4_crtc_init(struct drm_device *drm
 | ||||
|   | ||||
|  	if (!vc4->is_vc5) { | ||||
|  		drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); | ||||
| +	} else {
 | ||||
| +		/* This is a lie for hvs5 which uses a 16 point PWL, but it
 | ||||
| +		 * allows for something smarter than just 16 linearly spaced
 | ||||
| +		 * segments. Conversion is done in vc5_hvs_update_gamma_lut.
 | ||||
| +		 */
 | ||||
| +		drm_mode_crtc_set_gamma_size(crtc, 256);
 | ||||
| +	}
 | ||||
|   | ||||
| -		drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
 | ||||
| +	drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
 | ||||
|   | ||||
| +	if (!vc4->is_vc5) {
 | ||||
|  		/* We support CTM, but only for one CRTC at a time. It's therefore | ||||
|  		 * implemented as private driver state in vc4_kms, not here. | ||||
|  		 */ | ||||
|  		drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); | ||||
| -	}
 | ||||
|   | ||||
| -	for (i = 0; i < crtc->gamma_size; i++) {
 | ||||
| -		vc4_crtc->lut_r[i] = i;
 | ||||
| -		vc4_crtc->lut_g[i] = i;
 | ||||
| -		vc4_crtc->lut_b[i] = i;
 | ||||
| +		/* Initialize the VC4 gamma LUTs */
 | ||||
| +		for (i = 0; i < crtc->gamma_size; i++) {
 | ||||
| +			vc4_crtc->lut_r[i] = i;
 | ||||
| +			vc4_crtc->lut_g[i] = i;
 | ||||
| +			vc4_crtc->lut_b[i] = i;
 | ||||
| +		}
 | ||||
| +	} else {
 | ||||
| +		/* Initialize the VC5 gamma PWL entries. Assume 12-bit pipeline,
 | ||||
| +		 * evenly spread over full range.
 | ||||
| +		 */
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) {
 | ||||
| +			vc4_crtc->pwl_r[i] =
 | ||||
| +				VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
 | ||||
| +			vc4_crtc->pwl_g[i] =
 | ||||
| +				VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
 | ||||
| +			vc4_crtc->pwl_b[i] =
 | ||||
| +				VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
 | ||||
| +			vc4_crtc->pwl_a[i] =
 | ||||
| +				VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
 | ||||
| +		}
 | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| @@ -20,6 +20,7 @@
 | ||||
|  #include <drm/drm_modeset_lock.h> | ||||
|   | ||||
|  #include "uapi/drm/vc4_drm.h" | ||||
| +#include "vc4_regs.h"
 | ||||
|   | ||||
|  struct drm_device; | ||||
|  struct drm_gem_object; | ||||
| @@ -481,6 +482,17 @@ struct vc4_pv_data {
 | ||||
|  	enum vc4_encoder_type encoder_types[4]; | ||||
|  }; | ||||
|   | ||||
| +struct vc5_gamma_entry {
 | ||||
| +	u32 x_c_terms;
 | ||||
| +	u32 grad_term;
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define VC5_HVS_SET_GAMMA_ENTRY(x, c, g) (struct vc5_gamma_entry){	\
 | ||||
| +	.x_c_terms = VC4_SET_FIELD((x), SCALER5_DSPGAMMA_OFF_X) | 	\
 | ||||
| +		     VC4_SET_FIELD((c), SCALER5_DSPGAMMA_OFF_C),	\
 | ||||
| +	.grad_term = (g)						\
 | ||||
| +}
 | ||||
| +
 | ||||
|  struct vc4_crtc { | ||||
|  	struct drm_crtc base; | ||||
|  	struct platform_device *pdev; | ||||
| @@ -490,9 +502,19 @@ struct vc4_crtc {
 | ||||
|  	/* Timestamp at start of vblank irq - unaffected by lock delays. */ | ||||
|  	ktime_t t_vblank; | ||||
|   | ||||
| -	u8 lut_r[256];
 | ||||
| -	u8 lut_g[256];
 | ||||
| -	u8 lut_b[256];
 | ||||
| +	union {
 | ||||
| +		struct {  /* VC4 gamma LUT */
 | ||||
| +			u8 lut_r[256];
 | ||||
| +			u8 lut_g[256];
 | ||||
| +			u8 lut_b[256];
 | ||||
| +		};
 | ||||
| +		struct {  /* VC5 gamma PWL entries */
 | ||||
| +			struct vc5_gamma_entry pwl_r[SCALER5_DSPGAMMA_NUM_POINTS];
 | ||||
| +			struct vc5_gamma_entry pwl_g[SCALER5_DSPGAMMA_NUM_POINTS];
 | ||||
| +			struct vc5_gamma_entry pwl_b[SCALER5_DSPGAMMA_NUM_POINTS];
 | ||||
| +			struct vc5_gamma_entry pwl_a[SCALER5_DSPGAMMA_NUM_POINTS];
 | ||||
| +		};
 | ||||
| +	};
 | ||||
|   | ||||
|  	struct drm_pending_vblank_event *event; | ||||
|   | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -241,7 +241,8 @@ static void vc4_hvs_lut_load(struct vc4_
 | ||||
|  static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, | ||||
|  				     struct vc4_crtc *vc4_crtc) | ||||
|  { | ||||
| -	struct drm_crtc_state *crtc_state = vc4_crtc->base.state;
 | ||||
| +	struct drm_crtc *crtc = &vc4_crtc->base;
 | ||||
| +	struct drm_crtc_state *crtc_state = crtc->state;
 | ||||
|  	struct drm_color_lut *lut = crtc_state->gamma_lut->data; | ||||
|  	u32 length = drm_color_lut_size(crtc_state->gamma_lut); | ||||
|  	u32 i; | ||||
| @@ -255,6 +256,81 @@ static void vc4_hvs_update_gamma_lut(str
 | ||||
|  	vc4_hvs_lut_load(hvs, vc4_crtc); | ||||
|  } | ||||
|   | ||||
| +static void vc5_hvs_write_gamma_entry(struct vc4_hvs *hvs,
 | ||||
| +				      u32 offset,
 | ||||
| +				      struct vc5_gamma_entry *gamma)
 | ||||
| +{
 | ||||
| +	HVS_WRITE(offset, gamma->x_c_terms);
 | ||||
| +	HVS_WRITE(offset + 4, gamma->grad_term);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void vc5_hvs_lut_load(struct vc4_hvs *hvs,
 | ||||
| +			     struct vc4_crtc *vc4_crtc)
 | ||||
| +{
 | ||||
| +	struct drm_crtc *crtc = &vc4_crtc->base;
 | ||||
| +	struct drm_crtc_state *crtc_state = crtc->state;
 | ||||
| +	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
 | ||||
| +	u32 i;
 | ||||
| +	u32 offset = SCALER5_DSPGAMMA_START +
 | ||||
| +		vc4_state->assigned_channel * SCALER5_DSPGAMMA_CHAN_OFFSET;
 | ||||
| +
 | ||||
| +	for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
 | ||||
| +		vc5_hvs_write_gamma_entry(hvs, offset, &vc4_crtc->pwl_r[i]);
 | ||||
| +	for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
 | ||||
| +		vc5_hvs_write_gamma_entry(hvs, offset, &vc4_crtc->pwl_g[i]);
 | ||||
| +	for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
 | ||||
| +		vc5_hvs_write_gamma_entry(hvs, offset, &vc4_crtc->pwl_b[i]);
 | ||||
| +
 | ||||
| +	if (vc4_state->assigned_channel == 2) {
 | ||||
| +		/* Alpha only valid on channel 2 */
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8)
 | ||||
| +			vc5_hvs_write_gamma_entry(hvs, offset, &vc4_crtc->pwl_a[i]);
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void vc5_hvs_update_gamma_lut(struct vc4_hvs *hvs,
 | ||||
| +				     struct vc4_crtc *vc4_crtc)
 | ||||
| +{
 | ||||
| +	struct drm_crtc *crtc = &vc4_crtc->base;
 | ||||
| +	struct drm_color_lut *lut = crtc->state->gamma_lut->data;
 | ||||
| +	unsigned int step, i;
 | ||||
| +	u32 start, end;
 | ||||
| +
 | ||||
| +#define VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl, chan)			\
 | ||||
| +	start = drm_color_lut_extract(lut[i * step].chan, 12);		\
 | ||||
| +	end = drm_color_lut_extract(lut[(i + 1) * step - 1].chan, 12);	\
 | ||||
| +									\
 | ||||
| +	/* Negative gradients not permitted by the hardware, so		\
 | ||||
| +	 * flatten such points out.					\
 | ||||
| +	 */								\
 | ||||
| +	if (end < start)						\
 | ||||
| +		end = start;						\
 | ||||
| +									\
 | ||||
| +	/* Assume 12bit pipeline.					\
 | ||||
| +	 * X evenly spread over full range (12 bit).			\
 | ||||
| +	 * C as U12.4 format.						\
 | ||||
| +	 * Gradient as U4.8 format.					\
 | ||||
| +	*/								\
 | ||||
| +	vc4_crtc->pwl[i] =						\
 | ||||
| +		VC5_HVS_SET_GAMMA_ENTRY(i << 8, start << 4,		\
 | ||||
| +				((end - start) << 4) / (step - 1))
 | ||||
| +
 | ||||
| +	/* HVS5 has a 16 point piecewise linear function for each colour
 | ||||
| +	 * channel (including alpha on channel 2) on each display channel.
 | ||||
| +	 *
 | ||||
| +	 * Currently take a crude subsample of the gamma LUT, but this could
 | ||||
| +	 * be improved to implement curve fitting.
 | ||||
| +	 */
 | ||||
| +	step = crtc->gamma_size / SCALER5_DSPGAMMA_NUM_POINTS;
 | ||||
| +	for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) {
 | ||||
| +		VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_r, red);
 | ||||
| +		VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_g, green);
 | ||||
| +		VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_b, blue);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	vc5_hvs_lut_load(hvs, vc4_crtc);
 | ||||
| +}
 | ||||
| +
 | ||||
|  u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) | ||||
|  { | ||||
|  	struct drm_device *drm = &hvs->vc4->base; | ||||
| @@ -398,7 +474,10 @@ static int vc4_hvs_init_channel(struct v
 | ||||
|  	/* Reload the LUT, since the SRAMs would have been disabled if | ||||
|  	 * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. | ||||
|  	 */ | ||||
| -	vc4_hvs_lut_load(hvs, vc4_crtc);
 | ||||
| +	if (!vc4->is_vc5)
 | ||||
| +		vc4_hvs_lut_load(hvs, vc4_crtc);
 | ||||
| +	else
 | ||||
| +		vc5_hvs_lut_load(hvs, vc4_crtc);
 | ||||
|   | ||||
|  	drm_dev_exit(idx); | ||||
|   | ||||
| @@ -628,7 +707,11 @@ void vc4_hvs_atomic_flush(struct drm_crt
 | ||||
|  		u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel)); | ||||
|   | ||||
|  		if (crtc->state->gamma_lut) { | ||||
| -			vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
 | ||||
| +			if (!vc4->is_vc5)
 | ||||
| +				vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
 | ||||
| +			else
 | ||||
| +				vc5_hvs_update_gamma_lut(hvs, vc4_crtc);
 | ||||
| +
 | ||||
|  			dispbkgndx |= SCALER_DISPBKGND_GAMMA; | ||||
|  		} else { | ||||
|  			/* Unsetting DISPBKGND_GAMMA skips the gamma lut step | ||||
| --- a/drivers/gpu/drm/vc4/vc4_regs.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_regs.h
 | ||||
| @@ -512,6 +512,28 @@
 | ||||
|  #define SCALER_DLIST_START                      0x00002000 | ||||
|  #define SCALER_DLIST_SIZE                       0x00004000 | ||||
|   | ||||
| +/* Gamma PWL for each channel. 16 points for each of 4 colour channels (alpha
 | ||||
| + * only on channel 2). 8 bytes per entry, offsets first, then gradient:
 | ||||
| + *   Y = GRAD * X + C
 | ||||
| + *
 | ||||
| + * Values for X and C are left justified, and vary depending on the width of
 | ||||
| + * the HVS channel:
 | ||||
| + *    8-bit pipeline: X uses [31:24], C is U8.8 format, and GRAD is U4.8.
 | ||||
| + *   12-bit pipeline: X uses [31:20], C is U12.4 format, and GRAD is U4.8.
 | ||||
| + *
 | ||||
| + * The 3 HVS channels start at 0x400 offsets (ie chan 1 starts at 0x2400, and
 | ||||
| + * chan 2 at 0x2800).
 | ||||
| + */
 | ||||
| +#define SCALER5_DSPGAMMA_NUM_POINTS		16
 | ||||
| +#define SCALER5_DSPGAMMA_START			0x00002000
 | ||||
| +#define SCALER5_DSPGAMMA_CHAN_OFFSET		0x400
 | ||||
| +# define SCALER5_DSPGAMMA_OFF_X_MASK		VC4_MASK(31, 20)
 | ||||
| +# define SCALER5_DSPGAMMA_OFF_X_SHIFT		20
 | ||||
| +# define SCALER5_DSPGAMMA_OFF_C_MASK		VC4_MASK(15, 0)
 | ||||
| +# define SCALER5_DSPGAMMA_OFF_C_SHIFT		0
 | ||||
| +# define SCALER5_DSPGAMMA_GRAD_MASK		VC4_MASK(11, 0)
 | ||||
| +# define SCALER5_DSPGAMMA_GRAD_SHIFT		0
 | ||||
| +
 | ||||
|  #define SCALER5_DLIST_START			0x00004000 | ||||
|   | ||||
|  # define VC4_HDMI_SW_RESET_FORMAT_DETECT	BIT(1) | ||||
|  | @ -0,0 +1,122 @@ | |||
| From 1c28783729a1b8f68d26950874a92538beafa04f Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 28 Apr 2021 12:32:10 +0200 | ||||
| Subject: [PATCH] drm/vc4: Add debugfs node that dumps the vc5 gamma PWL | ||||
|  entries | ||||
| 
 | ||||
| This helps with debugging the conversion from a 256 point gamma LUT to | ||||
| 16 point PWL entries as used by the BCM2711. | ||||
| 
 | ||||
| Co-developed-by: Juerg Haefliger <juergh@canonical.com> | ||||
| Signed-off-by: Juerg Haefliger <juergh@canonical.com> | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 85 ++++++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 84 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -141,6 +141,85 @@ static int vc4_hvs_debugfs_dlist(struct
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data)
 | ||||
| +{
 | ||||
| +	struct drm_info_node *node = m->private;
 | ||||
| +	struct drm_device *dev = node->minor->dev;
 | ||||
| +	struct vc4_dev *vc4 = to_vc4_dev(dev);
 | ||||
| +	struct vc4_hvs *hvs = vc4->hvs;
 | ||||
| +	struct drm_printer p = drm_seq_file_printer(m);
 | ||||
| +	unsigned int i, chan;
 | ||||
| +	u32 dispstat, dispbkgndx;
 | ||||
| +
 | ||||
| +	for (chan = 0; chan < SCALER_CHANNELS_COUNT; chan++) {
 | ||||
| +		u32 x_c, grad;
 | ||||
| +		u32 offset = SCALER5_DSPGAMMA_START +
 | ||||
| +			chan * SCALER5_DSPGAMMA_CHAN_OFFSET;
 | ||||
| +
 | ||||
| +		dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
 | ||||
| +					 SCALER_DISPSTATX_MODE);
 | ||||
| +		if (dispstat == SCALER_DISPSTATX_MODE_DISABLED ||
 | ||||
| +		    dispstat == SCALER_DISPSTATX_MODE_EOF) {
 | ||||
| +			drm_printf(&p, "HVS channel %u: Channel disabled\n", chan);
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
 | ||||
| +		if (!(dispbkgndx & SCALER_DISPBKGND_GAMMA)) {
 | ||||
| +			drm_printf(&p, "HVS channel %u: Gamma disabled\n", chan);
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		drm_printf(&p, "HVS channel %u:\n", chan);
 | ||||
| +		drm_printf(&p, "  red:\n");
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
 | ||||
| +			x_c = HVS_READ(offset);
 | ||||
| +			grad = HVS_READ(offset + 4);
 | ||||
| +			drm_printf(&p, "  %08x %08x - x %u, c %u, grad %u\n",
 | ||||
| +				   x_c, grad,
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
 | ||||
| +				   grad);
 | ||||
| +		}
 | ||||
| +		drm_printf(&p, "  green:\n");
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
 | ||||
| +			x_c = HVS_READ(offset);
 | ||||
| +			grad = HVS_READ(offset + 4);
 | ||||
| +			drm_printf(&p, "  %08x %08x - x %u, c %u, grad %u\n",
 | ||||
| +				   x_c, grad,
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
 | ||||
| +				   grad);
 | ||||
| +		}
 | ||||
| +		drm_printf(&p, "  blue:\n");
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
 | ||||
| +			x_c = HVS_READ(offset);
 | ||||
| +			grad = HVS_READ(offset + 4);
 | ||||
| +			drm_printf(&p, "  %08x %08x - x %u, c %u, grad %u\n",
 | ||||
| +				   x_c, grad,
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
 | ||||
| +				   grad);
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* Alpha only valid on channel 2 */
 | ||||
| +		if (chan != 2)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		drm_printf(&p, "  alpha:\n");
 | ||||
| +		for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) {
 | ||||
| +			x_c = HVS_READ(offset);
 | ||||
| +			grad = HVS_READ(offset + 4);
 | ||||
| +			drm_printf(&p, "  %08x %08x - x %u, c %u, grad %u\n",
 | ||||
| +				   x_c, grad,
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X),
 | ||||
| +				   VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C),
 | ||||
| +				   grad);
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /* The filter kernel is composed of dwords each containing 3 9-bit | ||||
|   * signed integers packed next to each other. | ||||
|   */ | ||||
| @@ -833,11 +912,15 @@ int vc4_hvs_debugfs_init(struct drm_mino
 | ||||
|  	if (!vc4->hvs) | ||||
|  		return -ENODEV; | ||||
|   | ||||
| -	if (!vc4->is_vc5)
 | ||||
| +	if (!vc4->is_vc5) {
 | ||||
|  		debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR, | ||||
|  				    minor->debugfs_root, | ||||
|  				    &vc4->load_tracker_enabled); | ||||
|   | ||||
| +		vc4_debugfs_add_file(minor, "hvs_gamma", vc5_hvs_debugfs_gamma,
 | ||||
| +				     NULL);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ret = vc4_debugfs_add_file(minor, "hvs_dlists", | ||||
|  				   vc4_hvs_debugfs_dlist, NULL); | ||||
|  	if (ret) | ||||
|  | @ -0,0 +1,105 @@ | |||
| From 3d1acc837abb35e453ca1fe88222dad8e8307f76 Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 14 Jun 2021 15:28:30 +0200 | ||||
| Subject: [PATCH] drm/vc4: hvs: Force modeset on gamma lut change | ||||
| 
 | ||||
| The HVS Gamma block can only be updated when idle, so we need to disable | ||||
| the HVS channel when the gamma property is set in an atomic commit. | ||||
| 
 | ||||
| Since the pixelvalve cannot have its assigned channel halted without | ||||
| stalling unless it's disabled as well, in our case that means forcing a | ||||
| full disable / enable cycle on the pipeline. | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 17 +++++++++++++++++ | ||||
|  drivers/gpu/drm/vc4/vc4_drv.h  |  3 +++ | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c  | 32 +++++++++++++++++++++++++++++++- | ||||
|  3 files changed, 51 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -293,6 +293,23 @@ struct drm_encoder *vc4_get_crtc_encoder
 | ||||
|  	return NULL; | ||||
|  } | ||||
|   | ||||
| +#define drm_for_each_connector_mask(connector, dev, connector_mask) \
 | ||||
| +	list_for_each_entry((connector), &(dev)->mode_config.connector_list, head) \
 | ||||
| +		for_each_if ((connector_mask) & drm_connector_mask(connector))
 | ||||
| +
 | ||||
| +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc,
 | ||||
| +					     struct drm_crtc_state *state)
 | ||||
| +{
 | ||||
| +	struct drm_connector *connector;
 | ||||
| +
 | ||||
| +	WARN_ON(hweight32(state->connector_mask) > 1);
 | ||||
| +
 | ||||
| +	drm_for_each_connector_mask(connector, crtc->dev, state->connector_mask)
 | ||||
| +		return connector;
 | ||||
| +
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc) | ||||
|  { | ||||
|  	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| @@ -568,6 +568,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4
 | ||||
|  	return container_of(data, struct vc4_pv_data, base); | ||||
|  } | ||||
|   | ||||
| +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc,
 | ||||
| +					     struct drm_crtc_state *state);
 | ||||
| +
 | ||||
|  struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, | ||||
|  					 struct drm_crtc_state *state); | ||||
|   | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -594,6 +594,36 @@ out:
 | ||||
|  	drm_dev_exit(idx); | ||||
|  } | ||||
|   | ||||
| +static int vc4_hvs_gamma_check(struct drm_crtc *crtc,
 | ||||
| +			       struct drm_atomic_state *state)
 | ||||
| +{
 | ||||
| +	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
 | ||||
| +	struct drm_connector_state *conn_state;
 | ||||
| +	struct drm_connector *connector;
 | ||||
| +	struct drm_device *dev = crtc->dev;
 | ||||
| +	struct vc4_dev *vc4 = to_vc4_dev(dev);
 | ||||
| +
 | ||||
| +	if (!vc4->is_vc5)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	if (!crtc_state->color_mgmt_changed)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	connector = vc4_get_crtc_connector(crtc, crtc_state);
 | ||||
| +	if (!connector)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	conn_state = drm_atomic_get_connector_state(state, connector);
 | ||||
| +	if (!conn_state)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	crtc_state->mode_changed = true;
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
|  int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) | ||||
|  { | ||||
|  	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); | ||||
| @@ -624,7 +654,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	return 0;
 | ||||
| +	return vc4_hvs_gamma_check(crtc, state);
 | ||||
|  } | ||||
|   | ||||
|  static void vc4_hvs_install_dlist(struct drm_crtc *crtc) | ||||
|  | @ -0,0 +1,149 @@ | |||
| From 7232a4e31a7a38b4376596b386777d030923c9bb Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:08:08 +0200 | ||||
| Subject: [PATCH] drm/vc4: Relax VEC modeline requirements and add progressive | ||||
|  mode support | ||||
| 
 | ||||
| Make vc4_vec_encoder_atomic_check() accept arbitrary modelines, as long | ||||
| as they result in somewhat sane output from the VEC. The bounds have | ||||
| been determined empirically. Additionally, add support for the | ||||
| progressive 262-line and 312-line modes. | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c |  1 + | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c  | 94 ++++++++++++++++++++++++++++++---- | ||||
|  2 files changed, 85 insertions(+), 10 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -422,6 +422,7 @@ static void vc4_crtc_config_pv(struct dr
 | ||||
|  		CRTC_WRITE(PV_V_CONTROL, | ||||
|  			   PV_VCONTROL_CONTINUOUS | | ||||
|  			   (is_dsi ? PV_VCONTROL_DSI : 0)); | ||||
| +		CRTC_WRITE(PV_VSYNCD_EVEN, 0);
 | ||||
|  	} | ||||
|   | ||||
|  	CRTC_WRITE(PV_VERTA, | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -400,18 +400,11 @@ static int vc4_vec_connector_atomic_chec
 | ||||
|  	struct drm_connector_state *new_state = | ||||
|  		drm_atomic_get_new_connector_state(state, conn); | ||||
|   | ||||
| -	const struct vc4_vec_tv_mode *vec_mode =
 | ||||
| -		&vc4_vec_tv_modes[new_state->tv.mode];
 | ||||
| -
 | ||||
| -	if (new_state->crtc) {
 | ||||
| +	if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) {
 | ||||
|  		struct drm_crtc_state *crtc_state = | ||||
|  			drm_atomic_get_new_crtc_state(state, new_state->crtc); | ||||
|   | ||||
| -		if (!drm_mode_equal(vec_mode->mode, &crtc_state->mode))
 | ||||
| -			return -EINVAL;
 | ||||
| -
 | ||||
| -		if (old_state->tv.mode != new_state->tv.mode)
 | ||||
| -			crtc_state->mode_changed = true;
 | ||||
| +		crtc_state->mode_changed = true;
 | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| @@ -546,7 +539,10 @@ static void vc4_vec_encoder_enable(struc
 | ||||
|  	VEC_WRITE(VEC_CLMP0_START, 0xac); | ||||
|  	VEC_WRITE(VEC_CLMP0_END, 0xec); | ||||
|  	VEC_WRITE(VEC_CONFIG2, | ||||
| -		  VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS);
 | ||||
| +		  VEC_CONFIG2_UV_DIG_DIS |
 | ||||
| +		  VEC_CONFIG2_RGB_DIG_DIS |
 | ||||
| +		  ((encoder->crtc->state->adjusted_mode.flags &
 | ||||
| +		    DRM_MODE_FLAG_INTERLACE) ? 0 : VEC_CONFIG2_PROG_SCAN));
 | ||||
|  	VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); | ||||
|  	VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config); | ||||
|   | ||||
| @@ -575,8 +571,86 @@ err_put_runtime_pm:
 | ||||
|  err_dev_exit: | ||||
|  	drm_dev_exit(idx); | ||||
|  } | ||||
| +static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
 | ||||
| +					struct drm_crtc_state *crtc_state,
 | ||||
| +					struct drm_connector_state *conn_state)
 | ||||
| +{
 | ||||
| +	const struct drm_display_mode *reference_mode =
 | ||||
| +		vc4_vec_tv_modes[conn_state->tv.mode].mode;
 | ||||
| +
 | ||||
| +	if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock ||
 | ||||
| +	    crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal ||
 | ||||
| +	    crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 ||
 | ||||
| +	    crtc_state->adjusted_mode.crtc_hsync_end -
 | ||||
| +		    crtc_state->adjusted_mode.crtc_hsync_start < 1)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	switch (reference_mode->vtotal) {
 | ||||
| +	case 525:
 | ||||
| +		if (crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vdisplay > 253 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vsync_start -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vsync_end -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vsync_start != 3 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vsync_end < 4 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal > 262)
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		if ((crtc_state->adjusted_mode.flags &
 | ||||
| +		     DRM_MODE_FLAG_INTERLACE) &&
 | ||||
| +		    (crtc_state->adjusted_mode.vdisplay % 2 != 0 ||
 | ||||
| +		     crtc_state->adjusted_mode.vsync_start % 2 != 1 ||
 | ||||
| +		     crtc_state->adjusted_mode.vsync_end % 2 != 1 ||
 | ||||
| +		     crtc_state->adjusted_mode.vtotal % 2 != 1))
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		/* progressive mode is hard-wired to 262 total lines */
 | ||||
| +		if (!(crtc_state->adjusted_mode.flags &
 | ||||
| +		      DRM_MODE_FLAG_INTERLACE) &&
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal != 262)
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		break;
 | ||||
| +
 | ||||
| +	case 625:
 | ||||
| +		if (crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vdisplay > 305 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vsync_start -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vdisplay < 1 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vsync_end -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vsync_start != 3 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal -
 | ||||
| +			    crtc_state->adjusted_mode.crtc_vsync_end < 2 ||
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal > 312)
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		if ((crtc_state->adjusted_mode.flags &
 | ||||
| +		     DRM_MODE_FLAG_INTERLACE) &&
 | ||||
| +		    (crtc_state->adjusted_mode.vdisplay % 2 != 0 ||
 | ||||
| +		     crtc_state->adjusted_mode.vsync_start % 2 != 0 ||
 | ||||
| +		     crtc_state->adjusted_mode.vsync_end % 2 != 0 ||
 | ||||
| +		     crtc_state->adjusted_mode.vtotal % 2 != 1))
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		/* progressive mode is hard-wired to 312 total lines */
 | ||||
| +		if (!(crtc_state->adjusted_mode.flags &
 | ||||
| +		      DRM_MODE_FLAG_INTERLACE) &&
 | ||||
| +		    crtc_state->adjusted_mode.crtc_vtotal != 312)
 | ||||
| +			return -EINVAL;
 | ||||
| +
 | ||||
| +		break;
 | ||||
| +
 | ||||
| +	default:
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
|   | ||||
|  static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { | ||||
| +	.atomic_check = vc4_vec_encoder_atomic_check,
 | ||||
|  	.atomic_disable = vc4_vec_encoder_disable, | ||||
|  	.atomic_enable = vc4_vec_encoder_enable, | ||||
|  }; | ||||
|  | @ -0,0 +1,164 @@ | |||
| From acef474b7826abe16fb140da1f3ee03cdc9928b8 Mon Sep 17 00:00:00 2001 | ||||
| From: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| Date: Thu, 15 Jul 2021 01:08:11 +0200 | ||||
| Subject: [PATCH] drm/vc4: Make VEC progressive modes readily accessible | ||||
| 
 | ||||
| Add predefined modelines for the 240p (NTSC) and 288p (PAL) progressive | ||||
| modes, and report them through vc4_vec_connector_get_modes(). | ||||
| 
 | ||||
| Signed-off-by: Mateusz Kwiatkowski <kfyatek+publicgit@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_vec.c | 73 ++++++++++++++++++++++++++--------- | ||||
|  1 file changed, 55 insertions(+), 18 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_vec.c
 | ||||
| @@ -228,7 +228,8 @@ enum vc4_vec_tv_mode_id {
 | ||||
|  }; | ||||
|   | ||||
|  struct vc4_vec_tv_mode { | ||||
| -	const struct drm_display_mode *mode;
 | ||||
| +	const struct drm_display_mode *interlaced_mode;
 | ||||
| +	const struct drm_display_mode *progressive_mode;
 | ||||
|  	u32 config0; | ||||
|  	u32 config1; | ||||
|  	u32 custom_freq; | ||||
| @@ -262,61 +263,81 @@ static const struct debugfs_reg32 vec_re
 | ||||
|  }; | ||||
|   | ||||
|  static const struct drm_display_mode drm_mode_480i = { | ||||
| -	DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
| +	DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
|  		 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, | ||||
|  		 480, 480 + 7, 480 + 7 + 6, 525, 0, | ||||
|  		 DRM_MODE_FLAG_INTERLACE) | ||||
|  }; | ||||
|   | ||||
| +static const struct drm_display_mode drm_mode_240p = {
 | ||||
| +	DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
| +		 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
 | ||||
| +		 240, 240 + 3, 240 + 3 + 3, 262, 0, 0)
 | ||||
| +};
 | ||||
| +
 | ||||
|  static const struct drm_display_mode drm_mode_576i = { | ||||
| -	DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
| +	DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
|  		 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, | ||||
|  		 576, 576 + 4, 576 + 4 + 6, 625, 0, | ||||
|  		 DRM_MODE_FLAG_INTERLACE) | ||||
|  }; | ||||
|   | ||||
| +static const struct drm_display_mode drm_mode_288p = {
 | ||||
| +	DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500,
 | ||||
| +		 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
 | ||||
| +		 288, 288 + 2, 288 + 2 + 3, 312, 0, 0)
 | ||||
| +};
 | ||||
| +
 | ||||
|  static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { | ||||
|  	[VC4_VEC_TV_MODE_NTSC] = { | ||||
| -		.mode = &drm_mode_480i,
 | ||||
| +		.interlaced_mode = &drm_mode_480i,
 | ||||
| +		.progressive_mode = &drm_mode_240p,
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_NTSC_J] = { | ||||
| -		.mode = &drm_mode_480i,
 | ||||
| +		.interlaced_mode = &drm_mode_480i,
 | ||||
| +		.progressive_mode = &drm_mode_240p,
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_NTSC_443] = { | ||||
|  		/* NTSC with PAL chroma frequency */ | ||||
| -		.mode = &drm_mode_480i,
 | ||||
| +		.interlaced_mode = &drm_mode_480i,
 | ||||
| +		.progressive_mode = &drm_mode_240p,
 | ||||
|  		.config0 = VEC_CONFIG0_NTSC_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, | ||||
|  		.custom_freq = 0x2a098acb, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL] = { | ||||
| -		.mode = &drm_mode_576i,
 | ||||
| +		.interlaced_mode = &drm_mode_576i,
 | ||||
| +		.progressive_mode = &drm_mode_288p,
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_BDGHI_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL_M] = { | ||||
| -		.mode = &drm_mode_480i,
 | ||||
| +		.interlaced_mode = &drm_mode_480i,
 | ||||
| +		.progressive_mode = &drm_mode_240p,
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_M_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL_N] = { | ||||
| -		.mode = &drm_mode_576i,
 | ||||
| +		.interlaced_mode = &drm_mode_576i,
 | ||||
| +		.progressive_mode = &drm_mode_288p,
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_N_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_PAL60] = { | ||||
|  		/* PAL-M with chroma frequency of regular PAL */ | ||||
| -		.mode = &drm_mode_480i,
 | ||||
| +		.interlaced_mode = &drm_mode_480i,
 | ||||
| +		.progressive_mode = &drm_mode_240p,
 | ||||
|  		.config0 = VEC_CONFIG0_PAL_M_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, | ||||
|  		.custom_freq = 0x2a098acb, | ||||
|  	}, | ||||
|  	[VC4_VEC_TV_MODE_SECAM] = { | ||||
| -		.mode = &drm_mode_576i,
 | ||||
| +		.interlaced_mode = &drm_mode_576i,
 | ||||
| +		.progressive_mode = &drm_mode_288p,
 | ||||
|  		.config0 = VEC_CONFIG0_SECAM_STD, | ||||
|  		.config1 = VEC_CONFIG1_C_CVBS_CVBS, | ||||
|  		.custom_freq = 0x29c71c72, | ||||
| @@ -370,16 +391,32 @@ vc4_vec_connector_detect(struct drm_conn
 | ||||
|  static int vc4_vec_connector_get_modes(struct drm_connector *connector) | ||||
|  { | ||||
|  	struct drm_connector_state *state = connector->state; | ||||
| -	struct drm_display_mode *mode;
 | ||||
| +	struct drm_display_mode *interlaced_mode, *progressive_mode;
 | ||||
|   | ||||
| -	mode = drm_mode_duplicate(connector->dev,
 | ||||
| -				  vc4_vec_tv_modes[state->tv.mode].mode);
 | ||||
| -	if (!mode) {
 | ||||
| +	interlaced_mode =
 | ||||
| +		drm_mode_duplicate(connector->dev,
 | ||||
| +				   vc4_vec_tv_modes[state->tv.mode].interlaced_mode);
 | ||||
| +	progressive_mode =
 | ||||
| +		drm_mode_duplicate(connector->dev,
 | ||||
| +				   vc4_vec_tv_modes[state->tv.mode].progressive_mode);
 | ||||
| +	if (!interlaced_mode || !progressive_mode) {
 | ||||
|  		DRM_ERROR("Failed to create a new display mode\n"); | ||||
| +		drm_mode_destroy(connector->dev, interlaced_mode);
 | ||||
| +		drm_mode_destroy(connector->dev, progressive_mode);
 | ||||
|  		return -ENOMEM; | ||||
|  	} | ||||
|   | ||||
| -	drm_mode_probed_add(connector, mode);
 | ||||
| +	if (connector->cmdline_mode.specified &&
 | ||||
| +	    connector->cmdline_mode.refresh_specified &&
 | ||||
| +	    !connector->cmdline_mode.interlace)
 | ||||
| +		/* progressive mode set at boot, let's make it preferred */
 | ||||
| +		progressive_mode->type |= DRM_MODE_TYPE_PREFERRED;
 | ||||
| +	else
 | ||||
| +		/* otherwise, interlaced mode is preferred */
 | ||||
| +		interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED;
 | ||||
| +
 | ||||
| +	drm_mode_probed_add(connector, interlaced_mode);
 | ||||
| +	drm_mode_probed_add(connector, progressive_mode);
 | ||||
|   | ||||
|  	return 1; | ||||
|  } | ||||
| @@ -576,7 +613,7 @@ static int vc4_vec_encoder_atomic_check(
 | ||||
|  					struct drm_connector_state *conn_state) | ||||
|  { | ||||
|  	const struct drm_display_mode *reference_mode = | ||||
| -		vc4_vec_tv_modes[conn_state->tv.mode].mode;
 | ||||
| +		vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode;
 | ||||
|   | ||||
|  	if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock || | ||||
|  	    crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal || | ||||
|  | @ -0,0 +1,29 @@ | |||
| From 61cdd8f4f24116cf877a3b865fb3c01b74d018f4 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Tue, 2 Nov 2021 16:01:36 +0000 | ||||
| Subject: [PATCH] drm: Check whether the gamma lut has changed before updating | ||||
| 
 | ||||
| drm_crtc_legacy_gamma_set updates the gamma_lut blob unconditionally, | ||||
| which leads to unnecessary reprogramming of hardware. | ||||
| 
 | ||||
| Check whether the blob contents has actually changed before | ||||
| signalling that it has been updated. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/drm_color_mgmt.c | 4 +++- | ||||
|  1 file changed, 3 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/drm_color_mgmt.c
 | ||||
| +++ b/drivers/gpu/drm/drm_color_mgmt.c
 | ||||
| @@ -330,7 +330,9 @@ static int drm_crtc_legacy_gamma_set(str
 | ||||
|  	replaced = drm_property_replace_blob(&crtc_state->degamma_lut, | ||||
|  					     use_gamma_lut ? NULL : blob); | ||||
|  	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); | ||||
| -	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
 | ||||
| +	if (!crtc_state->gamma_lut || !crtc_state->gamma_lut->data ||
 | ||||
| +	    memcmp(crtc_state->gamma_lut->data, blob_data, blob->length))
 | ||||
| +		replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
 | ||||
|  					      use_gamma_lut ? blob : NULL); | ||||
|  	crtc_state->color_mgmt_changed |= replaced; | ||||
|   | ||||
|  | @ -0,0 +1,66 @@ | |||
| From 8757ab629ec26bd78885c66ea24a65df2b48bd55 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 8 Nov 2021 17:32:45 +0000 | ||||
| Subject: [PATCH] drm/vc4: Enable gamma block only when required. | ||||
| 
 | ||||
| With HVS5 the gamma block is now only reprogrammed with | ||||
| a disable/enable. Loading the table from vc4_hvs_init_channel | ||||
| (called from vc4_hvs_atomic_enable) appears to be at an | ||||
| invalid point in time and so isn't applied. | ||||
| 
 | ||||
| Switch to enabling and disabling the gamma table instead. This | ||||
| isn't safe if the pipeline is running, but it isn't now. | ||||
| For HVS4 it is safe to enable and disable dynamically, so | ||||
| adopt that approach there too. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 22 ++++++++++++++++------ | ||||
|  1 file changed, 16 insertions(+), 6 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -546,8 +546,11 @@ static int vc4_hvs_init_channel(struct v
 | ||||
|  	dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; | ||||
|  	dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; | ||||
|   | ||||
| +	if (crtc->state->gamma_lut)
 | ||||
| +		/* Enable gamma on if required */
 | ||||
| +		dispbkgndx |= SCALER_DISPBKGND_GAMMA;
 | ||||
| +
 | ||||
|  	HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | | ||||
| -		  ((!vc4->is_vc5) ? SCALER_DISPBKGND_GAMMA : 0) |
 | ||||
|  		  (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); | ||||
|   | ||||
|  	/* Reload the LUT, since the SRAMs would have been disabled if | ||||
| @@ -816,18 +819,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
 | ||||
|  		u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel)); | ||||
|   | ||||
|  		if (crtc->state->gamma_lut) { | ||||
| -			if (!vc4->is_vc5)
 | ||||
| +			if (!vc4->is_vc5) {
 | ||||
|  				vc4_hvs_update_gamma_lut(hvs, vc4_crtc); | ||||
| -			else
 | ||||
| +				dispbkgndx |= SCALER_DISPBKGND_GAMMA;
 | ||||
| +			} else {
 | ||||
|  				vc5_hvs_update_gamma_lut(hvs, vc4_crtc); | ||||
| -
 | ||||
| -			dispbkgndx |= SCALER_DISPBKGND_GAMMA;
 | ||||
| +			}
 | ||||
|  		} else { | ||||
|  			/* Unsetting DISPBKGND_GAMMA skips the gamma lut step | ||||
|  			 * in hardware, which is the same as a linear lut that | ||||
|  			 * DRM expects us to use in absence of a user lut. | ||||
| +			 *
 | ||||
| +			 * Do NOT change state dynamically for hvs5 as it
 | ||||
| +			 * inserts a delay in the pipeline that will cause
 | ||||
| +			 * stalls if enabled/disabled whilst running. The other
 | ||||
| +			 * should already be disabling/enabling the pipeline
 | ||||
| +			 * when gamma changes.
 | ||||
|  			 */ | ||||
| -			dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
 | ||||
| +			if (!vc4->is_vc5)
 | ||||
| +				dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
 | ||||
|  		} | ||||
|  		HVS_WRITE(SCALER_DISPBKGNDX(channel), dispbkgndx); | ||||
|  	} | ||||
|  | @ -0,0 +1,26 @@ | |||
| From 0f75707bf8c04af5a931b8e991c26aeef4ba881e Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 8 Nov 2021 18:25:49 +0000 | ||||
| Subject: [PATCH] drm/vc4: Only add gamma properties once. | ||||
| 
 | ||||
| Two calls were made to drm_crtc_enable_color_mgmt to add gamma | ||||
| and CTM, however they were both set to add the gamma properties, | ||||
| so they ended up added twice. | ||||
| 
 | ||||
| Fixes: 766cc6b1f7fc "drm/vc4: Add CTM support" | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -1358,7 +1358,7 @@ int vc4_crtc_init(struct drm_device *drm
 | ||||
|  		/* We support CTM, but only for one CRTC at a time. It's therefore | ||||
|  		 * implemented as private driver state in vc4_kms, not here. | ||||
|  		 */ | ||||
| -		drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
 | ||||
| +		drm_crtc_enable_color_mgmt(crtc, 0, true, 0);
 | ||||
|   | ||||
|  		/* Initialize the VC4 gamma LUTs */ | ||||
|  		for (i = 0; i < crtc->gamma_size; i++) { | ||||
|  | @ -0,0 +1,32 @@ | |||
| From a3133296825850c7f7328b91eb25dbeec65628e4 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 10 Nov 2021 16:36:12 +0000 | ||||
| Subject: [PATCH] drm/vc4: Validate the size of the gamma_lut | ||||
| 
 | ||||
| Add a check to vc4_hvs_gamma_check to ensure a new non-empty | ||||
| gamma LUT is of the correct length before accepting it. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -612,6 +612,16 @@ static int vc4_hvs_gamma_check(struct dr
 | ||||
|  	if (!crtc_state->color_mgmt_changed) | ||||
|  		return 0; | ||||
|   | ||||
| +	if (crtc_state->gamma_lut) {
 | ||||
| +		unsigned int len = drm_color_lut_size(crtc_state->gamma_lut);
 | ||||
| +
 | ||||
| +		if (len != crtc->gamma_size) {
 | ||||
| +			DRM_DEBUG_KMS("Invalid LUT size; got %u, expected %u\n",
 | ||||
| +				      len, crtc->gamma_size);
 | ||||
| +			return -EINVAL;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	connector = vc4_get_crtc_connector(crtc, crtc_state); | ||||
|  	if (!connector) | ||||
|  		return -EINVAL; | ||||
|  | @ -0,0 +1,36 @@ | |||
| From 8f2abdff33e32469f735aa68197969306d847727 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 13 Jan 2022 11:30:42 +0000 | ||||
| Subject: [PATCH] drm/vc4: Disable Gamma control on HVS5 due to issues writing | ||||
|  the table | ||||
| 
 | ||||
| Still under investigation, but the conditions under which the HVS | ||||
| will accept values written to the gamma PWL are not straightforward. | ||||
| 
 | ||||
| Disable gamma on HVS5 again until it can be resolved to avoid | ||||
| gamma being enabled with an incorrect table. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 8 +------- | ||||
|  1 file changed, 1 insertion(+), 7 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -1344,15 +1344,9 @@ int vc4_crtc_init(struct drm_device *drm
 | ||||
|   | ||||
|  	if (!vc4->is_vc5) { | ||||
|  		drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); | ||||
| -	} else {
 | ||||
| -		/* This is a lie for hvs5 which uses a 16 point PWL, but it
 | ||||
| -		 * allows for something smarter than just 16 linearly spaced
 | ||||
| -		 * segments. Conversion is done in vc5_hvs_update_gamma_lut.
 | ||||
| -		 */
 | ||||
| -		drm_mode_crtc_set_gamma_size(crtc, 256);
 | ||||
| +		drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
 | ||||
|  	} | ||||
|   | ||||
| -	drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
 | ||||
|   | ||||
|  	if (!vc4->is_vc5) { | ||||
|  		/* We support CTM, but only for one CRTC at a time. It's therefore | ||||
|  | @ -0,0 +1,223 @@ | |||
| From 06389a94233146be1104c6e90f0d5d99360d67ff Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 8 Apr 2020 16:12:02 +0100 | ||||
| Subject: [PATCH] drm/vc4_hdmi: Add Broadcast RGB property to allow override of | ||||
|  RGB range | ||||
| 
 | ||||
| Copy Intel's "Broadcast RGB" property semantics to add manual override | ||||
| of the HDMI pixel range for monitors that don't abide by the content | ||||
| of the AVI Infoframe. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hdmi.c | 104 +++++++++++++++++++++++++++++++++ | ||||
|  drivers/gpu/drm/vc4/vc4_hdmi.h |  15 +++++ | ||||
|  2 files changed, 119 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| @@ -57,6 +57,14 @@
 | ||||
|  #include "vc4_hdmi_regs.h" | ||||
|  #include "vc4_regs.h" | ||||
|   | ||||
| +/*
 | ||||
| + * "Broadcast RGB" property.
 | ||||
| + * Allows overriding of HDMI full or limited range RGB
 | ||||
| + */
 | ||||
| +#define VC4_BROADCAST_RGB_AUTO 0
 | ||||
| +#define VC4_BROADCAST_RGB_FULL 1
 | ||||
| +#define VC4_BROADCAST_RGB_LIMITED 2
 | ||||
| +
 | ||||
|  #define VC5_HDMI_HORZA_HFP_SHIFT		16 | ||||
|  #define VC5_HDMI_HORZA_HFP_MASK			VC4_MASK(28, 16) | ||||
|  #define VC5_HDMI_HORZA_VPOS			BIT(15) | ||||
| @@ -155,6 +163,11 @@ static bool vc4_hdmi_is_full_range_rgb(s
 | ||||
|  { | ||||
|  	struct drm_display_info *display = &vc4_hdmi->connector.display_info; | ||||
|   | ||||
| +	if (vc4_hdmi->broadcast_rgb == VC4_BROADCAST_RGB_LIMITED)
 | ||||
| +		return false;
 | ||||
| +	else if (vc4_hdmi->broadcast_rgb == VC4_BROADCAST_RGB_FULL)
 | ||||
| +		return true;
 | ||||
| +
 | ||||
|  	return !display->is_hdmi || | ||||
|  		drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL; | ||||
|  } | ||||
| @@ -544,6 +557,65 @@ static int vc4_hdmi_connector_atomic_che
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +/**
 | ||||
| + * vc4_hdmi_connector_atomic_get_property - hook for
 | ||||
| + *						connector->atomic_get_property.
 | ||||
| + * @connector: Connector to get the property for.
 | ||||
| + * @state: Connector state to retrieve the property from.
 | ||||
| + * @property: Property to retrieve.
 | ||||
| + * @val: Return value for the property.
 | ||||
| + *
 | ||||
| + * Returns the atomic property value for a digital connector.
 | ||||
| + */
 | ||||
| +int vc4_hdmi_connector_get_property(struct drm_connector *connector,
 | ||||
| +				    const struct drm_connector_state *state,
 | ||||
| +				    struct drm_property *property,
 | ||||
| +				    uint64_t *val)
 | ||||
| +{
 | ||||
| +	struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
 | ||||
| +	const struct vc4_hdmi_connector_state *vc4_conn_state =
 | ||||
| +				const_conn_state_to_vc4_hdmi_conn_state(state);
 | ||||
| +
 | ||||
| +	if (property == vc4_hdmi->broadcast_rgb_property) {
 | ||||
| +		*val = vc4_conn_state->broadcast_rgb;
 | ||||
| +	} else {
 | ||||
| +		DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
 | ||||
| +				 property->base.id, property->name);
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/**
 | ||||
| + * vc4_hdmi_connector_atomic_set_property - hook for
 | ||||
| + *						connector->atomic_set_property.
 | ||||
| + * @connector: Connector to set the property for.
 | ||||
| + * @state: Connector state to set the property on.
 | ||||
| + * @property: Property to set.
 | ||||
| + * @val: New value for the property.
 | ||||
| + *
 | ||||
| + * Sets the atomic property value for a digital connector.
 | ||||
| + */
 | ||||
| +int vc4_hdmi_connector_set_property(struct drm_connector *connector,
 | ||||
| +				    struct drm_connector_state *state,
 | ||||
| +				    struct drm_property *property,
 | ||||
| +				    uint64_t val)
 | ||||
| +{
 | ||||
| +	struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
 | ||||
| +	struct vc4_hdmi_connector_state *vc4_conn_state =
 | ||||
| +				conn_state_to_vc4_hdmi_conn_state(state);
 | ||||
| +
 | ||||
| +	if (property == vc4_hdmi->broadcast_rgb_property) {
 | ||||
| +		vc4_conn_state->broadcast_rgb = val;
 | ||||
| +		return 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
 | ||||
| +			 property->base.id, property->name);
 | ||||
| +	return -EINVAL;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void vc4_hdmi_connector_reset(struct drm_connector *connector) | ||||
|  { | ||||
|  	struct vc4_hdmi_connector_state *old_state = | ||||
| @@ -580,6 +652,7 @@ vc4_hdmi_connector_duplicate_state(struc
 | ||||
|  	new_state->tmds_char_rate = vc4_state->tmds_char_rate; | ||||
|  	new_state->output_bpc = vc4_state->output_bpc; | ||||
|  	new_state->output_format = vc4_state->output_format; | ||||
| +	new_state->broadcast_rgb = vc4_state->broadcast_rgb;
 | ||||
|  	__drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); | ||||
|   | ||||
|  	return &new_state->base; | ||||
| @@ -590,6 +663,8 @@ static const struct drm_connector_funcs
 | ||||
|  	.reset = vc4_hdmi_connector_reset, | ||||
|  	.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, | ||||
|  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||||
| +	.atomic_get_property = vc4_hdmi_connector_get_property,
 | ||||
| +	.atomic_set_property = vc4_hdmi_connector_set_property,
 | ||||
|  }; | ||||
|   | ||||
|  static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { | ||||
| @@ -598,6 +673,32 @@ static const struct drm_connector_helper
 | ||||
|  	.atomic_check = vc4_hdmi_connector_atomic_check, | ||||
|  }; | ||||
|   | ||||
| +static const struct drm_prop_enum_list broadcast_rgb_names[] = {
 | ||||
| +	{ VC4_BROADCAST_RGB_AUTO, "Automatic" },
 | ||||
| +	{ VC4_BROADCAST_RGB_FULL, "Full" },
 | ||||
| +	{ VC4_BROADCAST_RGB_LIMITED, "Limited 16:235" },
 | ||||
| +};
 | ||||
| +
 | ||||
| +static void
 | ||||
| +vc4_hdmi_attach_broadcast_rgb_property(struct drm_device *dev,
 | ||||
| +				       struct vc4_hdmi *vc4_hdmi)
 | ||||
| +{
 | ||||
| +	struct drm_property *prop = vc4_hdmi->broadcast_rgb_property;
 | ||||
| +
 | ||||
| +	if (!prop) {
 | ||||
| +		prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
 | ||||
| +						"Broadcast RGB",
 | ||||
| +						broadcast_rgb_names,
 | ||||
| +						ARRAY_SIZE(broadcast_rgb_names));
 | ||||
| +		if (!prop)
 | ||||
| +			return;
 | ||||
| +
 | ||||
| +		vc4_hdmi->broadcast_rgb_property = prop;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	drm_object_attach_property(&vc4_hdmi->connector.base, prop, 0);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int vc4_hdmi_connector_init(struct drm_device *dev, | ||||
|  				   struct vc4_hdmi *vc4_hdmi) | ||||
|  { | ||||
| @@ -644,6 +745,8 @@ static int vc4_hdmi_connector_init(struc
 | ||||
|  	if (vc4_hdmi->variant->supports_hdr) | ||||
|  		drm_connector_attach_hdr_output_metadata_property(connector); | ||||
|   | ||||
| +	vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi);
 | ||||
| +
 | ||||
|  	drm_connector_attach_encoder(connector, encoder); | ||||
|   | ||||
|  	return 0; | ||||
| @@ -1683,6 +1786,7 @@ static void vc4_hdmi_encoder_atomic_mode
 | ||||
|  	mutex_lock(&vc4_hdmi->mutex); | ||||
|  	drm_mode_copy(&vc4_hdmi->saved_adjusted_mode, | ||||
|  		      &crtc_state->adjusted_mode); | ||||
| +	vc4_hdmi->broadcast_rgb = vc4_state->broadcast_rgb;
 | ||||
|  	vc4_hdmi->output_bpc = vc4_state->output_bpc; | ||||
|  	vc4_hdmi->output_format = vc4_state->output_format; | ||||
|  	mutex_unlock(&vc4_hdmi->mutex); | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
 | ||||
| @@ -129,6 +129,8 @@ struct vc4_hdmi {
 | ||||
|   | ||||
|  	struct delayed_work scrambling_work; | ||||
|   | ||||
| +	struct drm_property *broadcast_rgb_property;
 | ||||
| +
 | ||||
|  	struct i2c_adapter *ddc; | ||||
|  	void __iomem *hdmicore_regs; | ||||
|  	void __iomem *hd_regs; | ||||
| @@ -229,6 +231,12 @@ struct vc4_hdmi {
 | ||||
|  	 * for use outside of KMS hooks. Protected by @mutex. | ||||
|  	 */ | ||||
|  	enum vc4_hdmi_output_format output_format; | ||||
| +
 | ||||
| +	/**
 | ||||
| +	 * @broadcast_rgb: Copy of @vc4_connector_state.broadcast_rgb
 | ||||
| +	 * for use outside of KMS hooks. Protected by @mutex.
 | ||||
| +	 */
 | ||||
| +	int broadcast_rgb;
 | ||||
|  }; | ||||
|   | ||||
|  static inline struct vc4_hdmi * | ||||
| @@ -249,6 +257,7 @@ struct vc4_hdmi_connector_state {
 | ||||
|  	unsigned long long		tmds_char_rate; | ||||
|  	unsigned int 			output_bpc; | ||||
|  	enum vc4_hdmi_output_format	output_format; | ||||
| +	int				broadcast_rgb;
 | ||||
|  }; | ||||
|   | ||||
|  static inline struct vc4_hdmi_connector_state * | ||||
| @@ -256,6 +265,12 @@ conn_state_to_vc4_hdmi_conn_state(struct
 | ||||
|  { | ||||
|  	return container_of(conn_state, struct vc4_hdmi_connector_state, base); | ||||
|  } | ||||
| +
 | ||||
| +static inline const struct vc4_hdmi_connector_state *
 | ||||
| +const_conn_state_to_vc4_hdmi_conn_state(const struct drm_connector_state *conn_state)
 | ||||
| +{
 | ||||
| +	return container_of(conn_state, struct vc4_hdmi_connector_state, base);
 | ||||
| +}
 | ||||
|   | ||||
|  void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, | ||||
|  		       struct vc4_hdmi_connector_state *vc4_conn_state); | ||||
|  | @ -0,0 +1,51 @@ | |||
| From c898725a27960d08d3aa31a4b23e3ba8840388cd Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 31 Jan 2022 16:28:43 +0000 | ||||
| Subject: [PATCH] drm/vc4: Add DRM 210101010 RGB formats for hvs5. | ||||
| 
 | ||||
| HVS5 supports the 210101010 RGB[A|X] formats, but they were | ||||
| missing from the DRM to HVS mapping list, so weren't available. | ||||
| Add them in. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 28 ++++++++++++++++++++++++++++ | ||||
|  1 file changed, 28 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -139,6 +139,34 @@ static const struct hvs_format {
 | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_XRGB2101010,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.hvs5_only = true,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_ARGB2101010,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.hvs5_only = true,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_ABGR2101010,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.hvs5_only = true,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_XBGR2101010,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.hvs5_only = true,
 | ||||
| +	},
 | ||||
|  }; | ||||
|   | ||||
|  static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) | ||||
|  | @ -0,0 +1,30 @@ | |||
| From 0fefc9f03f94ba67d97736c884d995b98547d2f0 Mon Sep 17 00:00:00 2001 | ||||
| From: Chris Morgan <macromorgan@hotmail.com> | ||||
| Date: Fri, 28 Jan 2022 17:39:54 -0600 | ||||
| Subject: [PATCH] drm/vc4: dpi: Support DPI interface in mode3 for RGB565 | ||||
| 
 | ||||
| Add support for the VC4 DPI driver to utilize DPI mode 3. This is | ||||
| defined here as xxxRRRRRxxGGGGGGxxxBBBBB: | ||||
| https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#parallel-display-interface-dpi | ||||
| 
 | ||||
| This mode is required to use the Geekworm MZP280 DPI display. | ||||
| 
 | ||||
| Signed-off-by: Chris Morgan <macromorgan@hotmail.com> | ||||
| Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dpi.c | 4 ++++ | ||||
|  1 file changed, 4 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
 | ||||
| @@ -188,6 +188,10 @@ static void vc4_dpi_encoder_enable(struc
 | ||||
|  				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_1, | ||||
|  						       DPI_FORMAT); | ||||
|  				break; | ||||
| +			case MEDIA_BUS_FMT_RGB565_1X24_CPADHI:
 | ||||
| +				dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_2,
 | ||||
| +						       DPI_FORMAT);
 | ||||
| +				break;
 | ||||
|  			default: | ||||
|  				DRM_ERROR("Unknown media bus format %d\n", | ||||
|  					  bus_format); | ||||
|  | @ -0,0 +1,89 @@ | |||
| From 311cdb11c4efd3c5c4a64c1f6569794728a4d45f Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Tue, 1 Feb 2022 12:20:20 +0000 | ||||
| Subject: [PATCH] drm/panel: Add and initialise an orientation field to | ||||
|  drm_panel | ||||
| 
 | ||||
| Current usage of drm_connector_set_panel_orientation is from a panel's | ||||
| get_modes call. However if the panel orientation property doesn't | ||||
| exist on the connector at this point, then drm_mode_object triggers | ||||
| WARNs as the connector is already registered. | ||||
| 
 | ||||
| Add an orientation variable to struct drm_panel and initialise it from | ||||
| drm_panel_init. | ||||
| panel_bridge_attach can then create the property before the connector | ||||
| is registered. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/bridge/panel.c |  4 ++++ | ||||
|  drivers/gpu/drm/drm_panel.c    | 15 ++++++++++----- | ||||
|  include/drm/drm_panel.h        |  8 ++++++++ | ||||
|  3 files changed, 22 insertions(+), 5 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/bridge/panel.c
 | ||||
| +++ b/drivers/gpu/drm/bridge/panel.c
 | ||||
| @@ -81,6 +81,10 @@ static int panel_bridge_attach(struct dr
 | ||||
|  		return ret; | ||||
|  	} | ||||
|   | ||||
| +	/* set up connector's "panel orientation" property */
 | ||||
| +	drm_connector_set_panel_orientation(&panel_bridge->connector,
 | ||||
| +					    panel_bridge->panel->orientation);
 | ||||
| +
 | ||||
|  	drm_connector_attach_encoder(&panel_bridge->connector, | ||||
|  					  bridge->encoder); | ||||
|   | ||||
| --- a/drivers/gpu/drm/drm_panel.c
 | ||||
| +++ b/drivers/gpu/drm/drm_panel.c
 | ||||
| @@ -61,6 +61,9 @@ void drm_panel_init(struct drm_panel *pa
 | ||||
|  	panel->dev = dev; | ||||
|  	panel->funcs = funcs; | ||||
|  	panel->connector_type = connector_type; | ||||
| +
 | ||||
| +	panel->orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
 | ||||
| +	of_drm_get_panel_orientation(dev->of_node, &panel->orientation);
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(drm_panel_init); | ||||
|   | ||||
| @@ -289,16 +292,18 @@ int of_drm_get_panel_orientation(const s
 | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
|   | ||||
| -	if (rotation == 0)
 | ||||
| +	if (rotation == 0) {
 | ||||
|  		*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; | ||||
| -	else if (rotation == 90)
 | ||||
| +	} else if (rotation == 90) {
 | ||||
|  		*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; | ||||
| -	else if (rotation == 180)
 | ||||
| +	} else if (rotation == 180) {
 | ||||
|  		*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; | ||||
| -	else if (rotation == 270)
 | ||||
| +	} else if (rotation == 270) {
 | ||||
|  		*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; | ||||
| -	else
 | ||||
| +	} else {
 | ||||
| +		DRM_ERROR("%pOF: invalid orientation %d\n", np, ret);
 | ||||
|  		return -EINVAL; | ||||
| +	}
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| --- a/include/drm/drm_panel.h
 | ||||
| +++ b/include/drm/drm_panel.h
 | ||||
| @@ -183,6 +183,14 @@ struct drm_panel {
 | ||||
|  	int connector_type; | ||||
|   | ||||
|  	/** | ||||
| +	 * @orientation:
 | ||||
| +	 *
 | ||||
| +	 * Panel orientation at initialisation. This is used to initialise the
 | ||||
| +	 * drm_connector property for panel orientation.
 | ||||
| +	 */
 | ||||
| +	enum drm_panel_orientation orientation;
 | ||||
| +
 | ||||
| +	/**
 | ||||
|  	 * @list: | ||||
|  	 * | ||||
|  	 * Panel entry in registry. | ||||
|  | @ -0,0 +1,76 @@ | |||
| From b41713786d56c559cfd093b313145cf34cb624d1 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 17 Dec 2021 13:36:52 +0000 | ||||
| Subject: [PATCH] drm/dsi: Document the meaning and spec references for | ||||
|  MIPI_DSI_MODE_* | ||||
| 
 | ||||
| The MIPI_DSI_MODE_* flags have fairly terse descriptions and no reference | ||||
| to the DSI specification as to their exact meaning. Usage has therefore | ||||
| been rather fluid. | ||||
| 
 | ||||
| Extend the descriptions and provide references to the part of the | ||||
| MIPI DSI specification regarding what they mean. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  include/drm/drm_mipi_dsi.h | 38 ++++++++++++++++++++++++++------------ | ||||
|  1 file changed, 26 insertions(+), 12 deletions(-) | ||||
| 
 | ||||
| --- a/include/drm/drm_mipi_dsi.h
 | ||||
| +++ b/include/drm/drm_mipi_dsi.h
 | ||||
| @@ -113,29 +113,43 @@ struct mipi_dsi_host *of_find_mipi_dsi_h
 | ||||
|   | ||||
|  /* DSI mode flags */ | ||||
|   | ||||
| -/* video mode */
 | ||||
| +/* Video mode display.
 | ||||
| + * Not set denotes a command mode display.
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO		BIT(0) | ||||
| -/* video burst mode */
 | ||||
| +/* Video burst mode.
 | ||||
| + * Link frequency to be configured via platform configuration.
 | ||||
| + * This should always be set in conjunction with MIPI_DSI_MODE_VIDEO.
 | ||||
| + * (DSI spec V1.1 8.11.4)
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_BURST	BIT(1) | ||||
| -/* video pulse mode */
 | ||||
| +/* Video pulse mode.
 | ||||
| + * Not set denotes sync event mode. (DSI spec V1.1 8.11.2)
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_SYNC_PULSE	BIT(2) | ||||
| -/* enable auto vertical count mode */
 | ||||
| +/* Enable auto vertical count mode */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_AUTO_VERT	BIT(3) | ||||
| -/* enable hsync-end packets in vsync-pulse and v-porch area */
 | ||||
| +/* Enable hsync-end packets in vsync-pulse and v-porch area */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_HSE		BIT(4) | ||||
| -/* disable hfront-porch area */
 | ||||
| +/* Transmit NULL packets or LP mode during hfront-porch area.
 | ||||
| + * Not set denotes sending a blanking packet instead. (DSI spec V1.1 8.11.1)
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_NO_HFP	BIT(5) | ||||
| -/* disable hback-porch area */
 | ||||
| +/* Transmit NULL packets or LP mode during hback-porch area.
 | ||||
| + * Not set denotes sending a blanking packet instead. (DSI spec V1.1 8.11.1)
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_NO_HBP	BIT(6) | ||||
| -/* disable hsync-active area */
 | ||||
| +/* Transmit NULL packets or LP mode during hsync-active area.
 | ||||
| + * Not set denotes sending a blanking packet instead. (DSI spec V1.1 8.11.1)
 | ||||
| + */
 | ||||
|  #define MIPI_DSI_MODE_VIDEO_NO_HSA	BIT(7) | ||||
| -/* flush display FIFO on vsync pulse */
 | ||||
| +/* Flush display FIFO on vsync pulse */
 | ||||
|  #define MIPI_DSI_MODE_VSYNC_FLUSH	BIT(8) | ||||
| -/* disable EoT packets in HS mode */
 | ||||
| +/* Disable EoT packets in HS mode. (DSI spec V1.1 8.1)  */
 | ||||
|  #define MIPI_DSI_MODE_NO_EOT_PACKET	BIT(9) | ||||
| -/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
 | ||||
| +/* Device supports non-continuous clock behavior (DSI spec V1.1 5.6.1) */
 | ||||
|  #define MIPI_DSI_CLOCK_NON_CONTINUOUS	BIT(10) | ||||
| -/* transmit data in low power */
 | ||||
| +/* Transmit data in low power */
 | ||||
|  #define MIPI_DSI_MODE_LPM		BIT(11) | ||||
|  /* transmit data ending at the same time for all lanes within one hsync */ | ||||
|  #define MIPI_DSI_HS_PKT_END_ALIGNED	BIT(12) | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 6638f69e510fe49de651d7ab0e2012e58fc2e6ca Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 20 Jan 2022 17:29:36 +0000 | ||||
| Subject: [PATCH] drm/bridge: tc358762: Ignore EPROBE_DEFER when logging errors | ||||
| 
 | ||||
| mipi_dsi_attach can fail due to resources not being available | ||||
| yet, therefore do not log error messages should they occur. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/bridge/tc358762.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/bridge/tc358762.c
 | ||||
| +++ b/drivers/gpu/drm/bridge/tc358762.c
 | ||||
| @@ -235,7 +235,7 @@ static int tc358762_probe(struct mipi_ds
 | ||||
|  	ret = mipi_dsi_attach(dsi); | ||||
|  	if (ret < 0) { | ||||
|  		drm_bridge_remove(&ctx->bridge); | ||||
| -		dev_err(dev, "failed to attach dsi\n");
 | ||||
| +		dev_err_probe(dev, ret, "failed to attach dsi\n");
 | ||||
|  	} | ||||
|   | ||||
|  	return ret; | ||||
|  | @ -0,0 +1,56 @@ | |||
| From 150b1bb8b1406807b94103e6beca1e023377e3a6 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 30 Sep 2021 17:51:16 +0100 | ||||
| Subject: [PATCH] drm/vc4: Rename bridge to out_bridge | ||||
| 
 | ||||
| In preparation for converting the encoder to being a bridge, | ||||
| rename the variable holding the next bridge in the chain to | ||||
| out_bridge, so that our bridge can be called bridge. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 12 ++++++------ | ||||
|  1 file changed, 6 insertions(+), 6 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -556,7 +556,7 @@ struct vc4_dsi {
 | ||||
|   | ||||
|  	struct platform_device *pdev; | ||||
|   | ||||
| -	struct drm_bridge *bridge;
 | ||||
| +	struct drm_bridge *out_bridge;
 | ||||
|  	struct list_head bridge_chain; | ||||
|   | ||||
|  	void __iomem *regs; | ||||
| @@ -800,7 +800,7 @@ static void vc4_dsi_encoder_disable(stru
 | ||||
|  		if (iter->funcs->disable) | ||||
|  			iter->funcs->disable(iter); | ||||
|   | ||||
| -		if (iter == dsi->bridge)
 | ||||
| +		if (iter == dsi->out_bridge)
 | ||||
|  			break; | ||||
|  	} | ||||
|   | ||||
| @@ -1723,9 +1723,9 @@ static int vc4_dsi_bind(struct device *d
 | ||||
|  		return ret; | ||||
|  	} | ||||
|   | ||||
| -	dsi->bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
 | ||||
| -	if (IS_ERR(dsi->bridge))
 | ||||
| -		return PTR_ERR(dsi->bridge);
 | ||||
| +	dsi->out_bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
 | ||||
| +	if (IS_ERR(dsi->out_bridge))
 | ||||
| +		return PTR_ERR(dsi->out_bridge);
 | ||||
|   | ||||
|  	/* The esc clock rate is supposed to always be 100Mhz. */ | ||||
|  	ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); | ||||
| @@ -1751,7 +1751,7 @@ static int vc4_dsi_bind(struct device *d
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
 | ||||
| +	ret = drm_bridge_attach(encoder, dsi->out_bridge, NULL, 0);
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|  	/* Disable the atomic helper calls into the bridge.  We | ||||
|  | @ -0,0 +1,64 @@ | |||
| From ccd746cc3efa45ce3a657bbe3729133ef8d20495 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 7 Feb 2022 17:14:51 +0000 | ||||
| Subject: [PATCH] drm/vc4: Move DSI initialisation to encoder_mode_set. | ||||
| 
 | ||||
| Breaking the bridge chain does not work for atomic bridges/panels | ||||
| and generally causes issues. | ||||
| We need to initialise the DSI host before the bridge pre_enables | ||||
| are called, so move that to encoder_mode_set in the same way that | ||||
| dw-mipi-dsi does. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 17 +++++++++++++---- | ||||
|  1 file changed, 13 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -867,18 +867,18 @@ static bool vc4_dsi_encoder_mode_fixup(s
 | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 | ||||
| +static void vc4_dsi_encoder_mode_set(struct drm_encoder *encoder,
 | ||||
| +				     struct drm_display_mode *mode,
 | ||||
| +				     struct drm_display_mode *adjusted_mode)
 | ||||
|  { | ||||
| -	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 | ||||
|  	struct vc4_dsi *dsi = to_vc4_dsi(encoder); | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
|  	bool debug_dump_regs = false; | ||||
| -	struct drm_bridge *iter;
 | ||||
|  	unsigned long hs_clock; | ||||
|  	u32 ui_ns; | ||||
|  	/* Minimum LP state duration in escape clock cycles. */ | ||||
|  	u32 lpx = dsi_esc_timing(60); | ||||
| -	unsigned long pixel_clock_hz = mode->clock * 1000;
 | ||||
| +	unsigned long pixel_clock_hz = adjusted_mode->clock * 1000;
 | ||||
|  	unsigned long dsip_clock; | ||||
|  	unsigned long phy_clock; | ||||
|  	int ret; | ||||
| @@ -1105,6 +1105,14 @@ static void vc4_dsi_encoder_enable(struc
 | ||||
|  		       ~DSI_PORT_BIT(PHY_AFEC0_RESET)); | ||||
|   | ||||
|  	vc4_dsi_ulps(dsi, false); | ||||
| +}
 | ||||
| +
 | ||||
| +static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 | ||||
| +{
 | ||||
| +	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
 | ||||
| +	struct vc4_dsi *dsi = vc4_encoder->dsi;
 | ||||
| +	bool debug_dump_regs = false;
 | ||||
| +	struct drm_bridge *iter;
 | ||||
|   | ||||
|  	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { | ||||
|  		if (iter->funcs->pre_enable) | ||||
| @@ -1370,6 +1378,7 @@ static const struct drm_encoder_helper_f
 | ||||
|  	.disable = vc4_dsi_encoder_disable, | ||||
|  	.enable = vc4_dsi_encoder_enable, | ||||
|  	.mode_fixup = vc4_dsi_encoder_mode_fixup, | ||||
| +	.mode_set = vc4_dsi_encoder_mode_set,
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_dsi_late_register(struct drm_encoder *encoder) | ||||
|  | @ -0,0 +1,117 @@ | |||
| From 5dc162306c4e5ff17493db7eaeccaaa60734ff8f Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 15 Dec 2021 17:44:49 +0000 | ||||
| Subject: [PATCH] drm/vc4: Remove splitting the bridge chain from the driver. | ||||
| 
 | ||||
| Splitting the bridge chain fails for atomic bridges as the | ||||
| framework can't add the relevant state in | ||||
| drm_atomic_add_encoder_bridges. | ||||
| The chain was split because we needed to power up before | ||||
| calling pre_enable, but that is now done in mode_set, and will | ||||
| move into the framework. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 47 ----------------------------------- | ||||
|  1 file changed, 47 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -557,7 +557,6 @@ struct vc4_dsi {
 | ||||
|  	struct platform_device *pdev; | ||||
|   | ||||
|  	struct drm_bridge *out_bridge; | ||||
| -	struct list_head bridge_chain;
 | ||||
|   | ||||
|  	void __iomem *regs; | ||||
|   | ||||
| @@ -794,23 +793,9 @@ static void vc4_dsi_encoder_disable(stru
 | ||||
|  { | ||||
|  	struct vc4_dsi *dsi = to_vc4_dsi(encoder); | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
| -	struct drm_bridge *iter;
 | ||||
| -
 | ||||
| -	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
 | ||||
| -		if (iter->funcs->disable)
 | ||||
| -			iter->funcs->disable(iter);
 | ||||
| -
 | ||||
| -		if (iter == dsi->out_bridge)
 | ||||
| -			break;
 | ||||
| -	}
 | ||||
|   | ||||
|  	vc4_dsi_ulps(dsi, true); | ||||
|   | ||||
| -	list_for_each_entry_from(iter, &dsi->bridge_chain, chain_node) {
 | ||||
| -		if (iter->funcs->post_disable)
 | ||||
| -			iter->funcs->post_disable(iter);
 | ||||
| -	}
 | ||||
| -
 | ||||
|  	clk_disable_unprepare(dsi->pll_phy_clock); | ||||
|  	clk_disable_unprepare(dsi->escape_clock); | ||||
|  	clk_disable_unprepare(dsi->pixel_clock); | ||||
| @@ -1112,12 +1097,6 @@ static void vc4_dsi_encoder_enable(struc
 | ||||
|  	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); | ||||
|  	struct vc4_dsi *dsi = vc4_encoder->dsi; | ||||
|  	bool debug_dump_regs = false; | ||||
| -	struct drm_bridge *iter;
 | ||||
| -
 | ||||
| -	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
 | ||||
| -		if (iter->funcs->pre_enable)
 | ||||
| -			iter->funcs->pre_enable(iter);
 | ||||
| -	}
 | ||||
|   | ||||
|  	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | ||||
|  		DSI_PORT_WRITE(DISP0_CTRL, | ||||
| @@ -1134,11 +1113,6 @@ static void vc4_dsi_encoder_enable(struc
 | ||||
|  			       DSI_DISP0_ENABLE); | ||||
|  	} | ||||
|   | ||||
| -	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
 | ||||
| -		if (iter->funcs->enable)
 | ||||
| -			iter->funcs->enable(iter);
 | ||||
| -	}
 | ||||
| -
 | ||||
|  	if (debug_dump_regs) { | ||||
|  		struct drm_printer p = drm_info_printer(&dsi->pdev->dev); | ||||
|  		dev_info(&dsi->pdev->dev, "DSI regs after:\n"); | ||||
| @@ -1626,7 +1600,6 @@ static int vc4_dsi_bind(struct device *d
 | ||||
|   | ||||
|  	dsi->variant = of_device_get_match_data(dev); | ||||
|   | ||||
| -	INIT_LIST_HEAD(&dsi->bridge_chain);
 | ||||
|  	dsi->encoder.type = dsi->variant->port ? | ||||
|  		VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; | ||||
|   | ||||
| @@ -1763,32 +1736,12 @@ static int vc4_dsi_bind(struct device *d
 | ||||
|  	ret = drm_bridge_attach(encoder, dsi->out_bridge, NULL, 0); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
| -	/* Disable the atomic helper calls into the bridge.  We
 | ||||
| -	 * manually call the bridge pre_enable / enable / etc. calls
 | ||||
| -	 * from our driver, since we need to sequence them within the
 | ||||
| -	 * encoder's enable/disable paths.
 | ||||
| -	 */
 | ||||
| -	list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_unbind(struct device *dev, struct device *master,
 | ||||
| -			   void *data)
 | ||||
| -{
 | ||||
| -	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 | ||||
| -	struct drm_encoder *encoder = &dsi->encoder.base;
 | ||||
| -
 | ||||
| -	/*
 | ||||
| -	 * Restore the bridge_chain so the bridge detach procedure can happen
 | ||||
| -	 * normally.
 | ||||
| -	 */
 | ||||
| -	list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain);
 | ||||
| -}
 | ||||
| -
 | ||||
|  static const struct component_ops vc4_dsi_ops = { | ||||
|  	.bind   = vc4_dsi_bind, | ||||
| -	.unbind = vc4_dsi_unbind,
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_dsi_dev_probe(struct platform_device *pdev) | ||||
|  | @ -0,0 +1,84 @@ | |||
| From fbb674cc8153a69d8224e8c5bb15ebbd1440fdc2 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 15 Dec 2021 17:47:14 +0000 | ||||
| Subject: [PATCH] drm/vc4: Convert vc4_dsi to use atomic | ||||
|  enable/disable/mode_set. | ||||
| 
 | ||||
| The atomic calls are preferred as the non-atomic ones | ||||
| are deprecated. In preparation for conversion to a bridge, | ||||
| switch to the atomic calls. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 23 +++++++++++++++-------- | ||||
|  1 file changed, 15 insertions(+), 8 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -789,7 +789,8 @@ dsi_esc_timing(u32 ns)
 | ||||
|  	return DIV_ROUND_UP(ns, ESC_TIME_NS); | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 | ||||
| +static void vc4_dsi_encoder_disable(struct drm_encoder *encoder,
 | ||||
| +				    struct drm_atomic_state *state)
 | ||||
|  { | ||||
|  	struct vc4_dsi *dsi = to_vc4_dsi(encoder); | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
| @@ -853,17 +854,18 @@ static bool vc4_dsi_encoder_mode_fixup(s
 | ||||
|  } | ||||
|   | ||||
|  static void vc4_dsi_encoder_mode_set(struct drm_encoder *encoder, | ||||
| -				     struct drm_display_mode *mode,
 | ||||
| -				     struct drm_display_mode *adjusted_mode)
 | ||||
| +				     struct drm_crtc_state *crtc_state,
 | ||||
| +				     struct drm_connector_state *conn_state)
 | ||||
|  { | ||||
|  	struct vc4_dsi *dsi = to_vc4_dsi(encoder); | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
| +	const struct drm_display_mode *mode;
 | ||||
|  	bool debug_dump_regs = false; | ||||
|  	unsigned long hs_clock; | ||||
|  	u32 ui_ns; | ||||
|  	/* Minimum LP state duration in escape clock cycles. */ | ||||
|  	u32 lpx = dsi_esc_timing(60); | ||||
| -	unsigned long pixel_clock_hz = adjusted_mode->clock * 1000;
 | ||||
| +	unsigned long pixel_clock_hz;
 | ||||
|  	unsigned long dsip_clock; | ||||
|  	unsigned long phy_clock; | ||||
|  	int ret; | ||||
| @@ -880,6 +882,10 @@ static void vc4_dsi_encoder_mode_set(str
 | ||||
|  		drm_print_regset32(&p, &dsi->regset); | ||||
|  	} | ||||
|   | ||||
| +	mode = &crtc_state->adjusted_mode;
 | ||||
| +
 | ||||
| +	pixel_clock_hz = mode->clock * 1000;
 | ||||
| +
 | ||||
|  	/* Round up the clk_set_rate() request slightly, since | ||||
|  	 * PLLD_DSI1 is an integer divider and its rate selection will | ||||
|  	 * never round up. | ||||
| @@ -1092,7 +1098,8 @@ static void vc4_dsi_encoder_mode_set(str
 | ||||
|  	vc4_dsi_ulps(dsi, false); | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 | ||||
| +static void vc4_dsi_encoder_enable(struct drm_encoder *encoder,
 | ||||
| +				   struct drm_atomic_state *state)
 | ||||
|  { | ||||
|  	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); | ||||
|  	struct vc4_dsi *dsi = vc4_encoder->dsi; | ||||
| @@ -1349,10 +1356,10 @@ static const struct mipi_dsi_host_ops vc
 | ||||
|  }; | ||||
|   | ||||
|  static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { | ||||
| -	.disable = vc4_dsi_encoder_disable,
 | ||||
| -	.enable = vc4_dsi_encoder_enable,
 | ||||
| +	.atomic_disable = vc4_dsi_encoder_disable,
 | ||||
| +	.atomic_enable = vc4_dsi_encoder_enable,
 | ||||
|  	.mode_fixup = vc4_dsi_encoder_mode_fixup, | ||||
| -	.mode_set = vc4_dsi_encoder_mode_set,
 | ||||
| +	.atomic_mode_set = vc4_dsi_encoder_mode_set,
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_dsi_late_register(struct drm_encoder *encoder) | ||||
|  | @ -0,0 +1,266 @@ | |||
| From e80165b59d158eb60ac5ade9553127497c4631d9 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 15 Dec 2021 17:57:45 +0000 | ||||
| Subject: [PATCH] drm/vc4: Convert vc4_dsi to using a bridge instead of | ||||
|  encoder. | ||||
| 
 | ||||
| Remove the encoder functions, and create a bridge attached to | ||||
| this dumb encoder which implements the same functionality. | ||||
| 
 | ||||
| As a bridge has state which an encoder doesn't, we need to | ||||
| add the state management functions as well. | ||||
| 
 | ||||
| As there is no bridge atomic_mode_set, move the initialisation | ||||
| code that was in mode_set into _pre_enable. | ||||
| The code to actually enable and disable sending video are split | ||||
| from the general control into _enable and _disable. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 122 +++++++++++++++++++++++++--------- | ||||
|  1 file changed, 90 insertions(+), 32 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -557,6 +557,7 @@ struct vc4_dsi {
 | ||||
|  	struct platform_device *pdev; | ||||
|   | ||||
|  	struct drm_bridge *out_bridge; | ||||
| +	struct drm_bridge bridge;
 | ||||
|   | ||||
|  	void __iomem *regs; | ||||
|   | ||||
| @@ -608,6 +609,12 @@ to_vc4_dsi(struct drm_encoder *encoder)
 | ||||
|  	return container_of(encoder, struct vc4_dsi, encoder.base); | ||||
|  } | ||||
|   | ||||
| +static inline struct vc4_dsi *
 | ||||
| +bridge_to_vc4_dsi(struct drm_bridge *bridge)
 | ||||
| +{
 | ||||
| +	return container_of(bridge, struct vc4_dsi, bridge);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static inline void | ||||
|  dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) | ||||
|  { | ||||
| @@ -789,10 +796,21 @@ dsi_esc_timing(u32 ns)
 | ||||
|  	return DIV_ROUND_UP(ns, ESC_TIME_NS); | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_encoder_disable(struct drm_encoder *encoder,
 | ||||
| -				    struct drm_atomic_state *state)
 | ||||
| +static void vc4_dsi_bridge_disable(struct drm_bridge *bridge,
 | ||||
| +				   struct drm_bridge_state *state)
 | ||||
|  { | ||||
| -	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
| +	u32 disp0_ctrl;
 | ||||
| +
 | ||||
| +	disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
 | ||||
| +	disp0_ctrl &= ~DSI_DISP0_ENABLE;
 | ||||
| +	DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
 | ||||
| +					struct drm_bridge_state *state)
 | ||||
| +{
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
|   | ||||
|  	vc4_dsi_ulps(dsi, true); | ||||
| @@ -817,11 +835,11 @@ static void vc4_dsi_encoder_disable(stru
 | ||||
|   * higher-than-expected clock rate to the panel, but that's what the | ||||
|   * firmware does too. | ||||
|   */ | ||||
| -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 | ||||
| -				       const struct drm_display_mode *mode,
 | ||||
| -				       struct drm_display_mode *adjusted_mode)
 | ||||
| +static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
 | ||||
| +				      const struct drm_display_mode *mode,
 | ||||
| +				      struct drm_display_mode *adjusted_mode)
 | ||||
|  { | ||||
| -	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
|  	struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); | ||||
|  	unsigned long parent_rate = clk_get_rate(phy_parent); | ||||
|  	unsigned long pixel_clock_hz = mode->clock * 1000; | ||||
| @@ -853,15 +871,18 @@ static bool vc4_dsi_encoder_mode_fixup(s
 | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -static void vc4_dsi_encoder_mode_set(struct drm_encoder *encoder,
 | ||||
| -				     struct drm_crtc_state *crtc_state,
 | ||||
| -				     struct drm_connector_state *conn_state)
 | ||||
| +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
 | ||||
| +				      struct drm_bridge_state *old_state)
 | ||||
|  { | ||||
| -	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 | ||||
| +	struct drm_atomic_state *state = old_state->base.state;
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
| +	const struct drm_crtc_state *crtc_state;
 | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
|  	const struct drm_display_mode *mode; | ||||
| +	struct drm_connector *connector;
 | ||||
|  	bool debug_dump_regs = false; | ||||
|  	unsigned long hs_clock; | ||||
| +	struct drm_crtc *crtc;
 | ||||
|  	u32 ui_ns; | ||||
|  	/* Minimum LP state duration in escape clock cycles. */ | ||||
|  	u32 lpx = dsi_esc_timing(60); | ||||
| @@ -882,6 +903,14 @@ static void vc4_dsi_encoder_mode_set(str
 | ||||
|  		drm_print_regset32(&p, &dsi->regset); | ||||
|  	} | ||||
|   | ||||
| +	/*
 | ||||
| +	 * Retrieve the CRTC adjusted mode. This requires a little dance to go
 | ||||
| +	 * from the bridge to the encoder, to the connector and to the CRTC.
 | ||||
| +	 */
 | ||||
| +	connector = drm_atomic_get_new_connector_for_encoder(state,
 | ||||
| +							     bridge->encoder);
 | ||||
| +	crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
 | ||||
| +	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
 | ||||
|  	mode = &crtc_state->adjusted_mode; | ||||
|   | ||||
|  	pixel_clock_hz = mode->clock * 1000; | ||||
| @@ -1096,14 +1125,6 @@ static void vc4_dsi_encoder_mode_set(str
 | ||||
|  		       ~DSI_PORT_BIT(PHY_AFEC0_RESET)); | ||||
|   | ||||
|  	vc4_dsi_ulps(dsi, false); | ||||
| -}
 | ||||
| -
 | ||||
| -static void vc4_dsi_encoder_enable(struct drm_encoder *encoder,
 | ||||
| -				   struct drm_atomic_state *state)
 | ||||
| -{
 | ||||
| -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
 | ||||
| -	struct vc4_dsi *dsi = vc4_encoder->dsi;
 | ||||
| -	bool debug_dump_regs = false;
 | ||||
|   | ||||
|  	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | ||||
|  		DSI_PORT_WRITE(DISP0_CTRL, | ||||
| @@ -1112,13 +1133,23 @@ static void vc4_dsi_encoder_enable(struc
 | ||||
|  			       VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) | | ||||
|  			       VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME, | ||||
|  					     DSI_DISP0_LP_STOP_CTRL) | | ||||
| -			       DSI_DISP0_ST_END |
 | ||||
| -			       DSI_DISP0_ENABLE);
 | ||||
| +			       DSI_DISP0_ST_END);
 | ||||
|  	} else { | ||||
|  		DSI_PORT_WRITE(DISP0_CTRL, | ||||
| -			       DSI_DISP0_COMMAND_MODE |
 | ||||
| -			       DSI_DISP0_ENABLE);
 | ||||
| +			       DSI_DISP0_COMMAND_MODE);
 | ||||
|  	} | ||||
| +}
 | ||||
| +
 | ||||
| +static void vc4_dsi_bridge_enable(struct drm_bridge *bridge,
 | ||||
| +				  struct drm_bridge_state *old_state)
 | ||||
| +{
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
| +	bool debug_dump_regs = false;
 | ||||
| +	u32 disp0_ctrl;
 | ||||
| +
 | ||||
| +	disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
 | ||||
| +	disp0_ctrl |= DSI_DISP0_ENABLE;
 | ||||
| +	DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
 | ||||
|   | ||||
|  	if (debug_dump_regs) { | ||||
|  		struct drm_printer p = drm_info_printer(&dsi->pdev->dev); | ||||
| @@ -1127,6 +1158,16 @@ static void vc4_dsi_encoder_enable(struc
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +static int vc4_dsi_bridge_attach(struct drm_bridge *bridge,
 | ||||
| +				 enum drm_bridge_attach_flags flags)
 | ||||
| +{
 | ||||
| +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 | ||||
| +
 | ||||
| +	/* Attach the panel or bridge to the dsi bridge */
 | ||||
| +	return drm_bridge_attach(bridge->encoder, dsi->out_bridge,
 | ||||
| +				 &dsi->bridge, flags);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, | ||||
|  				     const struct mipi_dsi_msg *msg) | ||||
|  { | ||||
| @@ -1303,6 +1344,7 @@ static int vc4_dsi_host_attach(struct mi
 | ||||
|  			       struct mipi_dsi_device *device) | ||||
|  { | ||||
|  	struct vc4_dsi *dsi = host_to_dsi(host); | ||||
| +	int ret;
 | ||||
|   | ||||
|  	dsi->lanes = device->lanes; | ||||
|  	dsi->channel = device->channel; | ||||
| @@ -1337,7 +1379,15 @@ static int vc4_dsi_host_attach(struct mi
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| -	return component_add(&dsi->pdev->dev, &vc4_dsi_ops);
 | ||||
| +	drm_bridge_add(&dsi->bridge);
 | ||||
| +
 | ||||
| +	ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops);
 | ||||
| +	if (ret) {
 | ||||
| +		drm_bridge_remove(&dsi->bridge);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
|  } | ||||
|   | ||||
|  static int vc4_dsi_host_detach(struct mipi_dsi_host *host, | ||||
| @@ -1346,6 +1396,7 @@ static int vc4_dsi_host_detach(struct mi
 | ||||
|  	struct vc4_dsi *dsi = host_to_dsi(host); | ||||
|   | ||||
|  	component_del(&dsi->pdev->dev, &vc4_dsi_ops); | ||||
| +	drm_bridge_remove(&dsi->bridge);
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1355,11 +1406,16 @@ static const struct mipi_dsi_host_ops vc
 | ||||
|  	.transfer = vc4_dsi_host_transfer, | ||||
|  }; | ||||
|   | ||||
| -static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
 | ||||
| -	.atomic_disable = vc4_dsi_encoder_disable,
 | ||||
| -	.atomic_enable = vc4_dsi_encoder_enable,
 | ||||
| -	.mode_fixup = vc4_dsi_encoder_mode_fixup,
 | ||||
| -	.atomic_mode_set = vc4_dsi_encoder_mode_set,
 | ||||
| +static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
 | ||||
| +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 | ||||
| +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
 | ||||
| +	.atomic_reset = drm_atomic_helper_bridge_reset,
 | ||||
| +	.atomic_pre_enable = vc4_dsi_bridge_pre_enable,
 | ||||
| +	.atomic_enable = vc4_dsi_bridge_enable,
 | ||||
| +	.atomic_disable = vc4_dsi_bridge_disable,
 | ||||
| +	.atomic_post_disable = vc4_dsi_bridge_post_disable,
 | ||||
| +	.attach = vc4_dsi_bridge_attach,
 | ||||
| +	.mode_fixup = vc4_dsi_bridge_mode_fixup,
 | ||||
|  }; | ||||
|   | ||||
|  static int vc4_dsi_late_register(struct drm_encoder *encoder) | ||||
| @@ -1734,13 +1790,11 @@ static int vc4_dsi_bind(struct device *d
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
 | ||||
| -
 | ||||
|  	ret = devm_pm_runtime_enable(dev); | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	ret = drm_bridge_attach(encoder, dsi->out_bridge, NULL, 0);
 | ||||
| +	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| @@ -1762,7 +1816,11 @@ static int vc4_dsi_dev_probe(struct plat
 | ||||
|  	dev_set_drvdata(dev, dsi); | ||||
|   | ||||
|  	kref_init(&dsi->kref); | ||||
| +
 | ||||
|  	dsi->pdev = pdev; | ||||
| +	dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
 | ||||
| +	dsi->bridge.of_node = dev->of_node;
 | ||||
| +	dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
 | ||||
|  	dsi->dsi_host.ops = &vc4_dsi_host_ops; | ||||
|  	dsi->dsi_host.dev = dev; | ||||
|  	mipi_dsi_host_register(&dsi->dsi_host); | ||||
|  | @ -0,0 +1,31 @@ | |||
| From 8e57d84f06b6847efff162a69223200fe6553319 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 11 Feb 2022 14:15:26 +0000 | ||||
| Subject: [PATCH] drm/vc4: Remove entry to ULPS from vc4_dsi post_disable | ||||
| 
 | ||||
| Post_disable was sending the D-PHY sequence to put any device | ||||
| into ULPS suspend mode, and then cutting power to the DSI block. | ||||
| The power-on reset state of the DSI block is for DSI to be in | ||||
| an operational state, not ULPS, so it then never sent the sequence | ||||
| for exiting ULPS. Any attached device that didn't have an external | ||||
| reset therefore remained in ULPS / standby, and didn't function. | ||||
| 
 | ||||
| Use of ULPS isn't well specified in DRM, therefore remove entering | ||||
| it to avoid the above situation. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_dsi.c | 2 -- | ||||
|  1 file changed, 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
 | ||||
| @@ -813,8 +813,6 @@ static void vc4_dsi_bridge_post_disable(
 | ||||
|  	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); | ||||
|  	struct device *dev = &dsi->pdev->dev; | ||||
|   | ||||
| -	vc4_dsi_ulps(dsi, true);
 | ||||
| -
 | ||||
|  	clk_disable_unprepare(dsi->pll_phy_clock); | ||||
|  	clk_disable_unprepare(dsi->escape_clock); | ||||
|  	clk_disable_unprepare(dsi->pixel_clock); | ||||
|  | @ -0,0 +1,47 @@ | |||
| From beb8ee0d475c6320eed3024686702d439db24f66 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Wed, 23 Feb 2022 15:36:56 +0000 | ||||
| Subject: [PATCH] drm/panel: Add prepare_upstream_first flag to drm_panel | ||||
| 
 | ||||
| Mapping to the drm_bridge flag pre_enable_upstream_first, | ||||
| add a new flag prepare_upstream_first to drm_panel to allow | ||||
| the panel driver to request that the upstream bridge should | ||||
| be pre_enabled before the panel prepare. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/bridge/panel.c |  3 +++ | ||||
|  include/drm/drm_panel.h        | 10 ++++++++++ | ||||
|  2 files changed, 13 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/bridge/panel.c
 | ||||
| +++ b/drivers/gpu/drm/bridge/panel.c
 | ||||
| @@ -258,6 +258,9 @@ struct drm_bridge *drm_panel_bridge_add_
 | ||||
|  	panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; | ||||
|  	panel_bridge->bridge.type = connector_type; | ||||
|   | ||||
| +	panel_bridge->bridge.pre_enable_upstream_first =
 | ||||
| +						panel->prepare_upstream_first;
 | ||||
| +
 | ||||
|  	drm_bridge_add(&panel_bridge->bridge); | ||||
|   | ||||
|  	return &panel_bridge->bridge; | ||||
| --- a/include/drm/drm_panel.h
 | ||||
| +++ b/include/drm/drm_panel.h
 | ||||
| @@ -196,6 +196,16 @@ struct drm_panel {
 | ||||
|  	 * Panel entry in registry. | ||||
|  	 */ | ||||
|  	struct list_head list; | ||||
| +
 | ||||
| +	/**
 | ||||
| +	 * @prepare_upstream_first:
 | ||||
| +	 *
 | ||||
| +	 * The upstream controller should be prepared first, before the prepare
 | ||||
| +	 * for the panel is called. This is largely required for DSI panels
 | ||||
| +	 * where the DSI host controller should be initialised to LP-11 before
 | ||||
| +	 * the panel is powered up.
 | ||||
| +	 */
 | ||||
| +	bool prepare_upstream_first;
 | ||||
|  }; | ||||
|   | ||||
|  void drm_panel_init(struct drm_panel *panel, struct device *dev, | ||||
|  | @ -0,0 +1,36 @@ | |||
| From 115fd0087451349c1e2761e89984545fe614c181 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 11 Mar 2022 17:24:37 +0000 | ||||
| Subject: [PATCH] drm: Include drm_connector.h from drm_panel.h | ||||
| 
 | ||||
| drm_panel.h wants to reference enum drm_panel_orientation which is defined | ||||
| in drm_connector.h (despite the name). | ||||
| Include drm_connector.h in drm_panel.h to avoid the rare situation where | ||||
| drm_panel.h is used with drm_connector.h | ||||
| 
 | ||||
| https://github.com/raspberrypi/linux/issues/4919 | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  include/drm/drm_panel.h | 3 +-- | ||||
|  1 file changed, 1 insertion(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/include/drm/drm_panel.h
 | ||||
| +++ b/include/drm/drm_panel.h
 | ||||
| @@ -24,6 +24,7 @@
 | ||||
|  #ifndef __DRM_PANEL_H__ | ||||
|  #define __DRM_PANEL_H__ | ||||
|   | ||||
| +#include <drm/drm_connector.h>
 | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/errno.h> | ||||
|  #include <linux/list.h> | ||||
| @@ -36,8 +37,6 @@ struct drm_device;
 | ||||
|  struct drm_panel; | ||||
|  struct display_timing; | ||||
|   | ||||
| -enum drm_panel_orientation;
 | ||||
| -
 | ||||
|  /** | ||||
|   * struct drm_panel_funcs - perform operations on a given panel | ||||
|   * | ||||
|  | @ -0,0 +1,25 @@ | |||
| From 4c5c0fa5ae4aeee2ca588935960aa48e666dafda Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 16 Dec 2021 15:33:43 +0000 | ||||
| Subject: [PATCH] drm/tc358762: Set the pre_enable_upstream_first flag to | ||||
|  configure DSI host | ||||
| 
 | ||||
| TC358762 wants the DSI host to be prepared before it is powered up, so | ||||
| set the flag to request that the upstream bridges have their | ||||
| pre_enable called first. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/bridge/tc358762.c | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/bridge/tc358762.c
 | ||||
| +++ b/drivers/gpu/drm/bridge/tc358762.c
 | ||||
| @@ -229,6 +229,7 @@ static int tc358762_probe(struct mipi_ds
 | ||||
|  	ctx->bridge.funcs = &tc358762_bridge_funcs; | ||||
|  	ctx->bridge.type = DRM_MODE_CONNECTOR_DPI; | ||||
|  	ctx->bridge.of_node = dev->of_node; | ||||
| +	ctx->bridge.pre_enable_upstream_first = true;
 | ||||
|   | ||||
|  	drm_bridge_add(&ctx->bridge); | ||||
|   | ||||
|  | @ -0,0 +1,144 @@ | |||
| From 5680572cb50065d522e64792a64f915cee0f6cfb Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Tue, 25 Jan 2022 17:28:18 +0000 | ||||
| Subject: [PATCH] drm/vc4: Support zpos on all planes | ||||
| 
 | ||||
| Adds the zpos property to all planes, and creates the dlist | ||||
| by placing the fragments in the correct order based on zpos. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c   | 43 +++++++++++++++++++++------------ | ||||
|  drivers/gpu/drm/vc4/vc4_kms.c   |  3 +-- | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 22 ++++++++++++++--- | ||||
|  3 files changed, 48 insertions(+), 20 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -769,6 +769,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
 | ||||
|  	bool enable_bg_fill = false; | ||||
|  	u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; | ||||
|  	u32 __iomem *dlist_next = dlist_start; | ||||
| +	unsigned int zpos = 0;
 | ||||
| +	bool found = false;
 | ||||
|  	int idx; | ||||
|   | ||||
|  	if (!drm_dev_enter(dev, &idx)) { | ||||
| @@ -782,23 +784,34 @@ void vc4_hvs_atomic_flush(struct drm_crt
 | ||||
|  	} | ||||
|   | ||||
|  	/* Copy all the active planes' dlist contents to the hardware dlist. */ | ||||
| -	drm_atomic_crtc_for_each_plane(plane, crtc) {
 | ||||
| -		/* Is this the first active plane? */
 | ||||
| -		if (dlist_next == dlist_start) {
 | ||||
| -			/* We need to enable background fill when a plane
 | ||||
| -			 * could be alpha blending from the background, i.e.
 | ||||
| -			 * where no other plane is underneath. It suffices to
 | ||||
| -			 * consider the first active plane here since we set
 | ||||
| -			 * needs_bg_fill such that either the first plane
 | ||||
| -			 * already needs it or all planes on top blend from
 | ||||
| -			 * the first or a lower plane.
 | ||||
| -			 */
 | ||||
| -			vc4_plane_state = to_vc4_plane_state(plane->state);
 | ||||
| -			enable_bg_fill = vc4_plane_state->needs_bg_fill;
 | ||||
| +	do {
 | ||||
| +		found = false;
 | ||||
| +
 | ||||
| +		drm_atomic_crtc_for_each_plane(plane, crtc) {
 | ||||
| +			if (plane->state->normalized_zpos != zpos)
 | ||||
| +				continue;
 | ||||
| +
 | ||||
| +			/* Is this the first active plane? */
 | ||||
| +			if (dlist_next == dlist_start) {
 | ||||
| +				/* We need to enable background fill when a plane
 | ||||
| +				 * could be alpha blending from the background, i.e.
 | ||||
| +				 * where no other plane is underneath. It suffices to
 | ||||
| +				 * consider the first active plane here since we set
 | ||||
| +				 * needs_bg_fill such that either the first plane
 | ||||
| +				 * already needs it or all planes on top blend from
 | ||||
| +				 * the first or a lower plane.
 | ||||
| +				 */
 | ||||
| +				vc4_plane_state = to_vc4_plane_state(plane->state);
 | ||||
| +				enable_bg_fill = vc4_plane_state->needs_bg_fill;
 | ||||
| +			}
 | ||||
| +
 | ||||
| +			dlist_next += vc4_plane_write_dlist(plane, dlist_next);
 | ||||
| +
 | ||||
| +			found = true;
 | ||||
|  		} | ||||
|   | ||||
| -		dlist_next += vc4_plane_write_dlist(plane, dlist_next);
 | ||||
| -	}
 | ||||
| +		zpos++;
 | ||||
| +	} while (found);
 | ||||
|   | ||||
|  	writel(SCALER_CTL0_END, dlist_next); | ||||
|  	dlist_next++; | ||||
| --- a/drivers/gpu/drm/vc4/vc4_kms.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_kms.c
 | ||||
| @@ -1066,8 +1066,7 @@ int vc4_kms_load(struct drm_device *dev)
 | ||||
|  	dev->mode_config.helper_private = &vc4_mode_config_helpers; | ||||
|  	dev->mode_config.preferred_depth = 24; | ||||
|  	dev->mode_config.async_page_flip = true; | ||||
| -	if (vc4->firmware_kms)
 | ||||
| -		dev->mode_config.normalize_zpos = true;
 | ||||
| +	dev->mode_config.normalize_zpos = true;
 | ||||
|   | ||||
|  	ret = vc4_ctm_obj_init(vc4); | ||||
|  	if (ret) | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -1600,9 +1600,14 @@ struct drm_plane *vc4_plane_init(struct
 | ||||
|  					  DRM_COLOR_YCBCR_BT709, | ||||
|  					  DRM_COLOR_YCBCR_LIMITED_RANGE); | ||||
|   | ||||
| +	if (type == DRM_PLANE_TYPE_PRIMARY)
 | ||||
| +		drm_plane_create_zpos_immutable_property(plane, 0);
 | ||||
| +
 | ||||
|  	return plane; | ||||
|  } | ||||
|   | ||||
| +#define VC4_NUM_OVERLAY_PLANES	16
 | ||||
| +
 | ||||
|  int vc4_plane_create_additional_planes(struct drm_device *drm) | ||||
|  { | ||||
|  	struct drm_plane *cursor_plane; | ||||
| @@ -1618,24 +1623,35 @@ int vc4_plane_create_additional_planes(s
 | ||||
|  	 * modest number of planes to expose, that should hopefully | ||||
|  	 * still cover any sane usecase. | ||||
|  	 */ | ||||
| -	for (i = 0; i < 16; i++) {
 | ||||
| +	for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) {
 | ||||
|  		struct drm_plane *plane = | ||||
|  			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, | ||||
|  				       GENMASK(drm->mode_config.num_crtc - 1, 0)); | ||||
|   | ||||
|  		if (IS_ERR(plane)) | ||||
|  			continue; | ||||
| +
 | ||||
| +		/* Create zpos property. Max of all the overlays + 1 primary +
 | ||||
| +		 * 1 cursor plane on a crtc.
 | ||||
| +		 */
 | ||||
| +		drm_plane_create_zpos_property(plane, i + 1, 1,
 | ||||
| +					       VC4_NUM_OVERLAY_PLANES + 1);
 | ||||
|  	} | ||||
|   | ||||
|  	drm_for_each_crtc(crtc, drm) { | ||||
|  		/* Set up the legacy cursor after overlay initialization, | ||||
| -		 * since we overlay planes on the CRTC in the order they were
 | ||||
| -		 * initialized.
 | ||||
| +		 * since the zpos fallback is that planes are rendered by plane
 | ||||
| +		 * ID order, and that then puts the cursor on top.
 | ||||
|  		 */ | ||||
|  		cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR, | ||||
|  					      drm_crtc_mask(crtc)); | ||||
|  		if (!IS_ERR(cursor_plane)) { | ||||
|  			crtc->cursor = cursor_plane; | ||||
| +
 | ||||
| +			drm_plane_create_zpos_property(cursor_plane,
 | ||||
| +						       VC4_NUM_OVERLAY_PLANES + 1,
 | ||||
| +						       1,
 | ||||
| +						       VC4_NUM_OVERLAY_PLANES + 1);
 | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
|  | @ -0,0 +1,301 @@ | |||
| From a363b78747ab3cbb671848c9d313deff4e3ef929 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 7 Mar 2022 15:19:38 +0000 | ||||
| Subject: [PATCH] drm/vc4: hdmi: Add CSC for BT601/709/2020 limited and full | ||||
|  range output | ||||
| 
 | ||||
| The HVS always composes in the RGB domain, but there is a colourspace | ||||
| conversion block on the output to allow for sending YCbCr over the | ||||
| HDMI interface. | ||||
| The colourspace on that link is configurable via the "Colorspace" | ||||
| property on the connector, and that updates the infoframes. There | ||||
| is also selection of limited or full range based on the mode selected | ||||
| or an override. | ||||
| 
 | ||||
| Add code to update the CSC as well so that the metadata matches the | ||||
| image data. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hdmi.c | 196 ++++++++++++++++++++++++--------- | ||||
|  1 file changed, 145 insertions(+), 51 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| @@ -158,8 +158,8 @@ static bool vc4_hdmi_mode_needs_scrambli
 | ||||
|  	return clock > HDMI_14_MAX_TMDS_CLK; | ||||
|  } | ||||
|   | ||||
| -static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi,
 | ||||
| -				       const struct drm_display_mode *mode)
 | ||||
| +static bool vc4_hdmi_is_full_range(struct vc4_hdmi *vc4_hdmi,
 | ||||
| +				   const struct drm_display_mode *mode)
 | ||||
|  { | ||||
|  	struct drm_display_info *display = &vc4_hdmi->connector.display_info; | ||||
|   | ||||
| @@ -901,7 +901,7 @@ static void vc4_hdmi_set_avi_infoframe(s
 | ||||
|   | ||||
|  	drm_hdmi_avi_infoframe_quant_range(&frame.avi, | ||||
|  					   connector, mode, | ||||
| -					   vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode) ?
 | ||||
| +					   vc4_hdmi_is_full_range(vc4_hdmi, mode) ?
 | ||||
|  					   HDMI_QUANTIZATION_RANGE_FULL : | ||||
|  					   HDMI_QUANTIZATION_RANGE_LIMITED); | ||||
|  	drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate); | ||||
| @@ -1154,7 +1154,7 @@ static void vc4_hdmi_csc_setup(struct vc
 | ||||
|  	csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, | ||||
|  				VC4_HD_CSC_CTL_ORDER); | ||||
|   | ||||
| -	if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) {
 | ||||
| +	if (!vc4_hdmi_is_full_range(vc4_hdmi, mode)) {
 | ||||
|  		/* CEA VICs other than #1 requre limited range RGB | ||||
|  		 * output unless overridden by an AVI infoframe. | ||||
|  		 * Apply a colorspace conversion to squash 0-255 down | ||||
| @@ -1193,15 +1193,6 @@ static void vc4_hdmi_csc_setup(struct vc
 | ||||
|   * [ 0      1      0      0] | ||||
|   * [ 0      0      1      0] | ||||
|   * | ||||
| - * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
 | ||||
| - */
 | ||||
| -static const u16 vc5_hdmi_csc_full_rgb_unity[3][4] = {
 | ||||
| -	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 | ||||
| -	{ 0x0000, 0x2000, 0x0000, 0x0000 },
 | ||||
| -	{ 0x0000, 0x0000, 0x2000, 0x0000 },
 | ||||
| -};
 | ||||
| -
 | ||||
| -/*
 | ||||
|   * CEA VICs other than #1 require limited range RGB output unless | ||||
|   * overridden by an AVI infoframe. Apply a colorspace conversion to | ||||
|   * squash 0-255 down to 16-235. The matrix here is: | ||||
| @@ -1212,43 +1203,105 @@ static const u16 vc5_hdmi_csc_full_rgb_u
 | ||||
|   * | ||||
|   * Matrix is signed 2p13 fixed point, with signed 9p6 offsets | ||||
|   */ | ||||
| -static const u16 vc5_hdmi_csc_full_rgb_to_limited_rgb[3][4] = {
 | ||||
| -	{ 0x1b80, 0x0000, 0x0000, 0x0400 },
 | ||||
| -	{ 0x0000, 0x1b80, 0x0000, 0x0400 },
 | ||||
| -	{ 0x0000, 0x0000, 0x1b80, 0x0400 },
 | ||||
| +static const u16 vc5_hdmi_csc_full_rgb_to_rgb[2][3][4] = {
 | ||||
| +	{
 | ||||
| +		/* Full range - unity */
 | ||||
| +		{ 0x2000, 0x0000, 0x0000, 0x0000 },
 | ||||
| +		{ 0x0000, 0x2000, 0x0000, 0x0000 },
 | ||||
| +		{ 0x0000, 0x0000, 0x2000, 0x0000 },
 | ||||
| +	}, {
 | ||||
| +		/* Limited range */
 | ||||
| +		{ 0x1b80, 0x0000, 0x0000, 0x0400 },
 | ||||
| +		{ 0x0000, 0x1b80, 0x0000, 0x0400 },
 | ||||
| +		{ 0x0000, 0x0000, 0x1b80, 0x0400 },
 | ||||
| +	}
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Conversion between Full Range RGB and YUV using the BT.601 Colorspace
 | ||||
| + *
 | ||||
| + * Full range
 | ||||
| + * [    0.299000   0.587000   0.114000   0.000000 ]
 | ||||
| + * [   -0.168736  -0.331264   0.500000 128.000000 ]
 | ||||
| + * [    0.500000  -0.418688  -0.081312 128.000000 ]
 | ||||
| + *
 | ||||
| + * Limited range
 | ||||
| + * [    0.255785   0.502160   0.097523  16.000000 ]
 | ||||
| + * [   -0.147644  -0.289856   0.437500 128.000000 ]
 | ||||
| + * [    0.437500  -0.366352  -0.071148 128.000000 ]
 | ||||
| + *
 | ||||
| + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
 | ||||
| + */
 | ||||
| +static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt601[2][3][4] = {
 | ||||
| +	{
 | ||||
| +		/* Full range */
 | ||||
| +		{ 0x0991, 0x12c9, 0x03a6, 0x0000 },
 | ||||
| +		{ 0xfa9b, 0xf567, 0x1000, 0x2000 },
 | ||||
| +		{ 0x1000, 0xf29b, 0xfd67, 0x2000 },
 | ||||
| +	}, {
 | ||||
| +		/* Limited range */
 | ||||
| +		{ 0x082f, 0x1012, 0x031f, 0x0400 },
 | ||||
| +		{ 0xfb48, 0xf6ba, 0x0e00, 0x2000 },
 | ||||
| +		{ 0x0e00, 0xf448, 0xfdba, 0x2000 },
 | ||||
| +	}
 | ||||
|  }; | ||||
|   | ||||
|  /* | ||||
| - * Conversion between Full Range RGB and Full Range YUV422 using the
 | ||||
| - * BT.709 Colorspace
 | ||||
| + * Conversion between Full Range RGB and YUV using the BT.709 Colorspace
 | ||||
|   * | ||||
| + * Full range
 | ||||
| + * [    0.212600   0.715200   0.072200   0.000000 ]
 | ||||
| + * [   -0.114572  -0.385428   0.500000 128.000000 ]
 | ||||
| + * [    0.500000  -0.454153  -0.045847 128.000000 ]
 | ||||
|   * | ||||
| - * [  0.181906  0.611804  0.061758  16  ]
 | ||||
| - * [ -0.100268 -0.337232  0.437500  128 ]
 | ||||
| - * [  0.437500 -0.397386 -0.040114  128 ]
 | ||||
| + * Limited range
 | ||||
| + * [    0.181873   0.611831   0.061765  16.000000 ]
 | ||||
| + * [   -0.100251  -0.337249   0.437500 128.000000 ]
 | ||||
| + * [    0.437500  -0.397384  -0.040116 128.000000 ]
 | ||||
|   * | ||||
|   * Matrix is signed 2p13 fixed point, with signed 9p6 offsets | ||||
|   */ | ||||
| -static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709[3][4] = {
 | ||||
| -	{ 0x05d2, 0x1394, 0x01fa, 0x0400 },
 | ||||
| -	{ 0xfccc, 0xf536, 0x0e00, 0x2000 },
 | ||||
| -	{ 0x0e00, 0xf34a, 0xfeb8, 0x2000 },
 | ||||
| +static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt709[2][3][4] = {
 | ||||
| +	{
 | ||||
| +		/* Full range */
 | ||||
| +		{ 0x06ce, 0x16e3, 0x024f, 0x0000 },
 | ||||
| +		{ 0xfc56, 0xf3ac, 0x1000, 0x2000 },
 | ||||
| +		{ 0x1000, 0xf179, 0xfe89, 0x2000 },
 | ||||
| +	}, {
 | ||||
| +		/* Limited range	*/
 | ||||
| +		{ 0x05d2, 0x1394, 0x01fa, 0x0400 },
 | ||||
| +		{ 0xfccc, 0xf536, 0x0e00, 0x2000 },
 | ||||
| +		{ 0x0e00, 0xf34a, 0xfeb8, 0x2000 },
 | ||||
| +	}
 | ||||
|  }; | ||||
|   | ||||
|  /* | ||||
| - * Conversion between Full Range RGB and Full Range YUV444 using the
 | ||||
| - * BT.709 Colorspace
 | ||||
| + * Conversion between Full Range RGB and YUV using the BT.2020 Colorspace
 | ||||
|   * | ||||
| - * [ -0.100268 -0.337232  0.437500  128 ]
 | ||||
| - * [  0.437500 -0.397386 -0.040114  128 ]
 | ||||
| - * [  0.181906  0.611804  0.061758  16  ]
 | ||||
| + * Full range
 | ||||
| + * [    0.262700   0.678000   0.059300   0.000000 ]
 | ||||
| + * [   -0.139630  -0.360370   0.500000 128.000000 ]
 | ||||
| + * [    0.500000  -0.459786  -0.040214 128.000000 ]
 | ||||
| + *
 | ||||
| + * Limited range
 | ||||
| + * [    0.224732   0.580008   0.050729  16.000000 ]
 | ||||
| + * [   -0.122176  -0.315324   0.437500 128.000000 ]
 | ||||
| + * [    0.437500  -0.402312  -0.035188 128.000000 ]
 | ||||
|   * | ||||
|   * Matrix is signed 2p13 fixed point, with signed 9p6 offsets | ||||
|   */ | ||||
| -static const u16 vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709[3][4] = {
 | ||||
| -	{ 0xfccc, 0xf536, 0x0e00, 0x2000 },
 | ||||
| -	{ 0x0e00, 0xf34a, 0xfeb8, 0x2000 },
 | ||||
| -	{ 0x05d2, 0x1394, 0x01fa, 0x0400 },
 | ||||
| +static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt2020[2][3][4] = {
 | ||||
| +	{
 | ||||
| +		/* Full range */
 | ||||
| +		{ 0x0868, 0x15b2, 0x01e6, 0x0000 },
 | ||||
| +		{ 0xfb89, 0xf479, 0x1000, 0x2000 },
 | ||||
| +		{ 0x1000, 0xf14a, 0xfeb8, 0x2000 },
 | ||||
| +	}, {
 | ||||
| +		/* Limited range */
 | ||||
| +		{ 0x0731, 0x128f, 0x01a0, 0x0400 },
 | ||||
| +		{ 0xfc18, 0xf5ea, 0x0e00, 0x2000 },
 | ||||
| +		{ 0x0e00, 0xf321, 0xfee1, 0x2000 },
 | ||||
| +	}
 | ||||
|  }; | ||||
|   | ||||
|  static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi, | ||||
| @@ -1264,6 +1317,20 @@ static void vc5_hdmi_set_csc_coeffs(stru
 | ||||
|  	HDMI_WRITE(HDMI_CSC_34_33, (coeffs[2][3] << 16) | coeffs[2][2]); | ||||
|  } | ||||
|   | ||||
| +static void vc5_hdmi_set_csc_coeffs_swap(struct vc4_hdmi *vc4_hdmi,
 | ||||
| +					 const u16 coeffs[3][4])
 | ||||
| +{
 | ||||
| +	lockdep_assert_held(&vc4_hdmi->hw_lock);
 | ||||
| +
 | ||||
| +	/* YUV444 needs the CSC matrices using the channels in a different order */
 | ||||
| +	HDMI_WRITE(HDMI_CSC_12_11, (coeffs[2][1] << 16) | coeffs[2][0]);
 | ||||
| +	HDMI_WRITE(HDMI_CSC_14_13, (coeffs[2][3] << 16) | coeffs[2][2]);
 | ||||
| +	HDMI_WRITE(HDMI_CSC_22_21, (coeffs[0][1] << 16) | coeffs[0][0]);
 | ||||
| +	HDMI_WRITE(HDMI_CSC_24_23, (coeffs[0][3] << 16) | coeffs[0][2]);
 | ||||
| +	HDMI_WRITE(HDMI_CSC_32_31, (coeffs[1][1] << 16) | coeffs[1][0]);
 | ||||
| +	HDMI_WRITE(HDMI_CSC_34_33, (coeffs[1][3] << 16) | coeffs[1][2]);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, | ||||
|  			       struct drm_connector_state *state, | ||||
|  			       const struct drm_display_mode *mode) | ||||
| @@ -1271,6 +1338,8 @@ static void vc5_hdmi_csc_setup(struct vc
 | ||||
|  	struct drm_device *drm = vc4_hdmi->connector.dev; | ||||
|  	struct vc4_hdmi_connector_state *vc4_state = | ||||
|  		conn_state_to_vc4_hdmi_conn_state(state); | ||||
| +	unsigned int lim_range = vc4_hdmi_is_full_range(vc4_hdmi, mode) ? 0 : 1;
 | ||||
| +	const u16 (*csc)[4];
 | ||||
|  	unsigned long flags; | ||||
|  	u32 if_cfg = 0; | ||||
|  	u32 if_xbar = 0x543210; | ||||
| @@ -1286,31 +1355,56 @@ static void vc5_hdmi_csc_setup(struct vc
 | ||||
|   | ||||
|  	switch (vc4_state->output_format) { | ||||
|  	case VC4_HDMI_OUTPUT_YUV444: | ||||
| -		vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv444_bt709);
 | ||||
| -		break;
 | ||||
| -
 | ||||
|  	case VC4_HDMI_OUTPUT_YUV422: | ||||
| -		csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
 | ||||
| -					 VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) |
 | ||||
| -			VC5_MT_CP_CSC_CTL_USE_444_TO_422 |
 | ||||
| -			VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION;
 | ||||
| -
 | ||||
| -		csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE,
 | ||||
| -					      VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP);
 | ||||
| +		switch (state->colorspace) {
 | ||||
| +		default:
 | ||||
| +		case DRM_MODE_COLORIMETRY_NO_DATA:
 | ||||
| +		case DRM_MODE_COLORIMETRY_BT709_YCC:
 | ||||
| +		case DRM_MODE_COLORIMETRY_XVYCC_709:
 | ||||
| +		case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED:
 | ||||
| +		case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT:
 | ||||
| +			csc = vc5_hdmi_csc_full_rgb_to_yuv_bt709[lim_range];
 | ||||
| +			break;
 | ||||
| +		case DRM_MODE_COLORIMETRY_SMPTE_170M_YCC:
 | ||||
| +		case DRM_MODE_COLORIMETRY_XVYCC_601:
 | ||||
| +		case DRM_MODE_COLORIMETRY_SYCC_601:
 | ||||
| +		case DRM_MODE_COLORIMETRY_OPYCC_601:
 | ||||
| +		case DRM_MODE_COLORIMETRY_BT601_YCC:
 | ||||
| +			csc = vc5_hdmi_csc_full_rgb_to_yuv_bt601[lim_range];
 | ||||
| +			break;
 | ||||
| +		case DRM_MODE_COLORIMETRY_BT2020_CYCC:
 | ||||
| +		case DRM_MODE_COLORIMETRY_BT2020_YCC:
 | ||||
| +		case DRM_MODE_COLORIMETRY_BT2020_RGB:
 | ||||
| +		case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65:
 | ||||
| +		case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER:
 | ||||
| +			csc = vc5_hdmi_csc_full_rgb_to_yuv_bt2020[lim_range];
 | ||||
| +			break;
 | ||||
| +		}
 | ||||
|   | ||||
| -		if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY,
 | ||||
| -					VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422);
 | ||||
| +		if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) {
 | ||||
| +			csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
 | ||||
| +						 VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) |
 | ||||
| +				VC5_MT_CP_CSC_CTL_USE_444_TO_422 |
 | ||||
| +				VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION;
 | ||||
| +
 | ||||
| +			csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE,
 | ||||
| +						      VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP);
 | ||||
| +
 | ||||
| +			if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY,
 | ||||
| +						VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422);
 | ||||
| +
 | ||||
| +			vc5_hdmi_set_csc_coeffs(vc4_hdmi, csc);
 | ||||
| +		} else {
 | ||||
| +			vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, csc);
 | ||||
| +		}
 | ||||
|   | ||||
| -		vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_yuv422_bt709);
 | ||||
|  		break; | ||||
|   | ||||
|  	case VC4_HDMI_OUTPUT_RGB: | ||||
|  		if_xbar = 0x354021; | ||||
|   | ||||
| -		if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
 | ||||
| -			vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
 | ||||
| -		else
 | ||||
| -			vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
 | ||||
| +		vc5_hdmi_set_csc_coeffs(vc4_hdmi,
 | ||||
| +					vc5_hdmi_csc_full_rgb_to_rgb[lim_range]);
 | ||||
|  		break; | ||||
|   | ||||
|  	default: | ||||
|  | @ -0,0 +1,212 @@ | |||
| From 75f54e3341ac80fc7d9a25b70f18fd0c1f586fb0 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Mon, 14 Mar 2022 17:56:10 +0000 | ||||
| Subject: [PATCH] vc4/drm: vc4_plane: Keep fractional source coords inside | ||||
|  state | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_drv.h   |  2 +- | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 68 ++++++++++++++++----------------- | ||||
|  2 files changed, 34 insertions(+), 36 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| @@ -384,7 +384,7 @@ struct vc4_plane_state {
 | ||||
|   | ||||
|  	/* Clipped coordinates of the plane on the display. */ | ||||
|  	int crtc_x, crtc_y, crtc_w, crtc_h; | ||||
| -	/* Clipped area being scanned from in the FB. */
 | ||||
| +	/* Clipped area being scanned from in the FB in u16.16 format */
 | ||||
|  	u32 src_x, src_y; | ||||
|   | ||||
|  	u32 src_w[2], src_h[2]; | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -183,9 +183,9 @@ static const struct hvs_format *vc4_get_
 | ||||
|   | ||||
|  static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) | ||||
|  { | ||||
| -	if (dst == src)
 | ||||
| +	if (dst == src >> 16)
 | ||||
|  		return VC4_SCALING_NONE; | ||||
| -	if (3 * dst >= 2 * src)
 | ||||
| +	if (3 * dst >= 2 * (src >> 16))
 | ||||
|  		return VC4_SCALING_PPF; | ||||
|  	else | ||||
|  		return VC4_SCALING_TPZ; | ||||
| @@ -394,15 +394,10 @@ static int vc4_plane_setup_clipping_and_
 | ||||
|  		vc4_state->offsets[i] = bo->dma_addr + fb->offsets[i]; | ||||
|  	} | ||||
|   | ||||
| -	/*
 | ||||
| -	 * We don't support subpixel source positioning for scaling,
 | ||||
| -	 * but fractional coordinates can be generated by clipping
 | ||||
| -	 * so just round for now
 | ||||
| -	 */
 | ||||
| -	vc4_state->src_x = DIV_ROUND_CLOSEST(state->src.x1, 1 << 16);
 | ||||
| -	vc4_state->src_y = DIV_ROUND_CLOSEST(state->src.y1, 1 << 16);
 | ||||
| -	vc4_state->src_w[0] = DIV_ROUND_CLOSEST(state->src.x2, 1 << 16) - vc4_state->src_x;
 | ||||
| -	vc4_state->src_h[0] = DIV_ROUND_CLOSEST(state->src.y2, 1 << 16) - vc4_state->src_y;
 | ||||
| +	vc4_state->src_x = state->src.x1;
 | ||||
| +	vc4_state->src_y = state->src.y1;
 | ||||
| +	vc4_state->src_w[0] = state->src.x2 - vc4_state->src_x;
 | ||||
| +	vc4_state->src_h[0] = state->src.y2 - vc4_state->src_y;
 | ||||
|   | ||||
|  	vc4_state->crtc_x = state->dst.x1; | ||||
|  	vc4_state->crtc_y = state->dst.y1; | ||||
| @@ -455,7 +450,7 @@ static void vc4_write_tpz(struct vc4_pla
 | ||||
|  { | ||||
|  	u32 scale, recip; | ||||
|   | ||||
| -	scale = (1 << 16) * src / dst;
 | ||||
| +	scale = src / dst;
 | ||||
|   | ||||
|  	/* The specs note that while the reciprocal would be defined | ||||
|  	 * as (1<<32)/scale, ~0 is close enough. | ||||
| @@ -501,7 +496,7 @@ static u32 vc4_lbm_size(struct drm_plane
 | ||||
|  	if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ) | ||||
|  		pix_per_line = vc4_state->crtc_w; | ||||
|  	else | ||||
| -		pix_per_line = vc4_state->src_w[0];
 | ||||
| +		pix_per_line = vc4_state->src_w[0] >> 16;
 | ||||
|   | ||||
|  	if (!vc4_state->is_yuv) { | ||||
|  		if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) | ||||
| @@ -592,7 +587,8 @@ static void vc4_plane_calc_load(struct d
 | ||||
|  	for (i = 0; i < fb->format->num_planes; i++) { | ||||
|  		/* Even if the bandwidth/plane required for a single frame is | ||||
|  		 * | ||||
| -		 * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh
 | ||||
| +		 * (vc4_state->src_w[i] >> 16) * (vc4_state->src_h[i] >> 16) *
 | ||||
| +		 *  cpp * vrefresh
 | ||||
|  		 * | ||||
|  		 * when downscaling, we have to read more pixels per line in | ||||
|  		 * the time frame reserved for a single line, so the bandwidth | ||||
| @@ -601,11 +597,11 @@ static void vc4_plane_calc_load(struct d
 | ||||
|  		 * load by this number. We're likely over-estimating the read | ||||
|  		 * demand, but that's better than under-estimating it. | ||||
|  		 */ | ||||
| -		vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i],
 | ||||
| +		vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i] >> 16,
 | ||||
|  					     vc4_state->crtc_h); | ||||
| -		vc4_state->membus_load += vc4_state->src_w[i] *
 | ||||
| -					  vc4_state->src_h[i] * vscale_factor *
 | ||||
| -					  fb->format->cpp[i];
 | ||||
| +		vc4_state->membus_load += (vc4_state->src_w[i] >> 16) *
 | ||||
| +					  (vc4_state->src_h[i] >> 16) *
 | ||||
| +					  vscale_factor * fb->format->cpp[i];
 | ||||
|  		vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w; | ||||
|  	} | ||||
|   | ||||
| @@ -758,7 +754,8 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  	bool mix_plane_alpha; | ||||
|  	bool covers_screen; | ||||
|  	u32 scl0, scl1, pitch0; | ||||
| -	u32 tiling, src_y;
 | ||||
| +	u32 tiling, src_x, src_y;
 | ||||
| +	u32 width, height;
 | ||||
|  	u32 hvs_format = format->hvs; | ||||
|  	unsigned int rotation; | ||||
|  	int ret, i; | ||||
| @@ -770,6 +767,9 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	width = vc4_state->src_w[0] >> 16;
 | ||||
| +	height = vc4_state->src_h[0] >> 16;
 | ||||
| +
 | ||||
|  	/* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB | ||||
|  	 * and 4:4:4, scl1 should be set to scl0 so both channels of | ||||
|  	 * the scaler do the same thing.  For YUV, the Y plane needs | ||||
| @@ -790,9 +790,11 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  					 DRM_MODE_REFLECT_Y); | ||||
|   | ||||
|  	/* We must point to the last line when Y reflection is enabled. */ | ||||
| -	src_y = vc4_state->src_y;
 | ||||
| +	src_y = vc4_state->src_y >> 16;
 | ||||
|  	if (rotation & DRM_MODE_REFLECT_Y) | ||||
| -		src_y += vc4_state->src_h[0] - 1;
 | ||||
| +		src_y += height - 1;
 | ||||
| +
 | ||||
| +	src_x = vc4_state->src_x >> 16;
 | ||||
|   | ||||
|  	switch (base_format_mod) { | ||||
|  	case DRM_FORMAT_MOD_LINEAR: | ||||
| @@ -807,7 +809,7 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  						 (i ? v_subsample : 1) * | ||||
|  						 fb->pitches[i]; | ||||
|   | ||||
| -			vc4_state->offsets[i] += vc4_state->src_x /
 | ||||
| +			vc4_state->offsets[i] += src_x /
 | ||||
|  						 (i ? h_subsample : 1) * | ||||
|  						 fb->format->cpp[i]; | ||||
|  		} | ||||
| @@ -830,7 +832,7 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		 *	pitch * tile_h == tile_size * tiles_per_row | ||||
|  		 */ | ||||
|  		u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); | ||||
| -		u32 tiles_l = vc4_state->src_x >> tile_w_shift;
 | ||||
| +		u32 tiles_l = src_x >> tile_w_shift;
 | ||||
|  		u32 tiles_r = tiles_w - tiles_l; | ||||
|  		u32 tiles_t = src_y >> tile_h_shift; | ||||
|  		/* Intra-tile offsets, which modify the base address (the | ||||
| @@ -840,7 +842,7 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		u32 tile_y = (src_y >> 4) & 1; | ||||
|  		u32 subtile_y = (src_y >> 2) & 3; | ||||
|  		u32 utile_y = src_y & 3; | ||||
| -		u32 x_off = vc4_state->src_x & tile_w_mask;
 | ||||
| +		u32 x_off = src_x & tile_w_mask;
 | ||||
|  		u32 y_off = src_y & tile_h_mask; | ||||
|   | ||||
|  		/* When Y reflection is requested we must set the | ||||
| @@ -936,7 +938,7 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  				 * of the 12-pixels in that 128-bit word is the | ||||
|  				 * first pixel to be used | ||||
|  				 */ | ||||
| -				u32 remaining_pixels = vc4_state->src_x % 96;
 | ||||
| +				u32 remaining_pixels = src_x % 96;
 | ||||
|  				u32 aligned = remaining_pixels / 12; | ||||
|  				u32 last_bits = remaining_pixels % 12; | ||||
|   | ||||
| @@ -958,12 +960,12 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  					return -EINVAL; | ||||
|  				} | ||||
|  				pix_per_tile = tile_w / fb->format->cpp[0]; | ||||
| -				x_off = (vc4_state->src_x % pix_per_tile) /
 | ||||
| +				x_off = (src_x % pix_per_tile) /
 | ||||
|  					(i ? h_subsample : 1) * | ||||
|  					fb->format->cpp[i]; | ||||
|  			} | ||||
|   | ||||
| -			tile = vc4_state->src_x / pix_per_tile;
 | ||||
| +			tile = src_x / pix_per_tile;
 | ||||
|   | ||||
|  			vc4_state->offsets[i] += param * tile_w * tile; | ||||
|  			vc4_state->offsets[i] += src_y / | ||||
| @@ -1024,10 +1026,8 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		vc4_dlist_write(vc4_state, | ||||
|  				(mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | | ||||
|  				vc4_hvs4_get_alpha_blend_mode(state) | | ||||
| -				VC4_SET_FIELD(vc4_state->src_w[0],
 | ||||
| -					      SCALER_POS2_WIDTH) |
 | ||||
| -				VC4_SET_FIELD(vc4_state->src_h[0],
 | ||||
| -					      SCALER_POS2_HEIGHT));
 | ||||
| +				VC4_SET_FIELD(width, SCALER_POS2_WIDTH) |
 | ||||
| +				VC4_SET_FIELD(height, SCALER_POS2_HEIGHT));
 | ||||
|   | ||||
|  		/* Position Word 3: Context.  Written by the HVS. */ | ||||
|  		vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||||
| @@ -1085,10 +1085,8 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		/* Position Word 2: Source Image Size */ | ||||
|  		vc4_state->pos2_offset = vc4_state->dlist_count; | ||||
|  		vc4_dlist_write(vc4_state, | ||||
| -				VC4_SET_FIELD(vc4_state->src_w[0],
 | ||||
| -					      SCALER5_POS2_WIDTH) |
 | ||||
| -				VC4_SET_FIELD(vc4_state->src_h[0],
 | ||||
| -					      SCALER5_POS2_HEIGHT));
 | ||||
| +				VC4_SET_FIELD(width, SCALER5_POS2_WIDTH) |
 | ||||
| +				VC4_SET_FIELD(height, SCALER5_POS2_HEIGHT));
 | ||||
|   | ||||
|  		/* Position Word 3: Context.  Written by the HVS. */ | ||||
|  		vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||||
|  | @ -0,0 +1,104 @@ | |||
| From 530def8213913d0726bd2084b9f868af6b4dfabd Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Fri, 9 Apr 2021 15:00:40 +0100 | ||||
| Subject: [PATCH] vc4/drm: Handle fractional coordinates using the phase field | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 61 ++++++++++++++++++++++++++++++--- | ||||
|  1 file changed, 56 insertions(+), 5 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -464,14 +464,47 @@ static void vc4_write_tpz(struct vc4_pla
 | ||||
|  			VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); | ||||
|  } | ||||
|   | ||||
| -static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
 | ||||
| +/* phase magnitude bits */
 | ||||
| +#define PHASE_BITS 6
 | ||||
| +
 | ||||
| +static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel)
 | ||||
|  { | ||||
| -	u32 scale = (1 << 16) * src / dst;
 | ||||
| +	u32 scale = src / dst;
 | ||||
| +	s32 offset, offset2;
 | ||||
| +	s32 phase;
 | ||||
| +
 | ||||
| +	/* Start the phase at 1/2 pixel from the 1st pixel at src_x.
 | ||||
| +	   1/4 pixel for YUV. */
 | ||||
| +	if (channel) {
 | ||||
| +		/* the phase is relative to scale_src->x, so shift it for display list's x value */
 | ||||
| +		offset = (xy & 0x1ffff) >> (16 - PHASE_BITS) >> 1;
 | ||||
| +		offset += -(1 << PHASE_BITS >> 2);
 | ||||
| +	} else {
 | ||||
| +		/* the phase is relative to scale_src->x, so shift it for display list's x value */
 | ||||
| +		offset = (xy & 0xffff) >> (16 - PHASE_BITS);
 | ||||
| +		offset += -(1 << PHASE_BITS >> 1);
 | ||||
| +
 | ||||
| +		/* This is a kludge to make sure the scaling factors are consitent with YUV's luma scaling.
 | ||||
| +		   we lose 1bit precision because of this. */
 | ||||
| +		scale &= ~1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* There may be a also small error introduced by precision of scale.
 | ||||
| +	   Add half of that as a compromise */
 | ||||
| +	offset2 = src - dst * scale;
 | ||||
| +	offset2 >>= 16 - PHASE_BITS;
 | ||||
| +	phase = offset + (offset2 >> 1);
 | ||||
| +
 | ||||
| +	/* Ensure +ve values don't touch the sign bit, then truncate negative values */
 | ||||
| +	if (phase >= 1 << PHASE_BITS)
 | ||||
| +		phase = (1 << PHASE_BITS) - 1;
 | ||||
| +
 | ||||
| +	phase &= SCALER_PPF_IPHASE_MASK;
 | ||||
|   | ||||
|  	vc4_dlist_write(vc4_state, | ||||
|  			SCALER_PPF_AGC | | ||||
|  			VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | | ||||
| -			VC4_SET_FIELD(0, SCALER_PPF_IPHASE));
 | ||||
| +			VC4_SET_FIELD(phase, SCALER_PPF_IPHASE));
 | ||||
|  } | ||||
|   | ||||
|  static u32 vc4_lbm_size(struct drm_plane_state *state) | ||||
| @@ -530,13 +563,13 @@ static void vc4_write_scaling_parameters
 | ||||
|  	/* Ch0 H-PPF Word 0: Scaling Parameters */ | ||||
|  	if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { | ||||
|  		vc4_write_ppf(vc4_state, | ||||
| -			      vc4_state->src_w[channel], vc4_state->crtc_w);
 | ||||
| +			      vc4_state->src_w[channel], vc4_state->crtc_w, vc4_state->src_x, channel);
 | ||||
|  	} | ||||
|   | ||||
|  	/* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ | ||||
|  	if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { | ||||
|  		vc4_write_ppf(vc4_state, | ||||
| -			      vc4_state->src_h[channel], vc4_state->crtc_h);
 | ||||
| +			      vc4_state->src_h[channel], vc4_state->crtc_h, vc4_state->src_y, channel);
 | ||||
|  		vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||||
|  	} | ||||
|   | ||||
| @@ -984,6 +1017,24 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| +	/* fetch an extra pixel if we don't actually line up with the left edge. */
 | ||||
| +	if ((vc4_state->src_x & 0xffff) && vc4_state->src_x < (state->fb->width << 16))
 | ||||
| +		width++;
 | ||||
| +
 | ||||
| +	/* same for the right side */
 | ||||
| +	if (((vc4_state->src_x + vc4_state->src_w[0]) & 0xffff) &&
 | ||||
| +	       vc4_state->src_x + vc4_state->src_w[0] < (state->fb->width << 16))
 | ||||
| +		width++;
 | ||||
| +
 | ||||
| +	/* now for the top */
 | ||||
| +	if ((vc4_state->src_y & 0xffff) && vc4_state->src_y < (state->fb->height << 16))
 | ||||
| +		height++;
 | ||||
| +
 | ||||
| +	/* and the bottom */
 | ||||
| +	if (((vc4_state->src_y + vc4_state->src_h[0]) & 0xffff) &&
 | ||||
| +	       vc4_state->src_y + vc4_state->src_h[0] < (state->fb->height << 16))
 | ||||
| +		height++;
 | ||||
| +
 | ||||
|  	/* Don't waste cycles mixing with plane alpha if the set alpha | ||||
|  	 * is opaque or there is no per-pixel alpha information. | ||||
|  	 * In any case we use the alpha property value as the fixed alpha. | ||||
|  | @ -0,0 +1,170 @@ | |||
| From 1772d6d383fe550ec9c8f7fa2526a114e036e3a9 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Wed, 26 Jan 2022 15:58:13 +0000 | ||||
| Subject: [PATCH] drm: Add chroma siting properties | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/drm_atomic_state_helper.c | 14 +++++++++ | ||||
|  drivers/gpu/drm/drm_atomic_uapi.c         |  8 +++++ | ||||
|  drivers/gpu/drm/drm_color_mgmt.c          | 36 +++++++++++++++++++++++ | ||||
|  include/drm/drm_color_mgmt.h              |  3 ++ | ||||
|  include/drm/drm_plane.h                   | 36 +++++++++++++++++++++++ | ||||
|  5 files changed, 97 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/drm_atomic_state_helper.c
 | ||||
| +++ b/drivers/gpu/drm/drm_atomic_state_helper.c
 | ||||
| @@ -267,6 +267,20 @@ void __drm_atomic_helper_plane_state_res
 | ||||
|  			plane_state->color_range = val; | ||||
|  	} | ||||
|   | ||||
| +	if (plane->chroma_siting_h_property) {
 | ||||
| +		if (!drm_object_property_get_default_value(&plane->base,
 | ||||
| +							   plane->chroma_siting_h_property,
 | ||||
| +							   &val))
 | ||||
| +			plane_state->chroma_siting_h = val;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (plane->chroma_siting_v_property) {
 | ||||
| +		if (!drm_object_property_get_default_value(&plane->base,
 | ||||
| +							   plane->chroma_siting_v_property,
 | ||||
| +							   &val))
 | ||||
| +			plane_state->chroma_siting_v = val;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	if (plane->zpos_property) { | ||||
|  		if (!drm_object_property_get_default_value(&plane->base, | ||||
|  							   plane->zpos_property, | ||||
| --- a/drivers/gpu/drm/drm_atomic_uapi.c
 | ||||
| +++ b/drivers/gpu/drm/drm_atomic_uapi.c
 | ||||
| @@ -562,6 +562,10 @@ static int drm_atomic_plane_set_property
 | ||||
|  		state->color_encoding = val; | ||||
|  	} else if (property == plane->color_range_property) { | ||||
|  		state->color_range = val; | ||||
| +	} else if (property == plane->chroma_siting_h_property) {
 | ||||
| +		state->chroma_siting_h = val;
 | ||||
| +	} else if (property == plane->chroma_siting_v_property) {
 | ||||
| +		state->chroma_siting_v = val;
 | ||||
|  	} else if (property == config->prop_fb_damage_clips) { | ||||
|  		ret = drm_atomic_replace_property_blob_from_id(dev, | ||||
|  					&state->fb_damage_clips, | ||||
| @@ -628,6 +632,10 @@ drm_atomic_plane_get_property(struct drm
 | ||||
|  		*val = state->color_encoding; | ||||
|  	} else if (property == plane->color_range_property) { | ||||
|  		*val = state->color_range; | ||||
| +	} else if (property == plane->chroma_siting_h_property) {
 | ||||
| +		*val = state->chroma_siting_h;
 | ||||
| +	} else if (property == plane->chroma_siting_v_property) {
 | ||||
| +		*val = state->chroma_siting_v;
 | ||||
|  	} else if (property == config->prop_fb_damage_clips) { | ||||
|  		*val = (state->fb_damage_clips) ? | ||||
|  			state->fb_damage_clips->base.id : 0; | ||||
| --- a/drivers/gpu/drm/drm_color_mgmt.c
 | ||||
| +++ b/drivers/gpu/drm/drm_color_mgmt.c
 | ||||
| @@ -591,6 +591,42 @@ int drm_plane_create_color_properties(st
 | ||||
|  EXPORT_SYMBOL(drm_plane_create_color_properties); | ||||
|   | ||||
|  /** | ||||
| + * drm_plane_create_chroma_siting_properties - chroma siting related plane properties
 | ||||
| + * @plane: plane object
 | ||||
| + *
 | ||||
| + * Create and attach plane specific CHROMA_SITING
 | ||||
| + * properties to @plane.
 | ||||
| + */
 | ||||
| +int drm_plane_create_chroma_siting_properties(struct drm_plane *plane,
 | ||||
| +						int32_t default_chroma_siting_h,
 | ||||
| +						int32_t default_chroma_siting_v)
 | ||||
| +{
 | ||||
| +	struct drm_device *dev = plane->dev;
 | ||||
| +	struct drm_property *prop;
 | ||||
| +
 | ||||
| +	prop = drm_property_create_range(dev, 0, "CHROMA_SITING_H",
 | ||||
| +					0, 1<<16);
 | ||||
| +	if (!prop)
 | ||||
| +		return -ENOMEM;
 | ||||
| +	plane->chroma_siting_h_property = prop;
 | ||||
| +	drm_object_attach_property(&plane->base, prop, default_chroma_siting_h);
 | ||||
| +
 | ||||
| +	prop = drm_property_create_range(dev, 0, "CHROMA_SITING_V",
 | ||||
| +					0, 1<<16);
 | ||||
| +	if (!prop)
 | ||||
| +		return -ENOMEM;
 | ||||
| +	plane->chroma_siting_v_property = prop;
 | ||||
| +	drm_object_attach_property(&plane->base, prop, default_chroma_siting_v);
 | ||||
| +
 | ||||
| +	if (plane->state) {
 | ||||
| +		plane->state->chroma_siting_h = default_chroma_siting_h;
 | ||||
| +		plane->state->chroma_siting_v = default_chroma_siting_v;
 | ||||
| +	}
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(drm_plane_create_chroma_siting_properties);
 | ||||
| +
 | ||||
| +/**
 | ||||
|   * drm_color_lut_check - check validity of lookup table | ||||
|   * @lut: property blob containing LUT to check | ||||
|   * @tests: bitmask of tests to run | ||||
| --- a/include/drm/drm_color_mgmt.h
 | ||||
| +++ b/include/drm/drm_color_mgmt.h
 | ||||
| @@ -93,6 +93,9 @@ int drm_plane_create_color_properties(st
 | ||||
|  				      enum drm_color_encoding default_encoding, | ||||
|  				      enum drm_color_range default_range); | ||||
|   | ||||
| +int drm_plane_create_chroma_siting_properties(struct drm_plane *plane,
 | ||||
| +						int32_t default_chroma_siting_h, int32_t default_chroma_siting_v);
 | ||||
| +
 | ||||
|  /** | ||||
|   * enum drm_color_lut_tests - hw-specific LUT tests to perform | ||||
|   * | ||||
| --- a/include/drm/drm_plane.h
 | ||||
| +++ b/include/drm/drm_plane.h
 | ||||
| @@ -178,6 +178,24 @@ struct drm_plane_state {
 | ||||
|  	enum drm_color_range color_range; | ||||
|   | ||||
|  	/** | ||||
| +	 * @chroma_siting_h:
 | ||||
| +	 *
 | ||||
| +	 * Location of chroma samples horizontally compared to luma
 | ||||
| +	 * 0 means chroma is sited with left luma
 | ||||
| +	 * 0x8000 is interstitial. 0x10000 is sited with right luma
 | ||||
| +	 */
 | ||||
| +	int32_t chroma_siting_h;
 | ||||
| +
 | ||||
| +	/**
 | ||||
| +	 * @chroma_siting_v:
 | ||||
| +	 *
 | ||||
| +	 * Location of chroma samples vertically compared to luma
 | ||||
| +	 * 0 means chroma is sited with top luma
 | ||||
| +	 * 0x8000 is interstitial. 0x10000 is sited with bottom luma
 | ||||
| +	 */
 | ||||
| +	int32_t chroma_siting_v;
 | ||||
| +
 | ||||
| +	/**
 | ||||
|  	 * @fb_damage_clips: | ||||
|  	 * | ||||
|  	 * Blob representing damage (area in plane framebuffer that changed | ||||
| @@ -748,6 +766,24 @@ struct drm_plane {
 | ||||
|  	 * scaling. | ||||
|  	 */ | ||||
|  	struct drm_property *scaling_filter_property; | ||||
| +
 | ||||
| +	/**
 | ||||
| +	 * @chroma_siting_h_property:
 | ||||
| +	 *
 | ||||
| +	 * Optional "CHROMA_SITING_H" property for specifying
 | ||||
| +	 * chroma siting for YUV formats.
 | ||||
| +	 * See drm_plane_create_chroma_siting_properties().
 | ||||
| +	 */
 | ||||
| +	struct drm_property *chroma_siting_h_property;
 | ||||
| +
 | ||||
| +	/**
 | ||||
| +	 * @chroma_siting_v_property:
 | ||||
| +	 *
 | ||||
| +	 * Optional "CHROMA_SITING_V" property for specifying
 | ||||
| +	 * chroma siting for YUV formats.
 | ||||
| +	 * See drm_plane_create_chroma_siting_properties().
 | ||||
| +	 */
 | ||||
| +	struct drm_property *chroma_siting_v_property;
 | ||||
|  }; | ||||
|   | ||||
|  #define obj_to_plane(x) container_of(x, struct drm_plane, base) | ||||
|  | @ -0,0 +1,60 @@ | |||
| From df674e66baf169f020b7d22450295df9bf16f007 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Thu, 27 Jan 2022 15:32:04 +0000 | ||||
| Subject: [PATCH] vc4/drm:plane: Make use of chroma siting parameter | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 13 +++++++++---- | ||||
|  1 file changed, 9 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -467,17 +467,18 @@ static void vc4_write_tpz(struct vc4_pla
 | ||||
|  /* phase magnitude bits */ | ||||
|  #define PHASE_BITS 6 | ||||
|   | ||||
| -static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel)
 | ||||
| +static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel, int chroma_offset)
 | ||||
|  { | ||||
|  	u32 scale = src / dst; | ||||
|  	s32 offset, offset2; | ||||
|  	s32 phase; | ||||
|   | ||||
|  	/* Start the phase at 1/2 pixel from the 1st pixel at src_x. | ||||
| -	   1/4 pixel for YUV. */
 | ||||
| +	   1/4 pixel for YUV, plus the offset for chroma siting */
 | ||||
|  	if (channel) { | ||||
|  		/* the phase is relative to scale_src->x, so shift it for display list's x value */ | ||||
|  		offset = (xy & 0x1ffff) >> (16 - PHASE_BITS) >> 1; | ||||
| +		offset -= chroma_offset >> (17 - PHASE_BITS);
 | ||||
|  		offset += -(1 << PHASE_BITS >> 2); | ||||
|  	} else { | ||||
|  		/* the phase is relative to scale_src->x, so shift it for display list's x value */ | ||||
| @@ -563,13 +564,15 @@ static void vc4_write_scaling_parameters
 | ||||
|  	/* Ch0 H-PPF Word 0: Scaling Parameters */ | ||||
|  	if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { | ||||
|  		vc4_write_ppf(vc4_state, | ||||
| -			      vc4_state->src_w[channel], vc4_state->crtc_w, vc4_state->src_x, channel);
 | ||||
| +			      vc4_state->src_w[channel], vc4_state->crtc_w, vc4_state->src_x, channel,
 | ||||
| +			      state->chroma_siting_h);
 | ||||
|  	} | ||||
|   | ||||
|  	/* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ | ||||
|  	if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { | ||||
|  		vc4_write_ppf(vc4_state, | ||||
| -			      vc4_state->src_h[channel], vc4_state->crtc_h, vc4_state->src_y, channel);
 | ||||
| +			      vc4_state->src_h[channel], vc4_state->crtc_h, vc4_state->src_y, channel,
 | ||||
| +			      state->chroma_siting_v);
 | ||||
|  		vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||||
|  	} | ||||
|   | ||||
| @@ -1649,6 +1652,8 @@ struct drm_plane *vc4_plane_init(struct
 | ||||
|  					  DRM_COLOR_YCBCR_BT709, | ||||
|  					  DRM_COLOR_YCBCR_LIMITED_RANGE); | ||||
|   | ||||
| +	drm_plane_create_chroma_siting_properties(plane, 0, 0);
 | ||||
| +
 | ||||
|  	if (type == DRM_PLANE_TYPE_PRIMARY) | ||||
|  		drm_plane_create_zpos_immutable_property(plane, 0); | ||||
|   | ||||
|  | @ -0,0 +1,57 @@ | |||
| From 9d5169f533e9da2b3aac09b3c4e7cc35afa9d8bf Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 1 Apr 2022 11:31:38 +0100 | ||||
| Subject: [PATCH] drm/vc4: Force trigger of dlist update on margins change | ||||
| 
 | ||||
| When the margins are changed, the dlist needs to be regenerated | ||||
| with the changed updated dest regions for each of the planes. | ||||
| 
 | ||||
| Setting the zpos_changed flag is sufficient to trigger that | ||||
| without doing a full modeset, therefore set it should the | ||||
| margins be changed. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_crtc.c | 14 ++++++++++---- | ||||
|  drivers/gpu/drm/vc4/vc4_drv.h  |  7 +------ | ||||
|  2 files changed, 11 insertions(+), 10 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
 | ||||
| @@ -757,10 +757,16 @@ static int vc4_crtc_atomic_check(struct
 | ||||
|  		if (conn_state->crtc != crtc) | ||||
|  			continue; | ||||
|   | ||||
| -		vc4_state->margins.left = conn_state->tv.margins.left;
 | ||||
| -		vc4_state->margins.right = conn_state->tv.margins.right;
 | ||||
| -		vc4_state->margins.top = conn_state->tv.margins.top;
 | ||||
| -		vc4_state->margins.bottom = conn_state->tv.margins.bottom;
 | ||||
| +		if (memcmp(&vc4_state->margins, &conn_state->tv.margins,
 | ||||
| +			   sizeof(vc4_state->margins))) {
 | ||||
| +			memcpy(&vc4_state->margins, &conn_state->tv.margins,
 | ||||
| +			       sizeof(vc4_state->margins));
 | ||||
| +
 | ||||
| +			/* Need to force the dlist entries for all planes to be
 | ||||
| +			 * updated so that the dest rectangles are changed.
 | ||||
| +			 */
 | ||||
| +			crtc_state->zpos_changed = true;
 | ||||
| +		}
 | ||||
|  		break; | ||||
|  	} | ||||
|   | ||||
| --- a/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_drv.h
 | ||||
| @@ -581,12 +581,7 @@ struct vc4_crtc_state {
 | ||||
|  	bool txp_armed; | ||||
|  	unsigned int assigned_channel; | ||||
|   | ||||
| -	struct {
 | ||||
| -		unsigned int left;
 | ||||
| -		unsigned int right;
 | ||||
| -		unsigned int top;
 | ||||
| -		unsigned int bottom;
 | ||||
| -	} margins;
 | ||||
| +	struct drm_connector_tv_margins margins;
 | ||||
|   | ||||
|  	unsigned long hvs_load; | ||||
|   | ||||
|  | @ -0,0 +1,121 @@ | |||
| From 713e11c488cb9066e0c52c1d454773c495ba718f Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Vetter <daniel.vetter@ffwll.ch> | ||||
| Date: Fri, 23 Oct 2020 14:39:23 +0200 | ||||
| Subject: [PATCH] drm/atomic-helpers: remove legacy_cursor_update hacks | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| The stuff never really worked, and leads to lots of fun because it | ||||
| out-of-order frees atomic states. Which upsets KASAN, among other | ||||
| things. | ||||
| 
 | ||||
| For async updates we now have a more solid solution with the | ||||
| ->atomic_async_check and ->atomic_async_commit hooks. Support for that
 | ||||
| for msm and vc4 landed. nouveau and i915 have their own commit | ||||
| routines, doing something similar. | ||||
| 
 | ||||
| For everyone else it's probably better to remove the use-after-free | ||||
| bug, and encourage folks to use the async support instead. The | ||||
| affected drivers which register a legacy cursor plane and don't either | ||||
| use the new async stuff or their own commit routine are: amdgpu, | ||||
| atmel, mediatek, qxl, rockchip, sti, sun4i, tegra, virtio, and vmwgfx. | ||||
| 
 | ||||
| Inspired by an amdgpu bug report. | ||||
| 
 | ||||
| v2: Drop RFC, I think with amdgpu converted over to use | ||||
| atomic_async_check/commit done in | ||||
| 
 | ||||
| commit 674e78acae0dfb4beb56132e41cbae5b60f7d662 | ||||
| Author: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> | ||||
| Date:   Wed Dec 5 14:59:07 2018 -0500 | ||||
| 
 | ||||
|     drm/amd/display: Add fast path for cursor plane updates | ||||
| 
 | ||||
| we don't have any driver anymore where we have userspace expecting | ||||
| solid legacy cursor support _and_ they are using the atomic helpers in | ||||
| their fully glory. So we can retire this. | ||||
| 
 | ||||
| v3: Paper over msm and i915 regression. The complete_all is the only | ||||
| thing missing afaict. | ||||
| 
 | ||||
| v4: Rebased on recent kernel, added extra link for vc4 bug. | ||||
| 
 | ||||
| Link: https://bugzilla.kernel.org/show_bug.cgi?id=199425 | ||||
| Link: https://lore.kernel.org/all/20220221134155.125447-9-maxime@cerno.tech/ | ||||
| Cc: mikita.lipski@amd.com | ||||
| Cc: Michel Dänzer <michel@daenzer.net> | ||||
| Cc: harry.wentland@amd.com | ||||
| Cc: Rob Clark <robdclark@gmail.com> | ||||
| Cc: "Kazlauskas, Nicholas" <nicholas.kazlauskas@amd.com> | ||||
| Tested-by: Maxime Ripard <maxime@cerno.tech> | ||||
| Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/drm_atomic_helper.c          | 13 ------------- | ||||
|  drivers/gpu/drm/i915/display/intel_display.c | 13 +++++++++++++ | ||||
|  drivers/gpu/drm/msm/msm_atomic.c             |  2 ++ | ||||
|  3 files changed, 15 insertions(+), 13 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/drm_atomic_helper.c
 | ||||
| +++ b/drivers/gpu/drm/drm_atomic_helper.c
 | ||||
| @@ -1626,13 +1626,6 @@ drm_atomic_helper_wait_for_vblanks(struc
 | ||||
|  	int i, ret; | ||||
|  	unsigned int crtc_mask = 0; | ||||
|   | ||||
| -	 /*
 | ||||
| -	  * Legacy cursor ioctls are completely unsynced, and userspace
 | ||||
| -	  * relies on that (by doing tons of cursor updates).
 | ||||
| -	  */
 | ||||
| -	if (old_state->legacy_cursor_update)
 | ||||
| -		return;
 | ||||
| -
 | ||||
|  	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { | ||||
|  		if (!new_crtc_state->active) | ||||
|  			continue; | ||||
| @@ -2282,12 +2275,6 @@ int drm_atomic_helper_setup_commit(struc
 | ||||
|  			complete_all(&commit->flip_done); | ||||
|  			continue; | ||||
|  		} | ||||
| -
 | ||||
| -		/* Legacy cursor updates are fully unsynced. */
 | ||||
| -		if (state->legacy_cursor_update) {
 | ||||
| -			complete_all(&commit->flip_done);
 | ||||
| -			continue;
 | ||||
| -		}
 | ||||
|   | ||||
|  		if (!new_crtc_state->event) { | ||||
|  			commit->event = kzalloc(sizeof(*commit->event), | ||||
| --- a/drivers/gpu/drm/i915/display/intel_display.c
 | ||||
| +++ b/drivers/gpu/drm/i915/display/intel_display.c
 | ||||
| @@ -7743,6 +7743,19 @@ static int intel_atomic_commit(struct dr
 | ||||
|  				state->base.legacy_cursor_update = false; | ||||
|  	} | ||||
|   | ||||
| +	/*
 | ||||
| +	 * FIXME: Cut over to (async) commit helpers instead of hand-rolling
 | ||||
| +	 * everything.
 | ||||
| +	 */
 | ||||
| +	if (state->base.legacy_cursor_update) {
 | ||||
| +		struct intel_crtc_state *new_crtc_state;
 | ||||
| +		struct intel_crtc *crtc;
 | ||||
| +		int i;
 | ||||
| +
 | ||||
| +		for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
 | ||||
| +			complete_all(&new_crtc_state->uapi.commit->flip_done);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ret = intel_atomic_prepare_commit(state); | ||||
|  	if (ret) { | ||||
|  		drm_dbg_atomic(&dev_priv->drm, | ||||
| --- a/drivers/gpu/drm/msm/msm_atomic.c
 | ||||
| +++ b/drivers/gpu/drm/msm/msm_atomic.c
 | ||||
| @@ -222,6 +222,8 @@ void msm_atomic_commit_tail(struct drm_a
 | ||||
|  		/* async updates are limited to single-crtc updates: */ | ||||
|  		WARN_ON(crtc_mask != drm_crtc_mask(async_crtc)); | ||||
|   | ||||
| +		complete_all(&async_crtc->state->commit->flip_done);
 | ||||
| +
 | ||||
|  		/* | ||||
|  		 * Start timer if we don't already have an update pending | ||||
|  		 * on this crtc: | ||||
|  | @ -0,0 +1,33 @@ | |||
| From 8fd080d355a9af07751c2080151347132c8ad1f2 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Thu, 5 May 2022 18:50:04 +0100 | ||||
| Subject: [PATCH] drm/vc4_hdmi: Force a modeset when Broadcast RGB setting | ||||
|  changes | ||||
| 
 | ||||
| Without this the change is not visible until the next modeset | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hdmi.c | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
 | ||||
| @@ -536,14 +536,17 @@ static int vc4_hdmi_connector_atomic_che
 | ||||
|  { | ||||
|  	struct drm_connector_state *old_state = | ||||
|  		drm_atomic_get_old_connector_state(state, connector); | ||||
| +	struct vc4_hdmi_connector_state *old_vc4_state = conn_state_to_vc4_hdmi_conn_state(old_state);
 | ||||
|  	struct drm_connector_state *new_state = | ||||
|  		drm_atomic_get_new_connector_state(state, connector); | ||||
| +	struct vc4_hdmi_connector_state *new_vc4_state = conn_state_to_vc4_hdmi_conn_state(new_state);
 | ||||
|  	struct drm_crtc *crtc = new_state->crtc; | ||||
|   | ||||
|  	if (!crtc) | ||||
|  		return 0; | ||||
|   | ||||
|  	if (old_state->colorspace != new_state->colorspace || | ||||
| +	    old_vc4_state->broadcast_rgb != new_vc4_state->broadcast_rgb ||
 | ||||
|  	    !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) { | ||||
|  		struct drm_crtc_state *crtc_state; | ||||
|   | ||||
|  | @ -0,0 +1,59 @@ | |||
| From 34b66f9e18412ee70ff12b1030df625d7adf5a51 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 1 Apr 2022 17:10:37 +0100 | ||||
| Subject: [PATCH] drm/atomic: If margins are updated, update all planes. | ||||
| 
 | ||||
| Margins may be implemented by scaling the planes, but as there | ||||
| is no way of intercepting the set_property for a standard property, | ||||
| and all planes are checked in drm_atomic_check_only before the | ||||
| connectors, there's now way to add the planes into the state | ||||
| from the driver. | ||||
| 
 | ||||
| If the margin properties change, add all corresponding planes to | ||||
| the state. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/drm_atomic_uapi.c | 11 +++++++++++ | ||||
|  1 file changed, 11 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/drm_atomic_uapi.c
 | ||||
| +++ b/drivers/gpu/drm/drm_atomic_uapi.c
 | ||||
| @@ -679,6 +679,7 @@ static int drm_atomic_connector_set_prop
 | ||||
|  { | ||||
|  	struct drm_device *dev = connector->dev; | ||||
|  	struct drm_mode_config *config = &dev->mode_config; | ||||
| +	bool margins_updated = false;
 | ||||
|  	bool replaced = false; | ||||
|  	int ret; | ||||
|   | ||||
| @@ -698,12 +699,16 @@ static int drm_atomic_connector_set_prop
 | ||||
|  		state->tv.subconnector = val; | ||||
|  	} else if (property == config->tv_left_margin_property) { | ||||
|  		state->tv.margins.left = val; | ||||
| +		margins_updated = true;
 | ||||
|  	} else if (property == config->tv_right_margin_property) { | ||||
|  		state->tv.margins.right = val; | ||||
| +		margins_updated = true;
 | ||||
|  	} else if (property == config->tv_top_margin_property) { | ||||
|  		state->tv.margins.top = val; | ||||
| +		margins_updated = true;
 | ||||
|  	} else if (property == config->tv_bottom_margin_property) { | ||||
|  		state->tv.margins.bottom = val; | ||||
| +		margins_updated = true;
 | ||||
|  	} else if (property == config->tv_mode_property) { | ||||
|  		state->tv.mode = val; | ||||
|  	} else if (property == config->tv_brightness_property) { | ||||
| @@ -784,6 +789,12 @@ static int drm_atomic_connector_set_prop
 | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| +	if (margins_updated && state->crtc) {
 | ||||
| +		ret = drm_atomic_add_affected_planes(state->state, state->crtc);
 | ||||
| +
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
|  | @ -0,0 +1,27 @@ | |||
| From ee4cb8b3a707633e0f4aa547779c6f860cc61b18 Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 6 Dec 2021 16:32:10 +0100 | ||||
| Subject: [PATCH] drm/vc4: hvs: Ignore atomic_flush if we're disabled | ||||
| 
 | ||||
| atomic_flush will be called for each CRTC even if they aren't enabled. | ||||
| 
 | ||||
| The whole code we have there will thus run without a properly affected | ||||
| channel, which can then result in all sorts of weird behaviour. | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -778,6 +778,9 @@ void vc4_hvs_atomic_flush(struct drm_crt
 | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| +	if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED)
 | ||||
| +		return;
 | ||||
| +
 | ||||
|  	if (debug_dump_regs) { | ||||
|  		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); | ||||
|  		vc4_hvs_dump_state(hvs); | ||||
|  | @ -0,0 +1,118 @@ | |||
| From 078bf356a2677a2bfb078aac5ac275e81c31d583 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 3 Jun 2022 16:49:09 +0100 | ||||
| Subject: [PATCH] drm: vc4: 0 is a valid value for pixel_order_hvs5, so fix | ||||
|  conditionals | ||||
| 
 | ||||
| vc4_plane_mode_set for HVS5 was using pixel_order unless pixel_order_hvs5 | ||||
| was non-zero, except 0 is a valid value for the pixel_order. | ||||
| 
 | ||||
| Specify pixel_order_hvs5 for all formats and remove the conditional. | ||||
| 
 | ||||
| Reported-by: vrazzer <teamvraz@pipmail.net> | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 20 ++++++++++++++------ | ||||
|  1 file changed, 14 insertions(+), 6 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -65,11 +65,13 @@ static const struct hvs_format {
 | ||||
|  		.drm = DRM_FORMAT_RGB565, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGB565, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XRGB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_BGR565, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGB565, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XBGR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_ARGB1555, | ||||
| @@ -87,56 +89,67 @@ static const struct hvs_format {
 | ||||
|  		.drm = DRM_FORMAT_RGB888, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGB888, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XRGB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_BGR888, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGB888, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XBGR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_YUV422, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_YVU422, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCRCB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_YUV420, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_YVU420, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCRCB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_NV12, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_NV21, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCRCB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_NV16, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_NV61, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCRCB, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
 | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_P030, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT, | ||||
|  		.pixel_order = HVS_PIXEL_ORDER_XYCBCR, | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  	{ | ||||
| @@ -1087,15 +1100,10 @@ static int vc4_plane_mode_set(struct drm
 | ||||
|  		vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||||
|   | ||||
|  	} else { | ||||
| -		u32 hvs_pixel_order = format->pixel_order;
 | ||||
| -
 | ||||
| -		if (format->pixel_order_hvs5)
 | ||||
| -			hvs_pixel_order = format->pixel_order_hvs5;
 | ||||
| -
 | ||||
|  		/* Control word */ | ||||
|  		vc4_dlist_write(vc4_state, | ||||
|  				SCALER_CTL0_VALID | | ||||
| -				(hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) |
 | ||||
| +				(format->pixel_order_hvs5 << SCALER_CTL0_ORDER_SHIFT) |
 | ||||
|  				(hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | | ||||
|  				VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | | ||||
|  				(vc4_state->is_unity ? | ||||
|  | @ -0,0 +1,54 @@ | |||
| From aae9a991d857f49d28bd4b102bf2d41a9b941c21 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 6 Jun 2022 12:23:28 +0100 | ||||
| Subject: [PATCH] drm: vc4: Omit pixel_order from the hvs_format for hvs5 only | ||||
|  formats | ||||
| 
 | ||||
| pixel_order is used for the earlier versions of the HVS, so is | ||||
| redundant on the 10:10:10:2 and 10bit YUV formats that are only | ||||
| supported on HVS5. | ||||
| Remove the assignment from the table to avoid confusion. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 5 ----- | ||||
|  1 file changed, 5 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -148,35 +148,30 @@ static const struct hvs_format {
 | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_P030, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT, | ||||
| -		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_XRGB2101010, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGBA1010102, | ||||
| -		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_ARGB2101010, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGBA1010102, | ||||
| -		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_ABGR2101010, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGBA1010102, | ||||
| -		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  	{ | ||||
|  		.drm = DRM_FORMAT_XBGR2101010, | ||||
|  		.hvs = HVS_PIXEL_FORMAT_RGBA1010102, | ||||
| -		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
|  | @ -0,0 +1,104 @@ | |||
| From 0ced8286b5b6e49895dcaf4e25de2aef1df8073c Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Fri, 3 Jun 2022 16:57:04 +0100 | ||||
| Subject: [PATCH] drm: vc4: Add 3:3:2 and 4:4:4:4 RGB/RGBX/RGBA formats | ||||
| 
 | ||||
| The hardware supports the 332 8bpp and 4:4:4:4 16bpp formats, | ||||
| but the table of supported formats didn't include them. | ||||
| Add them in. | ||||
| 
 | ||||
| In theory they are supported for T-format as well as linear, | ||||
| but without a way to test them just add them as linear for now. | ||||
| 
 | ||||
| Suggested-by: vrazzer <teamvraz@pipmail.net> | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_plane.c | 70 +++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 70 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_plane.c
 | ||||
| @@ -175,6 +175,66 @@ static const struct hvs_format {
 | ||||
|  		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, | ||||
|  		.hvs5_only = true, | ||||
|  	}, | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_RGB332,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGB332,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_BGR233,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGB332,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_XRGB4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_ARGB4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_XBGR4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_ABGR4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_BGRX4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_RGBA,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_BGRA4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_RGBA,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_RGBX4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_BGRA,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.drm = DRM_FORMAT_RGBA4444,
 | ||||
| +		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
 | ||||
| +		.pixel_order = HVS_PIXEL_ORDER_BGRA,
 | ||||
| +		.pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
 | ||||
| +	},
 | ||||
|  }; | ||||
|   | ||||
|  static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) | ||||
| @@ -1575,6 +1635,16 @@ static bool vc4_format_mod_supported(str
 | ||||
|  	case DRM_FORMAT_BGRX1010102: | ||||
|  	case DRM_FORMAT_RGBA1010102: | ||||
|  	case DRM_FORMAT_BGRA1010102: | ||||
| +	case DRM_FORMAT_XRGB4444:
 | ||||
| +	case DRM_FORMAT_ARGB4444:
 | ||||
| +	case DRM_FORMAT_XBGR4444:
 | ||||
| +	case DRM_FORMAT_ABGR4444:
 | ||||
| +	case DRM_FORMAT_RGBX4444:
 | ||||
| +	case DRM_FORMAT_RGBA4444:
 | ||||
| +	case DRM_FORMAT_BGRX4444:
 | ||||
| +	case DRM_FORMAT_BGRA4444:
 | ||||
| +	case DRM_FORMAT_RGB332:
 | ||||
| +	case DRM_FORMAT_BGR233:
 | ||||
|  	case DRM_FORMAT_YUV422: | ||||
|  	case DRM_FORMAT_YVU422: | ||||
|  	case DRM_FORMAT_YUV420: | ||||
|  | @ -0,0 +1,36 @@ | |||
| From 4f34c62b2e8672806287e789f126780cf7ecc93b Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Mon, 6 Jun 2022 14:53:56 +0100 | ||||
| Subject: [PATCH] drm: vc4: Add comments for which HVS_PIXEL_ORDER_xxx defines | ||||
|  apply | ||||
| 
 | ||||
| The HVS_PIXEL_ORDER_xxx defines apply to specific HVS_PIXEL_FORMAT_xxx | ||||
| modes, so add comments to make this obvious. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_regs.h | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_regs.h
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_regs.h
 | ||||
| @@ -870,16 +870,19 @@ enum hvs_pixel_format {
 | ||||
|  /* Note: the LSB is the rightmost character shown.  Only valid for | ||||
|   * HVS_PIXEL_FORMAT_RGB8888, not RGB888. | ||||
|   */ | ||||
| +/* For modes 332, 4444, 555, 5551, 6666, 8888, 10:10:10:2 */
 | ||||
|  #define HVS_PIXEL_ORDER_RGBA			0 | ||||
|  #define HVS_PIXEL_ORDER_BGRA			1 | ||||
|  #define HVS_PIXEL_ORDER_ARGB			2 | ||||
|  #define HVS_PIXEL_ORDER_ABGR			3 | ||||
|   | ||||
| +/* For modes 666 and 888 (4 & 5) */
 | ||||
|  #define HVS_PIXEL_ORDER_XBRG			0 | ||||
|  #define HVS_PIXEL_ORDER_XRBG			1 | ||||
|  #define HVS_PIXEL_ORDER_XRGB			2 | ||||
|  #define HVS_PIXEL_ORDER_XBGR			3 | ||||
|   | ||||
| +/* For YCbCr modes (8-12, and 17) */
 | ||||
|  #define HVS_PIXEL_ORDER_XYCBCR			0 | ||||
|  #define HVS_PIXEL_ORDER_XYCRCB			1 | ||||
|  #define HVS_PIXEL_ORDER_YXCBCR			2 | ||||
|  | @ -0,0 +1,87 @@ | |||
| From e8bb5f7a69eeb3493659a72a6fc003eabfc005b1 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <8911409+pelwell@users.noreply.github.com> | ||||
| Date: Wed, 24 Aug 2022 11:14:40 +0100 | ||||
| Subject: [PATCH] drm/vc4: Add async update support for cursor planes | ||||
| 
 | ||||
| Now that cursors are implemented as regular planes, all cursor | ||||
| movements result in atomic updates. As the firmware-kms driver | ||||
| doesn't support asynchronous updates, these are synchronous, which | ||||
| limits the update rate to the screen refresh rate. Xorg seems unaware | ||||
| of this (or at least of the effect of this), because if the mouse is | ||||
| configured with a higher update rate than the screen then continuous | ||||
| mouse movement results in an increasing backlog of mouse events - | ||||
| cue extreme lag. | ||||
| 
 | ||||
| Add minimal support for asynchronous updates - limited to cursor | ||||
| planes - to eliminate the lag. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/linux/pull/4971 | ||||
|      https://github.com/raspberrypi/linux/issues/4988 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_firmware_kms.c | 46 ++++++++++++++++++++++++++ | ||||
|  1 file changed, 46 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
 | ||||
| @@ -675,6 +675,50 @@ static int vc4_plane_atomic_check(struct
 | ||||
|  	return vc4_plane_to_mb(plane, &vc4_plane->mb, new_plane_state); | ||||
|  } | ||||
|   | ||||
| +static void vc4_plane_atomic_async_update(struct drm_plane *plane,
 | ||||
| +					  struct drm_atomic_state *state)
 | ||||
| +{
 | ||||
| +	struct drm_plane_state *new_plane_state =
 | ||||
| +		drm_atomic_get_new_plane_state(state, plane);
 | ||||
| +
 | ||||
| +	swap(plane->state->fb, new_plane_state->fb);
 | ||||
| +	plane->state->crtc_x = new_plane_state->crtc_x;
 | ||||
| +	plane->state->crtc_y = new_plane_state->crtc_y;
 | ||||
| +	plane->state->crtc_w = new_plane_state->crtc_w;
 | ||||
| +	plane->state->crtc_h = new_plane_state->crtc_h;
 | ||||
| +	plane->state->src_x = new_plane_state->src_x;
 | ||||
| +	plane->state->src_y = new_plane_state->src_y;
 | ||||
| +	plane->state->src_w = new_plane_state->src_w;
 | ||||
| +	plane->state->src_h = new_plane_state->src_h;
 | ||||
| +	plane->state->alpha = new_plane_state->alpha;
 | ||||
| +	plane->state->pixel_blend_mode = new_plane_state->pixel_blend_mode;
 | ||||
| +	plane->state->rotation = new_plane_state->rotation;
 | ||||
| +	plane->state->zpos = new_plane_state->zpos;
 | ||||
| +	plane->state->normalized_zpos = new_plane_state->normalized_zpos;
 | ||||
| +	plane->state->color_encoding = new_plane_state->color_encoding;
 | ||||
| +	plane->state->color_range = new_plane_state->color_range;
 | ||||
| +	plane->state->src = new_plane_state->src;
 | ||||
| +	plane->state->dst = new_plane_state->dst;
 | ||||
| +	plane->state->visible = new_plane_state->visible;
 | ||||
| +
 | ||||
| +	vc4_plane_set_blank(plane, false);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int vc4_plane_atomic_async_check(struct drm_plane *plane,
 | ||||
| +					struct drm_atomic_state *state)
 | ||||
| +{
 | ||||
| +	struct drm_plane_state *new_plane_state =
 | ||||
| +		drm_atomic_get_new_plane_state(state, plane);
 | ||||
| +	int ret = -EINVAL;
 | ||||
| +
 | ||||
| +	if (plane->type == 2 &&
 | ||||
| +	    plane->state->fb &&
 | ||||
| +	    new_plane_state->crtc->state->active)
 | ||||
| +		ret = 0;
 | ||||
| +
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /* Called during init to allocate the plane's atomic state. */ | ||||
|  static void vc4_plane_reset(struct drm_plane *plane) | ||||
|  { | ||||
| @@ -769,6 +813,8 @@ static const struct drm_plane_helper_fun
 | ||||
|  	.atomic_check = vc4_plane_atomic_check, | ||||
|  	.atomic_update = vc4_plane_atomic_update, | ||||
|  	.atomic_disable = vc4_plane_atomic_disable, | ||||
| +	.atomic_async_check = vc4_plane_atomic_async_check,
 | ||||
| +	.atomic_async_update = vc4_plane_atomic_async_update,
 | ||||
|  }; | ||||
|   | ||||
|  static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev, | ||||
|  | @ -0,0 +1,96 @@ | |||
| From 482f1cbc27b336c12cbea38360a580cc0c8a8e62 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 11 Aug 2022 13:49:16 +0100 | ||||
| Subject: [PATCH] drm/vc4: Configure the HVS COB allocations | ||||
| 
 | ||||
| The HVS Composite Output Buffer (COB) is the memory used to | ||||
| generate the output pixel data. | ||||
| Until now the vc4 driver has been relying on the firmware to | ||||
| have set these to sensible values. | ||||
| 
 | ||||
| In testing triple screen support it has been noted that only | ||||
| 1 line was being assigned to HVS channel 2. Whilst that is fine | ||||
| for the transposer (TXP), and indeed needed as only some pixels | ||||
| have an alpha channel, it is insufficient to run a live display. | ||||
| 
 | ||||
| Split the COB more evenly between the 3 HVS channels. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| 
 | ||||
| Revert vc4_regs change | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 56 ++++++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 55 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -1013,7 +1013,7 @@ static int vc4_hvs_bind(struct device *d
 | ||||
|  	struct vc4_hvs *hvs = NULL; | ||||
|  	int ret; | ||||
|  	u32 dispctrl; | ||||
| -	u32 reg;
 | ||||
| +	u32 reg, top;
 | ||||
|   | ||||
|  	hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL); | ||||
|  	if (!hvs) | ||||
| @@ -1151,6 +1151,60 @@ static int vc4_hvs_bind(struct device *d
 | ||||
|   | ||||
|  	HVS_WRITE(SCALER_DISPCTRL, dispctrl); | ||||
|   | ||||
| +	/* Recompute Composite Output Buffer (COB) allocations for the displays
 | ||||
| +	 */
 | ||||
| +	if (!vc4->is_vc5) {
 | ||||
| +		/* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
 | ||||
| +		 * The bottom 2048 pixels are full 32bpp RGBA (intended for the
 | ||||
| +		 * TXP composing RGBA to memory), whilst the remainder are only
 | ||||
| +		 * 24bpp RGB.
 | ||||
| +		 *
 | ||||
| +		 * Assign 3 lines to channels 1 & 2, and just over 4 lines to
 | ||||
| +		 * channel 0.
 | ||||
| +		 */
 | ||||
| +		#define VC4_COB_SIZE		20736
 | ||||
| +		#define VC4_COB_LINE_WIDTH	2048
 | ||||
| +		#define VC4_COB_NUM_LINES	3
 | ||||
| +		reg = 0;
 | ||||
| +		top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
 | ||||
| +		reg |= (top - 1) << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE2, reg);
 | ||||
| +		reg = top;
 | ||||
| +		top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
 | ||||
| +		reg |= (top - 1) << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE1, reg);
 | ||||
| +		reg = top;
 | ||||
| +		top = VC4_COB_SIZE;
 | ||||
| +		reg |= (top - 1) << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE0, reg);
 | ||||
| +	} else {
 | ||||
| +		/* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
 | ||||
| +		 * The bottom 4096 pixels are full RGBA (intended for the TXP
 | ||||
| +		 * composing RGBA to memory), whilst the remainder are only
 | ||||
| +		 * RGB. Addressing is always pixel wide.
 | ||||
| +		 *
 | ||||
| +		 * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
 | ||||
| +		 * lines. to channel 0.
 | ||||
| +		 */
 | ||||
| +		#define VC5_COB_SIZE		44416
 | ||||
| +		#define VC5_COB_LINE_WIDTH	4096
 | ||||
| +		#define VC5_COB_NUM_LINES	3
 | ||||
| +		reg = 0;
 | ||||
| +		top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
 | ||||
| +		reg |= top << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE2, reg);
 | ||||
| +		top += 16;
 | ||||
| +		reg = top;
 | ||||
| +		top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
 | ||||
| +		reg |= top << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE1, reg);
 | ||||
| +		top += 16;
 | ||||
| +		reg = top;
 | ||||
| +		top = VC5_COB_SIZE;
 | ||||
| +		reg |= top << 16;
 | ||||
| +		HVS_WRITE(SCALER_DISPBASE0, reg);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ret = devm_request_irq(dev, platform_get_irq(pdev, 0), | ||||
|  			       vc4_hvs_irq_handler, 0, "vc4 hvs", drm); | ||||
|  	if (ret) | ||||
|  | @ -0,0 +1,39 @@ | |||
| From 87fed1718f9ce64127dea253c37a9f13ec987ee0 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| Date: Thu, 11 Aug 2022 13:59:34 +0100 | ||||
| Subject: [PATCH] drm/vc4: Set AXI panic modes for the HVS | ||||
| 
 | ||||
| The HVS can change AXI request mode based on how full the COB | ||||
| FIFOs are. | ||||
| Until now the vc4 driver has been relying on the firmware to | ||||
| have set these to sensible values. | ||||
| 
 | ||||
| With HVS channel 2 now being used for live video, change the | ||||
| panic mode for all channels to be explicitly set by the driver, | ||||
| and the same for all channels. | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 11 +++++++++++ | ||||
|  1 file changed, 11 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -1149,6 +1149,17 @@ static int vc4_hvs_bind(struct device *d
 | ||||
|  	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC1); | ||||
|  	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2); | ||||
|   | ||||
| +	/* Set AXI panic mode.
 | ||||
| +	 * VC4 panics when < 2 lines in FIFO.
 | ||||
| +	 * VC5 panics when less than 1 line in the FIFO.
 | ||||
| +	 */
 | ||||
| +	dispctrl &= ~(SCALER_DISPCTRL_PANIC0_MASK |
 | ||||
| +		      SCALER_DISPCTRL_PANIC1_MASK |
 | ||||
| +		      SCALER_DISPCTRL_PANIC2_MASK);
 | ||||
| +	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC0);
 | ||||
| +	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC1);
 | ||||
| +	dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2);
 | ||||
| +
 | ||||
|  	HVS_WRITE(SCALER_DISPCTRL, dispctrl); | ||||
|   | ||||
|  	/* Recompute Composite Output Buffer (COB) allocations for the displays | ||||
|  | @ -0,0 +1,25 @@ | |||
| From 816a2693d6dd7058f96b0f8d089ec1ece8b6db14 Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 11 Jul 2022 10:38:25 +0200 | ||||
| Subject: [PATCH] drm/vc4: hvs: Skip DebugFS Registration for FKMS | ||||
| 
 | ||||
| FKMS doesn't have an HVS and it's expected. Return from the debugfs init | ||||
| function immediately if we're running with fkms. | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/gpu/drm/vc4/vc4_hvs.c | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
 | ||||
| @@ -975,6 +975,9 @@ int vc4_hvs_debugfs_init(struct drm_mino
 | ||||
|  	struct vc4_hvs *hvs = vc4->hvs; | ||||
|  	int ret; | ||||
|   | ||||
| +	if (vc4->firmware_kms)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
|  	if (!vc4->hvs) | ||||
|  		return -ENODEV; | ||||
|   | ||||
|  | @ -0,0 +1,39 @@ | |||
| From e2339bd2c4fd484b8be3e2b662bfaf514834e3c7 Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 15 Aug 2022 13:34:02 +0200 | ||||
| Subject: [PATCH] media: uapi: Add some RGB bus formats for VC4 DPI output | ||||
| 
 | ||||
| The VC4 DPI controller can output more RGB formats that aren't described | ||||
| through a media bus format yet, so let's add them. | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  include/uapi/linux/media-bus-format.h | 5 ++++- | ||||
|  1 file changed, 4 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/include/uapi/linux/media-bus-format.h
 | ||||
| +++ b/include/uapi/linux/media-bus-format.h
 | ||||
| @@ -34,19 +34,22 @@
 | ||||
|   | ||||
|  #define MEDIA_BUS_FMT_FIXED			0x0001 | ||||
|   | ||||
| -/* RGB - next is	0x1022 */
 | ||||
| +/* RGB - next is	0x1025 */
 | ||||
|  #define MEDIA_BUS_FMT_RGB444_1X12		0x1016 | ||||
|  #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE	0x1001 | ||||
|  #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE	0x1002 | ||||
|  #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE	0x1003 | ||||
|  #define MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE	0x1004 | ||||
|  #define MEDIA_BUS_FMT_RGB565_1X16		0x1017 | ||||
| +#define MEDIA_BUS_FMT_RGB565_1X24_CPADHI	0x1022
 | ||||
|  #define MEDIA_BUS_FMT_BGR565_2X8_BE		0x1005 | ||||
|  #define MEDIA_BUS_FMT_BGR565_2X8_LE		0x1006 | ||||
|  #define MEDIA_BUS_FMT_RGB565_2X8_BE		0x1007 | ||||
|  #define MEDIA_BUS_FMT_RGB565_2X8_LE		0x1008 | ||||
| +#define MEDIA_BUS_FMT_BGR666_1X18		0x1023
 | ||||
|  #define MEDIA_BUS_FMT_RGB666_1X18		0x1009 | ||||
|  #define MEDIA_BUS_FMT_RBG888_1X24		0x100e | ||||
| +#define MEDIA_BUS_FMT_BGR666_1X24_CPADHI	0x1024
 | ||||
|  #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI	0x1015 | ||||
|  #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG		0x1010 | ||||
|  #define MEDIA_BUS_FMT_BGR888_1X24		0x1013 | ||||
|  | @ -0,0 +1,99 @@ | |||
| From f4f85afe1641fc846b011dff80ef5c0b6b206258 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Thu, 7 Apr 2022 18:23:07 +0100 | ||||
| Subject: [PATCH] raspberrypi-firmware: Update mailbox commands | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  include/soc/bcm2835/raspberrypi-firmware.h | 28 +++++++++++++++++++++- | ||||
|  1 file changed, 27 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/include/soc/bcm2835/raspberrypi-firmware.h
 | ||||
| +++ b/include/soc/bcm2835/raspberrypi-firmware.h
 | ||||
| @@ -36,6 +36,8 @@ struct rpi_firmware_property_tag_header
 | ||||
|  enum rpi_firmware_property_tag { | ||||
|  	RPI_FIRMWARE_PROPERTY_END =                           0, | ||||
|  	RPI_FIRMWARE_GET_FIRMWARE_REVISION =                  0x00000001, | ||||
| +	RPI_FIRMWARE_GET_FIRMWARE_VARIANT =                   0x00000002,
 | ||||
| +	RPI_FIRMWARE_GET_FIRMWARE_HASH =                      0x00000003,
 | ||||
|   | ||||
|  	RPI_FIRMWARE_SET_CURSOR_INFO =                        0x00008010, | ||||
|  	RPI_FIRMWARE_SET_CURSOR_STATE =                       0x00008011, | ||||
| @@ -71,6 +73,7 @@ enum rpi_firmware_property_tag {
 | ||||
|  	RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE =       0x00030014, | ||||
|  	RPI_FIRMWARE_GET_EDID_BLOCK =                         0x00030020, | ||||
|  	RPI_FIRMWARE_GET_CUSTOMER_OTP =                       0x00030021, | ||||
| +	RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY =                 0x00030023,
 | ||||
|  	RPI_FIRMWARE_GET_DOMAIN_STATE =                       0x00030030, | ||||
|  	RPI_FIRMWARE_GET_THROTTLED =                          0x00030046, | ||||
|  	RPI_FIRMWARE_GET_CLOCK_MEASURED =                     0x00030047, | ||||
| @@ -89,8 +92,11 @@ enum rpi_firmware_property_tag {
 | ||||
|  	RPI_FIRMWARE_GET_PERIPH_REG =                         0x00030045, | ||||
|  	RPI_FIRMWARE_SET_PERIPH_REG =                         0x00038045, | ||||
|  	RPI_FIRMWARE_GET_POE_HAT_VAL =                        0x00030049, | ||||
| -	RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00030050,
 | ||||
| +	RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00038049,
 | ||||
| +	RPI_FIRMWARE_SET_POE_HAT_VAL_OLD =                    0x00030050,
 | ||||
|  	RPI_FIRMWARE_NOTIFY_XHCI_RESET =                      0x00030058, | ||||
| +	RPI_FIRMWARE_GET_REBOOT_FLAGS =                       0x00030064,
 | ||||
| +	RPI_FIRMWARE_SET_REBOOT_FLAGS =                       0x00038064,
 | ||||
|  	RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066, | ||||
|   | ||||
|  	/* Dispmanx TAGS */ | ||||
| @@ -105,9 +111,16 @@ enum rpi_firmware_property_tag {
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET =         0x00040009, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN =               0x0004000a, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE =                0x0004000b, | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_LAYER =                  0x0004000c,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_TRANSFORM =              0x0004000d,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_VSYNC =                  0x0004000e,
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF =               0x0004000f, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF =            0x00040010, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_RELEASE =                    0x00048001, | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID =             0x00040016,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM =            0x00048013,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS =           0x00040013,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS =       0x00040014,
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT =  0x00044004, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH =                 0x00044005, | ||||
| @@ -116,26 +129,39 @@ enum rpi_firmware_property_tag {
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET =        0x00044009, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN =              0x0004400a, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE =               0x0004400b, | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_TEST_LAYER =                 0x0004400c,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_TEST_TRANSFORM =             0x0004400d,
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_TEST_VSYNC =                 0x0004400e, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT =  0x00048003, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT =   0x00048004, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH =                  0x00048005, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER =            0x00048006, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE =             0x00048007, | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH =                  0x00048008,
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET =         0x00048009, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN =               0x0004800a, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE =                0x0004800b, | ||||
| +
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF =               0x0004801f, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF =            0x00048020, | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC =                  0x0004800e, | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_SET_LAYER =                  0x0004800c,
 | ||||
| +	RPI_FIRMWARE_FRAMEBUFFER_SET_TRANSFORM =              0x0004800d,
 | ||||
|  	RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT =              0x0004800f, | ||||
|   | ||||
|  	RPI_FIRMWARE_VCHIQ_INIT =                             0x00048010, | ||||
|   | ||||
| +	RPI_FIRMWARE_SET_PLANE =                              0x00048015,
 | ||||
| +	RPI_FIRMWARE_GET_DISPLAY_TIMING =                     0x00040017,
 | ||||
| +	RPI_FIRMWARE_SET_TIMING =                             0x00048017,
 | ||||
| +	RPI_FIRMWARE_GET_DISPLAY_CFG =                        0x00040018,
 | ||||
| +	RPI_FIRMWARE_SET_DISPLAY_POWER =		      0x00048019,
 | ||||
|  	RPI_FIRMWARE_GET_COMMAND_LINE =                       0x00050001, | ||||
|  	RPI_FIRMWARE_GET_DMA_CHANNELS =                       0x00060001, | ||||
|  }; | ||||
|   | ||||
| +#define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64
 | ||||
| +
 | ||||
|  #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE) | ||||
|  int rpi_firmware_property(struct rpi_firmware *fw, | ||||
|  			  u32 tag, void *data, size_t len); | ||||
|  | @ -0,0 +1,70 @@ | |||
| From e8d0e03fcc00e871ecf963838d99b91b992252ea Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 11 Jul 2022 15:58:36 +0200 | ||||
| Subject: [PATCH] clk: bcm: rpi: Create helper to retrieve private data | ||||
| 
 | ||||
| The RaspberryPi firmware clocks driver uses in several instances a | ||||
| container_of to retrieve the struct raspberrypi_clk_data from a pointer | ||||
| to struct clk_hw. Let's create a small function to avoid duplicating it | ||||
| all over the place. | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-raspberrypi.c | 18 ++++++++++-------- | ||||
|  1 file changed, 10 insertions(+), 8 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-raspberrypi.c
 | ||||
| +++ b/drivers/clk/bcm/clk-raspberrypi.c
 | ||||
| @@ -75,6 +75,12 @@ struct raspberrypi_clk_data {
 | ||||
|  	struct raspberrypi_clk *rpi; | ||||
|  }; | ||||
|   | ||||
| +static inline
 | ||||
| +const struct raspberrypi_clk_data *clk_hw_to_data(const struct clk_hw *hw)
 | ||||
| +{
 | ||||
| +	return container_of(hw, struct raspberrypi_clk_data, hw);
 | ||||
| +}
 | ||||
| +
 | ||||
|  struct raspberrypi_clk_variant { | ||||
|  	bool		export; | ||||
|  	char		*clkdev; | ||||
| @@ -187,8 +193,7 @@ static int raspberrypi_clock_property(st
 | ||||
|   | ||||
|  static int raspberrypi_fw_is_prepared(struct clk_hw *hw) | ||||
|  { | ||||
| -	struct raspberrypi_clk_data *data =
 | ||||
| -		container_of(hw, struct raspberrypi_clk_data, hw);
 | ||||
| +	const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
 | ||||
|  	struct raspberrypi_clk *rpi = data->rpi; | ||||
|  	u32 val = 0; | ||||
|  	int ret; | ||||
| @@ -205,8 +210,7 @@ static int raspberrypi_fw_is_prepared(st
 | ||||
|  static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, | ||||
|  					     unsigned long parent_rate) | ||||
|  { | ||||
| -	struct raspberrypi_clk_data *data =
 | ||||
| -		container_of(hw, struct raspberrypi_clk_data, hw);
 | ||||
| +	const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
 | ||||
|  	struct raspberrypi_clk *rpi = data->rpi; | ||||
|  	u32 val = 0; | ||||
|  	int ret; | ||||
| @@ -222,8 +226,7 @@ static unsigned long raspberrypi_fw_get_
 | ||||
|  static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, | ||||
|  				   unsigned long parent_rate) | ||||
|  { | ||||
| -	struct raspberrypi_clk_data *data =
 | ||||
| -		container_of(hw, struct raspberrypi_clk_data, hw);
 | ||||
| +	const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
 | ||||
|  	struct raspberrypi_clk *rpi = data->rpi; | ||||
|  	u32 _rate = rate; | ||||
|  	int ret; | ||||
| @@ -240,8 +243,7 @@ static int raspberrypi_fw_set_rate(struc
 | ||||
|  static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, | ||||
|  					      struct clk_rate_request *req) | ||||
|  { | ||||
| -	struct raspberrypi_clk_data *data =
 | ||||
| -		container_of(hw, struct raspberrypi_clk_data, hw);
 | ||||
| +	const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
 | ||||
|  	struct raspberrypi_clk_variant *variant = data->variant; | ||||
|   | ||||
|  	/* | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 59af37c19b6ceb1caa4f8aa144d114c30667961b Mon Sep 17 00:00:00 2001 | ||||
| From: Maxime Ripard <maxime@cerno.tech> | ||||
| Date: Mon, 6 Jun 2022 11:02:16 +0200 | ||||
| Subject: [PATCH] arm64: setup: Fix build warning | ||||
| 
 | ||||
| Signed-off-by: Maxime Ripard <maxime@cerno.tech> | ||||
| ---
 | ||||
|  arch/arm64/kernel/setup.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/arch/arm64/kernel/setup.c
 | ||||
| +++ b/arch/arm64/kernel/setup.c
 | ||||
| @@ -222,9 +222,9 @@ static void __init request_standard_reso
 | ||||
|  	size_t res_size; | ||||
|   | ||||
|  	kernel_code.start   = __pa_symbol(_stext); | ||||
| -	kernel_code.end     = __pa_symbol(__init_begin - 1);
 | ||||
| +	kernel_code.end     = __pa_symbol(__init_begin) - 1;
 | ||||
|  	kernel_data.start   = __pa_symbol(_sdata); | ||||
| -	kernel_data.end     = __pa_symbol(_end - 1);
 | ||||
| +	kernel_data.end     = __pa_symbol(_end) - 1;
 | ||||
|  	insert_resource(&iomem_resource, &kernel_code); | ||||
|  	insert_resource(&iomem_resource, &kernel_data); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,22 @@ | |||
| From e83914825e59198988203f5ed879a64f3557b280 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Tue, 12 Apr 2022 20:07:20 +0100 | ||||
| Subject: [PATCH] clk-raspberrypi: Add ISP to exported clocks | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-raspberrypi.c | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-raspberrypi.c
 | ||||
| +++ b/drivers/clk/bcm/clk-raspberrypi.c
 | ||||
| @@ -143,6 +143,9 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NU
 | ||||
|  	[RPI_FIRMWARE_HEVC_CLK_ID] = { | ||||
|  		.export = true, | ||||
|  	}, | ||||
| +	[RPI_FIRMWARE_ISP_CLK_ID] = {
 | ||||
| +		.export = true,
 | ||||
| +	},
 | ||||
|  	[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = { | ||||
|  		.export = true, | ||||
|  	}, | ||||
|  | @ -0,0 +1,45 @@ | |||
| From ae2e43a22f1044d9a1d5ea1f9c23871a8a2e1ae8 Mon Sep 17 00:00:00 2001 | ||||
| From: Martin Sperl <kernel@martin.sperl.org> | ||||
| Date: Fri, 2 Sep 2016 16:45:27 +0100 | ||||
| Subject: [PATCH] Register the clocks early during the boot process, so that | ||||
|  special/critical clocks can get enabled early on in the boot process avoiding | ||||
|  the risk of disabling a clock, pll_divider or pll when a claiming driver | ||||
|  fails to install propperly - maybe it needs to defer. | ||||
| 
 | ||||
| Signed-off-by: Martin Sperl <kernel@martin.sperl.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 15 +++++++++++++-- | ||||
|  1 file changed, 13 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -2319,8 +2319,15 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
 | ||||
| +	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
 | ||||
|  				      &cprman->onecell); | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	/* note that we have registered all the clocks */
 | ||||
| +	dev_dbg(dev, "registered %d clocks\n", asize);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
|  } | ||||
|   | ||||
|  static const struct cprman_plat_data cprman_bcm2835_plat_data = { | ||||
| @@ -2346,7 +2353,11 @@ static struct platform_driver bcm2835_cl
 | ||||
|  	.probe          = bcm2835_clk_probe, | ||||
|  }; | ||||
|   | ||||
| -builtin_platform_driver(bcm2835_clk_driver);
 | ||||
| +static int __init __bcm2835_clk_driver_init(void)
 | ||||
| +{
 | ||||
| +	return platform_driver_register(&bcm2835_clk_driver);
 | ||||
| +}
 | ||||
| +core_initcall(__bcm2835_clk_driver_init);
 | ||||
|   | ||||
|  MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); | ||||
|  MODULE_DESCRIPTION("BCM2835 clock driver"); | ||||
|  | @ -0,0 +1,28 @@ | |||
| From 2f4b354fedfd33cd996d261f66c887a87cbee641 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Mon, 13 Feb 2017 17:20:08 +0000 | ||||
| Subject: [PATCH] clk-bcm2835: Mark used PLLs and dividers CRITICAL | ||||
| 
 | ||||
| The VPU configures and relies on several PLLs and dividers. Mark all | ||||
| enabled dividers and their PLLs as CRITICAL to prevent the kernel from | ||||
| switching them off. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 5 +++++ | ||||
|  1 file changed, 5 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -1407,6 +1407,11 @@ bcm2835_register_pll_divider(struct bcm2
 | ||||
|  	divider->div.hw.init = &init; | ||||
|  	divider->div.table = NULL; | ||||
|   | ||||
| +	if (!(cprman_read(cprman, divider_data->cm_reg) & divider_data->hold_mask)) {
 | ||||
| +		init.flags |= CLK_IS_CRITICAL;
 | ||||
| +		divider->div.flags |= CLK_IS_CRITICAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	divider->cprman = cprman; | ||||
|  	divider->data = divider_data; | ||||
|   | ||||
|  | @ -0,0 +1,118 @@ | |||
| From 2bd63288dcd33f32b529ef073e9b716444846e48 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Mon, 13 Feb 2017 17:20:08 +0000 | ||||
| Subject: [PATCH] clk-bcm2835: Add claim-clocks property | ||||
| 
 | ||||
| The claim-clocks property can be used to prevent PLLs and dividers | ||||
| from being marked as critical. It contains a vector of clock IDs, | ||||
| as defined by dt-bindings/clock/bcm2835.h. | ||||
| 
 | ||||
| Use this mechanism to claim PLLD_DSI0, PLLD_DSI1, PLLH_AUX and | ||||
| PLLH_PIX for the vc4_kms_v3d driver. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 43 +++++++++++++++++++++++++++++++++-- | ||||
|  1 file changed, 41 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -1335,6 +1335,8 @@ static const struct clk_ops bcm2835_vpu_
 | ||||
|  	.debug_init = bcm2835_clock_debug_init, | ||||
|  }; | ||||
|   | ||||
| +static bool bcm2835_clk_is_claimed(const char *name);
 | ||||
| +
 | ||||
|  static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, | ||||
|  					   const void *data) | ||||
|  { | ||||
| @@ -1352,6 +1354,9 @@ static struct clk_hw *bcm2835_register_p
 | ||||
|  	init.ops = &bcm2835_pll_clk_ops; | ||||
|  	init.flags = pll_data->flags | CLK_IGNORE_UNUSED; | ||||
|   | ||||
| +	if (!bcm2835_clk_is_claimed(pll_data->name))
 | ||||
| +		init.flags |= CLK_IS_CRITICAL;
 | ||||
| +
 | ||||
|  	pll = kzalloc(sizeof(*pll), GFP_KERNEL); | ||||
|  	if (!pll) | ||||
|  		return NULL; | ||||
| @@ -1408,8 +1413,10 @@ bcm2835_register_pll_divider(struct bcm2
 | ||||
|  	divider->div.table = NULL; | ||||
|   | ||||
|  	if (!(cprman_read(cprman, divider_data->cm_reg) & divider_data->hold_mask)) { | ||||
| -		init.flags |= CLK_IS_CRITICAL;
 | ||||
| -		divider->div.flags |= CLK_IS_CRITICAL;
 | ||||
| +		if (!bcm2835_clk_is_claimed(divider_data->source_pll))
 | ||||
| +			init.flags |= CLK_IS_CRITICAL;
 | ||||
| +		if (!bcm2835_clk_is_claimed(divider_data->name))
 | ||||
| +			divider->div.flags |= CLK_IS_CRITICAL;
 | ||||
|  	} | ||||
|   | ||||
|  	divider->cprman = cprman; | ||||
| @@ -1466,6 +1473,15 @@ static struct clk_hw *bcm2835_register_c
 | ||||
|  	init.flags = clock_data->flags | CLK_IGNORE_UNUSED; | ||||
|   | ||||
|  	/* | ||||
| +	 * Some GPIO clocks for ethernet/wifi PLLs are marked as
 | ||||
| +	 * critical (since some platforms use them), but if the
 | ||||
| +	 * firmware didn't have them turned on then they clearly
 | ||||
| +	 * aren't actually critical.
 | ||||
| +	 */
 | ||||
| +	if ((cprman_read(cprman, clock_data->ctl_reg) & CM_ENABLE) == 0)
 | ||||
| +		init.flags &= ~CLK_IS_CRITICAL;
 | ||||
| +
 | ||||
| +	/*
 | ||||
|  	 * Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate | ||||
|  	 * rate changes on at least of the parents. | ||||
|  	 */ | ||||
| @@ -2245,6 +2261,8 @@ static const struct bcm2835_clk_desc clk
 | ||||
|  		.ctl_reg = CM_PERIICTL), | ||||
|  }; | ||||
|   | ||||
| +static bool bcm2835_clk_claimed[ARRAY_SIZE(clk_desc_array)];
 | ||||
| +
 | ||||
|  /* | ||||
|   * Permanently take a reference on the parent of the SDRAM clock. | ||||
|   * | ||||
| @@ -2264,6 +2282,19 @@ static int bcm2835_mark_sdc_parent_criti
 | ||||
|  	return clk_prepare_enable(parent); | ||||
|  } | ||||
|   | ||||
| +static bool bcm2835_clk_is_claimed(const char *name)
 | ||||
| +{
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
 | ||||
| +		const char *clk_name = *(const char **)(clk_desc_array[i].data);
 | ||||
| +		if (!strcmp(name, clk_name))
 | ||||
| +		    return bcm2835_clk_claimed[i];
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int bcm2835_clk_probe(struct platform_device *pdev) | ||||
|  { | ||||
|  	struct device *dev = &pdev->dev; | ||||
| @@ -2273,6 +2304,7 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  	const size_t asize = ARRAY_SIZE(clk_desc_array); | ||||
|  	const struct cprman_plat_data *pdata; | ||||
|  	size_t i; | ||||
| +	u32 clk_id;
 | ||||
|  	int ret; | ||||
|   | ||||
|  	pdata = of_device_get_match_data(&pdev->dev); | ||||
| @@ -2291,6 +2323,13 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  	if (IS_ERR(cprman->regs)) | ||||
|  		return PTR_ERR(cprman->regs); | ||||
|   | ||||
| +	memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed));
 | ||||
| +	for (i = 0;
 | ||||
| +	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
 | ||||
| +					 i, &clk_id);
 | ||||
| +	     i++)
 | ||||
| +		bcm2835_clk_claimed[clk_id]= true;
 | ||||
| +
 | ||||
|  	memcpy(cprman->real_parent_names, cprman_parent_names, | ||||
|  	       sizeof(cprman_parent_names)); | ||||
|  	of_clk_parent_fill(dev->of_node, cprman->real_parent_names, | ||||
|  | @ -0,0 +1,115 @@ | |||
| From 34a1764a301c3a18ec016f4331ba104d7cd38f26 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Mon, 6 Mar 2017 09:06:18 +0000 | ||||
| Subject: [PATCH] clk-bcm2835: Read max core clock from firmware | ||||
| 
 | ||||
| The VPU is responsible for managing the core clock, usually under | ||||
| direction from the bcm2835-cpufreq driver but not via the clk-bcm2835 | ||||
| driver. Since the core frequency can change without warning, it is | ||||
| safer to report the maximum clock rate to users of the core clock - | ||||
| I2C, SPI and the mini UART - to err on the safe side when calculating | ||||
| clock divisors. | ||||
| 
 | ||||
| If the DT node for the clock driver includes a reference to the | ||||
| firmware node, use the firmware API to query the maximum core clock | ||||
| instead of reading the divider registers. | ||||
| 
 | ||||
| Prior to this patch, a "100KHz" I2C bus was sometimes clocked at about | ||||
| 160KHz. In particular, switching to the 4.9 kernel was likely to break | ||||
| SenseHAT usage on a Pi3. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 39 ++++++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 38 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -36,6 +36,7 @@
 | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/slab.h> | ||||
|  #include <dt-bindings/clock/bcm2835.h> | ||||
| +#include <soc/bcm2835/raspberrypi-firmware.h>
 | ||||
|   | ||||
|  #define CM_PASSWORD		0x5a000000 | ||||
|   | ||||
| @@ -296,6 +297,8 @@
 | ||||
|  #define SOC_BCM2711		BIT(1) | ||||
|  #define SOC_ALL			(SOC_BCM2835 | SOC_BCM2711) | ||||
|   | ||||
| +#define VCMSG_ID_CORE_CLOCK     4
 | ||||
| +
 | ||||
|  /* | ||||
|   * Names of clocks used within the driver that need to be replaced | ||||
|   * with an external parent's name.  This array is in the order that | ||||
| @@ -314,6 +317,7 @@ static const char *const cprman_parent_n
 | ||||
|  struct bcm2835_cprman { | ||||
|  	struct device *dev; | ||||
|  	void __iomem *regs; | ||||
| +	struct rpi_firmware *fw;
 | ||||
|  	spinlock_t regs_lock; /* spinlock for all clocks */ | ||||
|  	unsigned int soc; | ||||
|   | ||||
| @@ -1039,6 +1043,30 @@ static unsigned long bcm2835_clock_get_r
 | ||||
|  	return rate; | ||||
|  } | ||||
|   | ||||
| +static unsigned long bcm2835_clock_get_rate_vpu(struct clk_hw *hw,
 | ||||
| +						unsigned long parent_rate)
 | ||||
| +{
 | ||||
| +	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
 | ||||
| +	struct bcm2835_cprman *cprman = clock->cprman;
 | ||||
| +
 | ||||
| +	if (cprman->fw) {
 | ||||
| +		struct {
 | ||||
| +			u32 id;
 | ||||
| +			u32 val;
 | ||||
| +		} packet;
 | ||||
| +
 | ||||
| +		packet.id = VCMSG_ID_CORE_CLOCK;
 | ||||
| +		packet.val = 0;
 | ||||
| +
 | ||||
| +		if (!rpi_firmware_property(cprman->fw,
 | ||||
| +					   RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
 | ||||
| +					   &packet, sizeof(packet)))
 | ||||
| +			return packet.val;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return bcm2835_clock_get_rate(hw, parent_rate);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock) | ||||
|  { | ||||
|  	struct bcm2835_cprman *cprman = clock->cprman; | ||||
| @@ -1327,7 +1355,7 @@ static int bcm2835_vpu_clock_is_on(struc
 | ||||
|   */ | ||||
|  static const struct clk_ops bcm2835_vpu_clock_clk_ops = { | ||||
|  	.is_prepared = bcm2835_vpu_clock_is_on, | ||||
| -	.recalc_rate = bcm2835_clock_get_rate,
 | ||||
| +	.recalc_rate = bcm2835_clock_get_rate_vpu,
 | ||||
|  	.set_rate = bcm2835_clock_set_rate, | ||||
|  	.determine_rate = bcm2835_clock_determine_rate, | ||||
|  	.set_parent = bcm2835_clock_set_parent, | ||||
| @@ -2303,6 +2331,7 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  	const struct bcm2835_clk_desc *desc; | ||||
|  	const size_t asize = ARRAY_SIZE(clk_desc_array); | ||||
|  	const struct cprman_plat_data *pdata; | ||||
| +	struct device_node *fw_node;
 | ||||
|  	size_t i; | ||||
|  	u32 clk_id; | ||||
|  	int ret; | ||||
| @@ -2323,6 +2352,14 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  	if (IS_ERR(cprman->regs)) | ||||
|  		return PTR_ERR(cprman->regs); | ||||
|   | ||||
| +	fw_node = of_parse_phandle(dev->of_node, "firmware", 0);
 | ||||
| +	if (fw_node) {
 | ||||
| +		struct rpi_firmware *fw = rpi_firmware_get(NULL);
 | ||||
| +		if (!fw)
 | ||||
| +			return -EPROBE_DEFER;
 | ||||
| +		cprman->fw = fw;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed)); | ||||
|  	for (i = 0; | ||||
|  	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks", | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 06e0ba66373d40aa76f1a927b8228b66ecac07d4 Mon Sep 17 00:00:00 2001 | ||||
| From: Dave Stevenson <dave.stevenson@raspberrypi.org> | ||||
| Date: Thu, 24 Jan 2019 15:09:28 +0000 | ||||
| Subject: [PATCH] clk: clk-bcm2835: Use %zd when printing size_t | ||||
| 
 | ||||
| The debug text for how many clocks have been registered | ||||
| uses "%d" with a size_t. Correct it to "%zd". | ||||
| 
 | ||||
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -2406,7 +2406,7 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|  		return ret; | ||||
|   | ||||
|  	/* note that we have registered all the clocks */ | ||||
| -	dev_dbg(dev, "registered %d clocks\n", asize);
 | ||||
| +	dev_dbg(dev, "registered %zd clocks\n", asize);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
|  | @ -0,0 +1,38 @@ | |||
| From 9c849cd78ee9d78443303af3b55cc3fc6318f31f Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Wed, 23 Jan 2019 16:11:50 +0000 | ||||
| Subject: [PATCH] clk-bcm2835: Don't wait for pllh lock | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 18 ++++++++++-------- | ||||
|  1 file changed, 10 insertions(+), 8 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -647,15 +647,17 @@ static int bcm2835_pll_on(struct clk_hw
 | ||||
|  	spin_unlock(&cprman->regs_lock); | ||||
|   | ||||
|  	/* Wait for the PLL to lock. */ | ||||
| -	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
 | ||||
| -	while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) {
 | ||||
| -		if (ktime_after(ktime_get(), timeout)) {
 | ||||
| -			dev_err(cprman->dev, "%s: couldn't lock PLL\n",
 | ||||
| -				clk_hw_get_name(hw));
 | ||||
| -			return -ETIMEDOUT;
 | ||||
| -		}
 | ||||
| +	if (strcmp(data->name, "pllh")) {
 | ||||
| +		timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
 | ||||
| +		while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) {
 | ||||
| +			if (ktime_after(ktime_get(), timeout)) {
 | ||||
| +				dev_err(cprman->dev, "%s: couldn't lock PLL\n",
 | ||||
| +					clk_hw_get_name(hw));
 | ||||
| +				return -ETIMEDOUT;
 | ||||
| +			}
 | ||||
|   | ||||
| -		cpu_relax();
 | ||||
| +			cpu_relax();
 | ||||
| +		}
 | ||||
|  	} | ||||
|   | ||||
|  	cprman_write(cprman, data->a2w_ctrl_reg, | ||||
|  | @ -0,0 +1,53 @@ | |||
| From c5a0182a6aec56695354cfe10bb5536a6a38c5f3 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Anholt <eric@anholt.net> | ||||
| Date: Thu, 2 May 2019 15:11:05 -0700 | ||||
| Subject: [PATCH] clk: bcm2835: Add support for setting leaf clock rates while | ||||
|  running. | ||||
| 
 | ||||
| As long as you wait for !BUSY, you can do glitch-free updates of clock | ||||
| rate while the clock is running. | ||||
| 
 | ||||
| Signed-off-by: Eric Anholt <eric@anholt.net> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 22 +++++++++++++--------- | ||||
|  1 file changed, 13 insertions(+), 9 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -1138,15 +1138,19 @@ static int bcm2835_clock_set_rate(struct
 | ||||
|   | ||||
|  	spin_lock(&cprman->regs_lock); | ||||
|   | ||||
| -	/*
 | ||||
| -	 * Setting up frac support
 | ||||
| -	 *
 | ||||
| -	 * In principle it is recommended to stop/start the clock first,
 | ||||
| -	 * but as we set CLK_SET_RATE_GATE during registration of the
 | ||||
| -	 * clock this requirement should be take care of by the
 | ||||
| -	 * clk-framework.
 | ||||
| +	ctl = cprman_read(cprman, data->ctl_reg);
 | ||||
| +
 | ||||
| +	/* If the clock is running, we have to pause clock generation while
 | ||||
| +	 * updating the control and div regs.  This is glitchless (no clock
 | ||||
| +	 * signals generated faster than the rate) but each reg access is two
 | ||||
| +	 * OSC cycles so the clock will slow down for a moment.
 | ||||
|  	 */ | ||||
| -	ctl = cprman_read(cprman, data->ctl_reg) & ~CM_FRAC;
 | ||||
| +	if (ctl & CM_ENABLE) {
 | ||||
| +		cprman_write(cprman, data->ctl_reg, ctl & ~CM_ENABLE);
 | ||||
| +		bcm2835_clock_wait_busy(clock);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	ctl &= ~CM_FRAC;
 | ||||
|  	ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0; | ||||
|  	cprman_write(cprman, data->ctl_reg, ctl); | ||||
|   | ||||
| @@ -1522,7 +1526,7 @@ static struct clk_hw *bcm2835_register_c
 | ||||
|  		init.ops = &bcm2835_vpu_clock_clk_ops; | ||||
|  	} else { | ||||
|  		init.ops = &bcm2835_clock_clk_ops; | ||||
| -		init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
 | ||||
| +		init.flags |= CLK_SET_PARENT_GATE;
 | ||||
|   | ||||
|  		/* If the clock wasn't actually enabled at boot, it's not | ||||
|  		 * critical. | ||||
|  | @ -0,0 +1,71 @@ | |||
| From a9f709127c1a912517bd4e1160360bb4ab3e1498 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Anholt <eric@anholt.net> | ||||
| Date: Thu, 2 May 2019 15:24:04 -0700 | ||||
| Subject: [PATCH] clk: bcm2835: Allow reparenting leaf clocks while they're | ||||
|  running. | ||||
| 
 | ||||
| This falls under the same "we can reprogram glitch-free as long as we | ||||
| pause generation" rule as updating the div/frac fields.  This can be | ||||
| used for runtime reclocking of V3D to manage power leakage. | ||||
| 
 | ||||
| Signed-off-by: Eric Anholt <eric@anholt.net> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 19 ++++++++++++++++--- | ||||
|  1 file changed, 16 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -1127,8 +1127,10 @@ static int bcm2835_clock_on(struct clk_h
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int bcm2835_clock_set_rate(struct clk_hw *hw,
 | ||||
| -				  unsigned long rate, unsigned long parent_rate)
 | ||||
| +static int bcm2835_clock_set_rate_and_parent(struct clk_hw *hw,
 | ||||
| +					     unsigned long rate,
 | ||||
| +					     unsigned long parent_rate,
 | ||||
| +					     u8 parent)
 | ||||
|  { | ||||
|  	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); | ||||
|  	struct bcm2835_cprman *cprman = clock->cprman; | ||||
| @@ -1150,6 +1152,11 @@ static int bcm2835_clock_set_rate(struct
 | ||||
|  		bcm2835_clock_wait_busy(clock); | ||||
|  	} | ||||
|   | ||||
| +	if (parent != 0xff) {
 | ||||
| +		ctl &= ~(CM_SRC_MASK << CM_SRC_SHIFT);
 | ||||
| +		ctl |= parent << CM_SRC_SHIFT;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ctl &= ~CM_FRAC; | ||||
|  	ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0; | ||||
|  	cprman_write(cprman, data->ctl_reg, ctl); | ||||
| @@ -1161,6 +1168,12 @@ static int bcm2835_clock_set_rate(struct
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int bcm2835_clock_set_rate(struct clk_hw *hw,
 | ||||
| +				  unsigned long rate, unsigned long parent_rate)
 | ||||
| +{
 | ||||
| +	return bcm2835_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static bool | ||||
|  bcm2835_clk_is_pllc(struct clk_hw *hw) | ||||
|  { | ||||
| @@ -1344,6 +1357,7 @@ static const struct clk_ops bcm2835_cloc
 | ||||
|  	.unprepare = bcm2835_clock_off, | ||||
|  	.recalc_rate = bcm2835_clock_get_rate, | ||||
|  	.set_rate = bcm2835_clock_set_rate, | ||||
| +	.set_rate_and_parent = bcm2835_clock_set_rate_and_parent,
 | ||||
|  	.determine_rate = bcm2835_clock_determine_rate, | ||||
|  	.set_parent = bcm2835_clock_set_parent, | ||||
|  	.get_parent = bcm2835_clock_get_parent, | ||||
| @@ -1526,7 +1540,6 @@ static struct clk_hw *bcm2835_register_c
 | ||||
|  		init.ops = &bcm2835_vpu_clock_clk_ops; | ||||
|  	} else { | ||||
|  		init.ops = &bcm2835_clock_clk_ops; | ||||
| -		init.flags |= CLK_SET_PARENT_GATE;
 | ||||
|   | ||||
|  		/* If the clock wasn't actually enabled at boot, it's not | ||||
|  		 * critical. | ||||
|  | @ -0,0 +1,29 @@ | |||
| From 1c9ad88925bb574d8623477c441d586d0756a91e Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Tue, 6 Aug 2019 15:23:14 +0100 | ||||
| Subject: [PATCH] clk-bcm2835: Avoid null pointer exception | ||||
| 
 | ||||
| clk_desc_array[BCM2835_PLLB] doesn't exist so we dereference null when iterating | ||||
| 
 | ||||
| Signed-off-by: popcornmix <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 8 +++++--- | ||||
|  1 file changed, 5 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -2334,9 +2334,11 @@ static bool bcm2835_clk_is_claimed(const
 | ||||
|  	int i; | ||||
|   | ||||
|  	for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) { | ||||
| -		const char *clk_name = *(const char **)(clk_desc_array[i].data);
 | ||||
| -		if (!strcmp(name, clk_name))
 | ||||
| -		    return bcm2835_clk_claimed[i];
 | ||||
| +		if (clk_desc_array[i].data) {
 | ||||
| +			const char *clk_name = *(const char **)(clk_desc_array[i].data);
 | ||||
| +			if (!strcmp(name, clk_name))
 | ||||
| +				return bcm2835_clk_claimed[i];
 | ||||
| +		}
 | ||||
|  	} | ||||
|   | ||||
|  	return false; | ||||
|  | @ -0,0 +1,58 @@ | |||
| From bcca26f6e384cfb43bcdcf36873e85476de9fab1 Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Tue, 3 Sep 2019 20:28:00 +0100 | ||||
| Subject: [PATCH] clk-bcm2835: Disable v3d clock | ||||
| 
 | ||||
| This is controlled by firmware, see clk-raspberrypi.c | ||||
| 
 | ||||
| Signed-off-by: popcornmix <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 30 ++++++++++++------------------ | ||||
|  1 file changed, 12 insertions(+), 18 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -1764,16 +1764,12 @@ static const struct bcm2835_clk_desc clk
 | ||||
|  		.hold_mask = CM_PLLA_HOLDCORE, | ||||
|  		.fixed_divider = 1, | ||||
|  		.flags = CLK_SET_RATE_PARENT), | ||||
| -	[BCM2835_PLLA_PER]	= REGISTER_PLL_DIV(
 | ||||
| -		SOC_ALL,
 | ||||
| -		.name = "plla_per",
 | ||||
| -		.source_pll = "plla",
 | ||||
| -		.cm_reg = CM_PLLA,
 | ||||
| -		.a2w_reg = A2W_PLLA_PER,
 | ||||
| -		.load_mask = CM_PLLA_LOADPER,
 | ||||
| -		.hold_mask = CM_PLLA_HOLDPER,
 | ||||
| -		.fixed_divider = 1,
 | ||||
| -		.flags = CLK_SET_RATE_PARENT),
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * PLLA_PER is used for gpu clocks. Controlled by firmware, see
 | ||||
| +	 * clk-raspberrypi.c.
 | ||||
| +	 */
 | ||||
| +
 | ||||
|  	[BCM2835_PLLA_DSI0]	= REGISTER_PLL_DIV( | ||||
|  		SOC_ALL, | ||||
|  		.name = "plla_dsi0", | ||||
| @@ -2074,14 +2070,12 @@ static const struct bcm2835_clk_desc clk
 | ||||
|  		.int_bits = 6, | ||||
|  		.frac_bits = 0, | ||||
|  		.tcnt_mux = 3), | ||||
| -	[BCM2835_CLOCK_V3D]	= REGISTER_VPU_CLK(
 | ||||
| -		SOC_ALL,
 | ||||
| -		.name = "v3d",
 | ||||
| -		.ctl_reg = CM_V3DCTL,
 | ||||
| -		.div_reg = CM_V3DDIV,
 | ||||
| -		.int_bits = 4,
 | ||||
| -		.frac_bits = 8,
 | ||||
| -		.tcnt_mux = 4),
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * CLOCK_V3D is used for v3d clock. Controlled by firmware, see
 | ||||
| +	 * clk-raspberrypi.c.
 | ||||
| +	 */
 | ||||
| +
 | ||||
|  	/* | ||||
|  	 * VPU clock.  This doesn't have an enable bit, since it drives | ||||
|  	 * the bus for everything else, and is special so it doesn't need | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 83438d9e501ae9439b951f225d3d81bea7930568 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Thu, 8 Jul 2021 09:37:10 +0100 | ||||
| Subject: [PATCH] clk: bcm2835: Pass DT node to rpi_firmware_get | ||||
| 
 | ||||
| The fw_node pointer has already been retrieved, and using it allows | ||||
| us to remove a downstream patch to the firmware driver. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -2369,7 +2369,7 @@ static int bcm2835_clk_probe(struct plat
 | ||||
|   | ||||
|  	fw_node = of_parse_phandle(dev->of_node, "firmware", 0); | ||||
|  	if (fw_node) { | ||||
| -		struct rpi_firmware *fw = rpi_firmware_get(NULL);
 | ||||
| +		struct rpi_firmware *fw = rpi_firmware_get(fw_node);
 | ||||
|  		if (!fw) | ||||
|  			return -EPROBE_DEFER; | ||||
|  		cprman->fw = fw; | ||||
|  | @ -0,0 +1,34 @@ | |||
| From 88c07badc7a952682f5914abeb0bb789015b67c6 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Tue, 19 Oct 2021 14:14:55 +0100 | ||||
| Subject: [PATCH] clk-bcm2835: Remove VEC clock support | ||||
| 
 | ||||
| Signed-off-by: Dom Cobley <popcornmix@gmail.com> | ||||
| ---
 | ||||
|  drivers/clk/bcm/clk-bcm2835.c | 15 --------------- | ||||
|  1 file changed, 15 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| +++ b/drivers/clk/bcm/clk-bcm2835.c
 | ||||
| @@ -2238,21 +2238,6 @@ static const struct bcm2835_clk_desc clk
 | ||||
|  		.tcnt_mux = 28, | ||||
|  		.round_up = true), | ||||
|   | ||||
| -	/* TV encoder clock.  Only operating frequency is 108Mhz.  */
 | ||||
| -	[BCM2835_CLOCK_VEC]	= REGISTER_PER_CLK(
 | ||||
| -		SOC_ALL,
 | ||||
| -		.name = "vec",
 | ||||
| -		.ctl_reg = CM_VECCTL,
 | ||||
| -		.div_reg = CM_VECDIV,
 | ||||
| -		.int_bits = 4,
 | ||||
| -		.frac_bits = 0,
 | ||||
| -		/*
 | ||||
| -		 * Allow rate change propagation only on PLLH_AUX which is
 | ||||
| -		 * assigned index 7 in the parent array.
 | ||||
| -		 */
 | ||||
| -		.set_rate_parent = BIT(7),
 | ||||
| -		.tcnt_mux = 29),
 | ||||
| -
 | ||||
|  	/* dsi clocks */ | ||||
|  	[BCM2835_CLOCK_DSI0E]	= REGISTER_PER_CLK( | ||||
|  		SOC_ALL, | ||||
|  | @ -0,0 +1,99 @@ | |||
| From 3f58295a84a9a9eb5c6aa06e7c0c93fe9959afe9 Mon Sep 17 00:00:00 2001 | ||||
| From: Dan Pasanen <dan.pasanen@gmail.com> | ||||
| Date: Thu, 21 Sep 2017 09:55:42 -0500 | ||||
| Subject: [PATCH] arm: partially revert | ||||
|  702b94bff3c50542a6e4ab9a4f4cef093262fe65 | ||||
| 
 | ||||
| * Re-expose some dmi APIs for use in VCSM | ||||
| ---
 | ||||
|  arch/arm/include/asm/cacheflush.h | 21 +++++++++++++++++++++ | ||||
|  arch/arm/include/asm/glue-cache.h |  2 ++ | ||||
|  arch/arm/mm/proc-macros.S         |  2 ++ | ||||
|  arch/arm/mm/proc-syms.c           |  3 +++ | ||||
|  4 files changed, 28 insertions(+) | ||||
| 
 | ||||
| --- a/arch/arm/include/asm/cacheflush.h
 | ||||
| +++ b/arch/arm/include/asm/cacheflush.h
 | ||||
| @@ -91,6 +91,21 @@
 | ||||
|   *	DMA Cache Coherency | ||||
|   *	=================== | ||||
|   * | ||||
| + *	dma_inv_range(start, end)
 | ||||
| + *
 | ||||
| + *		Invalidate (discard) the specified virtual address range.
 | ||||
| + *		May not write back any entries.  If 'start' or 'end'
 | ||||
| + *		are not cache line aligned, those lines must be written
 | ||||
| + *		back.
 | ||||
| + *		- start  - virtual start address
 | ||||
| + *		- end    - virtual end address
 | ||||
| + *
 | ||||
| + *	dma_clean_range(start, end)
 | ||||
| + *
 | ||||
| + *		Clean (write back) the specified virtual address range.
 | ||||
| + *		- start  - virtual start address
 | ||||
| + *		- end    - virtual end address
 | ||||
| + *
 | ||||
|   *	dma_flush_range(start, end) | ||||
|   * | ||||
|   *		Clean and invalidate the specified virtual address range. | ||||
| @@ -112,6 +127,8 @@ struct cpu_cache_fns {
 | ||||
|  	void (*dma_map_area)(const void *, size_t, int); | ||||
|  	void (*dma_unmap_area)(const void *, size_t, int); | ||||
|   | ||||
| +	void (*dma_inv_range)(const void *, const void *);
 | ||||
| +	void (*dma_clean_range)(const void *, const void *);
 | ||||
|  	void (*dma_flush_range)(const void *, const void *); | ||||
|  } __no_randomize_layout; | ||||
|   | ||||
| @@ -137,6 +154,8 @@ extern struct cpu_cache_fns cpu_cache;
 | ||||
|   * is visible to DMA, or data written by DMA to system memory is | ||||
|   * visible to the CPU. | ||||
|   */ | ||||
| +#define dmac_inv_range			cpu_cache.dma_inv_range
 | ||||
| +#define dmac_clean_range		cpu_cache.dma_clean_range
 | ||||
|  #define dmac_flush_range		cpu_cache.dma_flush_range | ||||
|   | ||||
|  #else | ||||
| @@ -156,6 +175,8 @@ extern void __cpuc_flush_dcache_area(voi
 | ||||
|   * is visible to DMA, or data written by DMA to system memory is | ||||
|   * visible to the CPU. | ||||
|   */ | ||||
| +extern void dmac_inv_range(const void *, const void *);
 | ||||
| +extern void dmac_clean_range(const void *, const void *);
 | ||||
|  extern void dmac_flush_range(const void *, const void *); | ||||
|   | ||||
|  #endif | ||||
| --- a/arch/arm/include/asm/glue-cache.h
 | ||||
| +++ b/arch/arm/include/asm/glue-cache.h
 | ||||
| @@ -155,6 +155,8 @@ static inline void nop_dma_unmap_area(co
 | ||||
|  #define __cpuc_coherent_user_range	__glue(_CACHE,_coherent_user_range) | ||||
|  #define __cpuc_flush_dcache_area	__glue(_CACHE,_flush_kern_dcache_area) | ||||
|   | ||||
| +#define dmac_inv_range			__glue(_CACHE,_dma_inv_range)
 | ||||
| +#define dmac_clean_range		__glue(_CACHE,_dma_clean_range)
 | ||||
|  #define dmac_flush_range		__glue(_CACHE,_dma_flush_range) | ||||
|  #endif | ||||
|   | ||||
| --- a/arch/arm/mm/proc-macros.S
 | ||||
| +++ b/arch/arm/mm/proc-macros.S
 | ||||
| @@ -333,6 +333,8 @@ ENTRY(\name\()_cache_fns)
 | ||||
|  	.long	\name\()_flush_kern_dcache_area | ||||
|  	.long	\name\()_dma_map_area | ||||
|  	.long	\name\()_dma_unmap_area | ||||
| +	.long	\name\()_dma_inv_range
 | ||||
| +	.long	\name\()_dma_clean_range
 | ||||
|  	.long	\name\()_dma_flush_range | ||||
|  	.size	\name\()_cache_fns, . - \name\()_cache_fns | ||||
|  .endm | ||||
| --- a/arch/arm/mm/proc-syms.c
 | ||||
| +++ b/arch/arm/mm/proc-syms.c
 | ||||
| @@ -27,6 +27,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all);
 | ||||
|  EXPORT_SYMBOL(__cpuc_flush_user_range); | ||||
|  EXPORT_SYMBOL(__cpuc_coherent_kern_range); | ||||
|  EXPORT_SYMBOL(__cpuc_flush_dcache_area); | ||||
| +EXPORT_SYMBOL(dmac_inv_range);
 | ||||
| +EXPORT_SYMBOL(dmac_clean_range);
 | ||||
| +EXPORT_SYMBOL(dmac_flush_range);
 | ||||
|  #else | ||||
|  EXPORT_SYMBOL(cpu_cache); | ||||
|  #endif | ||||
|  | @ -0,0 +1,53 @@ | |||
| From 2d2b074c9a029ad9dfc1c62376a478be4d7b1c75 Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Fri, 25 Aug 2017 19:18:13 +0100 | ||||
| Subject: [PATCH] cache: export clean and invalidate | ||||
| 
 | ||||
| hack: cache: Fix linker error | ||||
| ---
 | ||||
|  arch/arm/mm/cache-v6.S | 4 ++-- | ||||
|  arch/arm/mm/cache-v7.S | 6 ++++-- | ||||
|  2 files changed, 6 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/arch/arm/mm/cache-v6.S
 | ||||
| +++ b/arch/arm/mm/cache-v6.S
 | ||||
| @@ -198,7 +198,7 @@ ENTRY(v6_flush_kern_dcache_area)
 | ||||
|   *	- start   - virtual start address of region | ||||
|   *	- end     - virtual end address of region | ||||
|   */ | ||||
| -v6_dma_inv_range:
 | ||||
| +ENTRY(v6_dma_inv_range)
 | ||||
|  #ifdef CONFIG_DMA_CACHE_RWFO | ||||
|  	ldrb	r2, [r0]			@ read for ownership | ||||
|  	strb	r2, [r0]			@ write for ownership | ||||
| @@ -243,7 +243,7 @@ v6_dma_inv_range:
 | ||||
|   *	- start   - virtual start address of region | ||||
|   *	- end     - virtual end address of region | ||||
|   */ | ||||
| -v6_dma_clean_range:
 | ||||
| +ENTRY(v6_dma_clean_range)
 | ||||
|  	bic	r0, r0, #D_CACHE_LINE_SIZE - 1 | ||||
|  1: | ||||
|  #ifdef CONFIG_DMA_CACHE_RWFO | ||||
| --- a/arch/arm/mm/cache-v7.S
 | ||||
| +++ b/arch/arm/mm/cache-v7.S
 | ||||
| @@ -359,7 +359,8 @@ ENDPROC(v7_flush_kern_dcache_area)
 | ||||
|   *	- start   - virtual start address of region | ||||
|   *	- end     - virtual end address of region | ||||
|   */ | ||||
| -v7_dma_inv_range:
 | ||||
| +ENTRY(b15_dma_inv_range)
 | ||||
| +ENTRY(v7_dma_inv_range)
 | ||||
|  	dcache_line_size r2, r3 | ||||
|  	sub	r3, r2, #1 | ||||
|  	tst	r0, r3 | ||||
| @@ -389,7 +390,8 @@ ENDPROC(v7_dma_inv_range)
 | ||||
|   *	- start   - virtual start address of region | ||||
|   *	- end     - virtual end address of region | ||||
|   */ | ||||
| -v7_dma_clean_range:
 | ||||
| +ENTRY(b15_dma_clean_range)
 | ||||
| +ENTRY(v7_dma_clean_range)
 | ||||
|  	dcache_line_size r2, r3 | ||||
|  	sub	r3, r2, #1 | ||||
|  	bic	r0, r0, r3 | ||||
|  | @ -0,0 +1,32 @@ | |||
| From 3f93e06e535477cdb96d8056f8309288744a3a8d Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Mon, 20 Apr 2020 13:41:10 +0100 | ||||
| Subject: [PATCH] Revert "spi: spidev: Fix CS polarity if GPIO descriptors are | ||||
|  used" | ||||
| 
 | ||||
| This reverts commit 83b2a8fe43bda0c11981ad6afa5dd0104d78be28. | ||||
| ---
 | ||||
|  drivers/spi/spidev.c | 5 ----- | ||||
|  1 file changed, 5 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/spi/spidev.c
 | ||||
| +++ b/drivers/spi/spidev.c
 | ||||
| @@ -414,7 +414,6 @@ spidev_ioctl(struct file *filp, unsigned
 | ||||
|  		else | ||||
|  			retval = get_user(tmp, (u32 __user *)arg); | ||||
|  		if (retval == 0) { | ||||
| -			struct spi_controller *ctlr = spi->controller;
 | ||||
|  			u32	save = spi->mode; | ||||
|   | ||||
|  			if (tmp & ~SPI_MODE_MASK) { | ||||
| @@ -422,10 +421,6 @@ spidev_ioctl(struct file *filp, unsigned
 | ||||
|  				break; | ||||
|  			} | ||||
|   | ||||
| -			if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
 | ||||
| -			    ctlr->cs_gpiods[spi->chip_select])
 | ||||
| -				tmp |= SPI_CS_HIGH;
 | ||||
| -
 | ||||
|  			tmp |= spi->mode & ~SPI_MODE_MASK; | ||||
|  			spi->mode = tmp & SPI_MODE_USER_MASK; | ||||
|  			retval = spi_setup(spi); | ||||
|  | @ -0,0 +1,45 @@ | |||
| From 6e0505463b9e5c402d9b7fb340c599537f9d1bb8 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Mon, 1 Mar 2021 09:12:44 +0000 | ||||
| Subject: [PATCH] Revert "Bluetooth: Always request for user confirmation for | ||||
|  Just Works (LE SC)" | ||||
| 
 | ||||
| This reverts commit ffee202a78c2980688bc5d2f7d56480e69a5e0c9. | ||||
| 
 | ||||
| The commit "Bluetooth: Always request for user confirmation for Just | ||||
| Works" prevents BLE devices pairing in (at least) the Raspberry Pi OS | ||||
| GUI. After reverting it, pairing works again. Although this companion | ||||
| commit ("... (LE SC)") has not been demonstrated to be problematic, | ||||
| it follows the same logic and therefore could affect some use cases. | ||||
| 
 | ||||
| If another solution to the problem is found then this reversion will | ||||
| be removed. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/linux/issues/4139 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  net/bluetooth/smp.c | 5 +---- | ||||
|  1 file changed, 1 insertion(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/net/bluetooth/smp.c
 | ||||
| +++ b/net/bluetooth/smp.c
 | ||||
| @@ -2207,7 +2207,7 @@ mackey_and_ltk:
 | ||||
|  	if (err) | ||||
|  		return SMP_UNSPECIFIED; | ||||
|   | ||||
| -	if (smp->method == REQ_OOB) {
 | ||||
| +	if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
 | ||||
|  		if (hcon->out) { | ||||
|  			sc_dhkey_check(smp); | ||||
|  			SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); | ||||
| @@ -2222,9 +2222,6 @@ mackey_and_ltk:
 | ||||
|  	confirm_hint = 0; | ||||
|   | ||||
|  confirm: | ||||
| -	if (smp->method == JUST_WORKS)
 | ||||
| -		confirm_hint = 1;
 | ||||
| -
 | ||||
|  	err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, | ||||
|  					hcon->dst_type, passkey, confirm_hint); | ||||
|  	if (err) | ||||
|  | @ -0,0 +1,43 @@ | |||
| From a7def069d38b0f6b4304d6019c99e364ee1ad297 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Mon, 1 Mar 2021 09:14:35 +0000 | ||||
| Subject: [PATCH] Revert "Bluetooth: Always request for user confirmation for | ||||
|  Just Works" | ||||
| 
 | ||||
| This reverts commit 92516cd97fd4d8ad5b1421a0d51771044f453a5f. | ||||
| 
 | ||||
| Thi commit "Bluetooth: Always request for user confirmation for Just | ||||
| Works" prevents BLE devices pairing in (at least) the Raspberry Pi OS | ||||
| GUI. After reverting it, pairing works again. | ||||
| 
 | ||||
| If another solution to the problem is found then this reversion will | ||||
| be removed. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/linux/issues/4139 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  net/bluetooth/smp.c | 11 ++--------- | ||||
|  1 file changed, 2 insertions(+), 9 deletions(-) | ||||
| 
 | ||||
| --- a/net/bluetooth/smp.c
 | ||||
| +++ b/net/bluetooth/smp.c
 | ||||
| @@ -883,16 +883,9 @@ static int tk_request(struct l2cap_conn
 | ||||
|  	    hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) | ||||
|  		smp->method = JUST_WORKS; | ||||
|   | ||||
| -	/* If Just Works, Continue with Zero TK and ask user-space for
 | ||||
| -	 * confirmation */
 | ||||
| +	/* If Just Works, Continue with Zero TK */
 | ||||
|  	if (smp->method == JUST_WORKS) { | ||||
| -		ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
 | ||||
| -						hcon->type,
 | ||||
| -						hcon->dst_type,
 | ||||
| -						passkey, 1);
 | ||||
| -		if (ret)
 | ||||
| -			return ret;
 | ||||
| -		set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
 | ||||
| +		set_bit(SMP_FLAG_TK_VALID, &smp->flags);
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
|  | @ -0,0 +1,28 @@ | |||
| From c0ef0339d77fc9f5919ac06b9b961289f5c865cc Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.com> | ||||
| Date: Mon, 7 Mar 2022 16:18:55 +0000 | ||||
| Subject: [PATCH] Revert "net: bcmgenet: Request APD, DLL disable and IDDQ-SR" | ||||
| 
 | ||||
| This reverts commit c3a4c69360ab43560f212eed326c9d8bde35b14c, which | ||||
| broke rebooting when network booting. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/rpi-eeprom/issues/417 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/net/ethernet/broadcom/genet/bcmmii.c | 4 +--- | ||||
|  1 file changed, 1 insertion(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
 | ||||
| +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
 | ||||
| @@ -290,9 +290,7 @@ int bcmgenet_mii_probe(struct net_device
 | ||||
|  	struct device_node *dn = kdev->of_node; | ||||
|  	phy_interface_t phy_iface = priv->phy_interface; | ||||
|  	struct phy_device *phydev; | ||||
| -	u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE |
 | ||||
| -			PHY_BRCM_DIS_TXCRXC_NOENRGY |
 | ||||
| -			PHY_BRCM_IDDQ_SUSPEND;
 | ||||
| +	u32 phy_flags = 0;
 | ||||
|  	int ret; | ||||
|   | ||||
|  	/* Communicate the integrated PHY revision */ | ||||
|  | @ -0,0 +1,47 @@ | |||
| From 5811c0719511310ea778259dc83dffd1ee0e3e1c Mon Sep 17 00:00:00 2001 | ||||
| From: Steve Glendinning <steve.glendinning@smsc.com> | ||||
| Date: Thu, 19 Feb 2015 18:47:12 +0000 | ||||
| Subject: [PATCH] smsx95xx: fix crimes against truesize | ||||
| 
 | ||||
| smsc95xx is adjusting truesize when it shouldn't, and following a recent patch from Eric this is now triggering warnings. | ||||
| 
 | ||||
| This patch stops smsc95xx from changing truesize. | ||||
| 
 | ||||
| Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com> | ||||
| ---
 | ||||
|  drivers/net/usb/smsc95xx.c | 10 ++++++++-- | ||||
|  1 file changed, 8 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/usb/smsc95xx.c
 | ||||
| +++ b/drivers/net/usb/smsc95xx.c
 | ||||
| @@ -79,6 +79,10 @@ static bool turbo_mode = true;
 | ||||
|  module_param(turbo_mode, bool, 0644); | ||||
|  MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); | ||||
|   | ||||
| +static bool truesize_mode = false;
 | ||||
| +module_param(truesize_mode, bool, 0644);
 | ||||
| +MODULE_PARM_DESC(truesize_mode, "Report larger truesize value");
 | ||||
| +
 | ||||
|  static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, | ||||
|  					  u32 *data) | ||||
|  { | ||||
| @@ -1870,7 +1874,8 @@ static int smsc95xx_rx_fixup(struct usbn
 | ||||
|  				if (dev->net->features & NETIF_F_RXCSUM) | ||||
|  					smsc95xx_rx_csum_offload(skb); | ||||
|  				skb_trim(skb, skb->len - 4); /* remove fcs */ | ||||
| -				skb->truesize = size + sizeof(struct sk_buff);
 | ||||
| +				if (truesize_mode)
 | ||||
| +					skb->truesize = size + sizeof(struct sk_buff);
 | ||||
|   | ||||
|  				return 1; | ||||
|  			} | ||||
| @@ -1888,7 +1893,8 @@ static int smsc95xx_rx_fixup(struct usbn
 | ||||
|  			if (dev->net->features & NETIF_F_RXCSUM) | ||||
|  				smsc95xx_rx_csum_offload(ax_skb); | ||||
|  			skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ | ||||
| -			ax_skb->truesize = size + sizeof(struct sk_buff);
 | ||||
| +			if (truesize_mode)
 | ||||
| +				ax_skb->truesize = size + sizeof(struct sk_buff);
 | ||||
|   | ||||
|  			usbnet_skb_return(dev, ax_skb); | ||||
|  		} | ||||
|  | @ -0,0 +1,43 @@ | |||
| From a9b5e24cb96753cbc74090246239e83ba53569da Mon Sep 17 00:00:00 2001 | ||||
| From: Sam Nazarko <email@samnazarko.co.uk> | ||||
| Date: Fri, 1 Apr 2016 17:27:21 +0100 | ||||
| Subject: [PATCH] smsc95xx: Experimental: Enable turbo_mode and packetsize=2560 | ||||
|  by default | ||||
| 
 | ||||
| See: http://forum.kodi.tv/showthread.php?tid=285288 | ||||
| ---
 | ||||
|  drivers/net/usb/smsc95xx.c | 14 +++++++++----- | ||||
|  1 file changed, 9 insertions(+), 5 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/usb/smsc95xx.c
 | ||||
| +++ b/drivers/net/usb/smsc95xx.c
 | ||||
| @@ -83,6 +83,10 @@ static bool truesize_mode = false;
 | ||||
|  module_param(truesize_mode, bool, 0644); | ||||
|  MODULE_PARM_DESC(truesize_mode, "Report larger truesize value"); | ||||
|   | ||||
| +static int packetsize = 2560;
 | ||||
| +module_param(packetsize, int, 0644);
 | ||||
| +MODULE_PARM_DESC(packetsize, "Override the RX URB packet size");
 | ||||
| +
 | ||||
|  static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, | ||||
|  					  u32 *data) | ||||
|  { | ||||
| @@ -936,13 +940,13 @@ static int smsc95xx_reset(struct usbnet
 | ||||
|   | ||||
|  	if (!turbo_mode) { | ||||
|  		burst_cap = 0; | ||||
| -		dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
 | ||||
| +		dev->rx_urb_size = packetsize ? packetsize : MAX_SINGLE_PACKET_SIZE;
 | ||||
|  	} else if (dev->udev->speed == USB_SPEED_HIGH) { | ||||
| -		burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
 | ||||
| -		dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
 | ||||
| +		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_HS_BURST_CAP_SIZE;
 | ||||
| +		burst_cap = dev->rx_urb_size / HS_USB_PKT_SIZE;
 | ||||
|  	} else { | ||||
| -		burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
 | ||||
| -		dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
 | ||||
| +		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_FS_BURST_CAP_SIZE;
 | ||||
| +		burst_cap = dev->rx_urb_size / FS_USB_PKT_SIZE;
 | ||||
|  	} | ||||
|   | ||||
|  	netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", | ||||
|  | @ -0,0 +1,99 @@ | |||
| From 429531c0dc5f4e495658320b45a5164c6668a533 Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Tue, 26 Mar 2013 17:26:38 +0000 | ||||
| Subject: [PATCH] Allow mac address to be set in smsc95xx | ||||
| 
 | ||||
| Signed-off-by: popcornmix <popcornmix@gmail.com> | ||||
| 
 | ||||
| SQUASH: smsc95xx: Use dev_mod_addr to set MAC addr | ||||
| 
 | ||||
| Since adeef3e32146 ("net: constify netdev->dev_addr") it has been | ||||
| illegal to write to the dev_addr MAC address field. Later commits | ||||
| have added explicit checks that it hasn't been modified by nefarious | ||||
| means. The dev_addr_mod helper function is the accepted way to change | ||||
| the dev_addr field, so use it. | ||||
| 
 | ||||
| Squash with 96c1def63ee1 ("Allow mac address to be set in smsc95xx"). | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.com> | ||||
| ---
 | ||||
|  drivers/net/usb/smsc95xx.c | 54 ++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 54 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/net/usb/smsc95xx.c
 | ||||
| +++ b/drivers/net/usb/smsc95xx.c
 | ||||
| @@ -87,6 +87,10 @@ static int packetsize = 2560;
 | ||||
|  module_param(packetsize, int, 0644); | ||||
|  MODULE_PARM_DESC(packetsize, "Override the RX URB packet size"); | ||||
|   | ||||
| +static char *macaddr = ":";
 | ||||
| +module_param(macaddr, charp, 0);
 | ||||
| +MODULE_PARM_DESC(macaddr, "MAC address");
 | ||||
| +
 | ||||
|  static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, | ||||
|  					  u32 *data) | ||||
|  { | ||||
| @@ -809,6 +813,52 @@ static int smsc95xx_ioctl(struct net_dev
 | ||||
|  	return phy_mii_ioctl(netdev->phydev, rq, cmd); | ||||
|  } | ||||
|   | ||||
| +/* Check the macaddr module parameter for a MAC address */
 | ||||
| +static int smsc95xx_is_macaddr_param(struct usbnet *dev, struct net_device *nd)
 | ||||
| +{
 | ||||
| +       int i, j, got_num, num;
 | ||||
| +       u8 mtbl[ETH_ALEN];
 | ||||
| +
 | ||||
| +       if (macaddr[0] == ':')
 | ||||
| +               return 0;
 | ||||
| +
 | ||||
| +       i = 0;
 | ||||
| +       j = 0;
 | ||||
| +       num = 0;
 | ||||
| +       got_num = 0;
 | ||||
| +       while (j < ETH_ALEN) {
 | ||||
| +               if (macaddr[i] && macaddr[i] != ':') {
 | ||||
| +                       got_num++;
 | ||||
| +                       if ('0' <= macaddr[i] && macaddr[i] <= '9')
 | ||||
| +                               num = num * 16 + macaddr[i] - '0';
 | ||||
| +                       else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
 | ||||
| +                               num = num * 16 + 10 + macaddr[i] - 'A';
 | ||||
| +                       else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
 | ||||
| +                               num = num * 16 + 10 + macaddr[i] - 'a';
 | ||||
| +                       else
 | ||||
| +                               break;
 | ||||
| +                       i++;
 | ||||
| +               } else if (got_num == 2) {
 | ||||
| +                       mtbl[j++] = (u8) num;
 | ||||
| +                       num = 0;
 | ||||
| +                       got_num = 0;
 | ||||
| +                       i++;
 | ||||
| +               } else {
 | ||||
| +                       break;
 | ||||
| +               }
 | ||||
| +       }
 | ||||
| +
 | ||||
| +       if (j == ETH_ALEN) {
 | ||||
| +               netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
 | ||||
| +               "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
 | ||||
| +                                               mtbl[3], mtbl[4], mtbl[5]);
 | ||||
| +	       dev_addr_mod(nd, 0, mtbl, ETH_ALEN);
 | ||||
| +               return 1;
 | ||||
| +       } else {
 | ||||
| +               return 0;
 | ||||
| +       }
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void smsc95xx_init_mac_address(struct usbnet *dev) | ||||
|  { | ||||
|  	u8 addr[ETH_ALEN]; | ||||
| @@ -832,6 +882,10 @@ static void smsc95xx_init_mac_address(st
 | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	/* Check module parameters */
 | ||||
| +	if (smsc95xx_is_macaddr_param(dev, dev->net))
 | ||||
| +		return;
 | ||||
| +
 | ||||
|  	/* no useful static MAC address found. generate a random one */ | ||||
|  	eth_hw_addr_random(dev->net); | ||||
|  	netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); | ||||
|  | @ -0,0 +1,85 @@ | |||
| From b2670b95c0cbe7b6ba038a7615725d87ad59af4f Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Mon, 27 Nov 2017 17:14:54 +0000 | ||||
| Subject: [PATCH] cgroup: Disable cgroup "memory" by default | ||||
| 
 | ||||
| Some Raspberry Pis have limited RAM and most users won't use the | ||||
| cgroup memory support so it is disabled by default. Enable with: | ||||
| 
 | ||||
|     cgroup_enable=memory | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/linux/issues/1950 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  kernel/cgroup/cgroup.c | 38 ++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 38 insertions(+) | ||||
| 
 | ||||
| --- a/kernel/cgroup/cgroup.c
 | ||||
| +++ b/kernel/cgroup/cgroup.c
 | ||||
| @@ -6061,6 +6061,9 @@ int __init cgroup_init_early(void)
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static u16 cgroup_enable_mask __initdata;
 | ||||
| +static int __init cgroup_disable(char *str);
 | ||||
| +
 | ||||
|  /** | ||||
|   * cgroup_init - cgroup initialization | ||||
|   * | ||||
| @@ -6094,6 +6097,12 @@ int __init cgroup_init(void)
 | ||||
|   | ||||
|  	cgroup_unlock(); | ||||
|   | ||||
| +	/*
 | ||||
| +	 * Apply an implicit disable, knowing that an explicit enable will
 | ||||
| +	 * prevent if from doing anything.
 | ||||
| +	 */
 | ||||
| +	cgroup_disable("memory");
 | ||||
| +
 | ||||
|  	for_each_subsys(ss, ssid) { | ||||
|  		if (ss->early_init) { | ||||
|  			struct cgroup_subsys_state *css = | ||||
| @@ -6734,6 +6743,10 @@ static int __init cgroup_disable(char *s
 | ||||
|  			    strcmp(token, ss->legacy_name)) | ||||
|  				continue; | ||||
|   | ||||
| +			/* An explicit cgroup_enable overrides a disable */
 | ||||
| +			if (cgroup_enable_mask & (1 << i))
 | ||||
| +				continue;
 | ||||
| +
 | ||||
|  			static_branch_disable(cgroup_subsys_enabled_key[i]); | ||||
|  			pr_info("Disabling %s control group subsystem\n", | ||||
|  				ss->name); | ||||
| @@ -6752,6 +6765,31 @@ static int __init cgroup_disable(char *s
 | ||||
|  } | ||||
|  __setup("cgroup_disable=", cgroup_disable); | ||||
|   | ||||
| +static int __init cgroup_enable(char *str)
 | ||||
| +{
 | ||||
| +	struct cgroup_subsys *ss;
 | ||||
| +	char *token;
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	while ((token = strsep(&str, ",")) != NULL) {
 | ||||
| +		if (!*token)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		for_each_subsys(ss, i) {
 | ||||
| +			if (strcmp(token, ss->name) &&
 | ||||
| +			    strcmp(token, ss->legacy_name))
 | ||||
| +				continue;
 | ||||
| +
 | ||||
| +			cgroup_enable_mask |= 1 << i;
 | ||||
| +			static_branch_enable(cgroup_subsys_enabled_key[i]);
 | ||||
| +			pr_info("Enabling %s control group subsystem\n",
 | ||||
| +				ss->name);
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	return 1;
 | ||||
| +}
 | ||||
| +__setup("cgroup_enable=", cgroup_enable);
 | ||||
| +
 | ||||
|  void __init __weak enable_debug_cgroup(void) { } | ||||
|   | ||||
|  static int __init enable_cgroup_debug(char *str) | ||||
|  | @ -0,0 +1,27 @@ | |||
| From 600eecf6486e7bd0f752863800296f99fe6ab05a Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Fri, 13 Mar 2015 12:43:36 +0000 | ||||
| Subject: [PATCH] Protect __release_resource against resources without parents | ||||
| 
 | ||||
| Without this patch, removing a device tree overlay can crash here. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  kernel/resource.c | 6 ++++++ | ||||
|  1 file changed, 6 insertions(+) | ||||
| 
 | ||||
| --- a/kernel/resource.c
 | ||||
| +++ b/kernel/resource.c
 | ||||
| @@ -200,6 +200,12 @@ static int __release_resource(struct res
 | ||||
|  { | ||||
|  	struct resource *tmp, **p, *chd; | ||||
|   | ||||
| +	if (!old->parent) {
 | ||||
| +		WARN(old->sibling, "sibling but no parent");
 | ||||
| +		if (old->sibling)
 | ||||
| +			return -EINVAL;
 | ||||
| +		return 0;
 | ||||
| +	}
 | ||||
|  	p = &old->parent->child; | ||||
|  	for (;;) { | ||||
|  		tmp = *p; | ||||
|  | @ -0,0 +1,24 @@ | |||
| From b22008959b2e03d2946e0e7e825cdda079baebb4 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Thu, 9 Feb 2017 14:33:30 +0000 | ||||
| Subject: [PATCH] irq-bcm2836: Avoid "Invalid trigger warning" | ||||
| 
 | ||||
| Initialise the level for each IRQ to avoid a warning from the | ||||
| arm arch timer code. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/irqchip/irq-bcm2836.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/irqchip/irq-bcm2836.c
 | ||||
| +++ b/drivers/irqchip/irq-bcm2836.c
 | ||||
| @@ -128,7 +128,7 @@ static int bcm2836_map(struct irq_domain
 | ||||
|  	irq_set_percpu_devid(irq); | ||||
|  	irq_domain_set_info(d, irq, hw, chip, d->host_data, | ||||
|  			    handle_percpu_devid_irq, NULL, NULL); | ||||
| -	irq_set_status_flags(irq, IRQ_NOAUTOEN);
 | ||||
| +	irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
|  | @ -0,0 +1,127 @@ | |||
| From 94d16d548bce05e1c330842f3e6ba9370d522b05 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> | ||||
| Date: Fri, 12 Jun 2015 19:01:05 +0200 | ||||
| Subject: [PATCH] irqchip: bcm2835: Add FIQ support | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Add a duplicate irq range with an offset on the hwirq's so the | ||||
| driver can detect that enable_fiq() is used. | ||||
| Tested with downstream dwc_otg USB controller driver. | ||||
| 
 | ||||
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> | ||||
| Reviewed-by: Eric Anholt <eric@anholt.net> | ||||
| Acked-by: Stephen Warren <swarren@wwwdotorg.org> | ||||
| ---
 | ||||
|  arch/arm/mach-bcm/Kconfig     |  1 + | ||||
|  drivers/irqchip/irq-bcm2835.c | 51 +++++++++++++++++++++++++++++++---- | ||||
|  2 files changed, 47 insertions(+), 5 deletions(-) | ||||
| 
 | ||||
| --- a/arch/arm/mach-bcm/Kconfig
 | ||||
| +++ b/arch/arm/mach-bcm/Kconfig
 | ||||
| @@ -159,6 +159,7 @@ config ARCH_BCM2835
 | ||||
|  	select ARM_TIMER_SP804 | ||||
|  	select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7 | ||||
|  	select BCM2835_TIMER | ||||
| +	select FIQ
 | ||||
|  	select PINCTRL | ||||
|  	select PINCTRL_BCM2835 | ||||
|  	select MFD_CORE | ||||
| --- a/drivers/irqchip/irq-bcm2835.c
 | ||||
| +++ b/drivers/irqchip/irq-bcm2835.c
 | ||||
| @@ -45,7 +45,7 @@
 | ||||
|  #include <asm/exception.h> | ||||
|   | ||||
|  /* Put the bank and irq (32 bits) into the hwirq */ | ||||
| -#define MAKE_HWIRQ(b, n)	((b << 5) | (n))
 | ||||
| +#define MAKE_HWIRQ(b, n)	(((b) << 5) | (n))
 | ||||
|  #define HWIRQ_BANK(i)		(i >> 5) | ||||
|  #define HWIRQ_BIT(i)		BIT(i & 0x1f) | ||||
|   | ||||
| @@ -62,9 +62,13 @@
 | ||||
|   | ||||
|  #define REG_FIQ_CONTROL		0x0c | ||||
|  #define FIQ_CONTROL_ENABLE	BIT(7) | ||||
| +#define REG_FIQ_ENABLE		FIQ_CONTROL_ENABLE
 | ||||
| +#define REG_FIQ_DISABLE	0
 | ||||
|   | ||||
|  #define NR_BANKS		3 | ||||
|  #define IRQS_PER_BANK		32 | ||||
| +#define NUMBER_IRQS		MAKE_HWIRQ(NR_BANKS, 0)
 | ||||
| +#define FIQ_START		(NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0))
 | ||||
|   | ||||
|  static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; | ||||
|  static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; | ||||
| @@ -89,14 +93,38 @@ static void __exception_irq_entry bcm283
 | ||||
|  	struct pt_regs *regs); | ||||
|  static void bcm2836_chained_handle_irq(struct irq_desc *desc); | ||||
|   | ||||
| +static inline unsigned int hwirq_to_fiq(unsigned long hwirq)
 | ||||
| +{
 | ||||
| +	hwirq -= NUMBER_IRQS;
 | ||||
| +	/*
 | ||||
| +	 * The hwirq numbering used in this driver is:
 | ||||
| +	 *   BASE (0-7) GPU1 (32-63) GPU2 (64-95).
 | ||||
| +	 * This differ from the one used in the FIQ register:
 | ||||
| +	 *   GPU1 (0-31) GPU2 (32-63) BASE (64-71)
 | ||||
| +	 */
 | ||||
| +	if (hwirq >= 32)
 | ||||
| +		return hwirq - 32;
 | ||||
| +
 | ||||
| +	return hwirq + 64;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void armctrl_mask_irq(struct irq_data *d) | ||||
|  { | ||||
| -	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]);
 | ||||
| +	if (d->hwirq >= NUMBER_IRQS)
 | ||||
| +		writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
 | ||||
| +	else
 | ||||
| +		writel_relaxed(HWIRQ_BIT(d->hwirq),
 | ||||
| +			       intc.disable[HWIRQ_BANK(d->hwirq)]);
 | ||||
|  } | ||||
|   | ||||
|  static void armctrl_unmask_irq(struct irq_data *d) | ||||
|  { | ||||
| -	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]);
 | ||||
| +	if (d->hwirq >= NUMBER_IRQS)
 | ||||
| +		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
 | ||||
| +			       intc.base + REG_FIQ_CONTROL);
 | ||||
| +	else
 | ||||
| +		writel_relaxed(HWIRQ_BIT(d->hwirq),
 | ||||
| +			       intc.enable[HWIRQ_BANK(d->hwirq)]);
 | ||||
|  } | ||||
|   | ||||
|  static struct irq_chip armctrl_chip = { | ||||
| @@ -142,8 +170,9 @@ static int __init armctrl_of_init(struct
 | ||||
|  	if (!base) | ||||
|  		panic("%pOF: unable to map IC registers\n", node); | ||||
|   | ||||
| -	intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0),
 | ||||
| -			&armctrl_ops, NULL);
 | ||||
| +	intc.base = base;
 | ||||
| +	intc.domain = irq_domain_add_linear(node, NUMBER_IRQS * 2,
 | ||||
| +					    &armctrl_ops, NULL);
 | ||||
|  	if (!intc.domain) | ||||
|  		panic("%pOF: unable to create IRQ domain\n", node); | ||||
|   | ||||
| @@ -186,6 +215,18 @@ static int __init armctrl_of_init(struct
 | ||||
|  		set_handle_irq(bcm2835_handle_irq); | ||||
|  	} | ||||
|   | ||||
| +	/* Make a duplicate irq range which is used to enable FIQ */
 | ||||
| +	for (b = 0; b < NR_BANKS; b++) {
 | ||||
| +		for (i = 0; i < bank_irqs[b]; i++) {
 | ||||
| +			irq = irq_create_mapping(intc.domain,
 | ||||
| +					MAKE_HWIRQ(b, i) + NUMBER_IRQS);
 | ||||
| +			BUG_ON(irq <= 0);
 | ||||
| +			irq_set_chip(irq, &armctrl_chip);
 | ||||
| +			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	init_FIQ(FIQ_START);
 | ||||
| +
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
|  | @ -0,0 +1,99 @@ | |||
| From b4e8413bf340d2b79e72fc002872a92a93433c75 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> | ||||
| Date: Fri, 23 Oct 2015 16:26:55 +0200 | ||||
| Subject: [PATCH] irqchip: irq-bcm2835: Add 2836 FIQ support | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> | ||||
| ---
 | ||||
|  drivers/irqchip/irq-bcm2835.c | 43 +++++++++++++++++++++++++++++++++-- | ||||
|  1 file changed, 41 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/irqchip/irq-bcm2835.c
 | ||||
| +++ b/drivers/irqchip/irq-bcm2835.c
 | ||||
| @@ -41,8 +41,11 @@
 | ||||
|  #include <linux/of_irq.h> | ||||
|  #include <linux/irqchip.h> | ||||
|  #include <linux/irqdomain.h> | ||||
| +#include <linux/mfd/syscon.h>
 | ||||
| +#include <linux/regmap.h>
 | ||||
|   | ||||
|  #include <asm/exception.h> | ||||
| +#include <asm/mach/irq.h>
 | ||||
|   | ||||
|  /* Put the bank and irq (32 bits) into the hwirq */ | ||||
|  #define MAKE_HWIRQ(b, n)	(((b) << 5) | (n)) | ||||
| @@ -60,6 +63,9 @@
 | ||||
|  #define BANK0_VALID_MASK	(BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \ | ||||
|  					| SHORTCUT1_MASK | SHORTCUT2_MASK) | ||||
|   | ||||
| +#undef ARM_LOCAL_GPU_INT_ROUTING
 | ||||
| +#define ARM_LOCAL_GPU_INT_ROUTING 0x0c
 | ||||
| +
 | ||||
|  #define REG_FIQ_CONTROL		0x0c | ||||
|  #define FIQ_CONTROL_ENABLE	BIT(7) | ||||
|  #define REG_FIQ_ENABLE		FIQ_CONTROL_ENABLE | ||||
| @@ -86,6 +92,7 @@ struct armctrl_ic {
 | ||||
|  	void __iomem *enable[NR_BANKS]; | ||||
|  	void __iomem *disable[NR_BANKS]; | ||||
|  	struct irq_domain *domain; | ||||
| +	struct regmap *local_regmap;
 | ||||
|  }; | ||||
|   | ||||
|  static struct armctrl_ic intc __read_mostly; | ||||
| @@ -119,12 +126,35 @@ static void armctrl_mask_irq(struct irq_
 | ||||
|   | ||||
|  static void armctrl_unmask_irq(struct irq_data *d) | ||||
|  { | ||||
| -	if (d->hwirq >= NUMBER_IRQS)
 | ||||
| +	if (d->hwirq >= NUMBER_IRQS) {
 | ||||
| +		if (num_online_cpus() > 1) {
 | ||||
| +			unsigned int data;
 | ||||
| +			int ret;
 | ||||
| +
 | ||||
| +			if (!intc.local_regmap) {
 | ||||
| +				pr_err("FIQ is disabled due to missing regmap\n");
 | ||||
| +				return;
 | ||||
| +			}
 | ||||
| +
 | ||||
| +			ret = regmap_read(intc.local_regmap,
 | ||||
| +					  ARM_LOCAL_GPU_INT_ROUTING, &data);
 | ||||
| +			if (ret) {
 | ||||
| +				pr_err("Failed to read int routing %d\n", ret);
 | ||||
| +				return;
 | ||||
| +			}
 | ||||
| +
 | ||||
| +			data &= ~0xc;
 | ||||
| +			data |= (1 << 2);
 | ||||
| +			regmap_write(intc.local_regmap,
 | ||||
| +				     ARM_LOCAL_GPU_INT_ROUTING, data);
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq), | ||||
|  			       intc.base + REG_FIQ_CONTROL); | ||||
| -	else
 | ||||
| +	} else {
 | ||||
|  		writel_relaxed(HWIRQ_BIT(d->hwirq), | ||||
|  			       intc.enable[HWIRQ_BANK(d->hwirq)]); | ||||
| +	}
 | ||||
|  } | ||||
|   | ||||
|  static struct irq_chip armctrl_chip = { | ||||
| @@ -215,6 +245,15 @@ static int __init armctrl_of_init(struct
 | ||||
|  		set_handle_irq(bcm2835_handle_irq); | ||||
|  	} | ||||
|   | ||||
| +	if (is_2836) {
 | ||||
| +		intc.local_regmap =
 | ||||
| +			syscon_regmap_lookup_by_compatible("brcm,bcm2836-arm-local");
 | ||||
| +		if (IS_ERR(intc.local_regmap)) {
 | ||||
| +			pr_err("Failed to get local register map. FIQ is disabled for cpus > 1\n");
 | ||||
| +			intc.local_regmap = NULL;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	/* Make a duplicate irq range which is used to enable FIQ */ | ||||
|  	for (b = 0; b < NR_BANKS; b++) { | ||||
|  		for (i = 0; i < bank_irqs[b]; i++) { | ||||
|  | @ -0,0 +1,24 @@ | |||
| From 3e8d60e56b3274521715183e76ce6d9c326537b2 Mon Sep 17 00:00:00 2001 | ||||
| From: Dom Cobley <popcornmix@gmail.com> | ||||
| Date: Mon, 24 Jan 2022 13:41:16 +0000 | ||||
| Subject: [PATCH] spi: spidev: Completely disable the spidev warning | ||||
| 
 | ||||
| An alternative strategy would be to use "rpi,spidev" instead, but that | ||||
| would require many Raspberry Pi Device Tree changes. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/spi/spidev.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/spi/spidev.c
 | ||||
| +++ b/drivers/spi/spidev.c
 | ||||
| @@ -707,7 +707,7 @@ MODULE_DEVICE_TABLE(spi, spidev_spi_ids)
 | ||||
|   */ | ||||
|  static int spidev_of_check(struct device *dev) | ||||
|  { | ||||
| -	if (device_property_match_string(dev, "compatible", "spidev") < 0)
 | ||||
| +	if (1 || device_property_match_string(dev, "compatible", "spidev") < 0)
 | ||||
|  		return 0; | ||||
|   | ||||
|  	dev_err(dev, "spidev listed directly in DT is not supported\n"); | ||||
|  | @ -0,0 +1,100 @@ | |||
| From 3b7845accab532c7324e2ecd9dab278ade4af19d Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> | ||||
| Date: Sat, 3 Oct 2015 22:22:55 +0200 | ||||
| Subject: [PATCH] dmaengine: bcm2835: Load driver early and support legacy API | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Load driver early since at least bcm2708_fb doesn't support deferred | ||||
| probing and even if it did, we don't want the video driver deferred. | ||||
| Support the legacy DMA API which is needed by bcm2708_fb. | ||||
| Don't mask out channel 2. | ||||
| 
 | ||||
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> | ||||
| ---
 | ||||
|  drivers/dma/Kconfig       |  2 +- | ||||
|  drivers/dma/bcm2835-dma.c | 26 +++++++++++++++++++++++++- | ||||
|  2 files changed, 26 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/dma/Kconfig
 | ||||
| +++ b/drivers/dma/Kconfig
 | ||||
| @@ -135,7 +135,7 @@ config BCM_SBA_RAID
 | ||||
|   | ||||
|  config DMA_BCM2835 | ||||
|  	tristate "BCM2835 DMA engine support" | ||||
| -	depends on ARCH_BCM2835
 | ||||
| +	depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709
 | ||||
|  	select DMA_ENGINE | ||||
|  	select DMA_VIRTUAL_CHANNELS | ||||
|   | ||||
| --- a/drivers/dma/bcm2835-dma.c
 | ||||
| +++ b/drivers/dma/bcm2835-dma.c
 | ||||
| @@ -25,6 +25,7 @@
 | ||||
|  #include <linux/interrupt.h> | ||||
|  #include <linux/list.h> | ||||
|  #include <linux/module.h> | ||||
| +#include <linux/platform_data/dma-bcm2708.h>
 | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/io.h> | ||||
| @@ -36,6 +37,7 @@
 | ||||
|   | ||||
|  #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14 | ||||
|  #define BCM2835_DMA_CHAN_NAME_SIZE 8 | ||||
| +#define BCM2835_DMA_BULK_MASK  BIT(0)
 | ||||
|   | ||||
|  /** | ||||
|   * struct bcm2835_dmadev - BCM2835 DMA controller | ||||
| @@ -906,6 +908,9 @@ static int bcm2835_dma_probe(struct plat
 | ||||
|  	base = devm_ioremap_resource(&pdev->dev, res); | ||||
|  	if (IS_ERR(base)) | ||||
|  		return PTR_ERR(base); | ||||
| +	rc = bcm_dmaman_probe(pdev, base, BCM2835_DMA_BULK_MASK);
 | ||||
| +	if (rc)
 | ||||
| +		dev_err(&pdev->dev, "Failed to initialize the legacy API\n");
 | ||||
|   | ||||
|  	od->base = base; | ||||
|   | ||||
| @@ -951,6 +956,9 @@ static int bcm2835_dma_probe(struct plat
 | ||||
|  		goto err_no_dma; | ||||
|  	} | ||||
|   | ||||
| +	/* Channel 0 is used by the legacy API */
 | ||||
| +	chans_available &= ~BCM2835_DMA_BULK_MASK;
 | ||||
| +
 | ||||
|  	/* get irqs for each channel that we support */ | ||||
|  	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { | ||||
|  		/* skip masked out channels */ | ||||
| @@ -1025,6 +1033,7 @@ static int bcm2835_dma_remove(struct pla
 | ||||
|  { | ||||
|  	struct bcm2835_dmadev *od = platform_get_drvdata(pdev); | ||||
|   | ||||
| +	bcm_dmaman_remove(pdev);
 | ||||
|  	dma_async_device_unregister(&od->ddev); | ||||
|  	bcm2835_dma_free(od); | ||||
|   | ||||
| @@ -1040,7 +1049,22 @@ static struct platform_driver bcm2835_dm
 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| -module_platform_driver(bcm2835_dma_driver);
 | ||||
| +static int bcm2835_dma_init(void)
 | ||||
| +{
 | ||||
| +	return platform_driver_register(&bcm2835_dma_driver);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void bcm2835_dma_exit(void)
 | ||||
| +{
 | ||||
| +	platform_driver_unregister(&bcm2835_dma_driver);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Load after serial driver (arch_initcall) so we see the messages if it fails,
 | ||||
| + * but before drivers (module_init) that need a DMA channel.
 | ||||
| + */
 | ||||
| +subsys_initcall(bcm2835_dma_init);
 | ||||
| +module_exit(bcm2835_dma_exit);
 | ||||
|   | ||||
|  MODULE_ALIAS("platform:bcm2835-dma"); | ||||
|  MODULE_DESCRIPTION("BCM2835 DMA engine driver"); | ||||
|  | @ -0,0 +1,20 @@ | |||
| From dfdd1db51e019b0960ce337e10ef854ac317282d Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Wed, 15 Jun 2016 16:48:41 +0100 | ||||
| Subject: [PATCH] rtc: Add SPI alias for pcf2123 driver | ||||
| 
 | ||||
| Without this alias, Device Tree won't cause the driver | ||||
| to be loaded. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/linux/pull/1510 | ||||
| ---
 | ||||
|  drivers/rtc/rtc-pcf2123.c | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| 
 | ||||
| --- a/drivers/rtc/rtc-pcf2123.c
 | ||||
| +++ b/drivers/rtc/rtc-pcf2123.c
 | ||||
| @@ -474,3 +474,4 @@ module_spi_driver(pcf2123_driver);
 | ||||
|  MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); | ||||
|  MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); | ||||
|  MODULE_LICENSE("GPL"); | ||||
| +MODULE_ALIAS("spi:rtc-pcf2123");
 | ||||
|  | @ -0,0 +1,102 @@ | |||
| From 16e876906575f53df3ac9bf8d0cea3a456d1a150 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> | ||||
| Date: Fri, 7 Oct 2016 16:50:59 +0200 | ||||
| Subject: [PATCH] watchdog: bcm2835: Support setting reboot partition | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| The Raspberry Pi firmware looks at the RSTS register to know which | ||||
| partition to boot from. The reboot syscall command | ||||
| LINUX_REBOOT_CMD_RESTART2 supports passing in a string argument. | ||||
| 
 | ||||
| Add support for passing in a partition number 0..63 to boot from. | ||||
| Partition 63 is a special partiton indicating halt. | ||||
| If the partition doesn't exist, the firmware falls back to partition 0. | ||||
| 
 | ||||
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> | ||||
| ---
 | ||||
|  drivers/watchdog/bcm2835_wdt.c | 49 +++++++++++++++++++--------------- | ||||
|  1 file changed, 27 insertions(+), 22 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/watchdog/bcm2835_wdt.c
 | ||||
| +++ b/drivers/watchdog/bcm2835_wdt.c
 | ||||
| @@ -32,13 +32,7 @@
 | ||||
|  #define PM_RSTC_WRCFG_SET		0x00000030 | ||||
|  #define PM_RSTC_WRCFG_FULL_RESET	0x00000020 | ||||
|  #define PM_RSTC_RESET			0x00000102 | ||||
| -
 | ||||
| -/*
 | ||||
| - * The Raspberry Pi firmware uses the RSTS register to know which partition
 | ||||
| - * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10.
 | ||||
| - * Partition 63 is a special partition used by the firmware to indicate halt.
 | ||||
| - */
 | ||||
| -#define PM_RSTS_RASPBERRYPI_HALT	0x555
 | ||||
| +#define PM_RSTS_PARTITION_CLR          0xfffffaaa
 | ||||
|   | ||||
|  #define SECS_TO_WDOG_TICKS(x) ((x) << 16) | ||||
|  #define WDOG_TICKS_TO_SECS(x) ((x) >> 16) | ||||
| @@ -97,9 +91,24 @@ static unsigned int bcm2835_wdt_get_time
 | ||||
|  	return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET); | ||||
|  } | ||||
|   | ||||
| -static void __bcm2835_restart(struct bcm2835_wdt *wdt)
 | ||||
| +/*
 | ||||
| + * The Raspberry Pi firmware uses the RSTS register to know which partiton
 | ||||
| + * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10.
 | ||||
| + * Partiton 63 is a special partition used by the firmware to indicate halt.
 | ||||
| + */
 | ||||
| +
 | ||||
| +static void __bcm2835_restart(struct bcm2835_wdt *wdt, u8 partition)
 | ||||
|  { | ||||
| -	u32 val;
 | ||||
| +	u32 val, rsts;
 | ||||
| +
 | ||||
| +	rsts = (partition & BIT(0)) | ((partition & BIT(1)) << 1) |
 | ||||
| +	       ((partition & BIT(2)) << 2) | ((partition & BIT(3)) << 3) |
 | ||||
| +	       ((partition & BIT(4)) << 4) | ((partition & BIT(5)) << 5);
 | ||||
| +
 | ||||
| +	val = readl_relaxed(wdt->base + PM_RSTS);
 | ||||
| +	val &= PM_RSTS_PARTITION_CLR;
 | ||||
| +	val |= PM_PASSWORD | rsts;
 | ||||
| +	writel_relaxed(val, wdt->base + PM_RSTS);
 | ||||
|   | ||||
|  	/* use a timeout of 10 ticks (~150us) */ | ||||
|  	writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); | ||||
| @@ -117,7 +126,13 @@ static int bcm2835_restart(struct watchd
 | ||||
|  { | ||||
|  	struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); | ||||
|   | ||||
| -	__bcm2835_restart(wdt);
 | ||||
| +	unsigned long long val;
 | ||||
| +	u8 partition = 0;
 | ||||
| +
 | ||||
| +	if (data && !kstrtoull(data, 0, &val) && val <= 63)
 | ||||
| +		partition = val;
 | ||||
| +
 | ||||
| +	__bcm2835_restart(wdt, partition);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -152,19 +167,9 @@ static struct watchdog_device bcm2835_wd
 | ||||
|  static void bcm2835_power_off(void) | ||||
|  { | ||||
|  	struct bcm2835_wdt *wdt = bcm2835_power_off_wdt; | ||||
| -	u32 val;
 | ||||
| -
 | ||||
| -	/*
 | ||||
| -	 * We set the watchdog hard reset bit here to distinguish this reset
 | ||||
| -	 * from the normal (full) reset. bootcode.bin will not reboot after a
 | ||||
| -	 * hard reset.
 | ||||
| -	 */
 | ||||
| -	val = readl_relaxed(wdt->base + PM_RSTS);
 | ||||
| -	val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
 | ||||
| -	writel_relaxed(val, wdt->base + PM_RSTS);
 | ||||
|   | ||||
| -	/* Continue with normal reset mechanism */
 | ||||
| -	__bcm2835_restart(wdt);
 | ||||
| +	/* Partition 63 tells the firmware that this is a halt */
 | ||||
| +	__bcm2835_restart(wdt, 63);
 | ||||
|  } | ||||
|   | ||||
|  static int bcm2835_wdt_probe(struct platform_device *pdev) | ||||
|  | @ -0,0 +1,23 @@ | |||
| From 900d45ec28bcc58c12314555038b1a31f5add91e Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Tue, 5 Apr 2016 19:40:12 +0100 | ||||
| Subject: [PATCH] reboot: Use power off rather than busy spinning when halt is | ||||
|  requested | ||||
| 
 | ||||
| ---
 | ||||
|  arch/arm/kernel/reboot.c | 4 +--- | ||||
|  1 file changed, 1 insertion(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/arch/arm/kernel/reboot.c
 | ||||
| +++ b/arch/arm/kernel/reboot.c
 | ||||
| @@ -102,9 +102,7 @@ void machine_shutdown(void)
 | ||||
|   */ | ||||
|  void machine_halt(void) | ||||
|  { | ||||
| -	local_irq_disable();
 | ||||
| -	smp_send_stop();
 | ||||
| -	while (1);
 | ||||
| +	machine_power_off();
 | ||||
|  } | ||||
|   | ||||
|  /* | ||||
|  | @ -0,0 +1,19 @@ | |||
| From faa7cd1d39c61cdcab867711580752a803b02451 Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Wed, 9 Nov 2016 13:02:52 +0000 | ||||
| Subject: [PATCH] bcm: Make RASPBERRYPI_POWER depend on PM | ||||
| 
 | ||||
| ---
 | ||||
|  drivers/soc/bcm/Kconfig | 1 + | ||||
|  1 file changed, 1 insertion(+) | ||||
| 
 | ||||
| --- a/drivers/soc/bcm/Kconfig
 | ||||
| +++ b/drivers/soc/bcm/Kconfig
 | ||||
| @@ -17,6 +17,7 @@ config RASPBERRYPI_POWER
 | ||||
|  	bool "Raspberry Pi power domain driver" | ||||
|  	depends on ARCH_BCM2835 || (COMPILE_TEST && OF) | ||||
|  	depends on RASPBERRYPI_FIRMWARE=y | ||||
| +	depends on PM
 | ||||
|  	select PM_GENERIC_DOMAINS if PM | ||||
|  	help | ||||
|  	  This enables support for the RPi power domains which can be enabled | ||||
|  | @ -0,0 +1,25 @@ | |||
| From 13b88f30d62a5248afb02b762539ae0b5d2d91f8 Mon Sep 17 00:00:00 2001 | ||||
| From: popcornmix <popcornmix@gmail.com> | ||||
| Date: Tue, 6 Dec 2016 17:05:39 +0000 | ||||
| Subject: [PATCH] bcm2835-rng: Avoid initialising if already enabled | ||||
| 
 | ||||
| Avoids the 0x40000 cycles of warmup again if firmware has already used it | ||||
| ---
 | ||||
|  drivers/char/hw_random/bcm2835-rng.c | 6 ++++-- | ||||
|  1 file changed, 4 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/char/hw_random/bcm2835-rng.c
 | ||||
| +++ b/drivers/char/hw_random/bcm2835-rng.c
 | ||||
| @@ -106,8 +106,10 @@ static int bcm2835_rng_init(struct hwrng
 | ||||
|  	} | ||||
|   | ||||
|  	/* set warm-up count & enable */ | ||||
| -	rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
 | ||||
| -	rng_writel(priv, RNG_RBGEN, RNG_CTRL);
 | ||||
| +	if (!(rng_readl(priv, RNG_CTRL) & RNG_RBGEN)) {
 | ||||
| +		rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
 | ||||
| +		rng_writel(priv, RNG_RBGEN, RNG_CTRL);
 | ||||
| +	}
 | ||||
|   | ||||
|  	return ret; | ||||
|  } | ||||
|  | @ -0,0 +1,35 @@ | |||
| From 29aa62aa5af2f290d9dd2a20f4bdd5d5bb612a65 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Thu, 9 Feb 2017 14:36:44 +0000 | ||||
| Subject: [PATCH] sound: Demote deferral errors to INFO level | ||||
| 
 | ||||
| At present there is no mechanism to specify driver load order, | ||||
| which can lead to deferrals and repeated retries until successful. | ||||
| Since this situation is expected, reduce the dmesg level to | ||||
| INFO and mention that the operation will be retried. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  sound/soc/soc-core.c | 4 ++-- | ||||
|  1 file changed, 2 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/sound/soc/soc-core.c
 | ||||
| +++ b/sound/soc/soc-core.c
 | ||||
| @@ -995,7 +995,7 @@ int snd_soc_add_pcm_runtime(struct snd_s
 | ||||
|  	for_each_link_cpus(dai_link, i, cpu) { | ||||
|  		asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu); | ||||
|  		if (!asoc_rtd_to_cpu(rtd, i)) { | ||||
| -			dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
 | ||||
| +			dev_info(card->dev, "ASoC: CPU DAI %s not registered - will retry\n",
 | ||||
|  				 cpu->dai_name); | ||||
|  			goto _err_defer; | ||||
|  		} | ||||
| @@ -1006,7 +1006,7 @@ int snd_soc_add_pcm_runtime(struct snd_s
 | ||||
|  	for_each_link_codecs(dai_link, i, codec) { | ||||
|  		asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec); | ||||
|  		if (!asoc_rtd_to_codec(rtd, i)) { | ||||
| -			dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
 | ||||
| +			dev_info(card->dev, "ASoC: CODEC DAI %s not registered- will retry\n",
 | ||||
|  				 codec->dai_name); | ||||
|  			goto _err_defer; | ||||
|  		} | ||||
|  | @ -0,0 +1,137 @@ | |||
| From e9519c347701ddb3bf4915d4077ab61cb373c8c3 Mon Sep 17 00:00:00 2001 | ||||
| From: Claggy3 <stephen.maclagan@hotmail.com> | ||||
| Date: Sat, 11 Feb 2017 14:00:30 +0000 | ||||
| Subject: [PATCH] Update vfpmodule.c | ||||
| 
 | ||||
| Christopher Alexander Tobias Schulze - May 2, 2015, 11:57 a.m. | ||||
| This patch fixes a problem with VFP state save and restore related | ||||
| to exception handling (panic with message "BUG: unsupported FP | ||||
| instruction in kernel mode") present on VFP11 floating point units | ||||
| (as used with ARM1176JZF-S CPUs, e.g. on first generation Raspberry | ||||
| Pi boards). This patch was developed and discussed on | ||||
| 
 | ||||
|    https://github.com/raspberrypi/linux/issues/859 | ||||
| 
 | ||||
| A precondition to see the crashes is that floating point exception | ||||
| traps are enabled. In this case, the VFP11 might determine that a FPU | ||||
| operation needs to trap at a point in time when it is not possible to | ||||
| signal this to the ARM11 core any more. The VFP11 will then set the | ||||
| FPEXC.EX bit and store the trapped opcode in FPINST. (In some cases, | ||||
| a second opcode might have been accepted by the VFP11 before the | ||||
| exception was detected and could be reported to the ARM11 - in this | ||||
| case, the VFP11 also sets FPEXC.FP2V and stores the second opcode in | ||||
| FPINST2.) | ||||
| 
 | ||||
| If FPEXC.EX is set, the VFP11 will "bounce" the next FPU opcode issued | ||||
| by the ARM11 CPU, which will be seen by the ARM11 as an undefined opcode | ||||
| trap. The VFP support code examines the FPEXC.EX and FPEXC.FP2V bits | ||||
| to decide what actions to take, i.e., whether to emulate the opcodes | ||||
| found in FPINST and FPINST2, and whether to retry the bounced instruction. | ||||
| 
 | ||||
| If a user space application has left the VFP11 in this "pending trap" | ||||
| state, the next FPU opcode issued to the VFP11 might actually be the | ||||
| VSTMIA operation vfp_save_state() uses to store the FPU registers | ||||
| to memory (in our test cases, when building the signal stack frame). | ||||
| In this case, the kernel crashes as described above. | ||||
| 
 | ||||
| This patch fixes the problem by making sure that vfp_save_state() is | ||||
| always entered with FPEXC.EX cleared. (The current value of FPEXC has | ||||
| already been saved, so this does not corrupt the context. Clearing | ||||
| FPEXC.EX has no effects on FPINST or FPINST2. Also note that many | ||||
| callers already modify FPEXC by setting FPEXC.EN before invoking | ||||
| vfp_save_state().) | ||||
| 
 | ||||
| This patch also addresses a second problem related to FPEXC.EX: After | ||||
| returning from signal handling, the kernel reloads the VFP context | ||||
| from the user mode stack. However, the current code explicitly clears | ||||
| both FPEXC.EX and FPEXC.FP2V during reload. As VFP11 requires these | ||||
| bits to be preserved, this patch disables clearing them for VFP | ||||
| implementations belonging to architecture 1. There should be no | ||||
| negative side effects: the user can set both bits by executing FPU | ||||
| opcodes anyway, and while user code may now place arbitrary values | ||||
| into FPINST and FPINST2 (e.g., non-VFP ARM opcodes) the VFP support | ||||
| code knows which instructions can be emulated, and rejects other | ||||
| opcodes with "unhandled bounce" messages, so there should be no | ||||
| security impact from allowing reloading FPEXC.EX and FPEXC.FP2V. | ||||
| 
 | ||||
| Signed-off-by: Christopher Alexander Tobias Schulze <cat.schulze@alice-dsl.net> | ||||
| ---
 | ||||
|  arch/arm/vfp/vfpmodule.c | 25 +++++++++++++++++++------ | ||||
|  1 file changed, 19 insertions(+), 6 deletions(-) | ||||
| 
 | ||||
| --- a/arch/arm/vfp/vfpmodule.c
 | ||||
| +++ b/arch/arm/vfp/vfpmodule.c
 | ||||
| @@ -176,8 +176,11 @@ static int vfp_notifier(struct notifier_
 | ||||
|  		 * case the thread migrates to a different CPU. The | ||||
|  		 * restoring is done lazily. | ||||
|  		 */ | ||||
| -		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
 | ||||
| +		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
 | ||||
| +			/* vfp_save_state oopses on VFP11 if EX bit set */
 | ||||
| +			fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | ||||
|  			vfp_save_state(vfp_current_hw_state[cpu], fpexc); | ||||
| +		}
 | ||||
|  #endif | ||||
|   | ||||
|  		/* | ||||
| @@ -454,13 +457,16 @@ static int vfp_pm_suspend(void)
 | ||||
|  	/* if vfp is on, then save state for resumption */ | ||||
|  	if (fpexc & FPEXC_EN) { | ||||
|  		pr_debug("%s: saving vfp state\n", __func__); | ||||
| +		/* vfp_save_state oopses on VFP11 if EX bit set */
 | ||||
| +		fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | ||||
|  		vfp_save_state(&ti->vfpstate, fpexc); | ||||
|   | ||||
|  		/* disable, just in case */ | ||||
|  		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||||
|  	} else if (vfp_current_hw_state[ti->cpu]) { | ||||
|  #ifndef CONFIG_SMP | ||||
| -		fmxr(FPEXC, fpexc | FPEXC_EN);
 | ||||
| +		/* vfp_save_state oopses on VFP11 if EX bit set */
 | ||||
| +		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 | ||||
|  		vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); | ||||
|  		fmxr(FPEXC, fpexc); | ||||
|  #endif | ||||
| @@ -523,7 +529,8 @@ void vfp_sync_hwstate(struct thread_info
 | ||||
|  		/* | ||||
|  		 * Save the last VFP state on this CPU. | ||||
|  		 */ | ||||
| -		fmxr(FPEXC, fpexc | FPEXC_EN);
 | ||||
| +		/* vfp_save_state oopses on VFP11 if EX bit set */
 | ||||
| +		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 | ||||
|  		vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN); | ||||
|  		fmxr(FPEXC, fpexc); | ||||
|  	} | ||||
| @@ -589,6 +596,7 @@ int vfp_restore_user_hwstate(struct user
 | ||||
|  	struct thread_info *thread = current_thread_info(); | ||||
|  	struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; | ||||
|  	unsigned long fpexc; | ||||
| +	u32 fpsid = fmrx(FPSID);
 | ||||
|   | ||||
|  	/* Disable VFP to avoid corrupting the new thread state. */ | ||||
|  	vfp_flush_hwstate(thread); | ||||
| @@ -611,8 +619,12 @@ int vfp_restore_user_hwstate(struct user
 | ||||
|  	/* Ensure the VFP is enabled. */ | ||||
|  	fpexc |= FPEXC_EN; | ||||
|   | ||||
| -	/* Ensure FPINST2 is invalid and the exception flag is cleared. */
 | ||||
| -	fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
 | ||||
| +	/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
 | ||||
| +	if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
 | ||||
| +		/* Ensure FPINST2 is invalid and the exception flag is cleared. */
 | ||||
| +		fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	hwstate->fpexc = fpexc; | ||||
|   | ||||
|  	hwstate->fpinst = ufp_exc->fpinst; | ||||
| @@ -726,7 +738,8 @@ void kernel_neon_begin(void)
 | ||||
|  	cpu = get_cpu(); | ||||
|   | ||||
|  	fpexc = fmrx(FPEXC) | FPEXC_EN; | ||||
| -	fmxr(FPEXC, fpexc);
 | ||||
| +	/* vfp_save_state oopses on VFP11 if EX bit set */
 | ||||
| +	fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | ||||
|   | ||||
|  	/* | ||||
|  	 * Save the userland NEON/VFP state. Under UP, | ||||
|  | @ -0,0 +1,189 @@ | |||
| From dc5fcaa7e000dfff11f2ac4e37a84a44d15d69f6 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> | ||||
| Date: Tue, 1 Nov 2016 15:15:41 +0100 | ||||
| Subject: [PATCH] i2c: bcm2835: Add debug support | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| This adds a debug module parameter to aid in debugging transfer issues | ||||
| by printing info to the kernel log. When enabled, status values are | ||||
| collected in the interrupt routine and msg info in | ||||
| bcm2835_i2c_start_transfer(). This is done in a way that tries to avoid | ||||
| affecting timing. Having printk in the isr can mask issues. | ||||
| 
 | ||||
| debug values (additive): | ||||
| 1: Print info on error | ||||
| 2: Print info on all transfers | ||||
| 3: Print messages before transfer is started | ||||
| 
 | ||||
| The value can be changed at runtime: | ||||
| /sys/module/i2c_bcm2835/parameters/debug | ||||
| 
 | ||||
| Example output, debug=3: | ||||
| [  747.114448] bcm2835_i2c_xfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1] | ||||
| [  747.114463] bcm2835_i2c_xfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1] | ||||
| [  747.117809] start_transfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1] | ||||
| [  747.117825] isr: remain=2, status=0x30000055 : TA TXW TXD TXE  [i2c1] | ||||
| [  747.117839] start_transfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1] | ||||
| [  747.117849] isr: remain=32, status=0xd0000039 : TA RXR TXD RXD  [i2c1] | ||||
| [  747.117861] isr: remain=20, status=0xd0000039 : TA RXR TXD RXD  [i2c1] | ||||
| [  747.117870] isr: remain=8, status=0x32 : DONE TXD RXD  [i2c1] | ||||
| 
 | ||||
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> | ||||
| ---
 | ||||
|  drivers/i2c/busses/i2c-bcm2835.c | 99 +++++++++++++++++++++++++++++++- | ||||
|  1 file changed, 98 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/i2c/busses/i2c-bcm2835.c
 | ||||
| +++ b/drivers/i2c/busses/i2c-bcm2835.c
 | ||||
| @@ -56,6 +56,18 @@
 | ||||
|  #define BCM2835_I2C_CDIV_MIN	0x0002 | ||||
|  #define BCM2835_I2C_CDIV_MAX	0xFFFE | ||||
|   | ||||
| +static unsigned int debug;
 | ||||
| +module_param(debug, uint, 0644);
 | ||||
| +MODULE_PARM_DESC(debug, "1=err, 2=isr, 3=xfer");
 | ||||
| +
 | ||||
| +#define BCM2835_DEBUG_MAX	512
 | ||||
| +struct bcm2835_debug {
 | ||||
| +	struct i2c_msg *msg;
 | ||||
| +	int msg_idx;
 | ||||
| +	size_t remain;
 | ||||
| +	u32 status;
 | ||||
| +};
 | ||||
| +
 | ||||
|  struct bcm2835_i2c_dev { | ||||
|  	struct device *dev; | ||||
|  	void __iomem *regs; | ||||
| @@ -68,8 +80,78 @@ struct bcm2835_i2c_dev {
 | ||||
|  	u32 msg_err; | ||||
|  	u8 *msg_buf; | ||||
|  	size_t msg_buf_remaining; | ||||
| +	struct bcm2835_debug debug[BCM2835_DEBUG_MAX];
 | ||||
| +	unsigned int debug_num;
 | ||||
| +	unsigned int debug_num_msgs;
 | ||||
|  }; | ||||
|   | ||||
| +static inline void bcm2835_debug_add(struct bcm2835_i2c_dev *i2c_dev, u32 s)
 | ||||
| +{
 | ||||
| +	if (!i2c_dev->debug_num_msgs || i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	i2c_dev->debug[i2c_dev->debug_num].msg = i2c_dev->curr_msg;
 | ||||
| +	i2c_dev->debug[i2c_dev->debug_num].msg_idx =
 | ||||
| +				i2c_dev->debug_num_msgs - i2c_dev->num_msgs;
 | ||||
| +	i2c_dev->debug[i2c_dev->debug_num].remain = i2c_dev->msg_buf_remaining;
 | ||||
| +	i2c_dev->debug[i2c_dev->debug_num].status = s;
 | ||||
| +	i2c_dev->debug_num++;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void bcm2835_debug_print_status(struct bcm2835_i2c_dev *i2c_dev,
 | ||||
| +				       struct bcm2835_debug *d)
 | ||||
| +{
 | ||||
| +	u32 s = d->status;
 | ||||
| +
 | ||||
| +	pr_info("isr: remain=%zu, status=0x%x : %s%s%s%s%s%s%s%s%s%s [i2c%d]\n",
 | ||||
| +		d->remain, s,
 | ||||
| +		s & BCM2835_I2C_S_TA ? "TA " : "",
 | ||||
| +		s & BCM2835_I2C_S_DONE ? "DONE " : "",
 | ||||
| +		s & BCM2835_I2C_S_TXW ? "TXW " : "",
 | ||||
| +		s & BCM2835_I2C_S_RXR ? "RXR " : "",
 | ||||
| +		s & BCM2835_I2C_S_TXD ? "TXD " : "",
 | ||||
| +		s & BCM2835_I2C_S_RXD ? "RXD " : "",
 | ||||
| +		s & BCM2835_I2C_S_TXE ? "TXE " : "",
 | ||||
| +		s & BCM2835_I2C_S_RXF ? "RXF " : "",
 | ||||
| +		s & BCM2835_I2C_S_ERR ? "ERR " : "",
 | ||||
| +		s & BCM2835_I2C_S_CLKT ? "CLKT " : "",
 | ||||
| +		i2c_dev->adapter.nr);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void bcm2835_debug_print_msg(struct bcm2835_i2c_dev *i2c_dev,
 | ||||
| +				    struct i2c_msg *msg, int i, int total,
 | ||||
| +				    const char *fname)
 | ||||
| +{
 | ||||
| +	pr_info("%s: msg(%d/%d) %s addr=0x%02x, len=%u flags=%s%s%s%s%s%s%s [i2c%d]\n",
 | ||||
| +		fname, i, total,
 | ||||
| +		msg->flags & I2C_M_RD ? "read" : "write", msg->addr, msg->len,
 | ||||
| +		msg->flags & I2C_M_TEN ? "TEN" : "",
 | ||||
| +		msg->flags & I2C_M_RECV_LEN ? "RECV_LEN" : "",
 | ||||
| +		msg->flags & I2C_M_NO_RD_ACK ? "NO_RD_ACK" : "",
 | ||||
| +		msg->flags & I2C_M_IGNORE_NAK ? "IGNORE_NAK" : "",
 | ||||
| +		msg->flags & I2C_M_REV_DIR_ADDR ? "REV_DIR_ADDR" : "",
 | ||||
| +		msg->flags & I2C_M_NOSTART ? "NOSTART" : "",
 | ||||
| +		msg->flags & I2C_M_STOP ? "STOP" : "",
 | ||||
| +		i2c_dev->adapter.nr);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void bcm2835_debug_print(struct bcm2835_i2c_dev *i2c_dev)
 | ||||
| +{
 | ||||
| +	struct bcm2835_debug *d;
 | ||||
| +	unsigned int i;
 | ||||
| +
 | ||||
| +	for (i = 0; i < i2c_dev->debug_num; i++) {
 | ||||
| +		d = &i2c_dev->debug[i];
 | ||||
| +		if (d->status == ~0)
 | ||||
| +			bcm2835_debug_print_msg(i2c_dev, d->msg, d->msg_idx,
 | ||||
| +				i2c_dev->debug_num_msgs, "start_transfer");
 | ||||
| +		else
 | ||||
| +			bcm2835_debug_print_status(i2c_dev, d);
 | ||||
| +	}
 | ||||
| +	if (i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
 | ||||
| +		pr_info("BCM2835_DEBUG_MAX reached\n");
 | ||||
| +}
 | ||||
| +
 | ||||
|  static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, | ||||
|  				      u32 reg, u32 val) | ||||
|  { | ||||
| @@ -257,6 +339,7 @@ static void bcm2835_i2c_start_transfer(s
 | ||||
|  	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); | ||||
|  	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); | ||||
|  	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); | ||||
| +	bcm2835_debug_add(i2c_dev, ~0);
 | ||||
|  } | ||||
|   | ||||
|  static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev) | ||||
| @@ -283,6 +366,7 @@ static irqreturn_t bcm2835_i2c_isr(int t
 | ||||
|  	u32 val, err; | ||||
|   | ||||
|  	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); | ||||
| +	bcm2835_debug_add(i2c_dev, val);
 | ||||
|   | ||||
|  	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); | ||||
|  	if (err) { | ||||
| @@ -349,6 +433,13 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | ||||
|  	unsigned long time_left; | ||||
|  	int i; | ||||
|   | ||||
| +	if (debug)
 | ||||
| +		i2c_dev->debug_num_msgs = num;
 | ||||
| +
 | ||||
| +	if (debug > 2)
 | ||||
| +		for (i = 0; i < num; i++)
 | ||||
| +			bcm2835_debug_print_msg(i2c_dev, &msgs[i], i + 1, num, __func__);
 | ||||
| +
 | ||||
|  	for (i = 0; i < (num - 1); i++) | ||||
|  		if (msgs[i].flags & I2C_M_RD) { | ||||
|  			dev_warn_once(i2c_dev->dev, | ||||
| @@ -367,6 +458,10 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | ||||
|   | ||||
|  	bcm2835_i2c_finish_transfer(i2c_dev); | ||||
|   | ||||
| +	if (debug > 1 || (debug && (!time_left || i2c_dev->msg_err)))
 | ||||
| +		bcm2835_debug_print(i2c_dev);
 | ||||
| +	i2c_dev->debug_num_msgs = 0;
 | ||||
| +	i2c_dev->debug_num = 0;
 | ||||
|  	if (!time_left) { | ||||
|  		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, | ||||
|  				   BCM2835_I2C_C_CLEAR); | ||||
| @@ -377,7 +472,9 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | ||||
|  	if (!i2c_dev->msg_err) | ||||
|  		return num; | ||||
|   | ||||
| -	dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
 | ||||
| +	if (debug)
 | ||||
| +		dev_err(i2c_dev->dev, "i2c transfer failed: %x\n",
 | ||||
| +			i2c_dev->msg_err);
 | ||||
|   | ||||
|  	if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) | ||||
|  		return -EREMOTEIO; | ||||
|  | @ -0,0 +1,112 @@ | |||
| From 9e2bbe439ce51f4af6952dd80febb5bb32ef3491 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Tue, 23 Jan 2018 16:52:45 +0000 | ||||
| Subject: [PATCH] irqchip: irq-bcm2836: Remove regmap and syscon use | ||||
| 
 | ||||
| The syscon node defines a register range that duplicates that used by | ||||
| the local_intc node on bcm2836/7. Since irq-bcm2835 and irq-bcm2836 are | ||||
| built in and always present together (both drivers are enabled by | ||||
| CONFIG_ARCH_BCM2835), it is possible to replace the syscon usage with a | ||||
| global variable that simplifies the code. Doing so does lose the | ||||
| locking provided by regmap, but as only one side is using the regmap | ||||
| interface (irq-bcm2835 uses readl and write) there is no loss of | ||||
| atomicity. | ||||
| 
 | ||||
| See: https://github.com/raspberrypi/firmware/issues/926 | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/irqchip/irq-bcm2835.c | 32 ++++++++++++-------------------- | ||||
|  drivers/irqchip/irq-bcm2836.c |  5 +++++ | ||||
|  2 files changed, 17 insertions(+), 20 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/irqchip/irq-bcm2835.c
 | ||||
| +++ b/drivers/irqchip/irq-bcm2835.c
 | ||||
| @@ -41,8 +41,6 @@
 | ||||
|  #include <linux/of_irq.h> | ||||
|  #include <linux/irqchip.h> | ||||
|  #include <linux/irqdomain.h> | ||||
| -#include <linux/mfd/syscon.h>
 | ||||
| -#include <linux/regmap.h>
 | ||||
|   | ||||
|  #include <asm/exception.h> | ||||
|  #include <asm/mach/irq.h> | ||||
| @@ -92,7 +90,7 @@ struct armctrl_ic {
 | ||||
|  	void __iomem *enable[NR_BANKS]; | ||||
|  	void __iomem *disable[NR_BANKS]; | ||||
|  	struct irq_domain *domain; | ||||
| -	struct regmap *local_regmap;
 | ||||
| +	void __iomem *local_base;
 | ||||
|  }; | ||||
|   | ||||
|  static struct armctrl_ic intc __read_mostly; | ||||
| @@ -129,24 +127,20 @@ static void armctrl_unmask_irq(struct ir
 | ||||
|  	if (d->hwirq >= NUMBER_IRQS) { | ||||
|  		if (num_online_cpus() > 1) { | ||||
|  			unsigned int data; | ||||
| -			int ret;
 | ||||
|   | ||||
| -			if (!intc.local_regmap) {
 | ||||
| -				pr_err("FIQ is disabled due to missing regmap\n");
 | ||||
| +			if (!intc.local_base) {
 | ||||
| +				pr_err("FIQ is disabled due to missing arm_local_intc\n");
 | ||||
|  				return; | ||||
|  			} | ||||
|   | ||||
| -			ret = regmap_read(intc.local_regmap,
 | ||||
| -					  ARM_LOCAL_GPU_INT_ROUTING, &data);
 | ||||
| -			if (ret) {
 | ||||
| -				pr_err("Failed to read int routing %d\n", ret);
 | ||||
| -				return;
 | ||||
| -			}
 | ||||
| +			data = readl_relaxed(intc.local_base +
 | ||||
| +					     ARM_LOCAL_GPU_INT_ROUTING);
 | ||||
|   | ||||
|  			data &= ~0xc; | ||||
|  			data |= (1 << 2); | ||||
| -			regmap_write(intc.local_regmap,
 | ||||
| -				     ARM_LOCAL_GPU_INT_ROUTING, data);
 | ||||
| +			writel_relaxed(data,
 | ||||
| +				       intc.local_base +
 | ||||
| +				       ARM_LOCAL_GPU_INT_ROUTING);
 | ||||
|  		} | ||||
|   | ||||
|  		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq), | ||||
| @@ -246,12 +240,10 @@ static int __init armctrl_of_init(struct
 | ||||
|  	} | ||||
|   | ||||
|  	if (is_2836) { | ||||
| -		intc.local_regmap =
 | ||||
| -			syscon_regmap_lookup_by_compatible("brcm,bcm2836-arm-local");
 | ||||
| -		if (IS_ERR(intc.local_regmap)) {
 | ||||
| -			pr_err("Failed to get local register map. FIQ is disabled for cpus > 1\n");
 | ||||
| -			intc.local_regmap = NULL;
 | ||||
| -		}
 | ||||
| +		extern void __iomem * __attribute__((weak)) arm_local_intc;
 | ||||
| +		intc.local_base = arm_local_intc;
 | ||||
| +		if (!intc.local_base)
 | ||||
| +			pr_err("Failed to get local intc base. FIQ is disabled for cpus > 1\n");
 | ||||
|  	} | ||||
|   | ||||
|  	/* Make a duplicate irq range which is used to enable FIQ */ | ||||
| --- a/drivers/irqchip/irq-bcm2836.c
 | ||||
| +++ b/drivers/irqchip/irq-bcm2836.c
 | ||||
| @@ -22,6 +22,9 @@ struct bcm2836_arm_irqchip_intc {
 | ||||
|   | ||||
|  static struct bcm2836_arm_irqchip_intc intc  __read_mostly; | ||||
|   | ||||
| +void __iomem *arm_local_intc;
 | ||||
| +EXPORT_SYMBOL_GPL(arm_local_intc);
 | ||||
| +
 | ||||
|  static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset, | ||||
|  						 unsigned int bit, | ||||
|  						 int cpu) | ||||
| @@ -323,6 +326,8 @@ static int __init bcm2836_arm_irqchip_l1
 | ||||
|  		panic("%pOF: unable to map local interrupt registers\n", node); | ||||
|  	} | ||||
|   | ||||
| +	arm_local_intc = intc.base;
 | ||||
| +
 | ||||
|  	bcm2835_init_local_timer_frequency(); | ||||
|   | ||||
|  	intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, | ||||
|  | @ -0,0 +1,49 @@ | |||
| From 3d918f796cd3832b4473c3d66e5c1285978ae7e1 Mon Sep 17 00:00:00 2001 | ||||
| From: Phil Elwell <phil@raspberrypi.org> | ||||
| Date: Tue, 17 Oct 2017 15:04:29 +0100 | ||||
| Subject: [PATCH] lan78xx: Enable LEDs and auto-negotiation | ||||
| 
 | ||||
| For applications of the LAN78xx that don't have valid programmed | ||||
| EEPROMs or OTPs, enabling both LEDs and auto-negotiation by default | ||||
| seems reasonable. | ||||
| 
 | ||||
| Signed-off-by: Phil Elwell <phil@raspberrypi.org> | ||||
| ---
 | ||||
|  drivers/net/usb/lan78xx.c | 12 ++++++++++++ | ||||
|  1 file changed, 12 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/net/usb/lan78xx.c
 | ||||
| +++ b/drivers/net/usb/lan78xx.c
 | ||||
| @@ -2881,6 +2881,11 @@ static int lan78xx_reset(struct lan78xx_
 | ||||
|  	int ret; | ||||
|  	u32 buf; | ||||
|  	u8 sig; | ||||
| +	bool has_eeprom;
 | ||||
| +	bool has_otp;
 | ||||
| +
 | ||||
| +	has_eeprom = !lan78xx_read_eeprom(dev, 0, 0, NULL);
 | ||||
| +	has_otp = !lan78xx_read_otp(dev, 0, 0, NULL);
 | ||||
|   | ||||
|  	ret = lan78xx_read_reg(dev, HW_CFG, &buf); | ||||
|  	if (ret < 0) | ||||
| @@ -2945,6 +2950,10 @@ static int lan78xx_reset(struct lan78xx_
 | ||||
|   | ||||
|  	buf |= HW_CFG_MEF_; | ||||
|   | ||||
| +	/* If no valid EEPROM and no valid OTP, enable the LEDs by default */
 | ||||
| +	if (!has_eeprom && !has_otp)
 | ||||
| +	    buf |= HW_CFG_LED0_EN_ | HW_CFG_LED1_EN_;
 | ||||
| +
 | ||||
|  	ret = lan78xx_write_reg(dev, HW_CFG, buf); | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
| @@ -3043,6 +3052,9 @@ static int lan78xx_reset(struct lan78xx_
 | ||||
|  			buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; | ||||
|  		} | ||||
|  	} | ||||
| +	/* If no valid EEPROM and no valid OTP, enable AUTO negotiation */
 | ||||
| +	if (!has_eeprom && !has_otp)
 | ||||
| +	    buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
 | ||||
|  	ret = lan78xx_write_reg(dev, MAC_CR, buf); | ||||
|  	if (ret < 0) | ||||
|  		return ret; | ||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue