From patchwork Wed Apr 1 10:09:39 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heiko Stuebner X-Patchwork-Id: 6139041 Return-Path: X-Original-To: patchwork-linux-rockchip@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id A99D4BF4A6 for ; Wed, 1 Apr 2015 10:14:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 88D3E202EC for ; Wed, 1 Apr 2015 10:14:41 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6FF9020259 for ; Wed, 1 Apr 2015 10:14:40 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YdFfR-0000qa-NL; Wed, 01 Apr 2015 10:14:37 +0000 Received: from merlin.infradead.org ([2001:4978:20e::2]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YdFbm-0007AB-CO; Wed, 01 Apr 2015 10:10:50 +0000 Received: from gloria.sntech.de ([95.129.55.99]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YdFbh-0000y6-HU; Wed, 01 Apr 2015 10:10:48 +0000 Received: from ip92344031.dynamic.kabel-deutschland.de ([146.52.64.49] helo=diego.lan) by gloria.sntech.de with esmtpsa (TLS1.1:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1YdFaq-0002on-TK; Wed, 01 Apr 2015 12:09:53 +0200 From: Heiko Stuebner To: airlied@linux.ie, mark.yao@rock-chips.com, laurent.pinchart@ideasonboard.com Subject: [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Date: Wed, 1 Apr 2015 12:09:39 +0200 Message-Id: <1427882986-19110-6-git-send-email-heiko@sntech.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1427882986-19110-1-git-send-email-heiko@sntech.de> References: <1427882986-19110-1-git-send-email-heiko@sntech.de> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150401_061045_701557_D8264D74 X-CRM114-Status: GOOD ( 18.77 ) X-Spam-Score: -1.9 (-) Cc: devicetree@vger.kernel.org, Heiko Stuebner , dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, robdclark@gmail.com, djkurtz@chromium.org, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This adds a driver for generic vga connectors using a system i2c-bus for ddc. An exception is included for rcar-du which implements the vga-connector binding interally already and is not yet converted to the component framework. Signed-off-by: Heiko Stuebner --- drivers/gpu/drm/components/Kconfig | 6 + drivers/gpu/drm/components/Makefile | 1 + drivers/gpu/drm/components/vga-connector.c | 254 +++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 drivers/gpu/drm/components/vga-connector.c diff --git a/drivers/gpu/drm/components/Kconfig b/drivers/gpu/drm/components/Kconfig index 647cea6..8424143 100644 --- a/drivers/gpu/drm/components/Kconfig +++ b/drivers/gpu/drm/components/Kconfig @@ -6,4 +6,10 @@ config DRM_COMPONENTS_VGA_ENCODER help Support for generic vga encoder chips without any special controls. +config DRM_COMPONENTS_VGA_CONNECTOR: + tristate "Generic vga connector" + help + Support for simple vga connectors using a system i2c bus + for ddc. + endmenu diff --git a/drivers/gpu/drm/components/Makefile b/drivers/gpu/drm/components/Makefile index 719b1c9..2ff64da 100644 --- a/drivers/gpu/drm/components/Makefile +++ b/drivers/gpu/drm/components/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm obj-$(CONFIG_DRM_COMPONENTS_VGA_ENCODER) += vga-encoder.o +obj-$(CONFIG_DRM_COMPONENTS_VGA_CONNECTOR) += vga-connector.o diff --git a/drivers/gpu/drm/components/vga-connector.c b/drivers/gpu/drm/components/vga-connector.c new file mode 100644 index 0000000..400ceb7 --- /dev/null +++ b/drivers/gpu/drm/components/vga-connector.c @@ -0,0 +1,254 @@ +/* + * Simple vga encoder driver + * + * Copyright (C) 2014 Heiko Stuebner + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define connector_to_vga_connector(x) container_of(x, struct vga_connector, connector) + +struct vga_connector { + struct drm_connector connector; + struct device *dev; + struct i2c_adapter *ddc; + struct drm_encoder *encoder; +}; + +enum drm_connector_status vga_connector_detect(struct drm_connector *connector, + bool force) +{ + struct vga_connector *vga = connector_to_vga_connector(connector); + + if (!vga->ddc) + return connector_status_unknown; + + if (drm_probe_ddc(vga->ddc)) + return connector_status_connected; + + return connector_status_disconnected; +} + +void vga_connector_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +struct drm_connector_funcs vga_connector_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = vga_connector_detect, + .destroy = vga_connector_connector_destroy, +}; + +/* + * Connector helper functions + */ + +static int vga_connector_connector_get_modes(struct drm_connector *connector) +{ + struct vga_connector *vga = connector_to_vga_connector(connector); + struct edid *edid; + int ret = 0; + + if (!vga->ddc) + return 0; + + edid = drm_get_edid(connector, vga->ddc); + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return ret; +} + +static int vga_connector_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static struct drm_encoder +*vga_connector_connector_best_encoder(struct drm_connector *connector) +{ + struct vga_connector *vga = connector_to_vga_connector(connector); + + return vga->encoder; +} + +static struct drm_connector_helper_funcs vga_connector_connector_helper_funcs = { + .get_modes = vga_connector_connector_get_modes, + .best_encoder = vga_connector_connector_best_encoder, + .mode_valid = vga_connector_connector_mode_valid, +}; + + +static int vga_connector_bind(struct device *dev, struct device *master, + void *data) +{ + struct vga_connector *vga = dev_get_drvdata(dev); + struct drm_device *drm = data; + struct device_node *endpoint, *encp = NULL; + + vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + drm_connector_helper_add(&vga->connector, + &vga_connector_connector_helper_funcs); + drm_connector_init(drm, &vga->connector, &vga_connector_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (endpoint) + encp = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + + if (!encp) + return -ENODEV; + + vga->encoder = of_drm_find_encoder(encp); + of_node_put(encp); + + if (!vga->encoder) + return -EPROBE_DEFER; + + drm_mode_connector_attach_encoder(&vga->connector, vga->encoder); + + return 0; +} + +static void vga_connector_unbind(struct device *dev, struct device *master, + void *data) +{ + struct vga_connector *vga = dev_get_drvdata(dev); + + vga->connector.funcs->destroy(&vga->connector); +} + +static const struct component_ops vga_connector_ops = { + .bind = vga_connector_bind, + .unbind = vga_connector_unbind, +}; + +static int vga_connector_probe(struct platform_device *pdev) +{ + struct device_node *ddc_node, *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct vga_connector *vga; + int ret; + + if (!np) + return -ENODEV; + + vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL); + if (!vga) + return -ENOMEM; + + vga->dev = dev; + vga->connector.of_node = np; + + dev_set_drvdata(dev, vga); + + ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); + if (ddc_node) { + vga->ddc = of_find_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); + if (!vga->ddc) { + dev_dbg(vga->dev, "failed to read ddc node\n"); + return -EPROBE_DEFER; + } + } else { + dev_dbg(vga->dev, "no ddc property found\n"); + } + + ret = drm_connector_add(&vga->connector); + if (ret < 0) + return ret; + + return component_add(dev, &vga_connector_ops); +} + +static int vga_connector_remove(struct platform_device *pdev) +{ + struct vga_connector *vga = dev_get_drvdata(&pdev->dev); + + component_del(&pdev->dev, &vga_connector_ops); + drm_connector_remove(&vga->connector); + + return 0; +} + +static const struct of_device_id vga_connector_ids[] = { + { .compatible = "vga-connector", }, + {}, +}; +MODULE_DEVICE_TABLE(of, vga_connector_ids); + +static struct platform_driver vga_connector_driver = { + .probe = vga_connector_probe, + .remove = vga_connector_remove, + .driver = { + .name = "vga-connector", + .of_match_table = vga_connector_ids, + }, +}; + +static const struct of_device_id rcar_du_of_table[] = { + { .compatible = "renesas,du-r8a7779" }, + { .compatible = "renesas,du-r8a7790" }, + { .compatible = "renesas,du-r8a7791" }, + { } +}; + +static int __init vga_connector_init(void) +{ + struct device_node *np; + + /* + * Play nice with rcar-du that is having its own implementation + * of the vga-connector binding implementation and is not yet + * converted to using components. + */ + np = of_find_matching_node(NULL, rcar_du_of_table); + if (np) { + of_node_put(np); + return 0; + } + + return platform_driver_register(&vga_connector_driver); +} + +static void __exit vga_connector_exit(void) +{ + platform_driver_unregister(&vga_connector_driver); +} + +module_init(vga_connector_init); +module_exit(vga_connector_exit); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Simple vga converter"); +MODULE_LICENSE("GPL");