1*d7c52d51Sriastradh /* $NetBSD: rk_anxdp.c,v 1.6 2021/12/19 12:43:37 riastradh Exp $ */
241be96bfSjakllsch
341be96bfSjakllsch /*-
441be96bfSjakllsch * Copyright (c) 2019 Jonathan A. Kollasch <jakllsch@kollasch.net>
541be96bfSjakllsch * All rights reserved.
641be96bfSjakllsch *
741be96bfSjakllsch * Redistribution and use in source and binary forms, with or without
841be96bfSjakllsch * modification, are permitted provided that the following conditions
941be96bfSjakllsch * are met:
1041be96bfSjakllsch * 1. Redistributions of source code must retain the above copyright
1141be96bfSjakllsch * notice, this list of conditions and the following disclaimer.
1241be96bfSjakllsch * 2. Redistributions in binary form must reproduce the above copyright
1341be96bfSjakllsch * notice, this list of conditions and the following disclaimer in the
1441be96bfSjakllsch * documentation and/or other materials provided with the distribution.
1541be96bfSjakllsch *
1641be96bfSjakllsch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1741be96bfSjakllsch * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1841be96bfSjakllsch * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1941be96bfSjakllsch * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2041be96bfSjakllsch * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2141be96bfSjakllsch * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2241be96bfSjakllsch * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2341be96bfSjakllsch * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2441be96bfSjakllsch * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2541be96bfSjakllsch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2641be96bfSjakllsch * SUCH DAMAGE.
2741be96bfSjakllsch */
2841be96bfSjakllsch
2941be96bfSjakllsch #include <sys/cdefs.h>
30*d7c52d51Sriastradh __KERNEL_RCSID(0, "$NetBSD: rk_anxdp.c,v 1.6 2021/12/19 12:43:37 riastradh Exp $");
3141be96bfSjakllsch
3241be96bfSjakllsch #include <sys/param.h>
3341be96bfSjakllsch #include <sys/bus.h>
3441be96bfSjakllsch #include <sys/device.h>
3541be96bfSjakllsch #include <sys/intr.h>
3641be96bfSjakllsch #include <sys/systm.h>
3741be96bfSjakllsch #include <sys/kernel.h>
3841be96bfSjakllsch #include <sys/conf.h>
3941be96bfSjakllsch
403973e774Sriastradh #include <drm/drm_drv.h>
4141be96bfSjakllsch #include <drm/drm_crtc_helper.h>
4241be96bfSjakllsch
4341be96bfSjakllsch #include <dev/fdt/fdtvar.h>
4441be96bfSjakllsch #include <dev/fdt/fdt_port.h>
4541be96bfSjakllsch #include <dev/fdt/syscon.h>
4641be96bfSjakllsch
4741be96bfSjakllsch #include <dev/ic/anx_dp.h>
4841be96bfSjakllsch
4941be96bfSjakllsch #define RK3399_GRF_SOC_CON20 0x6250
5041be96bfSjakllsch #define EDP_LCDC_SEL __BIT(5)
5141be96bfSjakllsch
5241be96bfSjakllsch enum {
5341be96bfSjakllsch ANXDP_PORT_INPUT = 0,
5441be96bfSjakllsch ANXDP_PORT_OUTPUT = 1,
5541be96bfSjakllsch };
5641be96bfSjakllsch
576e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
586e54367aSthorpej { .compat = "rockchip,rk3399-edp" },
596e54367aSthorpej DEVICE_COMPAT_EOL
6041be96bfSjakllsch };
6141be96bfSjakllsch
6241be96bfSjakllsch struct rk_anxdp_softc {
6341be96bfSjakllsch struct anxdp_softc sc_base;
6441be96bfSjakllsch int sc_phandle;
6541be96bfSjakllsch
6641be96bfSjakllsch struct fdt_device_ports sc_ports;
6741be96bfSjakllsch struct drm_encoder sc_encoder;
6841be96bfSjakllsch struct drm_display_mode sc_curmode;
6941be96bfSjakllsch struct syscon *sc_grf;
7041be96bfSjakllsch
7141be96bfSjakllsch bool sc_activated;
7241be96bfSjakllsch };
7341be96bfSjakllsch
7441be96bfSjakllsch #define to_rk_anxdp_softc(x) container_of(x, struct rk_anxdp_softc, sc_base)
7541be96bfSjakllsch #define to_rk_anxdp_encoder(x) container_of(x, struct rk_anxdp_softc, sc_encoder)
7641be96bfSjakllsch
7741be96bfSjakllsch static void
rk_anxdp_select_input(struct rk_anxdp_softc * sc,u_int crtc_index)7841be96bfSjakllsch rk_anxdp_select_input(struct rk_anxdp_softc *sc, u_int crtc_index)
7941be96bfSjakllsch {
8041be96bfSjakllsch const uint32_t write_mask = EDP_LCDC_SEL << 16;
8141be96bfSjakllsch const uint32_t write_val = crtc_index == 0 ? EDP_LCDC_SEL : 0;
8241be96bfSjakllsch
8341be96bfSjakllsch syscon_lock(sc->sc_grf);
8441be96bfSjakllsch syscon_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val);
8541be96bfSjakllsch syscon_unlock(sc->sc_grf);
8641be96bfSjakllsch }
8741be96bfSjakllsch
8841be96bfSjakllsch static bool
rk_anxdp_encoder_mode_fixup(struct drm_encoder * encoder,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)8941be96bfSjakllsch rk_anxdp_encoder_mode_fixup(struct drm_encoder *encoder,
9041be96bfSjakllsch const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
9141be96bfSjakllsch {
9241be96bfSjakllsch return true;
9341be96bfSjakllsch }
9441be96bfSjakllsch
9541be96bfSjakllsch static void
rk_anxdp_encoder_mode_set(struct drm_encoder * encoder,struct drm_display_mode * mode,struct drm_display_mode * adjusted)9641be96bfSjakllsch rk_anxdp_encoder_mode_set(struct drm_encoder *encoder,
9741be96bfSjakllsch struct drm_display_mode *mode, struct drm_display_mode *adjusted)
9841be96bfSjakllsch {
9941be96bfSjakllsch }
10041be96bfSjakllsch
10141be96bfSjakllsch static void
rk_anxdp_encoder_enable(struct drm_encoder * encoder)10241be96bfSjakllsch rk_anxdp_encoder_enable(struct drm_encoder *encoder)
10341be96bfSjakllsch {
10441be96bfSjakllsch }
10541be96bfSjakllsch
10641be96bfSjakllsch static void
rk_anxdp_encoder_disable(struct drm_encoder * encoder)10741be96bfSjakllsch rk_anxdp_encoder_disable(struct drm_encoder *encoder)
10841be96bfSjakllsch {
10941be96bfSjakllsch }
11041be96bfSjakllsch
11141be96bfSjakllsch static void
rk_anxdp_encoder_prepare(struct drm_encoder * encoder)11241be96bfSjakllsch rk_anxdp_encoder_prepare(struct drm_encoder *encoder)
11341be96bfSjakllsch {
11441be96bfSjakllsch struct rk_anxdp_softc * const sc = to_rk_anxdp_encoder(encoder);
11541be96bfSjakllsch const u_int crtc_index = drm_crtc_index(encoder->crtc);
11641be96bfSjakllsch
11741be96bfSjakllsch rk_anxdp_select_input(sc, crtc_index);
11841be96bfSjakllsch }
11941be96bfSjakllsch
12041be96bfSjakllsch static const struct drm_encoder_funcs rk_anxdp_encoder_funcs = {
12141be96bfSjakllsch .destroy = drm_encoder_cleanup,
12241be96bfSjakllsch };
12341be96bfSjakllsch
12441be96bfSjakllsch static const struct drm_encoder_helper_funcs rk_anxdp_encoder_helper_funcs = {
12541be96bfSjakllsch .prepare = rk_anxdp_encoder_prepare,
12641be96bfSjakllsch .mode_fixup = rk_anxdp_encoder_mode_fixup,
12741be96bfSjakllsch .mode_set = rk_anxdp_encoder_mode_set,
12841be96bfSjakllsch .enable = rk_anxdp_encoder_enable,
12941be96bfSjakllsch .disable = rk_anxdp_encoder_disable,
13041be96bfSjakllsch };
13141be96bfSjakllsch
13241be96bfSjakllsch static int
rk_anxdp_ep_activate(device_t dev,struct fdt_endpoint * ep,bool activate)13341be96bfSjakllsch rk_anxdp_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
13441be96bfSjakllsch {
13541be96bfSjakllsch struct rk_anxdp_softc * const sc = device_private(dev);
13641be96bfSjakllsch struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep);
13741be96bfSjakllsch struct fdt_endpoint *out_ep, *out_rep;
13841be96bfSjakllsch struct drm_crtc *crtc;
13941be96bfSjakllsch int error;
14041be96bfSjakllsch
14141be96bfSjakllsch if (sc->sc_activated != false) {
14241be96bfSjakllsch return 0;
14341be96bfSjakllsch }
14441be96bfSjakllsch
14541be96bfSjakllsch if (!activate)
14641be96bfSjakllsch return EINVAL;
14741be96bfSjakllsch
14841be96bfSjakllsch if (fdt_endpoint_port_index(ep) != ANXDP_PORT_INPUT)
14941be96bfSjakllsch return EINVAL;
15041be96bfSjakllsch
15141be96bfSjakllsch switch (fdt_endpoint_type(in_ep)) {
15241be96bfSjakllsch case EP_DRM_CRTC:
15341be96bfSjakllsch crtc = fdt_endpoint_get_data(in_ep);
15441be96bfSjakllsch break;
15541be96bfSjakllsch default:
15641be96bfSjakllsch return EINVAL;
15741be96bfSjakllsch break;
15841be96bfSjakllsch }
15941be96bfSjakllsch
1606b8843d4Sjmcneill sc->sc_encoder.possible_crtcs = 0x2; /* VOPB only */
16141be96bfSjakllsch drm_encoder_init(crtc->dev, &sc->sc_encoder, &rk_anxdp_encoder_funcs,
1623973e774Sriastradh DRM_MODE_ENCODER_TMDS, NULL);
16341be96bfSjakllsch drm_encoder_helper_add(&sc->sc_encoder, &rk_anxdp_encoder_helper_funcs);
16441be96bfSjakllsch
16541be96bfSjakllsch out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, ANXDP_PORT_OUTPUT, 0);
16641be96bfSjakllsch if (out_ep != NULL) {
16741be96bfSjakllsch out_rep = fdt_endpoint_remote(out_ep);
16841be96bfSjakllsch if (out_rep != NULL && fdt_endpoint_type(out_rep) == EP_DRM_PANEL)
16941be96bfSjakllsch sc->sc_base.sc_panel = fdt_endpoint_get_data(out_rep);
17041be96bfSjakllsch }
17141be96bfSjakllsch
17241be96bfSjakllsch sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP;
17341be96bfSjakllsch error = anxdp_bind(&sc->sc_base, &sc->sc_encoder);
17441be96bfSjakllsch if (error != 0)
17541be96bfSjakllsch return error;
17641be96bfSjakllsch sc->sc_activated = true;
17741be96bfSjakllsch
17841be96bfSjakllsch if (out_ep != NULL) {
17941be96bfSjakllsch /* Ignore downstream connectors, we have our own. */
18041be96bfSjakllsch if (out_rep != NULL && fdt_endpoint_type(out_rep) == EP_DRM_CONNECTOR)
18141be96bfSjakllsch return 0;
18241be96bfSjakllsch error = fdt_endpoint_activate(out_ep, activate);
18341be96bfSjakllsch if (error != 0)
18441be96bfSjakllsch return error;
18541be96bfSjakllsch }
18641be96bfSjakllsch
18741be96bfSjakllsch return 0;
18841be96bfSjakllsch }
18941be96bfSjakllsch
19041be96bfSjakllsch static void *
rk_anxdp_ep_get_data(device_t dev,struct fdt_endpoint * ep)19141be96bfSjakllsch rk_anxdp_ep_get_data(device_t dev, struct fdt_endpoint *ep)
19241be96bfSjakllsch {
19341be96bfSjakllsch struct rk_anxdp_softc * const sc = device_private(dev);
19441be96bfSjakllsch
19541be96bfSjakllsch return &sc->sc_encoder;
19641be96bfSjakllsch }
19741be96bfSjakllsch
19841be96bfSjakllsch #if ANXDP_AUDIO
19941be96bfSjakllsch static audio_dai_tag_t
rk_anxdp_dai_get_tag(device_t dev,const void * data,size_t len)20041be96bfSjakllsch rk_anxdp_dai_get_tag(device_t dev, const void *data, size_t len)
20141be96bfSjakllsch {
20241be96bfSjakllsch struct rk_anxdp_softc * const sc = device_private(dev);
20341be96bfSjakllsch
20441be96bfSjakllsch if (len != 4)
20541be96bfSjakllsch return NULL;
20641be96bfSjakllsch
20741be96bfSjakllsch return &sc->sc_base.sc_dai;
20841be96bfSjakllsch }
20941be96bfSjakllsch
21041be96bfSjakllsch static struct fdtbus_dai_controller_func rk_anxdp_dai_funcs = {
21141be96bfSjakllsch .get_tag = rk_anxdp_dai_get_tag
21241be96bfSjakllsch };
21341be96bfSjakllsch #endif
21441be96bfSjakllsch
21541be96bfSjakllsch static int
rk_anxdp_match(device_t parent,cfdata_t cf,void * aux)21641be96bfSjakllsch rk_anxdp_match(device_t parent, cfdata_t cf, void *aux)
21741be96bfSjakllsch {
21841be96bfSjakllsch struct fdt_attach_args * const faa = aux;
21941be96bfSjakllsch
2206e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
22141be96bfSjakllsch }
22241be96bfSjakllsch
22341be96bfSjakllsch static void
rk_anxdp_attach(device_t parent,device_t self,void * aux)22441be96bfSjakllsch rk_anxdp_attach(device_t parent, device_t self, void *aux)
22541be96bfSjakllsch {
22641be96bfSjakllsch struct rk_anxdp_softc * const sc = device_private(self);
22741be96bfSjakllsch struct fdt_attach_args * const faa = aux;
22841be96bfSjakllsch const int phandle = faa->faa_phandle;
22941be96bfSjakllsch bus_addr_t addr;
23041be96bfSjakllsch bus_size_t size;
23141be96bfSjakllsch
23241be96bfSjakllsch if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
23341be96bfSjakllsch aprint_error(": couldn't get registers\n");
23441be96bfSjakllsch return;
23541be96bfSjakllsch }
23641be96bfSjakllsch
23741be96bfSjakllsch /* Required */
23841be96bfSjakllsch if (fdtbus_clock_enable(phandle, "pclk", true) != 0) {
23941be96bfSjakllsch aprint_error(": couldn't enable pclk clock\n");
24041be96bfSjakllsch return;
24141be96bfSjakllsch }
24241be96bfSjakllsch
24341be96bfSjakllsch /* Required */
24441be96bfSjakllsch if (fdtbus_clock_enable(phandle, "dp", true) != 0) {
24541be96bfSjakllsch aprint_error(": couldn't enable dp clock\n");
24641be96bfSjakllsch return;
24741be96bfSjakllsch }
24841be96bfSjakllsch
24941be96bfSjakllsch /* Optional */
25041be96bfSjakllsch if (fdtbus_clock_enable(phandle, "grf", false) != 0) {
25141be96bfSjakllsch aprint_error(": couldn't enable grf clock\n");
25241be96bfSjakllsch return;
25341be96bfSjakllsch }
25441be96bfSjakllsch
25541be96bfSjakllsch /* TODO: Optional phy */
25641be96bfSjakllsch
25741be96bfSjakllsch sc->sc_base.sc_dev = self;
25841be96bfSjakllsch sc->sc_base.sc_bst = faa->faa_bst;
25941be96bfSjakllsch if (bus_space_map(sc->sc_base.sc_bst, addr, size, 0, &sc->sc_base.sc_bsh) != 0) {
26041be96bfSjakllsch aprint_error(": couldn't map registers\n");
26141be96bfSjakllsch return;
26241be96bfSjakllsch }
26341be96bfSjakllsch sc->sc_phandle = faa->faa_phandle;
26441be96bfSjakllsch sc->sc_grf = fdtbus_syscon_acquire(phandle, "rockchip,grf");
26541be96bfSjakllsch if (sc->sc_grf == NULL) {
26641be96bfSjakllsch aprint_error(": couldn't get grf syscon\n");
26741be96bfSjakllsch return;
26841be96bfSjakllsch }
26941be96bfSjakllsch
27041be96bfSjakllsch aprint_naive("\n");
27141be96bfSjakllsch aprint_normal(": eDP TX\n");
27241be96bfSjakllsch
27341be96bfSjakllsch sc->sc_base.sc_flags |= ANXDP_FLAG_ROCKCHIP;
27441be96bfSjakllsch
27541be96bfSjakllsch if (anxdp_attach(&sc->sc_base) != 0) {
27641be96bfSjakllsch aprint_error_dev(self, "failed to attach driver\n");
27741be96bfSjakllsch return;
27841be96bfSjakllsch }
27941be96bfSjakllsch
28041be96bfSjakllsch sc->sc_ports.dp_ep_activate = rk_anxdp_ep_activate;
28141be96bfSjakllsch sc->sc_ports.dp_ep_get_data = rk_anxdp_ep_get_data;
28241be96bfSjakllsch fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER);
28341be96bfSjakllsch
28441be96bfSjakllsch #if ANXDP_AUDIO
28541be96bfSjakllsch fdtbus_register_dai_controller(self, phandle, &rk_anxdp_dai_funcs);
28641be96bfSjakllsch #endif
28741be96bfSjakllsch }
28841be96bfSjakllsch
28941be96bfSjakllsch CFATTACH_DECL_NEW(rk_anxdp, sizeof(struct rk_anxdp_softc),
29041be96bfSjakllsch rk_anxdp_match, rk_anxdp_attach, NULL, NULL);
291