tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

sunxi graphic console, try 2



Hello,
with Jared's feedback, I reimplemented console handling for the sunxi graphic
drivers. There are 3 problems to solve:
- proper handling for console=xxx in the bootargs. I want to switch from
  graphic to serial, or back, from bootargs without changing
  /chosen/stdout-path entry in the device tree each time.
  The kernel can change stdout-path early in the boot process,
  and at this time it's not pratical to make it point to anything
  but serial or /chosen/framebuffer (we don't want to parse the whole
  device tree at this time).
  So I just change sunxi_platform_bootstrap() to understand console=serial
  in bootargs, so is stdout-path points to some graphic device in the
  device tree we can switch back to serial.

- attaching the graphic console to the graphic pipeline instead of simplefb.
  The graphic driver doesn't want to claim the consle at FDT_CONSOLE() time,
  because at this time it doens't know if it will be active or not
  (not to mention which of the 2 pipelines will be active, and will be console).
  In addition, simplefb may be functional until the graphic driver takes over.
  So in sunxi_debe I use FDT_CONSOLE() only to remember the
  simple-framebuffer phandle, which sunxi_debe will need to decide if
  it's console later. 

- preventing simplefb from attaching. Once the graphic drivers have attached
  simplefb is not functional any more. So even if there is an active
  simple-framebuffer compatible not in the fdt, we should not attach it.
  I could add a sunxi_simplefb driver which return a higther match
  score, but I don't like the idea of adding a void driver just
  to prevent another one from attaching. Among others, this could
  cause strange effects if the kernel config omits it, hard to debug as
  this is touching the console.
  Basically we have several fdt nodes pointing to the same hardware,
  and we don't want to use them all.
  I added public fdt functions to explicitely inactivate arbitrary
  fdt nodes (here I remove them from the node list but that's implementation
  detail). There is fdt_remove_bycompat(const char *[]) to remove nodes
  matching on compatible strings; for completeness I also added
  fdt_remove_byhandle(int) but I don't need it.

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: dev/fdt/fdtbus.c
===================================================================
RCS file: /cvsroot/src/sys/dev/fdt/fdtbus.c,v
retrieving revision 1.15
diff -u -p -u -r1.15 fdtbus.c
--- dev/fdt/fdtbus.c	27 Aug 2017 19:13:31 -0000	1.15
+++ dev/fdt/fdtbus.c	7 Apr 2018 11:37:47 -0000
@@ -265,6 +265,31 @@ fdt_add_node(struct fdt_node *new_node)
 	TAILQ_INSERT_TAIL(&fdt_nodes, new_node, n_nodes);
 }
 
