mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Also removes random module and switches to new bcm2711 thermal driver. Boot tested on RPi 4B v1.1 4G. Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			283 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From b7a52df8162e1eb55a8403e9e9af79c581206335 Mon Sep 17 00:00:00 2001
 | 
						|
From: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
						|
Date: Wed, 3 Apr 2019 17:15:45 +0100
 | 
						|
Subject: [PATCH] drm: vc4: Add support for multiple displays to fkms
 | 
						|
 | 
						|
There is a slightly nasty hack in that all crtcs share the
 | 
						|
same SMI interrupt from the firmware. This seems to currently
 | 
						|
work well enough, but ought to be fixed at a later date.
 | 
						|
 | 
						|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
						|
---
 | 
						|
 drivers/gpu/drm/vc4/vc4_firmware_kms.c | 162 +++++++++++++++++--------
 | 
						|
 1 file changed, 113 insertions(+), 49 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
 | 
						|
+++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
 | 
						|
@@ -31,6 +31,8 @@
 | 
						|
 #include "vc_image_types.h"
 | 
						|
 #include <soc/bcm2835/raspberrypi-firmware.h>
 | 
						|
 
 | 
						|
+#define PLANES_PER_CRTC		3
 | 
						|
+
 | 
						|
 struct set_plane {
 | 
						|
 	u8 display;
 | 
						|
 	u8 plane_id;
 | 
						|
@@ -177,6 +179,7 @@ struct vc4_crtc {
 | 
						|
 	struct drm_pending_vblank_event *event;
 | 
						|
 	u32 overscan[4];
 | 
						|
 	bool vblank_enabled;
 | 
						|
+	u32 display_number;
 | 
						|
 };
 | 
						|
 
 | 
						|
 static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
 | 
						|
@@ -481,6 +484,7 @@ static const struct drm_plane_helper_fun
 | 
						|
 
 | 
						|
 static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
 | 
						|
 					     enum drm_plane_type type,
 | 
						|
+					     u8 display_num,
 | 
						|
 					     u8 plane_id)
 | 
						|
 {
 | 
						|
 	struct drm_plane *plane = NULL;
 | 
						|
@@ -544,7 +548,7 @@ static struct drm_plane *vc4_fkms_plane_
 | 
						|
 	vc4_plane->mb.tag.tag = RPI_FIRMWARE_SET_PLANE;
 | 
						|
 	vc4_plane->mb.tag.buf_size = sizeof(struct set_plane);
 | 
						|
 	vc4_plane->mb.tag.req_resp_size = 0;
 | 
						|
-	vc4_plane->mb.plane.display = 0;
 | 
						|
+	vc4_plane->mb.plane.display = display_num;
 | 
						|
 	vc4_plane->mb.plane.plane_id = plane_id;
 | 
						|
 	vc4_plane->mb.plane.layer = default_zpos ? default_zpos : -127;
 | 
						|
 
 | 
						|
@@ -631,16 +635,20 @@ static void vc4_crtc_handle_page_flip(st
 | 
						|
 
 | 
						|
 static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
 | 
						|
 {
 | 
						|
-	struct vc4_crtc *vc4_crtc = data;
 | 
						|
-	u32 stat = readl(vc4_crtc->regs + SMICS);
 | 
						|
+	struct vc4_crtc **crtc_list = data;
 | 
						|
+	int i;
 | 
						|
+	u32 stat = readl(crtc_list[0]->regs + SMICS);
 | 
						|
 	irqreturn_t ret = IRQ_NONE;
 | 
						|
 
 | 
						|
 	if (stat & SMICS_INTERRUPTS) {
 | 
						|
-		writel(0, vc4_crtc->regs + SMICS);
 | 
						|
-		if (vc4_crtc->vblank_enabled)
 | 
						|
-			drm_crtc_handle_vblank(&vc4_crtc->base);
 | 
						|
-		vc4_crtc_handle_page_flip(vc4_crtc);
 | 
						|
-		ret = IRQ_HANDLED;
 | 
						|
+		writel(0, crtc_list[0]->regs + SMICS);
 | 
						|
+
 | 
						|
+		for (i = 0; crtc_list[i]; i++) {
 | 
						|
+			if (crtc_list[i]->vblank_enabled)
 | 
						|
+				drm_crtc_handle_vblank(&crtc_list[i]->base);
 | 
						|
+			vc4_crtc_handle_page_flip(crtc_list[i]);
 | 
						|
+			ret = IRQ_HANDLED;
 | 
						|
+		}
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	return ret;
 | 
						|
@@ -837,66 +845,55 @@ static const struct drm_encoder_helper_f
 | 
						|
 	.disable = vc4_fkms_encoder_disable,
 | 
						|
 };
 | 
						|
 
 | 
						|
-static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
 | 
						|
+static int vc4_fkms_create_screen(struct device *dev, struct drm_device *drm,
 | 
						|
+				  int display_idx, int display_ref,
 | 
						|
+				  struct vc4_crtc **ret_crtc)
 | 
						|
 {
 | 
						|
-	struct platform_device *pdev = to_platform_device(dev);
 | 
						|
-	struct drm_device *drm = dev_get_drvdata(master);
 | 
						|
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 | 
						|
 	struct vc4_crtc *vc4_crtc;
 | 
						|
 	struct vc4_fkms_encoder *vc4_encoder;
 | 
						|
 	struct drm_crtc *crtc;
 | 
						|
 	struct drm_plane *primary_plane, *overlay_plane, *cursor_plane;
 | 
						|
 	struct drm_plane *destroy_plane, *temp;
 | 
						|
-	struct device_node *firmware_node;
 | 
						|
 	u32 blank = 1;
 | 
						|
 	int ret;
 | 
						|
 
 | 
						|
-	vc4->firmware_kms = true;
 | 
						|
-
 | 
						|
-	/* firmware kms doesn't have precise a scanoutpos implementation, so
 | 
						|
-	 * we can't do the precise vblank timestamp mode.
 | 
						|
-	 */
 | 
						|
-	drm->driver->get_scanout_position = NULL;
 | 
						|
-	drm->driver->get_vblank_timestamp = NULL;
 | 
						|
-
 | 
						|
 	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
 | 
						|
 	if (!vc4_crtc)
 | 
						|
 		return -ENOMEM;
 | 
						|
 	crtc = &vc4_crtc->base;
 | 
						|
 
 | 
						|
-	firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
 | 
						|
-	vc4->firmware = rpi_firmware_get(firmware_node);
 | 
						|
-	if (!vc4->firmware) {
 | 
						|
-		DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
 | 
						|
-		return -EPROBE_DEFER;
 | 
						|
-	}
 | 
						|
-	of_node_put(firmware_node);
 | 
						|
-
 | 
						|
-	/* Map the SMI interrupt reg */
 | 
						|
-	vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
 | 
						|
-	if (IS_ERR(vc4_crtc->regs))
 | 
						|
-		return PTR_ERR(vc4_crtc->regs);
 | 
						|
+	vc4_crtc->display_number = display_ref;
 | 
						|
 
 | 
						|
 	/* Blank the firmware provided framebuffer */
 | 
						|
 	rpi_firmware_property(vc4->firmware,
 | 
						|
 			      RPI_FIRMWARE_FRAMEBUFFER_BLANK,
 | 
						|
 			      &blank, sizeof(blank));
 | 
						|
 
 | 
						|
-	primary_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0);
 | 
						|
+	primary_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_PRIMARY,
 | 
						|
+					    display_ref,
 | 
						|
+					    0 + (display_idx * PLANES_PER_CRTC)
 | 
						|
+					   );
 | 
						|
 	if (IS_ERR(primary_plane)) {
 | 
						|
 		dev_err(dev, "failed to construct primary plane\n");
 | 
						|
 		ret = PTR_ERR(primary_plane);
 | 
						|
 		goto err;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	overlay_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, 1);
 | 
						|
+	overlay_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
 | 
						|
+					    display_ref,
 | 
						|
+					    1 + (display_idx * PLANES_PER_CRTC)
 | 
						|
+					   );
 | 
						|
 	if (IS_ERR(overlay_plane)) {
 | 
						|
 		dev_err(dev, "failed to construct overlay plane\n");
 | 
						|
 		ret = PTR_ERR(overlay_plane);
 | 
						|
 		goto err;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	cursor_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_CURSOR, 2);
 | 
						|
+	cursor_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_CURSOR,
 | 
						|
+					   display_ref,
 | 
						|
+					   2 + (display_idx * PLANES_PER_CRTC)
 | 
						|
+					  );
 | 
						|
 	if (IS_ERR(cursor_plane)) {
 | 
						|
 		dev_err(dev, "failed to construct cursor plane\n");
 | 
						|
 		ret = PTR_ERR(cursor_plane);
 | 
						|
@@ -923,13 +920,6 @@ static int vc4_fkms_bind(struct device *
 | 
						|
 		goto err_destroy_encoder;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	writel(0, vc4_crtc->regs + SMICS);
 | 
						|
-	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
 | 
						|
-			       vc4_crtc_irq_handler, 0, "vc4 firmware kms",
 | 
						|
-			       vc4_crtc);
 | 
						|
-	if (ret)
 | 
						|
-		goto err_destroy_connector;
 | 
						|
-
 | 
						|
 	ret = rpi_firmware_property(vc4->firmware,
 | 
						|
 				    RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN,
 | 
						|
 				    &vc4_crtc->overscan,
 | 
						|
@@ -939,7 +929,7 @@ static int vc4_fkms_bind(struct device *
 | 
						|
 		memset(&vc4_crtc->overscan, 0, sizeof(vc4_crtc->overscan));
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	platform_set_drvdata(pdev, vc4_crtc);
 | 
						|
+	*ret_crtc = vc4_crtc;
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
 
 | 
						|
@@ -956,17 +946,91 @@ err:
 | 
						|
 	return ret;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
 | 
						|
+{
 | 
						|
+	struct platform_device *pdev = to_platform_device(dev);
 | 
						|
+	struct drm_device *drm = dev_get_drvdata(master);
 | 
						|
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
 | 
						|
+	struct device_node *firmware_node;
 | 
						|
+	struct vc4_crtc **crtc_list;
 | 
						|
+	u32 num_displays, display_num;
 | 
						|
+	int ret;
 | 
						|
+	const u32 display_num_lookup[] = {2, 7, 1};
 | 
						|
+
 | 
						|
+	vc4->firmware_kms = true;
 | 
						|
+
 | 
						|
+	/* firmware kms doesn't have precise a scanoutpos implementation, so
 | 
						|
+	 * we can't do the precise vblank timestamp mode.
 | 
						|
+	 */
 | 
						|
+	drm->driver->get_scanout_position = NULL;
 | 
						|
+	drm->driver->get_vblank_timestamp = NULL;
 | 
						|
+
 | 
						|
+	firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
 | 
						|
+	vc4->firmware = rpi_firmware_get(firmware_node);
 | 
						|
+	if (!vc4->firmware) {
 | 
						|
+		DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
 | 
						|
+		return -EPROBE_DEFER;
 | 
						|
+	}
 | 
						|
+	of_node_put(firmware_node);
 | 
						|
+
 | 
						|
+	ret = rpi_firmware_property(vc4->firmware,
 | 
						|
+				    RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
 | 
						|
+				    &num_displays, sizeof(u32));
 | 
						|
+
 | 
						|
+	/* If we fail to get the number of displays, or it returns 0, then
 | 
						|
+	 * assume old firmware that doesn't have the mailbox call, so just
 | 
						|
+	 * set one display
 | 
						|
+	 */
 | 
						|
+	if (ret || num_displays == 0) {
 | 
						|
+		num_displays = 1;
 | 
						|
+		DRM_WARN("Unable to determine number of displays's. Assuming 1\n");
 | 
						|
+		ret = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Allocate a list, with space for a NULL on the end */
 | 
						|
+	crtc_list = devm_kzalloc(dev, sizeof(crtc_list) * (num_displays + 1),
 | 
						|
+				 GFP_KERNEL);
 | 
						|
+	if (!crtc_list)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	for (display_num = 0; display_num < num_displays; display_num++) {
 | 
						|
+		ret = vc4_fkms_create_screen(dev, drm, display_num,
 | 
						|
+					     display_num_lookup[display_num],
 | 
						|
+					     &crtc_list[display_num]);
 | 
						|
+		if (ret)
 | 
						|
+			DRM_ERROR("Oh dear, failed to create display %u\n",
 | 
						|
+				  display_num);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Map the SMI interrupt reg */
 | 
						|
+	crtc_list[0]->regs = vc4_ioremap_regs(pdev, 0);
 | 
						|
+	if (IS_ERR(crtc_list[0]->regs))
 | 
						|
+		DRM_ERROR("Oh dear, failed to map registers\n");
 | 
						|
+
 | 
						|
+	writel(0, crtc_list[0]->regs + SMICS);
 | 
						|
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
 | 
						|
+			       vc4_crtc_irq_handler, 0, "vc4 firmware kms",
 | 
						|
+			       crtc_list);
 | 
						|
+	if (ret)
 | 
						|
+		DRM_ERROR("Oh dear, failed to register IRQ\n");
 | 
						|
+
 | 
						|
+	platform_set_drvdata(pdev, crtc_list);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static void vc4_fkms_unbind(struct device *dev, struct device *master,
 | 
						|
 			    void *data)
 | 
						|
 {
 | 
						|
-	struct drm_device *drm = dev_get_drvdata(master);
 | 
						|
 	struct platform_device *pdev = to_platform_device(dev);
 | 
						|
-	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
 | 
						|
+	struct vc4_crtc **crtc_list = dev_get_drvdata(dev);
 | 
						|
+	int i;
 | 
						|
 
 | 
						|
-	vc4_fkms_connector_destroy(vc4_crtc->connector);
 | 
						|
-	vc4_fkms_encoder_destroy(vc4_crtc->encoder);
 | 
						|
-	drm_atomic_helper_shutdown(drm);
 | 
						|
-	drm_crtc_cleanup(&vc4_crtc->base);
 | 
						|
+	for (i = 0; crtc_list[i]; i++) {
 | 
						|
+		vc4_fkms_connector_destroy(crtc_list[i]->connector);
 | 
						|
+		vc4_fkms_encoder_destroy(crtc_list[i]->encoder);
 | 
						|
+		drm_crtc_cleanup(&crtc_list[i]->base);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	platform_set_drvdata(pdev, NULL);
 | 
						|
 }
 |