| Message ID | 20190315130825.9005-6-jagan@amarulasolutions.com |
|---|---|
| State | New |
| Headers |
Return-Path: <linux-amarula+bncBD7MFH7A7EEBB3WHV3SAKGQEMI3LLVA@amarulasolutions.com> X-Original-To: linux-amarula@patchwork.amarulasolutions.com Delivered-To: linux-amarula@patchwork.amarulasolutions.com Received: from mail-pg1-f197.google.com (mail-pg1-f197.google.com [209.85.215.197]) by ganimede.amarulasolutions.com (Postfix) with ESMTPS id BB8753F033 for <linux-amarula@patchwork.amarulasolutions.com>; Fri, 15 Mar 2019 14:09:03 +0100 (CET) Received: by mail-pg1-f197.google.com with SMTP id m17sf10075664pgk.3 for <linux-amarula@patchwork.amarulasolutions.com>; Fri, 15 Mar 2019 06:09:03 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1552655342; cv=pass; d=google.com; s=arc-20160816; b=0nEF0ZlEWwTZ/rbi/wKd7OLLecPEPmk4R5RUHxf0jbfpSUZcGrj4VgOCCu5T1mKNuO wXK7L6Ht4PDgghxM3DrclnquJq6fGQF4EqAs6DrvXygFKH0afOjQ4SZ28GnFJ1A7gLsF M4BLG2HyP5L2dHxkVWT39VP2VIzff+L8ddfmeZ3mGm2qVP1I3hjfurIVAal4tz0Hlwda 8PZqQjDMs3znXS+RBJgWYMLw4Rb+57Ujyavc6FziuCF+KiL/GLxuCv6vs1NtkqxkRE5X HJvVne3B4A5/INOe7Z1ekIA7kF9VQ0bJoKDDk/8KhotsZN5ilZ0waSkxxtaxfuNiFspU o6sQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-archive:list-help:list-post:list-id :mailing-list:precedence:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=DMrmnXaqGTl+jKggjb/wKMGfuSzNVKDeOWRDJm0nMTQ=; b=PmLWWXBVSL7i8hERET7zpnGvIO6lGlyQfkR8upd/g444E5ZwbCLsq6JnqQNMOppf7X msF+beT/TNavYnM3k5dWHsbFnWP1hqEZgwMPu3pK20FLtYPH9al/9ZinOU98+08Xnn3b sV9tMaOAqX+kIKUd7Z3ElfGK6yQpGtqQD6m3qTD6Ms1n+mouadXuffP8/PhFM1dlZsCv bsdgcr1u5bMKFJ+M2ZlJFVhr5t5dZArpxWUIW+AzvwTrnUBUETyqb+ie+zx2mQ+/TbUe t9zPWbjLOQxACynvKf5gGKZ86RC1fxG66aEGQDnEi6+kLlmthSUMSYXnqvvtfFtj+rnr W3yQ== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=dD9jLFn+; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amarulasolutions.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-unsubscribe; bh=DMrmnXaqGTl+jKggjb/wKMGfuSzNVKDeOWRDJm0nMTQ=; b=A8G3Ly0MsiYhLpN8OQwR122cLLhND3Nn1qq8jtTTtFMwUV2WLiYdnJZ6/TRXXMIOkx Ap6ph6DqCmREMmiyJRRFVbFkzROEKtzJcjcyKbMR8FEcRmqwnLvi12fsfiFCM5kNyTJZ 0+z4ohl/10KxJRRY1wLJ0FV1IWt6f3n1AJmvg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-unsubscribe; bh=DMrmnXaqGTl+jKggjb/wKMGfuSzNVKDeOWRDJm0nMTQ=; b=i9Tc1TSYMzT62IekPDyHmPXyESZ+oJS3IqRa7RrLJtiH5yIxJmFD+bbVMnL9aCjtBw pjYiosgDEnin13hDBvJoiA4poS4aXaW7AUEheuG6hlmFRqDy3wYyO6OT3S0SBM589yDh z+c73844ySdysdxC1vikSZp4+/lMNwKJp2EuGs6A0cmETJzyaGNPy50TCjd/g6TIWxbn N5sKEf22cM8kHSGaGL9Rj0L7nR9lpF+5Auqo3rbWO/VnYq7Gng/FUnhr/wRo3Sla010R 8SJzOWweYDd05ld66oM1rNtNtFu7fYD8M90+abD84NmrgABknXUGlbDnP38c+ZZ5XC2S MSmw== X-Gm-Message-State: APjAAAU9p+OPW7z6k5OgnMAQeaUnfGGIAVUJeqPkRR/D1y6D34wkfk8k VgYP0BxbvKfnfIIJoIiyoOU+bvP/ X-Google-Smtp-Source: APXvYqzdG24vF+e3TXTXexn7cpY+hK2nq/yRSYdxStq/6zSHMuo6+yNZXodUZVAPlI/R/cbRtFPuLA== X-Received: by 2002:aa7:8d4d:: with SMTP id s13mr1344375pfe.92.1552655342524; Fri, 15 Mar 2019 06:09:02 -0700 (PDT) X-BeenThere: linux-amarula@amarulasolutions.com Received: by 2002:a63:cb48:: with SMTP id m8ls3542443pgi.12.gmail; Fri, 15 Mar 2019 06:09:02 -0700 (PDT) X-Received: by 2002:a65:4147:: with SMTP id x7mr3434867pgp.54.1552655342078; Fri, 15 Mar 2019 06:09:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1552655342; cv=none; d=google.com; s=arc-20160816; b=Nbm8esPvbiCHnipX+/5ym0KA6mT6TOiBKmCDd8DU6FwmRqZwykJHxpcgVK4MhFKszM MxoMGv0x63knK6AaOKtMR1GD05K5O8ZZJXIzyNZ65XY/3MAkHl+tud6VwDtXMjIuxQMC etqQ1MuDKG9MIrGD8BXZrIbOm5Wy+FmH2VvaHWhMRb7bBVFXCWT8hfQDswdiwNB+G0r+ sq9irIRoFn34MxYw83PDtQg5nD56pUBdAXbOBn32LJhVZ5aPq02kzJhvilLEJu23bI4o fH+IZ0zFfpa/XfhNHd4E/CAcDZ7pPTb7IM1oyBBmNUMLLAg2JLPL3ARR3FzK/is61XMC tSeA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=l7jDKekA1zKlo/xV9xtxEUytM5+wSMRdUwycfkyRP+g=; b=tUU8qTN19rLWiUfuT5e2S/pQb4CobGOhGCLwn0Cu0qcjMj2Va/zQRJit+7yQpdNUYt nHO/+97UMiQBAcujBAQ6HNtw7CPKPr0Lq+u3kpYmmV+yuC07cgngSAZQ8LBsC5Tsz0Q9 MjeH4WacaSMD8J6wDfa5rC1Gd1v+47ZkHGyz+QV0U6xFhunk89TWXkPxlmHrPUMrlaCP t4YCKgF2Bkdxun/vegmKjO3fMqD7x3rpmFISXV3wiYEUISKzjShKQrrnGbRdNrRUcxp9 dr2iTUBSnOd4WwHSbu2ZoKjMtH9ndT7RkHgvOTyg1pxqNXPNDoLyUTs6sr+DJhVpRzqJ C+3g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=dD9jLFn+; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com Received: from mail-sor-f65.google.com (mail-sor-f65.google.com. [209.85.220.65]) by mx.google.com with SMTPS id d13sor2919369pga.42.2019.03.15.06.09.02 for <linux-amarula@amarulasolutions.com> (Google Transport Security); Fri, 15 Mar 2019 06:09:02 -0700 (PDT) Received-SPF: pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) client-ip=209.85.220.65; X-Received: by 2002:a63:e40b:: with SMTP id a11mr3347314pgi.259.1552655341691; Fri, 15 Mar 2019 06:09:01 -0700 (PDT) Received: from localhost.localdomain ([183.82.230.185]) by smtp.gmail.com with ESMTPSA id e2sm854008pfa.64.2019.03.15.06.08.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 15 Mar 2019 06:09:01 -0700 (PDT) From: Jagan Teki <jagan@amarulasolutions.com> To: Andrzej Hajda <a.hajda@samsung.com>, Laurent Pinchart <Laurent.pinchart@ideasonboard.com>, Chen-Yu Tsai <wens@csie.org>, Maxime Ripard <maxime.ripard@bootlin.com>, David Airlie <airlied@linux.ie>, Daniel Vetter <daniel@ffwll.ch>, Rob Herring <robh+dt@kernel.org>, Mark Rutland <mark.rutland@arm.com> Cc: Michael Trimarchi <michael@amarulasolutions.com>, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@googlegroups.com, linux-amarula@amarulasolutions.com, Jagan Teki <jagan@amarulasolutions.com> Subject: [PATCH 5/6] drm/bridge: Add Chipone ICN6211 MIPI-DSI/RGB convertor bridge Date: Fri, 15 Mar 2019 18:38:24 +0530 Message-Id: <20190315130825.9005-6-jagan@amarulasolutions.com> X-Mailer: git-send-email 2.18.0.321.gffc6fa0e3 In-Reply-To: <20190315130825.9005-1-jagan@amarulasolutions.com> References: <20190315130825.9005-1-jagan@amarulasolutions.com> MIME-Version: 1.0 X-Original-Sender: jagan@amarulasolutions.com X-Original-Authentication-Results: mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=dD9jLFn+; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com Content-Type: text/plain; charset="UTF-8" Precedence: list Mailing-list: list linux-amarula@amarulasolutions.com; contact linux-amarula+owners@amarulasolutions.com List-ID: <linux-amarula.amarulasolutions.com> X-Spam-Checked-In-Group: linux-amarula@amarulasolutions.com X-Google-Group-Id: 476853432473 List-Post: <https://groups.google.com/a/amarulasolutions.com/group/linux-amarula/post>, <mailto:linux-amarula@amarulasolutions.com> List-Help: <https://support.google.com/a/amarulasolutions.com/bin/topic.py?topic=25838>, <mailto:linux-amarula+help@amarulasolutions.com> List-Archive: <https://groups.google.com/a/amarulasolutions.com/group/linux-amarula/> List-Unsubscribe: <mailto:googlegroups-manage+476853432473+unsubscribe@googlegroups.com>, <https://groups.google.com/a/amarulasolutions.com/group/linux-amarula/subscribe> |
| Series |
drm/bridge: Add ICN6211 MIPI-DSI/RGB bridge
|
|
Commit Message
Jagan Teki
March 15, 2019, 1:08 p.m. UTC
ICN6211 is MIPI-DSI/RGB converter bridge from chipone.
It has a flexible configuration of MIPI DSI signal input
and produce RGB565, RGB666, RGB888 output format.
Add bridge driver for it.
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
MAINTAINERS | 6 +
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/chipone-icn6211.c | 275 +++++++++++++++++++++++
4 files changed, 292 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c
Comments
On Fri, Mar 15, 2019 at 06:38:24PM +0530, Jagan Teki wrote: > ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > It has a flexible configuration of MIPI DSI signal input > and produce RGB565, RGB666, RGB888 output format. > > Add bridge driver for it. > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com> > --- > MAINTAINERS | 6 + > drivers/gpu/drm/bridge/Kconfig | 10 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/chipone-icn6211.c | 275 +++++++++++++++++++++++ > 4 files changed, 292 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 4de80222cffb..e529addd30f5 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4897,6 +4897,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc > S: Maintained > F: drivers/gpu/drm/bochs/ > > +DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE > +M: Jagan Teki <jagan@amarulasolutions.com> > +S: Maintained > +F: drivers/gpu/drm/bridge/chipone-icn6211.c > +F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt > + > DRM DRIVER FOR FARADAY TVE200 TV ENCODER > M: Linus Walleij <linus.walleij@linaro.org> > T: git git://anongit.freedesktop.org/drm/drm-misc > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index 8840f396a7b6..cd314572e4ed 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -36,6 +36,16 @@ config DRM_CDNS_DSI > Support Cadence DPI to DSI bridge. This is an internal > bridge and is meant to be directly embedded in a SoC. > > +config DRM_CHIPONE_ICN6211 > + tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge" > + depends on DRM && DRM_PANEL > + depends on OF > + select DRM_MIPI_DSI > + help > + ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > + It has a flexible configuration of MIPI DSI signal input > + and produce RGB565, RGB666, RGB888 output format. > + > config DRM_DUMB_VGA_DAC > tristate "Dumb VGA DAC Bridge support" > depends on OF > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index 4934fcf5a6f8..541fdccad10b 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -1,6 +1,7 @@ > # SPDX-License-Identifier: GPL-2.0 > obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o > +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o > obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o > obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o > obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o > diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c > new file mode 100644 > index 000000000000..cd2f3505f845 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c > @@ -0,0 +1,275 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2018 Amarula Solutions > + * Author: Jagan Teki <jagan@amarulasolutions.com> > + */ > + > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_fb_helper.h> > +#include <drm/drm_of.h> > +#include <drm/drm_panel.h> > +#include <drm/drm_print.h> > +#include <drm/drm_probe_helper.h> > +#include <drm/drm_mipi_dsi.h> > + > +#include <linux/delay.h> > +#include <linux/gpio/consumer.h> > +#include <linux/module.h> > +#include <linux/of_graph.h> > + > +#define ICN6211_INIT_CMD_LEN 2 > + > +struct chipone { > + struct device *dev; > + struct drm_bridge bridge; > + struct drm_connector connector; > + struct drm_panel *panel; > + > + struct gpio_desc *reset; > +}; > + > +static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) > +{ > + return container_of(bridge, struct chipone, bridge); > +} > + > +static inline > +struct chipone *connector_to_chipone(struct drm_connector *connector) > +{ > + return container_of(connector, struct chipone, connector); > +} > + > +struct icn6211_init_cmd { > + u8 data[ICN6211_INIT_CMD_LEN]; > +}; > + > +static const struct icn6211_init_cmd icn6211_init_cmds[] = { > + { .data = { 0x7A, 0xC1 } }, > + { .data = { 0x20, 0x20 } }, > + { .data = { 0x21, 0xE0 } }, > + { .data = { 0x22, 0x13 } }, > + { .data = { 0x23, 0x28 } }, > + { .data = { 0x24, 0x30 } }, > + { .data = { 0x25, 0x28 } }, > + { .data = { 0x26, 0x00 } }, > + { .data = { 0x27, 0x0D } }, > + { .data = { 0x28, 0x03 } }, > + { .data = { 0x29, 0x1D } }, > + { .data = { 0x34, 0x80 } }, > + { .data = { 0x36, 0x28 } }, > + { .data = { 0xB5, 0xA0 } }, > + { .data = { 0x5C, 0xFF } }, > + { .data = { 0x2A, 0x01 } }, > + { .data = { 0x56, 0x92 } }, > + { .data = { 0x6B, 0x71 } }, > + { .data = { 0x69, 0x2B } }, > + { .data = { 0x10, 0x40 } }, > + { .data = { 0x11, 0x98 } }, > + { .data = { 0xB6, 0x20 } }, > + { .data = { 0x51, 0x20 } }, > + { .data = { 0x09, 0x10 } }, > +}; What are those commands supposed to be? Some of them at least look pretty standard (and have proper functions): MIPI_DCS_EXIT_INVERT_MODE, _SET_DISPLAY_ON, _SET_TEAR_OFF, etc. > +static int chipone_get_modes(struct drm_connector *connector) > +{ > + struct chipone *icn = connector_to_chipone(connector); > + > + return drm_panel_get_modes(icn->panel); > +} > + > +static const > +struct drm_connector_helper_funcs chipone_connector_helper_funcs = { > + .get_modes = chipone_get_modes, > +}; > + > +static const struct drm_connector_funcs chipone_connector_funcs = { > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +static void chipone_disable(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + int ret = drm_panel_disable(bridge_to_chipone(bridge)->panel); > + > + if (ret < 0) > + DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret); > +} > + > +static void chipone_post_disable(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + int ret; > + > + ret = drm_panel_unprepare(icn->panel); > + if (ret < 0) > + DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret); > + > + msleep(50); > + > + gpiod_set_value(icn->reset, 0); > +} > + > +static void chipone_pre_enable(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev); > + unsigned int i; > + int ret; > + > + gpiod_set_value(icn->reset, 0); > + msleep(20); > + > + gpiod_set_value(icn->reset, 1); > + msleep(50); > + > + for (i = 0; i < ARRAY_SIZE(icn6211_init_cmds); i++) { > + const struct icn6211_init_cmd *cmd = &icn6211_init_cmds[i]; > + > + ret = mipi_dsi_generic_write(dsi, cmd->data, > + ICN6211_INIT_CMD_LEN); > + if (ret < 0) { > + DRM_DEV_ERROR(icn->dev, > + "failed to write cmd %d: %d\n", i, ret); > + return; > + } > + } > + > + ret = drm_panel_prepare(icn->panel); > + if (ret < 0) > + DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret); > +} > + > +static void chipone_enable(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + int ret = drm_panel_enable(icn->panel); > + > + if (ret < 0) > + DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret); > +} > + > +static int chipone_attach(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + struct drm_device *drm = bridge->dev; > + int ret; > + > + icn->connector.polled = DRM_CONNECTOR_POLL_HPD; > + ret = drm_connector_init(drm, &icn->connector, > + &chipone_connector_funcs, > + DRM_MODE_CONNECTOR_Unknown); It should be a DRM_MODE_CONNECTOR_DPI connector. > + if (ret) { > + DRM_ERROR("Failed to initialize connector\n"); > + return ret; > + } > + > + drm_connector_helper_add(&icn->connector, > + &chipone_connector_helper_funcs); > + drm_connector_attach_encoder(&icn->connector, bridge->encoder); > + drm_panel_attach(icn->panel, &icn->connector); > + icn->connector.funcs->reset(&icn->connector); > + drm_fb_helper_add_one_connector(drm->fb_helper, &icn->connector); What do you need that for? > + drm_connector_register(&icn->connector); > + > + return 0; > +} > + > +static void chipone_detach(struct drm_bridge *bridge) > +{ > + struct chipone *icn = bridge_to_chipone(bridge); > + struct drm_device *drm = bridge->dev; > + > + drm_connector_unregister(&icn->connector); > + drm_fb_helper_remove_one_connector(drm->fb_helper, &icn->connector); > + drm_panel_detach(icn->panel); > + icn->panel = NULL; > + drm_connector_put(&icn->connector); > +} > + > +static const struct drm_bridge_funcs chipone_bridge_funcs = { > + .disable = chipone_disable, > + .post_disable = chipone_post_disable, > + .enable = chipone_enable, > + .pre_enable = chipone_pre_enable, > + .attach = chipone_attach, > + .detach = chipone_detach, > +}; > + > +static int chipone_probe(struct mipi_dsi_device *dsi) > +{ > + struct device *dev = &dsi->dev; > + struct chipone *icn; > + int ret; > + > + icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); > + if (!icn) > + return -ENOMEM; > + > + mipi_dsi_set_drvdata(dsi, icn); > + > + icn->dev = dev; > + dsi->lanes = 4; > + dsi->format = MIPI_DSI_FMT_RGB888; > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; > + > + icn->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > + if (IS_ERR(icn->reset)) { > + DRM_DEV_ERROR(dev, "no reset GPIO pin provided\n"); > + return PTR_ERR(icn->reset); > + } > + > + ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0, > + &icn->panel, NULL); > + if (ret && ret != -EPROBE_DEFER) { > + DRM_DEV_ERROR(dev, "failed to find panel (ret = %d)\n", ret); > + return ret; > + } > + > + icn->bridge.funcs = &chipone_bridge_funcs; > + icn->bridge.of_node = dev->of_node; > + > + drm_bridge_add(&icn->bridge); > + > + ret = mipi_dsi_attach(dsi); > + if (ret < 0) { > + drm_bridge_remove(&icn->bridge); > + DRM_DEV_ERROR(dev, "failed to attach dsi (ret = %d)\n", ret); > + } > + > + return ret; > +} > + > +static int chipone_remove(struct mipi_dsi_device *dsi) > +{ > + struct chipone *icn = mipi_dsi_get_drvdata(dsi); > + > + mipi_dsi_detach(dsi); > + drm_bridge_remove(&icn->bridge); > + > + return 0; > +} > + > +static const struct of_device_id chipone_of_match[] = { > + { .compatible = "bananapi,icn6211" }, This should have your generic compatible listed Maxime
Hello, On Fri, Mar 15, 2019 at 02:33:04PM +0100, Maxime Ripard wrote: > On Fri, Mar 15, 2019 at 06:38:24PM +0530, Jagan Teki wrote: > > ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > It has a flexible configuration of MIPI DSI signal input > > and produce RGB565, RGB666, RGB888 output format. > > > > Add bridge driver for it. > > > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com> > > --- > > MAINTAINERS | 6 + > > drivers/gpu/drm/bridge/Kconfig | 10 + > > drivers/gpu/drm/bridge/Makefile | 1 + > > drivers/gpu/drm/bridge/chipone-icn6211.c | 275 +++++++++++++++++++++++ > > 4 files changed, 292 insertions(+) > > create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 4de80222cffb..e529addd30f5 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -4897,6 +4897,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc > > S: Maintained > > F: drivers/gpu/drm/bochs/ > > > > +DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE > > +M: Jagan Teki <jagan@amarulasolutions.com> > > +S: Maintained > > +F: drivers/gpu/drm/bridge/chipone-icn6211.c > > +F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt > > + > > DRM DRIVER FOR FARADAY TVE200 TV ENCODER > > M: Linus Walleij <linus.walleij@linaro.org> > > T: git git://anongit.freedesktop.org/drm/drm-misc > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > > index 8840f396a7b6..cd314572e4ed 100644 > > --- a/drivers/gpu/drm/bridge/Kconfig > > +++ b/drivers/gpu/drm/bridge/Kconfig > > @@ -36,6 +36,16 @@ config DRM_CDNS_DSI > > Support Cadence DPI to DSI bridge. This is an internal > > bridge and is meant to be directly embedded in a SoC. > > > > +config DRM_CHIPONE_ICN6211 > > + tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge" > > + depends on DRM && DRM_PANEL > > + depends on OF > > + select DRM_MIPI_DSI > > + help > > + ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > + It has a flexible configuration of MIPI DSI signal input > > + and produce RGB565, RGB666, RGB888 output format. > > + > > config DRM_DUMB_VGA_DAC > > tristate "Dumb VGA DAC Bridge support" > > depends on OF > > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > > index 4934fcf5a6f8..541fdccad10b 100644 > > --- a/drivers/gpu/drm/bridge/Makefile > > +++ b/drivers/gpu/drm/bridge/Makefile > > @@ -1,6 +1,7 @@ > > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o > > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o > > +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o > > obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o > > obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o > > obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o > > diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c > > new file mode 100644 > > index 000000000000..cd2f3505f845 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c > > @@ -0,0 +1,275 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * Copyright (C) 2018 Amarula Solutions > > + * Author: Jagan Teki <jagan@amarulasolutions.com> > > + */ > > + > > +#include <drm/drm_atomic_helper.h> > > +#include <drm/drm_fb_helper.h> > > +#include <drm/drm_of.h> > > +#include <drm/drm_panel.h> > > +#include <drm/drm_print.h> > > +#include <drm/drm_probe_helper.h> > > +#include <drm/drm_mipi_dsi.h> > > + > > +#include <linux/delay.h> > > +#include <linux/gpio/consumer.h> > > +#include <linux/module.h> > > +#include <linux/of_graph.h> > > + > > +#define ICN6211_INIT_CMD_LEN 2 > > + > > +struct chipone { > > + struct device *dev; > > + struct drm_bridge bridge; > > + struct drm_connector connector; > > + struct drm_panel *panel; > > + > > + struct gpio_desc *reset; > > +}; > > + > > +static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) > > +{ > > + return container_of(bridge, struct chipone, bridge); > > +} > > + > > +static inline > > +struct chipone *connector_to_chipone(struct drm_connector *connector) > > +{ > > + return container_of(connector, struct chipone, connector); > > +} > > + > > +struct icn6211_init_cmd { > > + u8 data[ICN6211_INIT_CMD_LEN]; > > +}; > > + > > +static const struct icn6211_init_cmd icn6211_init_cmds[] = { > > + { .data = { 0x7A, 0xC1 } }, > > + { .data = { 0x20, 0x20 } }, > > + { .data = { 0x21, 0xE0 } }, > > + { .data = { 0x22, 0x13 } }, > > + { .data = { 0x23, 0x28 } }, > > + { .data = { 0x24, 0x30 } }, > > + { .data = { 0x25, 0x28 } }, > > + { .data = { 0x26, 0x00 } }, > > + { .data = { 0x27, 0x0D } }, > > + { .data = { 0x28, 0x03 } }, > > + { .data = { 0x29, 0x1D } }, > > + { .data = { 0x34, 0x80 } }, > > + { .data = { 0x36, 0x28 } }, > > + { .data = { 0xB5, 0xA0 } }, > > + { .data = { 0x5C, 0xFF } }, > > + { .data = { 0x2A, 0x01 } }, > > + { .data = { 0x56, 0x92 } }, > > + { .data = { 0x6B, 0x71 } }, > > + { .data = { 0x69, 0x2B } }, > > + { .data = { 0x10, 0x40 } }, > > + { .data = { 0x11, 0x98 } }, > > + { .data = { 0xB6, 0x20 } }, > > + { .data = { 0x51, 0x20 } }, > > + { .data = { 0x09, 0x10 } }, > > +}; > > What are those commands supposed to be? Some of them at least look > pretty standard (and have proper functions): > MIPI_DCS_EXIT_INVERT_MODE, _SET_DISPLAY_ON, _SET_TEAR_OFF, etc. > > > +static int chipone_get_modes(struct drm_connector *connector) > > +{ > > + struct chipone *icn = connector_to_chipone(connector); > > + > > + return drm_panel_get_modes(icn->panel); > > +} > > + > > +static const > > +struct drm_connector_helper_funcs chipone_connector_helper_funcs = { > > + .get_modes = chipone_get_modes, > > +}; > > + > > +static const struct drm_connector_funcs chipone_connector_funcs = { > > + .fill_modes = drm_helper_probe_single_connector_modes, > > + .destroy = drm_connector_cleanup, > > + .reset = drm_atomic_helper_connector_reset, > > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > > +}; > > + > > +static void chipone_disable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret = drm_panel_disable(bridge_to_chipone(bridge)->panel); > > + > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret); > > +} > > + > > +static void chipone_post_disable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret; > > + > > + ret = drm_panel_unprepare(icn->panel); > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret); > > + > > + msleep(50); > > + > > + gpiod_set_value(icn->reset, 0); > > +} > > + > > +static void chipone_pre_enable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev); > > + unsigned int i; > > + int ret; > > + > > + gpiod_set_value(icn->reset, 0); > > + msleep(20); > > + > > + gpiod_set_value(icn->reset, 1); > > + msleep(50); > > + > > + for (i = 0; i < ARRAY_SIZE(icn6211_init_cmds); i++) { > > + const struct icn6211_init_cmd *cmd = &icn6211_init_cmds[i]; > > + > > + ret = mipi_dsi_generic_write(dsi, cmd->data, > > + ICN6211_INIT_CMD_LEN); > > + if (ret < 0) { > > + DRM_DEV_ERROR(icn->dev, > > + "failed to write cmd %d: %d\n", i, ret); > > + return; > > + } > > + } > > + > > + ret = drm_panel_prepare(icn->panel); > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret); > > +} > > + > > +static void chipone_enable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret = drm_panel_enable(icn->panel); > > + > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret); > > +} > > + > > +static int chipone_attach(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct drm_device *drm = bridge->dev; > > + int ret; > > + > > + icn->connector.polled = DRM_CONNECTOR_POLL_HPD; > > + ret = drm_connector_init(drm, &icn->connector, > > + &chipone_connector_funcs, > > + DRM_MODE_CONNECTOR_Unknown); > > It should be a DRM_MODE_CONNECTOR_DPI connector. I'd go one step further, the driver should use drm_panel_bridge_add() and not create a connector itself. It should also support the case where the RGB output is connected to a bridge instead of a panel. > > + if (ret) { > > + DRM_ERROR("Failed to initialize connector\n"); > > + return ret; > > + } > > + > > + drm_connector_helper_add(&icn->connector, > > + &chipone_connector_helper_funcs); > > + drm_connector_attach_encoder(&icn->connector, bridge->encoder); > > + drm_panel_attach(icn->panel, &icn->connector); > > + icn->connector.funcs->reset(&icn->connector); > > + drm_fb_helper_add_one_connector(drm->fb_helper, &icn->connector); > > What do you need that for? > > > + drm_connector_register(&icn->connector); > > + > > + return 0; > > +} > > + > > +static void chipone_detach(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct drm_device *drm = bridge->dev; > > + > > + drm_connector_unregister(&icn->connector); > > + drm_fb_helper_remove_one_connector(drm->fb_helper, &icn->connector); > > + drm_panel_detach(icn->panel); > > + icn->panel = NULL; > > + drm_connector_put(&icn->connector); > > +} > > + > > +static const struct drm_bridge_funcs chipone_bridge_funcs = { > > + .disable = chipone_disable, > > + .post_disable = chipone_post_disable, > > + .enable = chipone_enable, > > + .pre_enable = chipone_pre_enable, > > + .attach = chipone_attach, > > + .detach = chipone_detach, > > +}; > > + > > +static int chipone_probe(struct mipi_dsi_device *dsi) > > +{ > > + struct device *dev = &dsi->dev; > > + struct chipone *icn; > > + int ret; > > + > > + icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); > > + if (!icn) > > + return -ENOMEM; > > + > > + mipi_dsi_set_drvdata(dsi, icn); > > + > > + icn->dev = dev; > > + dsi->lanes = 4; > > + dsi->format = MIPI_DSI_FMT_RGB888; > > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; > > + > > + icn->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > > + if (IS_ERR(icn->reset)) { > > + DRM_DEV_ERROR(dev, "no reset GPIO pin provided\n"); > > + return PTR_ERR(icn->reset); > > + } > > + > > + ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0, > > + &icn->panel, NULL); > > + if (ret && ret != -EPROBE_DEFER) { > > + DRM_DEV_ERROR(dev, "failed to find panel (ret = %d)\n", ret); > > + return ret; > > + } > > + > > + icn->bridge.funcs = &chipone_bridge_funcs; > > + icn->bridge.of_node = dev->of_node; > > + > > + drm_bridge_add(&icn->bridge); > > + > > + ret = mipi_dsi_attach(dsi); > > + if (ret < 0) { > > + drm_bridge_remove(&icn->bridge); > > + DRM_DEV_ERROR(dev, "failed to attach dsi (ret = %d)\n", ret); > > + } > > + > > + return ret; > > +} > > + > > +static int chipone_remove(struct mipi_dsi_device *dsi) > > +{ > > + struct chipone *icn = mipi_dsi_get_drvdata(dsi); > > + > > + mipi_dsi_detach(dsi); > > + drm_bridge_remove(&icn->bridge); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id chipone_of_match[] = { > > + { .compatible = "bananapi,icn6211" }, > > This should have your generic compatible listed
On Fri, Mar 15, 2019 at 7:03 PM Maxime Ripard <maxime.ripard@bootlin.com> wrote: > > On Fri, Mar 15, 2019 at 06:38:24PM +0530, Jagan Teki wrote: > > ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > It has a flexible configuration of MIPI DSI signal input > > and produce RGB565, RGB666, RGB888 output format. > > > > Add bridge driver for it. > > > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com> > > --- > > MAINTAINERS | 6 + > > drivers/gpu/drm/bridge/Kconfig | 10 + > > drivers/gpu/drm/bridge/Makefile | 1 + > > drivers/gpu/drm/bridge/chipone-icn6211.c | 275 +++++++++++++++++++++++ > > 4 files changed, 292 insertions(+) > > create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 4de80222cffb..e529addd30f5 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -4897,6 +4897,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc > > S: Maintained > > F: drivers/gpu/drm/bochs/ > > > > +DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE > > +M: Jagan Teki <jagan@amarulasolutions.com> > > +S: Maintained > > +F: drivers/gpu/drm/bridge/chipone-icn6211.c > > +F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt > > + > > DRM DRIVER FOR FARADAY TVE200 TV ENCODER > > M: Linus Walleij <linus.walleij@linaro.org> > > T: git git://anongit.freedesktop.org/drm/drm-misc > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > > index 8840f396a7b6..cd314572e4ed 100644 > > --- a/drivers/gpu/drm/bridge/Kconfig > > +++ b/drivers/gpu/drm/bridge/Kconfig > > @@ -36,6 +36,16 @@ config DRM_CDNS_DSI > > Support Cadence DPI to DSI bridge. This is an internal > > bridge and is meant to be directly embedded in a SoC. > > > > +config DRM_CHIPONE_ICN6211 > > + tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge" > > + depends on DRM && DRM_PANEL > > + depends on OF > > + select DRM_MIPI_DSI > > + help > > + ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > + It has a flexible configuration of MIPI DSI signal input > > + and produce RGB565, RGB666, RGB888 output format. > > + > > config DRM_DUMB_VGA_DAC > > tristate "Dumb VGA DAC Bridge support" > > depends on OF > > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > > index 4934fcf5a6f8..541fdccad10b 100644 > > --- a/drivers/gpu/drm/bridge/Makefile > > +++ b/drivers/gpu/drm/bridge/Makefile > > @@ -1,6 +1,7 @@ > > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o > > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o > > +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o > > obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o > > obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o > > obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o > > diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c > > new file mode 100644 > > index 000000000000..cd2f3505f845 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c > > @@ -0,0 +1,275 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +/* > > + * Copyright (C) 2018 Amarula Solutions > > + * Author: Jagan Teki <jagan@amarulasolutions.com> > > + */ > > + > > +#include <drm/drm_atomic_helper.h> > > +#include <drm/drm_fb_helper.h> > > +#include <drm/drm_of.h> > > +#include <drm/drm_panel.h> > > +#include <drm/drm_print.h> > > +#include <drm/drm_probe_helper.h> > > +#include <drm/drm_mipi_dsi.h> > > + > > +#include <linux/delay.h> > > +#include <linux/gpio/consumer.h> > > +#include <linux/module.h> > > +#include <linux/of_graph.h> > > + > > +#define ICN6211_INIT_CMD_LEN 2 > > + > > +struct chipone { > > + struct device *dev; > > + struct drm_bridge bridge; > > + struct drm_connector connector; > > + struct drm_panel *panel; > > + > > + struct gpio_desc *reset; > > +}; > > + > > +static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) > > +{ > > + return container_of(bridge, struct chipone, bridge); > > +} > > + > > +static inline > > +struct chipone *connector_to_chipone(struct drm_connector *connector) > > +{ > > + return container_of(connector, struct chipone, connector); > > +} > > + > > +struct icn6211_init_cmd { > > + u8 data[ICN6211_INIT_CMD_LEN]; > > +}; > > + > > +static const struct icn6211_init_cmd icn6211_init_cmds[] = { > > + { .data = { 0x7A, 0xC1 } }, > > + { .data = { 0x20, 0x20 } }, > > + { .data = { 0x21, 0xE0 } }, > > + { .data = { 0x22, 0x13 } }, > > + { .data = { 0x23, 0x28 } }, > > + { .data = { 0x24, 0x30 } }, > > + { .data = { 0x25, 0x28 } }, > > + { .data = { 0x26, 0x00 } }, > > + { .data = { 0x27, 0x0D } }, > > + { .data = { 0x28, 0x03 } }, > > + { .data = { 0x29, 0x1D } }, > > + { .data = { 0x34, 0x80 } }, > > + { .data = { 0x36, 0x28 } }, > > + { .data = { 0xB5, 0xA0 } }, > > + { .data = { 0x5C, 0xFF } }, > > + { .data = { 0x2A, 0x01 } }, > > + { .data = { 0x56, 0x92 } }, > > + { .data = { 0x6B, 0x71 } }, > > + { .data = { 0x69, 0x2B } }, > > + { .data = { 0x10, 0x40 } }, > > + { .data = { 0x11, 0x98 } }, > > + { .data = { 0xB6, 0x20 } }, > > + { .data = { 0x51, 0x20 } }, > > + { .data = { 0x09, 0x10 } }, > > +}; > > What are those commands supposed to be? Some of them at least look > pretty standard (and have proper functions): > MIPI_DCS_EXIT_INVERT_MODE, _SET_DISPLAY_ON, _SET_TEAR_OFF, etc. As I said in previous Allwinner MIPI-DSI fixes and new code support series. For this bridge we don't have programming guide or any datasheet. The initialization sequence is taken from BSP panel driver and analyzed based on other references mentioned here [1]. Some of the command sequence value are based on the timings other based on standard MIPI commands as you said. Do you need to add comments to describe the same or some other changes? > > > +static int chipone_get_modes(struct drm_connector *connector) > > +{ > > + struct chipone *icn = connector_to_chipone(connector); > > + > > + return drm_panel_get_modes(icn->panel); > > +} > > + > > +static const > > +struct drm_connector_helper_funcs chipone_connector_helper_funcs = { > > + .get_modes = chipone_get_modes, > > +}; > > + > > +static const struct drm_connector_funcs chipone_connector_funcs = { > > + .fill_modes = drm_helper_probe_single_connector_modes, > > + .destroy = drm_connector_cleanup, > > + .reset = drm_atomic_helper_connector_reset, > > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > > +}; > > + > > +static void chipone_disable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret = drm_panel_disable(bridge_to_chipone(bridge)->panel); > > + > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret); > > +} > > + > > +static void chipone_post_disable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret; > > + > > + ret = drm_panel_unprepare(icn->panel); > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret); > > + > > + msleep(50); > > + > > + gpiod_set_value(icn->reset, 0); > > +} > > + > > +static void chipone_pre_enable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev); > > + unsigned int i; > > + int ret; > > + > > + gpiod_set_value(icn->reset, 0); > > + msleep(20); > > + > > + gpiod_set_value(icn->reset, 1); > > + msleep(50); > > + > > + for (i = 0; i < ARRAY_SIZE(icn6211_init_cmds); i++) { > > + const struct icn6211_init_cmd *cmd = &icn6211_init_cmds[i]; > > + > > + ret = mipi_dsi_generic_write(dsi, cmd->data, > > + ICN6211_INIT_CMD_LEN); > > + if (ret < 0) { > > + DRM_DEV_ERROR(icn->dev, > > + "failed to write cmd %d: %d\n", i, ret); > > + return; > > + } > > + } > > + > > + ret = drm_panel_prepare(icn->panel); > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret); > > +} > > + > > +static void chipone_enable(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + int ret = drm_panel_enable(icn->panel); > > + > > + if (ret < 0) > > + DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret); > > +} > > + > > +static int chipone_attach(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct drm_device *drm = bridge->dev; > > + int ret; > > + > > + icn->connector.polled = DRM_CONNECTOR_POLL_HPD; > > + ret = drm_connector_init(drm, &icn->connector, > > + &chipone_connector_funcs, > > + DRM_MODE_CONNECTOR_Unknown); > > It should be a DRM_MODE_CONNECTOR_DPI connector. Since the output connector is RGB, I have used 'Unknown' since we follow similar connector type in sun4i_rgb.c So these RGB comes under parallel display interface we can have to use this DPI connector is it? > > > + if (ret) { > > + DRM_ERROR("Failed to initialize connector\n"); > > + return ret; > > + } > > + > > + drm_connector_helper_add(&icn->connector, > > + &chipone_connector_helper_funcs); > > + drm_connector_attach_encoder(&icn->connector, bridge->encoder); > > + drm_panel_attach(icn->panel, &icn->connector); > > + icn->connector.funcs->reset(&icn->connector); > > + drm_fb_helper_add_one_connector(drm->fb_helper, &icn->connector); > > What do you need that for? I took the reference code, sorry it is accidentally present will wind-up in next version. > > > + drm_connector_register(&icn->connector); > > + > > + return 0; > > +} > > + > > +static void chipone_detach(struct drm_bridge *bridge) > > +{ > > + struct chipone *icn = bridge_to_chipone(bridge); > > + struct drm_device *drm = bridge->dev; > > + > > + drm_connector_unregister(&icn->connector); > > + drm_fb_helper_remove_one_connector(drm->fb_helper, &icn->connector); > > + drm_panel_detach(icn->panel); > > + icn->panel = NULL; > > + drm_connector_put(&icn->connector); > > +} > > + > > +static const struct drm_bridge_funcs chipone_bridge_funcs = { > > + .disable = chipone_disable, > > + .post_disable = chipone_post_disable, > > + .enable = chipone_enable, > > + .pre_enable = chipone_pre_enable, > > + .attach = chipone_attach, > > + .detach = chipone_detach, > > +}; > > + > > +static int chipone_probe(struct mipi_dsi_device *dsi) > > +{ > > + struct device *dev = &dsi->dev; > > + struct chipone *icn; > > + int ret; > > + > > + icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); > > + if (!icn) > > + return -ENOMEM; > > + > > + mipi_dsi_set_drvdata(dsi, icn); > > + > > + icn->dev = dev; > > + dsi->lanes = 4; > > + dsi->format = MIPI_DSI_FMT_RGB888; > > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; > > + > > + icn->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > > + if (IS_ERR(icn->reset)) { > > + DRM_DEV_ERROR(dev, "no reset GPIO pin provided\n"); > > + return PTR_ERR(icn->reset); > > + } > > + > > + ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0, > > + &icn->panel, NULL); > > + if (ret && ret != -EPROBE_DEFER) { > > + DRM_DEV_ERROR(dev, "failed to find panel (ret = %d)\n", ret); > > + return ret; > > + } > > + > > + icn->bridge.funcs = &chipone_bridge_funcs; > > + icn->bridge.of_node = dev->of_node; > > + > > + drm_bridge_add(&icn->bridge); > > + > > + ret = mipi_dsi_attach(dsi); > > + if (ret < 0) { > > + drm_bridge_remove(&icn->bridge); > > + DRM_DEV_ERROR(dev, "failed to attach dsi (ret = %d)\n", ret); > > + } > > + > > + return ret; > > +} > > + > > +static int chipone_remove(struct mipi_dsi_device *dsi) > > +{ > > + struct chipone *icn = mipi_dsi_get_drvdata(dsi); > > + > > + mipi_dsi_detach(dsi); > > + drm_bridge_remove(&icn->bridge); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id chipone_of_match[] = { > > + { .compatible = "bananapi,icn6211" }, > > This should have your generic compatible listed Any idea why? usually it can be know attached device associated IC chip isn't it? I can see this similar approach in some panel driver which I commented in 4/5 https://patchwork.freedesktop.org/patch/292416/ [1] https://patchwork.kernel.org/patch/10617921/
On Tue, Mar 19, 2019 at 1:59 AM Jagan Teki <jagan@amarulasolutions.com> wrote: > > On Fri, Mar 15, 2019 at 7:03 PM Maxime Ripard <maxime.ripard@bootlin.com> wrote: > > > > On Fri, Mar 15, 2019 at 06:38:24PM +0530, Jagan Teki wrote: > > > ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > > It has a flexible configuration of MIPI DSI signal input > > > and produce RGB565, RGB666, RGB888 output format. > > > > > > Add bridge driver for it. > > > > > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com> > > > --- > > > MAINTAINERS | 6 + > > > drivers/gpu/drm/bridge/Kconfig | 10 + > > > drivers/gpu/drm/bridge/Makefile | 1 + > > > drivers/gpu/drm/bridge/chipone-icn6211.c | 275 +++++++++++++++++++++++ > > > 4 files changed, 292 insertions(+) > > > create mode 100644 drivers/gpu/drm/bridge/chipone-icn6211.c > > > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > > index 4de80222cffb..e529addd30f5 100644 > > > --- a/MAINTAINERS > > > +++ b/MAINTAINERS > > > @@ -4897,6 +4897,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc > > > S: Maintained > > > F: drivers/gpu/drm/bochs/ > > > > > > +DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE > > > +M: Jagan Teki <jagan@amarulasolutions.com> > > > +S: Maintained > > > +F: drivers/gpu/drm/bridge/chipone-icn6211.c > > > +F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt > > > + > > > DRM DRIVER FOR FARADAY TVE200 TV ENCODER > > > M: Linus Walleij <linus.walleij@linaro.org> > > > T: git git://anongit.freedesktop.org/drm/drm-misc > > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > > > index 8840f396a7b6..cd314572e4ed 100644 > > > --- a/drivers/gpu/drm/bridge/Kconfig > > > +++ b/drivers/gpu/drm/bridge/Kconfig > > > @@ -36,6 +36,16 @@ config DRM_CDNS_DSI > > > Support Cadence DPI to DSI bridge. This is an internal > > > bridge and is meant to be directly embedded in a SoC. > > > > > > +config DRM_CHIPONE_ICN6211 > > > + tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge" > > > + depends on DRM && DRM_PANEL > > > + depends on OF > > > + select DRM_MIPI_DSI > > > + help > > > + ICN6211 is MIPI-DSI/RGB converter bridge from chipone. > > > + It has a flexible configuration of MIPI DSI signal input > > > + and produce RGB565, RGB666, RGB888 output format. > > > + > > > config DRM_DUMB_VGA_DAC > > > tristate "Dumb VGA DAC Bridge support" > > > depends on OF > > > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > > > index 4934fcf5a6f8..541fdccad10b 100644 > > > --- a/drivers/gpu/drm/bridge/Makefile > > > +++ b/drivers/gpu/drm/bridge/Makefile > > > @@ -1,6 +1,7 @@ > > > # SPDX-License-Identifier: GPL-2.0 > > > obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o > > > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o > > > +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o > > > obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o > > > obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o > > > obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o > > > diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c > > > new file mode 100644 > > > index 000000000000..cd2f3505f845 > > > --- /dev/null > > > +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c > > > @@ -0,0 +1,275 @@ > > > +// SPDX-License-Identifier: GPL-2.0+ > > > +/* > > > + * Copyright (C) 2018 Amarula Solutions > > > + * Author: Jagan Teki <jagan@amarulasolutions.com> > > > + */ > > > + > > > +#include <drm/drm_atomic_helper.h> > > > +#include <drm/drm_fb_helper.h> > > > +#include <drm/drm_of.h> > > > +#include <drm/drm_panel.h> > > > +#include <drm/drm_print.h> > > > +#include <drm/drm_probe_helper.h> > > > +#include <drm/drm_mipi_dsi.h> > > > + > > > +#include <linux/delay.h> > > > +#include <linux/gpio/consumer.h> > > > +#include <linux/module.h> > > > +#include <linux/of_graph.h> > > > + > > > +#define ICN6211_INIT_CMD_LEN 2 > > > + > > > +struct chipone { > > > + struct device *dev; > > > + struct drm_bridge bridge; > > > + struct drm_connector connector; > > > + struct drm_panel *panel; > > > + > > > + struct gpio_desc *reset; > > > +}; > > > + > > > +static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) > > > +{ > > > + return container_of(bridge, struct chipone, bridge); > > > +} > > > + > > > +static inline > > > +struct chipone *connector_to_chipone(struct drm_connector *connector) > > > +{ > > > + return container_of(connector, struct chipone, connector); > > > +} > > > + > > > +struct icn6211_init_cmd { > > > + u8 data[ICN6211_INIT_CMD_LEN]; > > > +}; > > > + > > > +static const struct icn6211_init_cmd icn6211_init_cmds[] = { > > > + { .data = { 0x7A, 0xC1 } }, > > > + { .data = { 0x20, 0x20 } }, > > > + { .data = { 0x21, 0xE0 } }, > > > + { .data = { 0x22, 0x13 } }, > > > + { .data = { 0x23, 0x28 } }, > > > + { .data = { 0x24, 0x30 } }, > > > + { .data = { 0x25, 0x28 } }, > > > + { .data = { 0x26, 0x00 } }, > > > + { .data = { 0x27, 0x0D } }, > > > + { .data = { 0x28, 0x03 } }, > > > + { .data = { 0x29, 0x1D } }, > > > + { .data = { 0x34, 0x80 } }, > > > + { .data = { 0x36, 0x28 } }, > > > + { .data = { 0xB5, 0xA0 } }, > > > + { .data = { 0x5C, 0xFF } }, > > > + { .data = { 0x2A, 0x01 } }, > > > + { .data = { 0x56, 0x92 } }, > > > + { .data = { 0x6B, 0x71 } }, > > > + { .data = { 0x69, 0x2B } }, > > > + { .data = { 0x10, 0x40 } }, > > > + { .data = { 0x11, 0x98 } }, > > > + { .data = { 0xB6, 0x20 } }, > > > + { .data = { 0x51, 0x20 } }, > > > + { .data = { 0x09, 0x10 } }, > > > +}; > > > > What are those commands supposed to be? Some of them at least look > > pretty standard (and have proper functions): > > MIPI_DCS_EXIT_INVERT_MODE, _SET_DISPLAY_ON, _SET_TEAR_OFF, etc. > > As I said in previous Allwinner MIPI-DSI fixes and new code support > series. For this bridge we don't have programming guide or any > datasheet. The initialization sequence is taken from BSP panel driver > and analyzed based on other references mentioned here [1]. Some of the > command sequence value are based on the timings other based on > standard MIPI commands as you said. Do you need to add comments to > describe the same or some other changes? Please at least put in proper macros or even calculated values for the stuff that we do know, such as the standard commands and basic timings that I already worked out for you previously. For a good reason why, see the slightly cleaned up but still awful table of init values in the ov5640 camera sensor driver. > > > > > +static int chipone_get_modes(struct drm_connector *connector) > > > +{ > > > + struct chipone *icn = connector_to_chipone(connector); > > > + > > > + return drm_panel_get_modes(icn->panel); > > > +} > > > + > > > +static const > > > +struct drm_connector_helper_funcs chipone_connector_helper_funcs = { > > > + .get_modes = chipone_get_modes, > > > +}; > > > + > > > +static const struct drm_connector_funcs chipone_connector_funcs = { > > > + .fill_modes = drm_helper_probe_single_connector_modes, > > > + .destroy = drm_connector_cleanup, > > > + .reset = drm_atomic_helper_connector_reset, > > > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > > > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > > > +}; > > > + > > > +static void chipone_disable(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + int ret = drm_panel_disable(bridge_to_chipone(bridge)->panel); > > > + > > > + if (ret < 0) > > > + DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret); > > > +} > > > + > > > +static void chipone_post_disable(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + int ret; > > > + > > > + ret = drm_panel_unprepare(icn->panel); > > > + if (ret < 0) > > > + DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret); > > > + > > > + msleep(50); > > > + > > > + gpiod_set_value(icn->reset, 0); > > > +} > > > + > > > +static void chipone_pre_enable(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev); > > > + unsigned int i; > > > + int ret; > > > + > > > + gpiod_set_value(icn->reset, 0); > > > + msleep(20); > > > + > > > + gpiod_set_value(icn->reset, 1); > > > + msleep(50); > > > + > > > + for (i = 0; i < ARRAY_SIZE(icn6211_init_cmds); i++) { > > > + const struct icn6211_init_cmd *cmd = &icn6211_init_cmds[i]; > > > + > > > + ret = mipi_dsi_generic_write(dsi, cmd->data, > > > + ICN6211_INIT_CMD_LEN); > > > + if (ret < 0) { > > > + DRM_DEV_ERROR(icn->dev, > > > + "failed to write cmd %d: %d\n", i, ret); > > > + return; > > > + } > > > + } > > > + > > > + ret = drm_panel_prepare(icn->panel); > > > + if (ret < 0) > > > + DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret); > > > +} > > > + > > > +static void chipone_enable(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + int ret = drm_panel_enable(icn->panel); > > > + > > > + if (ret < 0) > > > + DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret); > > > +} > > > + > > > +static int chipone_attach(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + struct drm_device *drm = bridge->dev; > > > + int ret; > > > + > > > + icn->connector.polled = DRM_CONNECTOR_POLL_HPD; > > > + ret = drm_connector_init(drm, &icn->connector, > > > + &chipone_connector_funcs, > > > + DRM_MODE_CONNECTOR_Unknown); > > > > It should be a DRM_MODE_CONNECTOR_DPI connector. > > Since the output connector is RGB, I have used 'Unknown' since we > follow similar connector type in sun4i_rgb.c So these RGB comes under > parallel display interface we can have to use this DPI connector is > it? It should be the DPI connector. We used the wrong connector to begin with and now we can't fix it, since the connector type is exported to userspace. > > > > > + if (ret) { > > > + DRM_ERROR("Failed to initialize connector\n"); > > > + return ret; > > > + } > > > + > > > + drm_connector_helper_add(&icn->connector, > > > + &chipone_connector_helper_funcs); > > > + drm_connector_attach_encoder(&icn->connector, bridge->encoder); > > > + drm_panel_attach(icn->panel, &icn->connector); > > > + icn->connector.funcs->reset(&icn->connector); > > > + drm_fb_helper_add_one_connector(drm->fb_helper, &icn->connector); > > > > What do you need that for? > > I took the reference code, sorry it is accidentally present will > wind-up in next version. > > > > > > + drm_connector_register(&icn->connector); > > > + > > > + return 0; > > > +} > > > + > > > +static void chipone_detach(struct drm_bridge *bridge) > > > +{ > > > + struct chipone *icn = bridge_to_chipone(bridge); > > > + struct drm_device *drm = bridge->dev; > > > + > > > + drm_connector_unregister(&icn->connector); > > > + drm_fb_helper_remove_one_connector(drm->fb_helper, &icn->connector); > > > + drm_panel_detach(icn->panel); > > > + icn->panel = NULL; > > > + drm_connector_put(&icn->connector); > > > +} > > > + > > > +static const struct drm_bridge_funcs chipone_bridge_funcs = { > > > + .disable = chipone_disable, > > > + .post_disable = chipone_post_disable, > > > + .enable = chipone_enable, > > > + .pre_enable = chipone_pre_enable, > > > + .attach = chipone_attach, > > > + .detach = chipone_detach, > > > +}; > > > + > > > +static int chipone_probe(struct mipi_dsi_device *dsi) > > > +{ > > > + struct device *dev = &dsi->dev; > > > + struct chipone *icn; > > > + int ret; > > > + > > > + icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); > > > + if (!icn) > > > + return -ENOMEM; > > > + > > > + mipi_dsi_set_drvdata(dsi, icn); > > > + > > > + icn->dev = dev; > > > + dsi->lanes = 4; > > > + dsi->format = MIPI_DSI_FMT_RGB888; > > > + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; > > > + > > > + icn->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > > > + if (IS_ERR(icn->reset)) { > > > + DRM_DEV_ERROR(dev, "no reset GPIO pin provided\n"); > > > + return PTR_ERR(icn->reset); > > > + } > > > + > > > + ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0, > > > + &icn->panel, NULL); > > > + if (ret && ret != -EPROBE_DEFER) { > > > + DRM_DEV_ERROR(dev, "failed to find panel (ret = %d)\n", ret); > > > + return ret; > > > + } > > > + > > > + icn->bridge.funcs = &chipone_bridge_funcs; > > > + icn->bridge.of_node = dev->of_node; > > > + > > > + drm_bridge_add(&icn->bridge); > > > + > > > + ret = mipi_dsi_attach(dsi); > > > + if (ret < 0) { > > > + drm_bridge_remove(&icn->bridge); > > > + DRM_DEV_ERROR(dev, "failed to attach dsi (ret = %d)\n", ret); > > > + } > > > + > > > + return ret; > > > +} > > > + > > > +static int chipone_remove(struct mipi_dsi_device *dsi) > > > +{ > > > + struct chipone *icn = mipi_dsi_get_drvdata(dsi); > > > + > > > + mipi_dsi_detach(dsi); > > > + drm_bridge_remove(&icn->bridge); > > > + > > > + return 0; > > > +} > > > + > > > +static const struct of_device_id chipone_of_match[] = { > > > + { .compatible = "bananapi,icn6211" }, > > > > This should have your generic compatible listed > > Any idea why? usually it can be know attached device associated IC > chip isn't it? I can see this similar approach in some panel driver > which I commented in 4/5 > https://patchwork.freedesktop.org/patch/292416/ A panel driver is tied to a specific set of timings, as required by the actual LCD panel. A bridge on the other hand is generic. Whatever timings needed are a property of the downstream end device. So you really need to use the generic compatible string here. You can add a big fat warning in this driver saying it currently only supports one panel, and even peek at the downstream panel and bail out if it's not the expected one. If other people encounter this chip, they can come in and either add their own set of init commands, or even help to generalize this driver. ChenYu > [1] https://patchwork.kernel.org/patch/10617921/ > > -- > You received this message because you are subscribed to the Google Groups "linux-sunxi" group. > To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com. > For more options, visit https://groups.google.com/d/optout.
diff --git a/MAINTAINERS b/MAINTAINERS index 4de80222cffb..e529addd30f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4897,6 +4897,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc S: Maintained F: drivers/gpu/drm/bochs/ +DRM DRIVER FOR CHIPONE ICN6211 MIPI-DSI to RGB CONVERTOR BRIDGE +M: Jagan Teki <jagan@amarulasolutions.com> +S: Maintained +F: drivers/gpu/drm/bridge/chipone-icn6211.c +F: Documentation/devicetree/bindings/display/bridge/chipone,icn6211.txt + DRM DRIVER FOR FARADAY TVE200 TV ENCODER M: Linus Walleij <linus.walleij@linaro.org> T: git git://anongit.freedesktop.org/drm/drm-misc diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 8840f396a7b6..cd314572e4ed 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -36,6 +36,16 @@ config DRM_CDNS_DSI Support Cadence DPI to DSI bridge. This is an internal bridge and is meant to be directly embedded in a SoC. +config DRM_CHIPONE_ICN6211 + tristate "Chipone ICN6211 MIPI-DSI/RGB converter bridge" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + help + ICN6211 is MIPI-DSI/RGB converter bridge from chipone. + It has a flexible configuration of MIPI DSI signal input + and produce RGB565, RGB666, RGB888 output format. + config DRM_DUMB_VGA_DAC tristate "Dumb VGA DAC Bridge support" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 4934fcf5a6f8..541fdccad10b 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o +obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c new file mode 100644 index 000000000000..cd2f3505f845 --- /dev/null +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Amarula Solutions + * Author: Jagan Teki <jagan@amarulasolutions.com> + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_mipi_dsi.h> + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_graph.h> + +#define ICN6211_INIT_CMD_LEN 2 + +struct chipone { + struct device *dev; + struct drm_bridge bridge; + struct drm_connector connector; + struct drm_panel *panel; + + struct gpio_desc *reset; +}; + +static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge) +{ + return container_of(bridge, struct chipone, bridge); +} + +static inline +struct chipone *connector_to_chipone(struct drm_connector *connector) +{ + return container_of(connector, struct chipone, connector); +} + +struct icn6211_init_cmd { + u8 data[ICN6211_INIT_CMD_LEN]; +}; + +static const struct icn6211_init_cmd icn6211_init_cmds[] = { + { .data = { 0x7A, 0xC1 } }, + { .data = { 0x20, 0x20 } }, + { .data = { 0x21, 0xE0 } }, + { .data = { 0x22, 0x13 } }, + { .data = { 0x23, 0x28 } }, + { .data = { 0x24, 0x30 } }, + { .data = { 0x25, 0x28 } }, + { .data = { 0x26, 0x00 } }, + { .data = { 0x27, 0x0D } }, + { .data = { 0x28, 0x03 } }, + { .data = { 0x29, 0x1D } }, + { .data = { 0x34, 0x80 } }, + { .data = { 0x36, 0x28 } }, + { .data = { 0xB5, 0xA0 } }, + { .data = { 0x5C, 0xFF } }, + { .data = { 0x2A, 0x01 } }, + { .data = { 0x56, 0x92 } }, + { .data = { 0x6B, 0x71 } }, + { .data = { 0x69, 0x2B } }, + { .data = { 0x10, 0x40 } }, + { .data = { 0x11, 0x98 } }, + { .data = { 0xB6, 0x20 } }, + { .data = { 0x51, 0x20 } }, + { .data = { 0x09, 0x10 } }, +}; + +static int chipone_get_modes(struct drm_connector *connector) +{ + struct chipone *icn = connector_to_chipone(connector); + + return drm_panel_get_modes(icn->panel); +} + +static const +struct drm_connector_helper_funcs chipone_connector_helper_funcs = { + .get_modes = chipone_get_modes, +}; + +static const struct drm_connector_funcs chipone_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static void chipone_disable(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + int ret = drm_panel_disable(bridge_to_chipone(bridge)->panel); + + if (ret < 0) + DRM_DEV_ERROR(icn->dev, "error disabling panel (%d)\n", ret); +} + +static void chipone_post_disable(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + int ret; + + ret = drm_panel_unprepare(icn->panel); + if (ret < 0) + DRM_DEV_ERROR(icn->dev, "error unpreparing panel (%d)\n", ret); + + msleep(50); + + gpiod_set_value(icn->reset, 0); +} + +static void chipone_pre_enable(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(icn->dev); + unsigned int i; + int ret; + + gpiod_set_value(icn->reset, 0); + msleep(20); + + gpiod_set_value(icn->reset, 1); + msleep(50); + + for (i = 0; i < ARRAY_SIZE(icn6211_init_cmds); i++) { + const struct icn6211_init_cmd *cmd = &icn6211_init_cmds[i]; + + ret = mipi_dsi_generic_write(dsi, cmd->data, + ICN6211_INIT_CMD_LEN); + if (ret < 0) { + DRM_DEV_ERROR(icn->dev, + "failed to write cmd %d: %d\n", i, ret); + return; + } + } + + ret = drm_panel_prepare(icn->panel); + if (ret < 0) + DRM_DEV_ERROR(icn->dev, "error preparing panel (%d)\n", ret); +} + +static void chipone_enable(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + int ret = drm_panel_enable(icn->panel); + + if (ret < 0) + DRM_DEV_ERROR(icn->dev, "error enabling panel (%d)\n", ret); +} + +static int chipone_attach(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + struct drm_device *drm = bridge->dev; + int ret; + + icn->connector.polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(drm, &icn->connector, + &chipone_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + return ret; + } + + drm_connector_helper_add(&icn->connector, + &chipone_connector_helper_funcs); + drm_connector_attach_encoder(&icn->connector, bridge->encoder); + drm_panel_attach(icn->panel, &icn->connector); + icn->connector.funcs->reset(&icn->connector); + drm_fb_helper_add_one_connector(drm->fb_helper, &icn->connector); + drm_connector_register(&icn->connector); + + return 0; +} + +static void chipone_detach(struct drm_bridge *bridge) +{ + struct chipone *icn = bridge_to_chipone(bridge); + struct drm_device *drm = bridge->dev; + + drm_connector_unregister(&icn->connector); + drm_fb_helper_remove_one_connector(drm->fb_helper, &icn->connector); + drm_panel_detach(icn->panel); + icn->panel = NULL; + drm_connector_put(&icn->connector); +} + +static const struct drm_bridge_funcs chipone_bridge_funcs = { + .disable = chipone_disable, + .post_disable = chipone_post_disable, + .enable = chipone_enable, + .pre_enable = chipone_pre_enable, + .attach = chipone_attach, + .detach = chipone_detach, +}; + +static int chipone_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct chipone *icn; + int ret; + + icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); + if (!icn) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, icn); + + icn->dev = dev; + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + + icn->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(icn->reset)) { + DRM_DEV_ERROR(dev, "no reset GPIO pin provided\n"); + return PTR_ERR(icn->reset); + } + + ret = drm_of_find_panel_or_bridge(icn->dev->of_node, 1, 0, + &icn->panel, NULL); + if (ret && ret != -EPROBE_DEFER) { + DRM_DEV_ERROR(dev, "failed to find panel (ret = %d)\n", ret); + return ret; + } + + icn->bridge.funcs = &chipone_bridge_funcs; + icn->bridge.of_node = dev->of_node; + + drm_bridge_add(&icn->bridge); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_bridge_remove(&icn->bridge); + DRM_DEV_ERROR(dev, "failed to attach dsi (ret = %d)\n", ret); + } + + return ret; +} + +static int chipone_remove(struct mipi_dsi_device *dsi) +{ + struct chipone *icn = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_bridge_remove(&icn->bridge); + + return 0; +} + +static const struct of_device_id chipone_of_match[] = { + { .compatible = "bananapi,icn6211" }, + { } +}; +MODULE_DEVICE_TABLE(of, chipone_of_match); + +static struct mipi_dsi_driver chipone_driver = { + .probe = chipone_probe, + .remove = chipone_remove, + .driver = { + .name = "chipone-icn6211", + .owner = THIS_MODULE, + .of_match_table = chipone_of_match, + }, +}; +module_mipi_dsi_driver(chipone_driver); + +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); +MODULE_DESCRIPTION("Chipone ICN6211 MIPI-DSI to RGB Convertor Bridge"); +MODULE_LICENSE("GPL v2");