+void
+fdt_remove_byhandle(int phandle)
+{
+	struct fdt_node *node;
+
+	TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
+		if (node->n_phandle == phandle) {
+			TAILQ_REMOVE(&fdt_nodes, node, n_nodes);
+			return;
+		}
+	}
+}
+
+void
+fdt_remove_bycompat(const char *compatible[])
+{
+	struct fdt_node *node, *next;
+
+	TAILQ_FOREACH_SAFE(node, &fdt_nodes, n_nodes, next) {
+		if (of_match_compatible(node->n_phandle, compatible)) {
+			TAILQ_REMOVE(&fdt_nodes, node, n_nodes);
+		}
+	}
+}
+
 static u_int
 fdt_get_order(int phandle)
 {
Index: dev/fdt/fdtvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/fdt/fdtvar.h,v
retrieving revision 1.28
diff -u -p -u -r1.28 fdtvar.h
--- dev/fdt/fdtvar.h	10 Dec 2017 21:38:27 -0000	1.28
+++ dev/fdt/fdtvar.h	7 Apr 2018 11:37:47 -0000
@@ -321,6 +321,8 @@ const void *	fdtbus_get_prop(int, const 
 const char *	fdtbus_get_string(int, const char *);
 const char *	fdtbus_get_string_index(int, const char *, u_int);
 
+void		fdt_remove_byhandle(int);
+void		fdt_remove_bycompat(const char *[]);
 int		fdtbus_print(void *, const char *);
 
 #endif /* _DEV_FDT_FDTVAR_H */
Index: arch/arm/sunxi/sunxi_debe.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/sunxi/sunxi_debe.c,v
retrieving revision 1.7
diff -u -p -u -r1.7 sunxi_debe.c
--- arch/arm/sunxi/sunxi_debe.c	5 Apr 2018 10:21:39 -0000	1.7
+++ arch/arm/sunxi/sunxi_debe.c	7 Apr 2018 11:37:47 -0000
@@ -58,6 +58,8 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_debe.c
 
 #include <arm/sunxi/sunxi_debereg.h>
 #include <arm/sunxi/sunxi_display.h>
+#include <arm/sunxi/sunxi_platform.h>
+#include <machine/bootconfig.h>
 
 enum sunxi_debe_type {
 	DEBE_A10 = 1,
@@ -128,6 +130,9 @@ static int	sunxi_debe_ioctl(device_t, u_
 static void	sunxi_befb_set_videomode(device_t, u_int, u_int);
 void sunxi_debe_dump_regs(int);
 
+static struct sunxi_debe_softc *debe_console_sc;
+static int sunxi_simplefb_phandle = -1;
+
 CFATTACH_DECL_NEW(sunxi_debe, sizeof(struct sunxi_debe_softc),
 	sunxi_debe_match, sunxi_debe_attach, NULL, NULL);
 
@@ -242,8 +247,6 @@ sunxi_debe_attach(device_t parent, devic
 	}
 }
 
-
-
 static void
 sunxi_debe_ep_connect(device_t self, struct fdt_endpoint *ep, bool connect)
 {
@@ -337,6 +340,32 @@ sunxi_debe_setup_fbdev(struct sunxi_debe
 	const u_int fb_height = (mode->vdisplay << interlace_p);
 
 	if (mode && sc->sc_fbdev == NULL) {
+		/* see if we are the console */
+		if (sunxi_simplefb_phandle >= 0) {
+			const char *cons_pipeline =
+			    fdtbus_get_string(sunxi_simplefb_phandle,
+			        "allwinner,pipeline");
+			struct fdt_endpoint *ep = fdt_endpoint_get_from_index(
+			    &sc->sc_ports, SUNXI_PORT_OUTPUT, sc->sc_unit);
+			struct fdt_endpoint *rep = fdt_endpoint_remote(ep);
+			if (sunxi_tcon_is_console(
+			    fdt_endpoint_device(rep), cons_pipeline))
+				debe_console_sc = sc;
+		} else if (debe_console_sc == NULL) {
+			if (match_bootconf_option(boot_args,
+			    "console", "fb0")) {
+				if (sc->sc_unit == 0)
+					debe_console_sc = sc;
+			} else if (match_bootconf_option(boot_args,
+			    "console", "fb1")) {
+				if (sc->sc_unit == 1)
+					debe_console_sc = sc;
+			} else if (match_bootconf_option(boot_args,
+			    "console", "fb")) {
+				/* match first activated */
+				debe_console_sc = sc;
+			}
+		}
 		struct sunxifb_attach_args afb = {
 			.afb_fb = sc->sc_dmap,
 			.afb_width = fb_width,
@@ -684,9 +713,6 @@ sunxi_befb_attach(device_t parent, devic
 	prop_dictionary_t cfg = device_properties(self);
 	struct genfb_ops ops;
 
-	if (sunxi_befb_consoledev == NULL)
-		sunxi_befb_consoledev = self;
-
 	sc->sc_gen.sc_dev = self;
 	sc->sc_debedev = parent;
 	sc->sc_dmat = afb->afb_dmat;
@@ -716,14 +742,27 @@ sunxi_befb_attach(device_t parent, devic
 
 	aprint_naive("\n");
 
-	bool is_console = false;
-	prop_dictionary_set_bool(cfg, "is_console", is_console);
-
+	bool is_console = (debe_console_sc == device_private(parent));
 	if (is_console)
 		aprint_normal(": switching to framebuffer console\n");
 	else
 		aprint_normal("\n");
 
+#ifdef WSDISPLAY_MULTICONS
+	/*
+	 * if we support multicons, only the first framebuffer is console,
+	 * unless we already know which framebuffer will be the console
+	 */
+	if (!is_console && debe_console_sc == NULL &&
+	    sunxi_befb_consoledev == NULL)
+		is_console = true;
+#endif
+	prop_dictionary_set_bool(cfg, "is_console", is_console);
+	if (is_console) {
+		KASSERT(sunxi_befb_consoledev == NULL);
+		sunxi_befb_consoledev = self;
+	}
+
 	genfb_attach(&sc->sc_gen, &ops);
 }
 
@@ -829,13 +868,47 @@ sunxi_debe_pipeline(int phandle, bool ac
 		return ENODEV;
 	}
 	error = fdt_endpoint_activate(ep, true);
-	if (error == 0) {
-		sc->sc_out_ep = ep;
-		fdt_endpoint_enable(ep, true);
-	}
+	if (error)
+		return error;
+
+	sc->sc_out_ep = ep;
+	error = fdt_endpoint_enable(ep, true);
 	return error;
 }
 
+/*
+ * we don't want to take over console at this time - simplefb will
+ * do a better job than us. We will take over later.
+ * But we want to record the /chose/framebuffer phandle if there is one
+ */
+
+static const char * const simplefb_compatible[] = {
+	"allwinner,simple-framebuffer",
+	NULL
+};
+
+static int
+sunxidebe_console_match(int phandle)
+{
+	if (of_match_compatible(phandle, simplefb_compatible)) {
+		sunxi_simplefb_phandle = phandle;
+	}
+	return 0;
+}
+
+static void
+sunxidebe_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
+{
+	panic("sunxidebe_console_consinit");
+}
+
+static const struct fdt_console sunxidebe_fdt_console = {
+	.match = sunxidebe_console_match,
+	.consinit = sunxidebe_console_consinit
+};
+
+FDT_CONSOLE(sunxidebe, &sunxidebe_fdt_console);
+
 #if defined(SUNXI_DEBE_DEBUG)
 void
 sunxi_debe_dump_regs(int u)
Index: arch/arm/sunxi/sunxi_dep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/sunxi/sunxi_dep.c,v
retrieving revision 1.2
diff -u -p -u -r1.2 sunxi_dep.c
--- arch/arm/sunxi/sunxi_dep.c	3 Apr 2018 13:38:13 -0000	1.2
+++ arch/arm/sunxi/sunxi_dep.c	7 Apr 2018 11:37:47 -0000
@@ -57,6 +57,11 @@ static const struct of_compat_data compa
 	{NULL}
 };
 
+static const char *fb_compat[] = {
+	"allwinner,simple-framebuffer",
+	NULL
+};
+
 static int sunxi_dep_match(device_t, cfdata_t, void *);
 static void sunxi_dep_attach(device_t, device_t, void *);
 
@@ -68,9 +73,8 @@ sunxi_dep_match(device_t parent, cfdata_
 {
 #if NSUNXI_DEBE > 0
 	struct fdt_attach_args * const faa = aux;
-	if (!of_match_compat_data(faa->faa_phandle, compat_data))
-		return 0;
-	return 1;
+
+	return  of_match_compat_data(faa->faa_phandle, compat_data);
 #else
 	return 0;
 #endif
@@ -82,6 +86,7 @@ sunxi_dep_attach(device_t parent, device
 	struct sunxi_dep_softc * const sc = device_private(self);
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
+	int sunxi_dep_npipelines = 0;
 	int len;
 	const u_int *buf;
 	u_int ref;
@@ -95,7 +100,6 @@ sunxi_dep_attach(device_t parent, device
 		aprint_error("bad/missing allwinner,pipelines property\n");
 		return;
 	}
-	aprint_naive("\n");
 	aprint_normal(": ");
 #if NSUNXI_DEBE > 0
 	for (int i = 0; i < (len / sizeof(ref)); i++) {
@@ -106,7 +110,12 @@ sunxi_dep_attach(device_t parent, device
 		    fdtbus_get_phandle_from_native(ref), true);
 		if (error)
 			aprint_error("can't activate pipeline %d\n", i);
+		else
+			sunxi_dep_npipelines++;
 	}
+	aprint_naive("\n");
+	if (sunxi_dep_npipelines > 0)
+		fdt_remove_bycompat(fb_compat);
 #else
 	aprint_error("debe not configured\n");
 	return;
Index: arch/arm/sunxi/sunxi_display.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/sunxi/sunxi_display.h,v
retrieving revision 1.1
diff -u -p -u -r1.1 sunxi_display.h
--- arch/arm/sunxi/sunxi_display.h	3 Apr 2018 12:52:16 -0000	1.1
+++ arch/arm/sunxi/sunxi_display.h	7 Apr 2018 11:37:47 -0000
@@ -37,3 +37,4 @@ struct videomode;
 int sunxi_debe_pipeline(int, bool);
 void sunxi_tcon1_set_videomode(device_t, const struct videomode *);
 void sunxi_debe_set_videomode(device_t, const struct videomode *);
+bool sunxi_tcon_is_console(device_t, const char *);
Index: arch/arm/sunxi/sunxi_platform.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/sunxi/sunxi_platform.c,v
retrieving revision 1.22
diff -u -p -u -r1.22 sunxi_platform.c
--- arch/arm/sunxi/sunxi_platform.c	1 Apr 2018 04:35:04 -0000	1.22
+++ arch/arm/sunxi/sunxi_platform.c	7 Apr 2018 11:37:47 -0000
@@ -176,12 +176,15 @@ sunxi_platform_uart_freq(void)
 static void
 sunxi_platform_bootstrap(void)
 {
+	void *fdt_data = __UNCONST(fdtbus_get_data());
+	const int chosen_off = fdt_path_offset(fdt_data, "/chosen");
+	if (chosen_off < 0)
+		return;
+
 	if (match_bootconf_option(boot_args, "console", "fb")) {
-		void *fdt_data = __UNCONST(fdtbus_get_data());
-		const int chosen_off = fdt_path_offset(fdt_data, "/chosen");
 		const int framebuffer_off =
 		    fdt_path_offset(fdt_data, "/chosen/framebuffer");
-		if (chosen_off >= 0 && framebuffer_off >= 0) {
+		if (framebuffer_off >= 0) {
 			const char *status = fdt_getprop(fdt_data,
 			    framebuffer_off, "status", NULL);
 			if (status == NULL || strncmp(status, "ok", 2) == 0) {
@@ -189,6 +192,9 @@ sunxi_platform_bootstrap(void)
 				    "stdout-path", "/chosen/framebuffer");
 			}
 		}
+	} else if (match_bootconf_option(boot_args, "console", "serial")) {
+		fdt_setprop_string(fdt_data, chosen_off,
+		    "stdout-path", "serial0:115200n8");
 	}
 }
 
Index: arch/arm/sunxi/sunxi_tcon.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/sunxi/sunxi_tcon.c,v
retrieving revision 1.4
diff -u -p -u -r1.4 sunxi_tcon.c
--- arch/arm/sunxi/sunxi_tcon.c	4 Apr 2018 16:01:05 -0000	1.4
+++ arch/arm/sunxi/sunxi_tcon.c	7 Apr 2018 11:37:47 -0000
@@ -812,6 +812,55 @@ sunxi_tcon1_set_videomode(device_t dev, 
 	}
 }
 
+/* check if this tcon is the console chosen by firmare */
+bool
+sunxi_tcon_is_console(device_t dev, const char *pipeline)
+{
+	struct sunxi_tcon_softc *sc = device_private(dev);
+	char p[64];
+	char *e, *n = p;
+	bool is_console = false;
+
+	KASSERT(device_is_a(dev, "sunxitcon"));
+	strncpy(p, pipeline, sizeof(p) - 1);
+	p[sizeof(p) - 1] = '\0';
+
+	/*
+	 * pipeline is like "de_be0-lcd0-hdmi"
+	 * of "de_be0-lcd1".
+	 * In the first case check output type
+	 * In the second check tcon unit number
+	 */
+	 n = p;
+	 e = strsep(&n, "-");
+	 if (e == NULL || memcmp(e, "de_be", 5) != 0)
+		goto bad;
+	 e = strsep(&n, "-");
+	 if (e == NULL)
+		goto bad;
+	 if (n == NULL) {
+		/* second case */
+		if (strcmp(e, "lcd0") == 0) {
+			if (sc->sc_unit == 0)
+				is_console = true;
+		 } else if (strcmp(e, "lcd1") == 0) {
+			if (sc->sc_unit == 1)
+				is_console = true;
+		} else
+			goto bad;
+		return is_console;
+	}
+	/* first case */
+	if (strcmp(n, "hdmi") == 0) {
+		if (sc->sc_output_type == OUTPUT_HDMI)
+			is_console = true;
+		return is_console;
+	}
+bad:
+	aprint_error("warning: can't parse pipeline %s\n", pipeline);
+	return is_console;
+}
+
 #if defined(DDB) || defined(SUNXI_TCON_DEBUG)
 void
 sunxi_tcon_dump_regs(int u)


Home | Main Index | Thread Index | Old Index