Module Name: src Committed By: bouyer Date: Tue Apr 3 12:40:20 UTC 2018
Modified Files: src/sys/dev/fdt: files.fdt Added Files: src/sys/dev/fdt: connector_fdt.c connector_fdt.h fdt_port.c fdt_port.h panel_fdt.c panel_fdt.h Log Message: Add connector and panel drivers (panel supports only panel-lvds and panel-dual-lvds at this time, but can easily be extended to other types of panels). Add an API for ports/endpoints. Proposed on tech-kern@ a few days ago, ok jmcneill@ To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/connector_fdt.c \ src/sys/dev/fdt/connector_fdt.h src/sys/dev/fdt/fdt_port.c \ src/sys/dev/fdt/fdt_port.h src/sys/dev/fdt/panel_fdt.c \ src/sys/dev/fdt/panel_fdt.h cvs rdiff -u -r1.22 -r1.23 src/sys/dev/fdt/files.fdt Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.22 src/sys/dev/fdt/files.fdt:1.23 --- src/sys/dev/fdt/files.fdt:1.22 Sun Oct 22 13:56:49 2017 +++ src/sys/dev/fdt/files.fdt Tue Apr 3 12:40:20 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.22 2017/10/22 13:56:49 jmcneill Exp $ +# $NetBSD: files.fdt,v 1.23 2018/04/03 12:40:20 bouyer Exp $ include "external/bsd/libfdt/conf/files.libfdt" @@ -34,6 +34,16 @@ device gpioleds: leds attach gpioleds at fdt file dev/fdt/gpioleds.c gpioleds +file dev/fdt/fdt_port.c fdt_port + +device connector: fdt_port +attach connector at fdt with fdt_connector +file dev/fdt/connector_fdt.c fdt_connector + +device panel: fdt_port +attach panel at fdt with fdt_panel +file dev/fdt/panel_fdt.c fdt_panel + file dev/fdt/fdt_openfirm.c fdtbus file dev/fdt/fdt_subr.c fdtbus file dev/fdt/fdt_clock.c fdtbus Added files: Index: src/sys/dev/fdt/connector_fdt.c diff -u /dev/null src/sys/dev/fdt/connector_fdt.c:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/connector_fdt.c Tue Apr 3 12:40:20 2018 @@ -0,0 +1,129 @@ +/* $NetBSD: connector_fdt.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * connector driver. + * specified in linux/Documentation/devicetree/bindings/display/connector/ + * basically it only register its endpoint. + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: connector_fdt.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/fdt_port.h> +#include <dev/fdt/connector_fdt.h> + +static int fdt_connector_match(device_t, cfdata_t, void *); +static void fdt_connector_attach(device_t, device_t, void *); +static void *fdt_connector_get_data(device_t, struct fdt_endpoint *); + +SLIST_HEAD(, fdt_connector_softc) fdt_connectors = + SLIST_HEAD_INITIALIZER(&fdt_connectors); + +struct fdt_connector_softc { + device_t sc_dev; + int sc_phandle; + struct fdt_connector sc_con; + SLIST_ENTRY(fdt_connector_softc) sc_list; + struct fdt_device_ports sc_ports; +}; + +#define sc_type sc_con.con_type + +CFATTACH_DECL_NEW(fdt_connector, sizeof(struct fdt_connector_softc), + fdt_connector_match, fdt_connector_attach, NULL, NULL); + +static const struct of_compat_data compat_data[] = { + {"composite-video-connector", CON_TV}, + {"dvi-connector", CON_DVI}, + {"hdmi-connector", CON_HDMI}, + {"vga-connector", CON_VGA}, + { NULL } +}; + +static int +fdt_connector_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +fdt_connector_attach(device_t parent, device_t self, void *aux) +{ + struct fdt_connector_softc *sc = device_private(self); + const struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_type = of_search_compatible(phandle, compat_data)->data; + + SLIST_INSERT_HEAD(&fdt_connectors, sc, sc_list); + + aprint_naive("\n"); + switch(sc->sc_type) { + case CON_VGA: + aprint_normal(": VGA"); + break; + case CON_DVI: + aprint_normal(": DVI"); + break; + case CON_HDMI: + aprint_normal(": HDMI"); + break; + case CON_TV: + aprint_normal(": composite"); + break; + default: + panic("unknown connector type %d\n", sc->sc_type); + } + aprint_normal(" connector\n"); + sc->sc_ports.dp_ep_get_data = fdt_connector_get_data; + fdt_ports_register(&sc->sc_ports, self, phandle, EP_CONNECTOR); +} + +static void * +fdt_connector_get_data(device_t dev, struct fdt_endpoint *ep) +{ + struct fdt_connector_softc *sc = device_private(dev); + + return &sc->sc_con; +} Index: src/sys/dev/fdt/connector_fdt.h diff -u /dev/null src/sys/dev/fdt/connector_fdt.h:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/connector_fdt.h Tue Apr 3 12:40:20 2018 @@ -0,0 +1,41 @@ +/* $NetBSD: connector_fdt.h,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +enum connector_type { + CON_VGA, + CON_DVI, + CON_HDMI, + CON_TV, +}; + +struct fdt_connector { + enum connector_type con_type; +}; Index: src/sys/dev/fdt/fdt_port.c diff -u /dev/null src/sys/dev/fdt/fdt_port.c:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/fdt_port.c Tue Apr 3 12:40:20 2018 @@ -0,0 +1,370 @@ +/* $NetBSD: fdt_port.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ports and endpoints management. from + * linux/Documentation/devicetree/bindings/graph.txt + * Given a device and its node, it enumerates all ports and endpoints for this + * device, and register connections with the remote endpoints. + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: fdt_port.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/fdt_port.h> + +struct fdt_endpoint; + +struct fdt_port { + int port_id; + int port_phandle; /* port's node */ + struct fdt_endpoint *port_ep; /* this port's endpoints */ + int port_nep; /* number of endpoints for this port */ + struct fdt_device_ports *port_dp; /* this port's device */ +}; + +struct fdt_endpoint { + int ep_id; + enum endpoint_type ep_type; + int ep_phandle; + struct fdt_port *ep_port; /* parent of this endpoint */ + int ep_rphandle; /* report endpoint */ + struct fdt_endpoint *ep_rep; + bool ep_active; + bool ep_enabled; +}; + +SLIST_HEAD(, fdt_device_ports) fdt_port_devices = + SLIST_HEAD_INITIALIZER(&fdt_port_devices); + +static void fdt_endpoints_register(int, struct fdt_port *, enum endpoint_type); +static const char *ep_name(struct fdt_endpoint *, char *, int); + +struct fdt_endpoint * +fdt_endpoint_get_from_phandle(int rphandle) +{ + struct fdt_device_ports *ports; + int p, e; + + if (rphandle < 0) + return NULL; + + SLIST_FOREACH(ports, &fdt_port_devices, dp_list) { + for (p = 0; p < ports->dp_nports; p++) { + struct fdt_port *port = &ports->dp_port[p]; + for (e = 0; e < port->port_nep; e++) { + struct fdt_endpoint *ep = &port->port_ep[e]; + if (ep->ep_phandle == rphandle) + return ep; + } + } + } + return NULL; + +} + +struct fdt_endpoint * +fdt_endpoint_get_from_index(struct fdt_device_ports *device_ports, + int port_index, int ep_index) +{ + int p, e; + for (p = 0; p < device_ports->dp_nports; p++) { + struct fdt_port *port = &device_ports->dp_port[p]; + if (port->port_id != port_index) + continue; + for (e = 0; e < port->port_nep; e++) { + struct fdt_endpoint *ep = &port->port_ep[e]; + if (ep->ep_id == ep_index) { + return ep; + } + } + } + return NULL; +} + +struct fdt_endpoint * +fdt_endpoint_remote(struct fdt_endpoint *ep) +{ + return ep->ep_rep; +} + +int +fdt_endpoint_port_index(struct fdt_endpoint *ep) +{ + return ep->ep_port->port_id; +} + +int +fdt_endpoint_index(struct fdt_endpoint *ep) +{ + return ep->ep_id; +} + +device_t +fdt_endpoint_device(struct fdt_endpoint *ep) +{ + return ep->ep_port->port_dp->dp_dev; +} + +bool +fdt_endpoint_is_active(struct fdt_endpoint *ep) +{ + return ep->ep_active; +} + +bool +fdt_endpoint_is_enabled(struct fdt_endpoint *ep) +{ + return ep->ep_enabled; +} + +int +fdt_endpoint_activate(struct fdt_endpoint *ep, bool activate) +{ + struct fdt_endpoint *rep = fdt_endpoint_remote(ep); + struct fdt_device_ports *rdp; + int error = 0; + + if (rep == NULL) + return ENODEV; + + KASSERT(ep->ep_active == rep->ep_active); + KASSERT(ep->ep_enabled == rep->ep_enabled); + if (!activate && ep->ep_enabled) + return EBUSY; + + rdp = rep->ep_port->port_dp; + if (rdp->dp_ep_activate) + error = rdp->dp_ep_activate(rdp->dp_dev, rep, activate); + + if (error == 0) + rep->ep_active = ep->ep_active = activate; + return error; +} + +int +fdt_endpoint_enable(struct fdt_endpoint *ep, bool enable) +{ + struct fdt_endpoint *rep = fdt_endpoint_remote(ep); + struct fdt_device_ports *rdp; + int error = 0; + + if (rep == NULL) + return EINVAL; + + KASSERT(ep->ep_active == rep->ep_active); + KASSERT(ep->ep_enabled == rep->ep_enabled); + if (ep->ep_active == false) + return EINVAL; + + rdp = rep->ep_port->port_dp; + if (rdp->dp_ep_enable) + error = rdp->dp_ep_enable(rdp->dp_dev, rep, enable); + + if (error == 0) + rep->ep_enabled = ep->ep_enabled = enable; + return error; +} + +void * +fdt_endpoint_get_data(struct fdt_endpoint *ep) +{ + struct fdt_device_ports *dp = ep->ep_port->port_dp; + + if (dp->dp_ep_get_data) + return dp->dp_ep_get_data(dp->dp_dev, ep); + + return NULL; +} + +int +fdt_ports_register(struct fdt_device_ports *ports, device_t self, + int phandle, enum endpoint_type type) +{ + int port_phandle, child; + int i; + char buf[20]; + uint64_t id; + + ports->dp_dev = self; + SLIST_INSERT_HEAD(&fdt_port_devices, ports, dp_list); + + /* + * walk the childs looking for ports. ports may be grouped under + * an optional ports node + */ + port_phandle = phandle; +again: + ports->dp_nports = 0; + for (child = OF_child(port_phandle); child; child = OF_peer(child)) { + if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) + continue; + if (strcmp(buf, "ports") == 0) { + port_phandle = child; + goto again; + } + if (strcmp(buf, "port") != 0) + continue; + ports->dp_nports++; + } + if (ports->dp_nports == 0) + return 0; + + ports->dp_port = + kmem_zalloc(sizeof(struct fdt_port) * ports->dp_nports, KM_SLEEP); + KASSERT(ports->dp_port != NULL); + /* now scan again ports, looking for endpoints */ + for (child = OF_child(port_phandle), i = 0; child; + child = OF_peer(child)) { + if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) + continue; + if (strcmp(buf, "ports") == 0) { + panic("fdt_ports_register: undetected ports"); + } + if (strcmp(buf, "port") != 0) + continue; + if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) { + if (ports->dp_nports > 1) + aprint_error_dev(self, + "%s: missing reg property", + fdtbus_get_string(child, "name")); + id = i; + } + ports->dp_port[i].port_id = id; + ports->dp_port[i].port_phandle = child; + ports->dp_port[i].port_dp = ports; + fdt_endpoints_register(child, &ports->dp_port[i], type); + i++; + } + KASSERT(i == ports->dp_nports); + return 0; +} + + +static void +fdt_endpoints_register(int phandle, struct fdt_port *port, + enum endpoint_type type) +{ + int child; + int i; + char buf[128]; + uint64_t id; + struct fdt_endpoint *ep, *rep; + struct fdt_device_ports *dp; + + port->port_nep = 0; + for (child = OF_child(phandle); child; child = OF_peer(child)) { + if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) + continue; + if (strcmp(buf, "endpoint") != 0) + continue; + port->port_nep++; + } + if (port->port_nep == 0) { + port->port_ep = NULL; + return; + } + + port->port_ep = + kmem_zalloc(sizeof(struct fdt_endpoint) * port->port_nep, KM_SLEEP); + KASSERT(port->port_ep != NULL); + /* now scan again ports, looking for endpoints */ + for (child = OF_child(phandle), i = 0; child; child = OF_peer(child)) { + if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) + continue; + if (strcmp(buf, "endpoint") != 0) + continue; + if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) { + if (port->port_nep > 1) + aprint_error_dev(port->port_dp->dp_dev, + "%s: missing reg property", + fdtbus_get_string(child, "name")); + id = i; + } + ep = &port->port_ep[i]; + ep->ep_id = id; + ep->ep_type = type; + ep->ep_phandle = child; + ep->ep_port = port; + ep->ep_rphandle = fdtbus_get_phandle(child, "remote-endpoint"); + ep->ep_rep = fdt_endpoint_get_from_phandle( + port->port_ep[i].ep_rphandle); + rep = ep->ep_rep; + if (rep != NULL && rep->ep_rep != NULL) { + aprint_error("%s: ", ep_name(ep, buf, sizeof(buf))); + aprint_error("remote endpoint %s ", + ep_name(rep, buf, sizeof(buf))); + aprint_error("already connected to %s\n", + ep_name(rep->ep_rep, buf, sizeof(buf))); + } else if (rep != NULL) { + rep->ep_rep = ep; + rep->ep_rphandle = child; + aprint_verbose("%s ", ep_name(ep, buf, sizeof(buf))); + aprint_verbose("connected to %s\n", + ep_name(rep, buf, sizeof(buf))); + if (rep->ep_type == EP_OTHER) + rep->ep_type = ep->ep_type; + else if (ep->ep_type == EP_OTHER) + ep->ep_type = rep->ep_type; + dp = port->port_dp; + if (dp->dp_ep_connect) + dp->dp_ep_connect(dp->dp_dev, ep, true); + dp = rep->ep_port->port_dp; + if (dp->dp_ep_connect) + dp->dp_ep_connect(dp->dp_dev, rep, true); + } + i++; + } + KASSERT(i == port->port_nep); +} + +static const char * +ep_name(struct fdt_endpoint *ep, char *buf, int size) +{ + int a; + + a = snprintf(&buf[0], size, "%s", + device_xname(ep->ep_port->port_dp->dp_dev)); + if (ep->ep_port->port_id >= 0 && a < size) + a += snprintf(&buf[a], size - a, " port %d", + ep->ep_port->port_id); + if (ep->ep_id >= 0 && a < size) + snprintf(&buf[a], size - a, " endpoint %d", ep->ep_id); + return buf; +} Index: src/sys/dev/fdt/fdt_port.h diff -u /dev/null src/sys/dev/fdt/fdt_port.h:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/fdt_port.h Tue Apr 3 12:40:20 2018 @@ -0,0 +1,108 @@ +/* $NetBSD: fdt_port.h,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ports and endpoints management. from + * linux/Documentation/devicetree/bindings/graph.txt + * 2 endpoints can be connected together in the device tree. In this case + * an endpoint will have a remote endpoint. + * A pair of connected endpoints can be activated by a driver; when it choose + * to use this path. + * A pair of active endpoints can be enabled; when a driver starts to send + * a signal. Disabling a pair of endpoints can cause appropriate actions + * to save power (stop clocks, disable output buffers, turn on/off power, ...) + */ + +#ifndef _DEV_FDT_FDT_PORT_H_ +#define _DEV_FDT_FDT_PORT_H_ + +struct fdt_port; +struct fdt_endpoint; + +struct fdt_device_ports { + struct fdt_port *dp_port; /* this device's ports */ + int dp_nports; /* number of ports for this device */ + device_t dp_dev; + /* callbacks to device drivers owning endpoints */ + void (*dp_ep_connect)(device_t, struct fdt_endpoint *, bool); + int (*dp_ep_activate)(device_t, struct fdt_endpoint *, bool); + int (*dp_ep_enable)(device_t, struct fdt_endpoint *, bool); + void * (*dp_ep_get_data)(device_t, struct fdt_endpoint *); + SLIST_ENTRY(fdt_device_ports) dp_list; +}; + +enum endpoint_type { + EP_OTHER = 0, + EP_CONNECTOR, + EP_PANEL, +}; + + +/* + * register a device's ports and enpoints into the provided fdt_device_ports. + * when and endpoint is connected to a remote endpoint, dp_ep_connect + * is called for the devices associated to both endpoints + */ +int fdt_ports_register(struct fdt_device_ports *, device_t, + int, enum endpoint_type); + +/* various methods to retrive an enpoint descriptor */ +struct fdt_endpoint *fdt_endpoint_get_from_phandle(int); +struct fdt_endpoint *fdt_endpoint_get_from_index(struct fdt_device_ports *, + int, int); +struct fdt_endpoint *fdt_endpoint_remote(struct fdt_endpoint *); + +/* + * get informations/data for a given endpoint + */ +int fdt_endpoint_port_index(struct fdt_endpoint *); +int fdt_endpoint_index(struct fdt_endpoint *); +device_t fdt_endpoint_device(struct fdt_endpoint *); +bool fdt_endpoint_is_active(struct fdt_endpoint *); +bool fdt_endpoint_is_enabled(struct fdt_endpoint *); +/* + * call dp_ep_get_data() for the endpoint. The returned pointer is + * type of driver-specific. + */ +void * fdt_endpoint_get_data(struct fdt_endpoint *); + +/* + * Activate/deactivate an endpoint. This causes dp_ep_activate() to be + * called for the remote endpoint + */ +int fdt_endpoint_activate(struct fdt_endpoint *, bool); +/* + * Enable/disable an endpoint. This causes dp_ep_enable() to be called for + * the remote endpoint + */ +int fdt_endpoint_enable(struct fdt_endpoint *, bool); + +#endif /* _DEV_FDT_FDT_PORT_H_ */ Index: src/sys/dev/fdt/panel_fdt.c diff -u /dev/null src/sys/dev/fdt/panel_fdt.c:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/panel_fdt.c Tue Apr 3 12:40:20 2018 @@ -0,0 +1,192 @@ +/* $NetBSD: panel_fdt.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * lvds panel driver. + * specified in linux/Documentation/devicetree/bindings/display/panel/ + * Simple RGB panels could be added as well + * registers an endpoint for use by graphic controller drivers + * + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: panel_fdt.c,v 1.1 2018/04/03 12:40:20 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/bus.h> +#include <sys/gpio.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/panel_fdt.h> + +static int fdt_panel_match(device_t, cfdata_t, void *); +static void fdt_panel_attach(device_t, device_t, void *); +static void *fdt_panel_get_data(device_t, struct fdt_endpoint *); +static int fdt_panel_enable(device_t, struct fdt_endpoint *, bool); + +struct fdt_panel_softc { + device_t sc_dev; + int sc_phandle; + struct fdt_panel sc_panel; + struct fdt_device_ports sc_ports; +#define MAX_GPIO_ENABLES 8 + struct fdtbus_gpio_pin *sc_gpios_enable[MAX_GPIO_ENABLES]; +}; + + +CFATTACH_DECL_NEW(fdt_panel, sizeof(struct fdt_panel_softc), + fdt_panel_match, fdt_panel_attach, NULL, NULL); + +static const struct of_compat_data compat_data[] = { + {"panel-lvds", PANEL_LVDS}, + {"panel-dual-lvds", PANEL_DUAL_LVDS}, + { NULL } +}; + +static int +fdt_panel_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +fdt_panel_attach(device_t parent, device_t self, void *aux) +{ + struct fdt_panel_softc *sc = device_private(self); + const struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + const struct display_timing * const timing = + &sc->sc_panel.panel_timing; + int child; + char buf[16]; + const char *val; + int i; + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_panel.panel_type = + of_search_compatible(phandle, compat_data)->data; + + if (of_getprop_uint32(phandle, "width-mm", &sc->sc_panel.panel_width) || + of_getprop_uint32(phandle, "height-mm", &sc->sc_panel.panel_height)){ + aprint_error(": missing width-mm or height-mm properties\n"); + return; + } + for (child = OF_child(phandle); child; child = OF_peer(child)) { + if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) + continue; + if (strcmp(buf, "panel-timing") != 0) + continue; + + if (display_timing_parse(child, + &sc->sc_panel.panel_timing) != 0) { + aprint_error(": failed to parse panel-timing\n"); + return; + } + } + if (sc->sc_panel.panel_timing.clock_freq == 0) { + aprint_error(": missing panel-timing\n"); + return; + } + switch(sc->sc_panel.panel_type) { + case PANEL_LVDS: + case PANEL_DUAL_LVDS: + val = fdtbus_get_string(phandle, "data-mapping"); + if (val == NULL) { + aprint_error(": missing data-mapping\n"); + return; + } + if (strcmp(val, "jeida-18") == 0) + sc->sc_panel.panel_lvds_format = LVDS_JEIDA_18; + else if (strcmp(val, "jeida-24") == 0) + sc->sc_panel.panel_lvds_format = LVDS_JEIDA_24; + else if (strcmp(val, "vesa-24") == 0) + sc->sc_panel.panel_lvds_format = LVDS_VESA_24; + else { + aprint_error(": unkown data-mapping \"%s\"\n", val); + return; + } + break; + default: + panic("unknown panel type %d", sc->sc_panel.panel_type); + } + + aprint_naive("\n"); + aprint_normal(": %dx%d", timing->hactive, timing->vactive); + switch(sc->sc_panel.panel_type) { + case PANEL_LVDS: + aprint_normal(" LVDS"); + break; + case PANEL_DUAL_LVDS: + aprint_normal(" dual-link LVDS"); + break; + default: + panic(" unknown panel type %d", sc->sc_panel.panel_type); + } + aprint_normal(" panel\n"); + + for (i = 0; i < MAX_GPIO_ENABLES ; i++) { + sc->sc_gpios_enable[i] = fdtbus_gpio_acquire_index(phandle, + "enable-gpios", i, GPIO_PIN_OUTPUT); + if (sc->sc_gpios_enable[i] == NULL) + break; + } + + aprint_verbose_dev(self, "%d enable GPIO%c\n", i, i > 1 ? 's' : ' '); + + sc->sc_ports.dp_ep_get_data = fdt_panel_get_data; + sc->sc_ports.dp_ep_enable = fdt_panel_enable; + fdt_ports_register(&sc->sc_ports, self, phandle, EP_PANEL); +} + +static void * +fdt_panel_get_data(device_t dev, struct fdt_endpoint *ep) +{ + struct fdt_panel_softc *sc = device_private(dev); + return &sc->sc_panel; +} + +static int +fdt_panel_enable(device_t dev, struct fdt_endpoint *ep, bool enable) +{ + struct fdt_panel_softc *sc = device_private(dev); + for (int i = 0; i < MAX_GPIO_ENABLES ; i++) { + if (sc->sc_gpios_enable[i] == NULL) + break; + fdtbus_gpio_write(sc->sc_gpios_enable[i], enable); + } + return 0; +} Index: src/sys/dev/fdt/panel_fdt.h diff -u /dev/null src/sys/dev/fdt/panel_fdt.h:1.1 --- /dev/null Tue Apr 3 12:40:20 2018 +++ src/sys/dev/fdt/panel_fdt.h Tue Apr 3 12:40:20 2018 @@ -0,0 +1,62 @@ +/* $NetBSD: panel_fdt.h,v 1.1 2018/04/03 12:40:20 bouyer Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * lvds panels driver. From + * linux/Documentation/devicetree/bindings/display/panel/ + * Simple RGB panels could be added as well + */ + +#include <dev/fdt/fdt_port.h> +#include <dev/fdt/display_timing.h> + +enum panel_type { + PANEL_LVDS = 1, + PANEL_DUAL_LVDS, +}; + +enum lvds_format { + LVDS_JEIDA_18, + LVDS_JEIDA_24, + LVDS_VESA_24 +}; + + +struct fdt_panel { + enum panel_type panel_type; + int panel_width; + int panel_height; + union { + enum lvds_format panel_lvds_format; + } format_u; +#define panel_lvds_format format_u.panel_lvds_format + struct display_timing panel_timing; +};