From patchwork Mon Jun 18 13:22:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138939 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949331lji; Mon, 18 Jun 2018 06:23:03 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLX2aC74mA8TtHKvwUykyXslVzoKFe1R+/xLxrqa3NuQBN2fYhpDvhlj7GyaKhlM8Nr++gG X-Received: by 2002:a65:56c1:: with SMTP id w1-v6mr11191775pgs.227.1529328183761; Mon, 18 Jun 2018 06:23:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328183; cv=none; d=google.com; s=arc-20160816; b=0+inYj64XwTrMkV7gRXsjGifc77+6g/Ce+y0eFycGCqp8dOQxYdnqHGS+D9XdyFS89 GOV+lfa/rPbWV7cJW+1joqL7ZOt9V4jmex6HFwJOYuP1CYR1EralvHVaK1wXq6BQYJl5 GTg9XpCHQZX/9treoCI/8tNNb+m4gYx5CoxKbFjgNHF9rBNXHkRiWUKctdL2IZBWWf4O XzTgVHpORZ3H8zrCEAEG6wFksf8yL0ZxJn72fAy9VMmI5RhefNa6FOD5GJRIu8wKAYgC sCAMxYVQfAUBaMDAH7UHvhLun8L5cwVU3ConnSV0k+zGaxdE6JNm8340mtiq5/ihiWcy oIxw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=TyxR1bFkkr69KAhBs8t0z8NS0A1p2Pmu+3WXaOKqnhM=; b=vHvM89APwFCsgi1z+Qedsq7MCj8X5Z7Ciy1kf3kXO4wSH23a65SBezejdRywwkPeA4 NtnX15jdGeZC13SnEtaqs3UATURtvuN0PMgAM7/akzN2xUH8jIFeJF3vzD4Q793nc8V9 dDNjT7eH1wG0WOPibWLA9744eAbuOhqM6nJBEvVDGwKGzwX3xihJq+VB2F3FcdY/luOo wMTvUlM1t4izByI7USaRJQToPwbv1zVknEw/wEzyZUU8ZsA2libG1NAyEAIaI0liSw06 3zoEjsWZBdq5CEcRXUzlTI/8fGdKwvi/Mh9vJ1ulTG1pLi/Sq37NMXnsAPNrS3Wq9SIL P2kw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=rg8n4wP2; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t66-v6si11868193pgc.6.2018.06.18.06.23.03; Mon, 18 Jun 2018 06:23:03 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=rg8n4wP2; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934068AbeFRNXB (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:01 -0400 Received: from lelnx193.ext.ti.com ([198.47.27.77]:60740 "EHLO lelnx193.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933738AbeFRNXA (ORCPT ); Mon, 18 Jun 2018 09:23:00 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by lelnx193.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDMsbe022217; Mon, 18 Jun 2018 08:22:54 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328174; bh=c4tyg1jZv7fD6Yu781Q5C4k9qLVE9HTkhjdjP2HFWHc=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=rg8n4wP2EVgcD+1g9wnONHRc4BxhEDyj2lzip4ytJvr9Y1eWFt2TJcjXtaupVzR4K YzUW6/367wJVCDMz/rFcZti8d9UeSK/x3jD5UCrel5sCE9X5Q1fG0opr87gCOOI31I IsFnwP9arK9Xz1c9sZ402ugK8cPICOZbPtc51VZU= Received: from DLEE109.ent.ti.com (dlee109.ent.ti.com [157.170.170.41]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMsxd003429; Mon, 18 Jun 2018 08:22:54 -0500 Received: from DLEE112.ent.ti.com (157.170.170.23) by DLEE109.ent.ti.com (157.170.170.41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:22:54 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DLEE112.ent.ti.com (157.170.170.23) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:22:54 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowo002369; Mon, 18 Jun 2018 08:22:52 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen Subject: [RFC PATCHv2 1/9] drm: Add support for extracting sync signal drive edge from videomode Date: Mon, 18 Jun 2018 16:22:34 +0300 Message-ID: <20180618132242.8673-2-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Peter Ujfalusi The sync in some panels needs to be driven by different edge of the pixel clock compared to data. This is reflected by the DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in videmode flags. Add similar similar definitions for bus_flags and convert the sync drive edge via drm_bus_flags_from_videomode(). Signed-off-by: Peter Ujfalusi Signed-off-by: Tomi Valkeinen Signed-off-by: Jyri Sarha --- drivers/gpu/drm/drm_modes.c | 15 +++++++++++---- include/drm/drm_connector.h | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Reviewed-by: Laurent Pinchart diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index e82b61e08f8c..1661bfc55687 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -659,10 +659,12 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); * drm_bus_flags_from_videomode - extract information about pixelclk and * DE polarity from videomode and store it in a separate variable * @vm: videomode structure to use - * @bus_flags: information about pixelclk and DE polarity will be stored here + * @bus_flags: information about pixelclk, sync and DE polarity will be stored + * here * - * Sets DRM_BUS_FLAG_DE_(LOW|HIGH) and DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE - * in @bus_flags according to DISPLAY_FLAGS found in @vm + * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE and + * DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS + * found in @vm */ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) { @@ -672,6 +674,11 @@ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE; + if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) + *bus_flags |= DRM_BUS_FLAG_SYNC_POSEDGE; + if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) + *bus_flags |= DRM_BUS_FLAG_SYNC_NEGEDGE; + if (vm->flags & DISPLAY_FLAGS_DE_LOW) *bus_flags |= DRM_BUS_FLAG_DE_LOW; if (vm->flags & DISPLAY_FLAGS_DE_HIGH) @@ -684,7 +691,7 @@ EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); * of_get_drm_display_mode - get a drm_display_mode from devicetree * @np: device_node with the timing specification * @dmode: will be set to the return value - * @bus_flags: information about pixelclk and DE polarity + * @bus_flags: information about pixelclk, sync and DE polarity * @index: index into the list of display timings in devicetree * * This function is expensive and should only be used, if only one mode is to be diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 675cc3f8cf85..8fa901637f00 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -290,6 +290,10 @@ struct drm_display_info { #define DRM_BUS_FLAG_DATA_MSB_TO_LSB (1<<4) /* data is transmitted LSB to MSB on the bus */ #define DRM_BUS_FLAG_DATA_LSB_TO_MSB (1<<5) +/* drive sync on pos. edge */ +#define DRM_BUS_FLAG_SYNC_POSEDGE (1<<6) +/* drive sync on neg. edge */ +#define DRM_BUS_FLAG_SYNC_NEGEDGE (1<<7) /** * @bus_flags: Additional information (like pixel signal polarity) for From patchwork Mon Jun 18 13:22:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138940 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949363lji; Mon, 18 Jun 2018 06:23:06 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJuQpWJvSbIpsYe1aVV5i5fqkLBpetRP3EXSpwbeKrXG8SFK8QPyVuoKjZaYN74zp77y74y X-Received: by 2002:a17:902:600a:: with SMTP id r10-v6mr14290328plj.70.1529328185967; Mon, 18 Jun 2018 06:23:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328185; cv=none; d=google.com; s=arc-20160816; b=s2LhceS38lC93Ra8ZtwCwm61eFfIO1pGSCPzsIAAddu6X5hLQGB8/P4gETMKDHpUKr IxY9jDHPbaCbB02NYIV31AfZit/8H/tuUHehrgSA/k+ou/D/UYe/hDBAdaa2iqSAbrMX bsreltnBouOTOOE8yyI9SCyn9pA0RgPc4xEXet12N4S91yq7LJ9G3GxWAFbXSLBIMWSR sB08rjqnBuQB0nzZkEz0rYkjxCJwcX684NWviE7U0HQm1k62i/m8ab3n694WAHMNUkNE TIVppBeVKmpJFZqMSWhyQdpzXgilOffgZ/C85d/eoEOjzPVy4gb/3kYvfuWENiSIjiVM mYzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=oKzas4f3vhtY4unfbvsREeoR6Y20hM/nCjUXjkI7Zow=; b=YveORaaOwB+IikEJPAXoCk6ugmzSYCuhfKTfhA8leNUhpJ0GA1gZO825a4dq7Xr+Dj 8+8T4DwB4Hm9lj7pll767n0UWqeN2bgUFuPW3LgHNrWggowL8OpkMNQXok6IlVYXLdkg YFd7MogtahlatV6YnXT0wfSTcg1WJPIBFVmeub9Ios+1ofc9RMCbj/lWhTmDC76h5JBf u/icz+9M0hmfNGpSnsZUtU/9dMif+nx3iUW1fc33J3Dq6Gt1KsXoRCnAqSmSyAtH8o1p xt5YexzrieQqd2Mx4Bssbk05W1xtCb3ITKpzdT5l0u2y0iwEHtiFaSxYhMKzyKcFLhsW 3fFw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=sWu7VJ4A; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t66-v6si11868193pgc.6.2018.06.18.06.23.05; Mon, 18 Jun 2018 06:23:05 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=sWu7VJ4A; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934240AbeFRNXE (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:04 -0400 Received: from fllnx209.ext.ti.com ([198.47.19.16]:26353 "EHLO fllnx209.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933182AbeFRNXB (ORCPT ); Mon, 18 Jun 2018 09:23:01 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by fllnx209.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDMw2W018650; Mon, 18 Jun 2018 08:22:58 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328178; bh=SHtXdCS2rrRdSaMMjBOqEV8xfVkPUG2xh+77vyJ93Yo=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=sWu7VJ4AXfLtD2bxm9KhDxPvkabwYerRKfmIc0g74jYJgc5S8L1B07TVJyL6atHOv MLmWEnW97FWUz14K5na9uVEQ3WDE4huxwab1wNGyaE3Jo5mY6drbsvU57TEKB3HpbA L3lsUvcAFyh9NJRf8yedyjIU0hw/JBwZawm3Jwqs= Received: from DFLE107.ent.ti.com (dfle107.ent.ti.com [10.64.6.28]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMwen003495; Mon, 18 Jun 2018 08:22:58 -0500 Received: from DFLE110.ent.ti.com (10.64.6.31) by DFLE107.ent.ti.com (10.64.6.28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:22:58 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE110.ent.ti.com (10.64.6.31) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:22:58 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowq002369; Mon, 18 Jun 2018 08:22:56 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen , Subject: [RFC PATCHv2 3/9] dt-bindings: display/ti: add am65x-dss bindings Date: Mon, 18 Jun 2018 16:22:36 +0300 Message-ID: <20180618132242.8673-4-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Add DT bindings for Texas Instruments AM65x SoC Display Subsystem. The DSS7 on AM65x SoC has two video ports (DPI and OLDI) and two video pipelines. Signed-off-by: Jyri Sarha Signed-off-by: Tomi Valkeinen Cc: devicetree@vger.kernel.org --- .../bindings/display/ti/ti,am6-dss.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/ti/ti,am6-dss.txt -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/display/ti/ti,am6-dss.txt b/Documentation/devicetree/bindings/display/ti/ti,am6-dss.txt new file mode 100644 index 000000000000..3466f095fb84 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ti/ti,am6-dss.txt @@ -0,0 +1,16 @@ +Texas Instruments AM65x Display Subsystem +========================================== + +Required properties: +- compatible: "ti,am65x-dss", "ti,am6-dss" +- reg: address and length of the register spaces for DSS submodules +- reg-names: "common", "vidl1", "vid", "ovr1", "ovr2", "vp1", "vp2" +- clocks: phandle to fclk, vp1, and vp2 clocks +- clock-names: "fck", "vp1", "vp2" +- interrupts: phandle to the DISPC interrupt +- syscon: phandle to syscon device handling OLDI_PWRDN_TX (partition 1 of + AM654 CTRL MMR0) + +The DSS outputs are described using the device graphs as documented in +Documentation/devicetree/bindings/graph.txt. AM6 DSS has a DPI output as port 0 +and an OLDI output as port 1. From patchwork Mon Jun 18 13:22:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138943 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949461lji; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) X-Google-Smtp-Source: ADUXVKI6JS/aGtxOZJmcXote+J2ak8QPA4/7UJjoabR8HdHwp7jGoi1gHbv8BU12hhfoRxz2xf5O X-Received: by 2002:a62:ed13:: with SMTP id u19-v6mr13520901pfh.125.1529328191235; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328191; cv=none; d=google.com; s=arc-20160816; b=lhwfKrcvXx5Cs5vElD6qIUUgEmzPszUHLkL4hFL4Ita/onbPibksSQA9c8oxsK4R5x PpTKo0s0PTzrMiTDkMUPSlrTMR82/3lm0DaIpF0MIb6YcckB6Xd72OwhBucW2JMeOft0 mMozxW3rfipcBP50+LDF4ajYylaqum/dk1n2F4Dk+GNr3WAvOkcLkAoUFcnWJm701gwU 3qbHccWBcbisx4Nm7NyQS11P9OYJ5XWyTWEGMQa/GoRUTcEhXNUK6ipD9ApQU8KSHivf EQy9xsxLerV0zIs1XMF3oIrDvXooUZGGzhTJ7fxZN1Q+hthKBuUj/WhHyKPsHq8Wu+xv oGhg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=aBbWoTObLKYBFqcb4NJAvltec+puJbUKAxwK4wS91GY=; b=IZ87h+aNw0WmcAcGl0k3QdLolpnG+jm4w+7k+efMttKVHlt3+8RvLyljVcmsglPD0I cDvLUuWQbELQZ4JONAIn566r24xIbE4wHSdO29aTr2wCAIhjb40lpRcKJDZtoOqli/eW +usTwlAYZpMxjQJds7cL0/sXfTnf0icAf8XE79jKn/JWosQVgWlTxEY4bkiIO0w7pNBe BpFkNdnmLaTPg9k0ujPJM2GRiqBP4/kudXMx3ogHHW0PRNnFLLN/Hq5yesRyrLj3bYk3 xAaANpBH1h6eGys6M/bzlojPCCPekgBjZMdmpK0cI0jPEim5qu5diE/xtFs+/p47/RhF Pv6w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=VV+hxG43; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t66-v6si11868193pgc.6.2018.06.18.06.23.10; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=VV+hxG43; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934244AbeFRNXJ (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:09 -0400 Received: from lelnx193.ext.ti.com ([198.47.27.77]:60746 "EHLO lelnx193.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933182AbeFRNXH (ORCPT ); Mon, 18 Jun 2018 09:23:07 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by lelnx193.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDN3ZG022238; Mon, 18 Jun 2018 08:23:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328183; bh=3ELrGQiDvOCyoBe/lTT7aEFmB70KPN/T+CXM6A2cTok=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=VV+hxG43ETTV/LbsV2OYaEOJT2zPGyjYWECuzMdEFmMrpAcu8M+I9TGdBSAAtxhsy DYNdOBtoYBtTB39xrFm3U4BTawvLJ7yNn7tYq/Bg9H7OmfvqqNlaGVR7kg9YzHruY6 AwThpqqtCymz45JbQYAT9BXkU+8kZtgU9PzRkt9U= Received: from DLEE115.ent.ti.com (dlee115.ent.ti.com [157.170.170.26]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDN33I003704; Mon, 18 Jun 2018 08:23:03 -0500 Received: from DLEE104.ent.ti.com (157.170.170.34) by DLEE115.ent.ti.com (157.170.170.26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:00 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DLEE104.ent.ti.com (157.170.170.34) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:00 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowr002369; Mon, 18 Jun 2018 08:22:58 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen Subject: [RFC PATCHv2 4/9] drm/tidss: add new driver for TI Keystone platforms Date: Mon, 18 Jun 2018 16:22:37 +0300 Message-ID: <20180618132242.8673-5-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org This patch adds a new DRM driver for Texas Instruments DSS6 IP used on Texas Instruments Keystone K2G SoC. The DSS6 IP is a major change to the older DSS IP versions, which are supported by the omapdrm driver, and while on higher level the DSS6 resembles the older DSS versions, the registers and the internal pipelines differ a lot. DSS6 IP on K2G is a "ultra-light" version, and has only a single plane and a single output. The driver will also support future DSS versions, which support multiple planes and outputs, so the driver already has support for those. Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/tidss/Kconfig | 10 + drivers/gpu/drm/tidss/Makefile | 11 + drivers/gpu/drm/tidss/tidss_crtc.c | 390 +++++++ drivers/gpu/drm/tidss/tidss_crtc.h | 49 + drivers/gpu/drm/tidss/tidss_dispc.h | 145 +++ drivers/gpu/drm/tidss/tidss_dispc6.c | 1450 +++++++++++++++++++++++++ drivers/gpu/drm/tidss/tidss_dispc6.h | 109 ++ drivers/gpu/drm/tidss/tidss_drv.c | 333 ++++++ drivers/gpu/drm/tidss/tidss_drv.h | 41 + drivers/gpu/drm/tidss/tidss_encoder.c | 101 ++ drivers/gpu/drm/tidss/tidss_encoder.h | 22 + drivers/gpu/drm/tidss/tidss_irq.c | 193 ++++ drivers/gpu/drm/tidss/tidss_irq.h | 25 + drivers/gpu/drm/tidss/tidss_kms.c | 85 ++ drivers/gpu/drm/tidss/tidss_kms.h | 14 + drivers/gpu/drm/tidss/tidss_plane.c | 186 ++++ drivers/gpu/drm/tidss/tidss_plane.h | 25 + 19 files changed, 3192 insertions(+) create mode 100644 drivers/gpu/drm/tidss/Kconfig create mode 100644 drivers/gpu/drm/tidss/Makefile create mode 100644 drivers/gpu/drm/tidss/tidss_crtc.c create mode 100644 drivers/gpu/drm/tidss/tidss_crtc.h create mode 100644 drivers/gpu/drm/tidss/tidss_dispc.h create mode 100644 drivers/gpu/drm/tidss/tidss_dispc6.c create mode 100644 drivers/gpu/drm/tidss/tidss_dispc6.h create mode 100644 drivers/gpu/drm/tidss/tidss_drv.c create mode 100644 drivers/gpu/drm/tidss/tidss_drv.h create mode 100644 drivers/gpu/drm/tidss/tidss_encoder.c create mode 100644 drivers/gpu/drm/tidss/tidss_encoder.h create mode 100644 drivers/gpu/drm/tidss/tidss_irq.c create mode 100644 drivers/gpu/drm/tidss/tidss_irq.h create mode 100644 drivers/gpu/drm/tidss/tidss_kms.c create mode 100644 drivers/gpu/drm/tidss/tidss_kms.h create mode 100644 drivers/gpu/drm/tidss/tidss_plane.c create mode 100644 drivers/gpu/drm/tidss/tidss_plane.h -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index deeefa7a1773..b8b6c28a6800 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -289,6 +289,8 @@ source "drivers/gpu/drm/pl111/Kconfig" source "drivers/gpu/drm/tve200/Kconfig" +source "drivers/gpu/drm/tidss/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 50093ff4479b..3aa032fbb11a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -103,3 +103,4 @@ obj-$(CONFIG_DRM_MXSFB) += mxsfb/ obj-$(CONFIG_DRM_TINYDRM) += tinydrm/ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ +obj-$(CONFIG_DRM_TIDSS) += tidss/ diff --git a/drivers/gpu/drm/tidss/Kconfig b/drivers/gpu/drm/tidss/Kconfig new file mode 100644 index 000000000000..818666db08a4 --- /dev/null +++ b/drivers/gpu/drm/tidss/Kconfig @@ -0,0 +1,10 @@ +config DRM_TIDSS + tristate "DRM Support for TI Keystone" + depends on DRM && OF + depends on ARM || ARM64 || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + select VIDEOMODE_HELPERS + help + TI Keystone diff --git a/drivers/gpu/drm/tidss/Makefile b/drivers/gpu/drm/tidss/Makefile new file mode 100644 index 000000000000..ee6b24db0441 --- /dev/null +++ b/drivers/gpu/drm/tidss/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +tidss-y := tidss_drv.o \ + tidss_kms.o \ + tidss_crtc.o \ + tidss_plane.o \ + tidss_encoder.o \ + tidss_dispc6.o \ + tidss_irq.o + +obj-$(CONFIG_DRM_TIDSS) += tidss.o diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c new file mode 100644 index 000000000000..22c11f1e3318 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tidss_crtc.h" +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_irq.h" + +/* ----------------------------------------------------------------------------- + * Page Flip + */ + +static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc) +{ + struct drm_device *ddev = tcrtc->crtc.dev; + struct tidss_device *tidss = ddev->dev_private; + struct drm_pending_vblank_event *event; + unsigned long flags; + bool busy; + + /* + * New settings are taken into use at VFP, and GO bit is cleared at + * the same time. This happens before the vertical blank interrupt. + * So there is a small change that the driver sets GO bit after VFP, but + * before vblank, and we have to check for that case here. + */ + busy = tidss->dispc_ops->vp_go_busy(tidss->dispc, tcrtc->hw_videoport); + if (busy) + return; + + spin_lock_irqsave(&ddev->event_lock, flags); + + event = tcrtc->event; + tcrtc->event = NULL; + + if (!event) { + spin_unlock_irqrestore(&ddev->event_lock, flags); + return; + } + + dev_dbg(ddev->dev, "%s\n", __func__); + + drm_crtc_send_vblank_event(&tcrtc->crtc, event); + + spin_unlock_irqrestore(&ddev->event_lock, flags); + + drm_crtc_vblank_put(&tcrtc->crtc); +} + +void tidss_crtc_vblank_irq(struct drm_crtc *crtc) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + drm_crtc_handle_vblank(crtc); + + tidss_crtc_finish_page_flip(tcrtc); +} + +void tidss_crtc_framedone_irq(struct drm_crtc *crtc) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + complete(&tcrtc->framedone_completion); +} + +void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + + dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n", + tcrtc->hw_videoport, irqstatus); +} + + +/* ----------------------------------------------------------------------------- + * CRTC Functions + */ + +static int tidss_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + const struct drm_display_mode *mode = &state->adjusted_mode; + struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(state); + int r; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->enable) + return 0; + + r = tidss->dispc_ops->vp_check_config(tidss->dispc, tcrtc->hw_videoport, + mode, + tcrtc_state->bus_format, + tcrtc_state->bus_flags); + + if (r == 0) + return 0; + + dev_err(ddev->dev, "%s: failed (%ux%u pclk %u kHz)\n", + __func__, mode->hdisplay, mode->vdisplay, mode->clock); + + return r; +} + +static void tidss_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *ddev = crtc->dev; + + dev_dbg(ddev->dev, "%s\n", __func__); +} + +static void tidss_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s, crtc enabled %d, event %p\n", + __func__, tcrtc->enabled, crtc->state->event); + + /* Only flush the CRTC if it is currently enabled. */ + if (!tcrtc->enabled) + return; + + WARN_ON(tidss->dispc_ops->vp_go_busy(tidss->dispc, + tcrtc->hw_videoport)); + + // I think we always need the event to signal flip done + WARN_ON(!crtc->state->event); + + if (crtc->state->color_mgmt_changed) { + struct drm_color_lut *lut = NULL; + uint length = 0; + + if (crtc->state->gamma_lut) { + lut = (struct drm_color_lut *) + crtc->state->gamma_lut->data; + length = crtc->state->gamma_lut->length / + sizeof(*lut); + } + tidss->dispc_ops->vp_set_gamma(tidss->dispc, + tcrtc->hw_videoport, + lut, length); + } + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irq(&ddev->event_lock); + tidss->dispc_ops->vp_go(tidss->dispc, tcrtc->hw_videoport); + + if (crtc->state->event) { + tcrtc->event = crtc->state->event; + crtc->state->event = NULL; + } + + spin_unlock_irq(&ddev->event_lock); +} + +static void tidss_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc->state); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + int r; + + dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); + + tidss->dispc_ops->runtime_get(tidss->dispc); + + r = tidss->dispc_ops->vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport, + mode->clock * 1000); + WARN_ON(r); + + r = tidss->dispc_ops->vp_enable_clk(tidss->dispc, tcrtc->hw_videoport); + WARN_ON(r); + + /* Turn vertical blanking interrupt reporting on. */ + drm_crtc_vblank_on(crtc); + + tcrtc->enabled = true; + + tidss->dispc_ops->vp_enable(tidss->dispc, tcrtc->hw_videoport, + mode, + tcrtc_state->bus_format, + tcrtc_state->bus_flags); + + spin_lock_irq(&ddev->event_lock); + + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + + spin_unlock_irq(&ddev->event_lock); +} + +static void tidss_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); + + reinit_completion(&tcrtc->framedone_completion); + + tidss->dispc_ops->vp_disable(tidss->dispc, tcrtc->hw_videoport); + + if (!wait_for_completion_timeout(&tcrtc->framedone_completion, + msecs_to_jiffies(500))) + dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d", + tcrtc->hw_videoport); + + spin_lock_irq(&ddev->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&ddev->event_lock); + + tcrtc->enabled = false; + + drm_crtc_vblank_off(crtc); + + tidss->dispc_ops->vp_disable_clk(tidss->dispc, tcrtc->hw_videoport); + + tidss->dispc_ops->runtime_put(tidss->dispc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static +enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + return tidss->dispc_ops->vp_check_mode(tidss->dispc, + tcrtc->hw_videoport, + mode); +} + +static const struct drm_crtc_helper_funcs crtc_helper_funcs = { + .atomic_check = tidss_crtc_atomic_check, + .atomic_begin = tidss_crtc_atomic_begin, + .atomic_flush = tidss_crtc_atomic_flush, + .atomic_enable = tidss_crtc_atomic_enable, + .atomic_disable = tidss_crtc_atomic_disable, + + .mode_valid = tidss_crtc_mode_valid, +}; + +static void tidss_crtc_reset(struct drm_crtc *crtc) +{ + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(crtc->state); + crtc->state = kzalloc(sizeof(struct tidss_crtc_state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; +} + +static struct drm_crtc_state * +tidss_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct tidss_crtc_state *state, *current_state; + + if (WARN_ON(!crtc->state)) + return NULL; + + current_state = to_tidss_crtc_state(crtc->state); + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + state->bus_format = current_state->bus_format; + state->bus_flags = current_state->bus_flags; + + return &state->base; +} + +static int tidss_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss->dispc_ops->runtime_get(tidss->dispc); + + tidss_irq_enable_vblank(crtc); + + return 0; +} + +static void tidss_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss_irq_disable_vblank(crtc); + + tidss->dispc_ops->runtime_put(tidss->dispc); +} + +static const struct drm_crtc_funcs crtc_funcs = { + .reset = tidss_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = tidss_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = tidss_crtc_enable_vblank, + .disable_vblank = tidss_crtc_disable_vblank, +}; + +struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss, u32 hw_videoport, + struct drm_plane *primary, struct device_node *epnode) +{ + struct tidss_crtc *tcrtc; + struct drm_crtc *crtc; + int ret; + + tcrtc = devm_kzalloc(tidss->dev, sizeof(*tcrtc), GFP_KERNEL); + if (!tcrtc) + return ERR_PTR(-ENOMEM); + + tcrtc->hw_videoport = hw_videoport; + init_completion(&tcrtc->framedone_completion); + + crtc = &tcrtc->crtc; + + ret = drm_crtc_init_with_planes(tidss->ddev, crtc, primary, + NULL, &crtc_funcs, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_crtc_helper_add(crtc, &crtc_helper_funcs); + + /* The dispc API adapts to what ever size we ask from in no + * matter what HW supports. X-server assumes 256 element gamma + * tables so lets use that. Size of HW gamma table can be + * extracted with dispc_vp_gamma_size(). If it returns 0 + * gamma table is not supprted. + */ + if (tidss->dispc_ops->vp_gamma_size(tidss->dispc, hw_videoport)) { + uint gamma_lut_size = 256; + + drm_crtc_enable_color_mgmt(crtc, 0, false, gamma_lut_size); + drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); + } + + return tcrtc; +} diff --git a/drivers/gpu/drm/tidss/tidss_crtc.h b/drivers/gpu/drm/tidss/tidss_crtc.h new file mode 100644 index 000000000000..9f32d81e55d9 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_crtc.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_CRTC_H__ +#define __TIDSS_CRTC_H__ + +#include +#include +#include + +#include "tidss_dispc.h" + +#define to_tidss_crtc(c) container_of((c), struct tidss_crtc, crtc) + +struct tidss_crtc { + struct drm_crtc crtc; + + u32 hw_videoport; + + struct drm_pending_vblank_event *event; + + /* has crtc_atomic_enable been called? */ + bool enabled; + + struct completion framedone_completion; +}; + +#define to_tidss_crtc_state(x) container_of(x, struct tidss_crtc_state, base) + +struct tidss_crtc_state { + /* Must be first. */ + struct drm_crtc_state base; + + u32 bus_format; + u32 bus_flags; +}; + +struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss, u32 hw_videoport, + struct drm_plane *primary, struct device_node *epnode); + + +void tidss_crtc_vblank_irq(struct drm_crtc *crtc); +void tidss_crtc_framedone_irq(struct drm_crtc *crtc); +void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h new file mode 100644 index 000000000000..38ee3d75404e --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_DISPC_H__ +#define __TIDSS_DISPC_H__ + +struct tidss_device; +struct dispc_device; +struct drm_color_lut; + +#define DSS_MAX_CHANNELS 8 +#define DSS_MAX_PLANES 8 + +/* + * Based on the above 2 defines the bellow defines describe following + * u64 IRQ bits: + * + * bit group |dev |mrg0|mrg1|mrg2|mrg3|mrg4|mrg5|mrg6|mrg7|plane 0-7| | + * bit use |Dfou|FEOL|FEOL|FEOL|FEOL|FEOL|FEOL|FEOL|FEOL|UUUU|UUUU| | | | | | + * bit number|0-3 |4-7 |8-11| 12-35 | 36-43 | 44-63 | + * + * device bits: D = OCP error + * WB bits: f = frame done wb, o = wb buffer overflow, + * u = wb buffer uncomplete + * vp bits: F = frame done, E = vsync even, O = vsync odd, L = sync lost + * plane bits: U = fifo underflow + */ + +#define DSS_IRQ_DEVICE_OCP_ERR BIT_ULL(0) + +#define DSS_IRQ_DEVICE_FRAMEDONEWB BIT_ULL(1) +#define DSS_IRQ_DEVICE_WBBUFFEROVERFLOW BIT_ULL(2) +#define DSS_IRQ_DEVICE_WBUNCOMPLETEERROR BIT_ULL(3) +#define DSS_IRQ_DEVICE_WB_MASK GENMASK_ULL(3, 1) + +#define DSS_IRQ_VP_BIT_N(ch, bit) (4 + 4 * ch + bit) +#define DSS_IRQ_PLANE_BIT_N(plane, bit) \ + (DSS_IRQ_VP_BIT_N(DSS_MAX_CHANNELS, 0) + 1 * plane + bit) + +#define DSS_IRQ_VP_BIT(ch, bit) BIT_ULL(DSS_IRQ_VP_BIT_N(ch, bit)) +#define DSS_IRQ_PLANE_BIT(plane, bit) BIT_ULL(DSS_IRQ_PLANE_BIT_N(plane, bit)) + +#define DSS_IRQ_VP_MASK(ch) \ + GENMASK_ULL(DSS_IRQ_VP_BIT_N(ch, 3), DSS_IRQ_VP_BIT_N(ch, 0)) +#define DSS_IRQ_PLANE_MASK(plane) \ + GENMASK_ULL(DSS_IRQ_PLANE_BIT_N(plane, 0), DSS_IRQ_PLANE_BIT_N(plane, 0)) + +#define DSS_IRQ_VP_FRAME_DONE(ch) DSS_IRQ_VP_BIT(ch, 0) +#define DSS_IRQ_VP_VSYNC_EVEN(ch) DSS_IRQ_VP_BIT(ch, 1) +#define DSS_IRQ_VP_VSYNC_ODD(ch) DSS_IRQ_VP_BIT(ch, 2) +#define DSS_IRQ_VP_SYNC_LOST(ch) DSS_IRQ_VP_BIT(ch, 3) + +#define DSS_IRQ_PLANE_FIFO_UNDERFLOW(plane) DSS_IRQ_PLANE_BIT(plane, 0) + +struct tidss_vp_info { + u32 default_color; +}; + +struct tidss_plane_info { + dma_addr_t paddr; + dma_addr_t p_uv_addr; /* for NV12 format */ + u16 fb_width; + u16 width; + u16 height; + u32 fourcc; + + u16 pos_x; + u16 pos_y; + u16 out_width; /* if 0, out_width == width */ + u16 out_height; /* if 0, out_height == height */ + u8 global_alpha; + u8 pre_mult_alpha; + u8 zorder; +}; + +struct dispc_ops { + u64 (*read_and_clear_irqstatus)(struct dispc_device *dispc); + void (*write_irqenable)(struct dispc_device *dispc, u64 enable); + + int (*runtime_get)(struct dispc_device *dispc); + void (*runtime_put)(struct dispc_device *dispc); + + int (*get_num_planes)(struct dispc_device *dispc); + int (*get_num_vps)(struct dispc_device *dispc); + + const char *(*plane_name)(struct dispc_device *dispc, + u32 hw_plane); + const char *(*vp_name)(struct dispc_device *dispc, + u32 hw_videoport); + + u32 (*get_memory_bandwidth_limit)(struct dispc_device *dispc); + + void (*vp_enable)(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags); + + void (*vp_disable)(struct dispc_device *dispc, u32 hw_videoport); + + bool (*vp_go_busy)(struct dispc_device *dispc, + u32 hw_videoport); + void (*vp_go)(struct dispc_device *dispc, u32 hw_videoport); + + void (*vp_setup)(struct dispc_device *dispc, u32 hw_videoport, + const struct tidss_vp_info *info); + + enum drm_mode_status (*vp_check_mode)(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_display_mode *mode); + + int (*vp_check_config)(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags); + + u32 (*vp_gamma_size)(struct dispc_device *dispc, + u32 hw_videoport); + void (*vp_set_gamma)(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_color_lut *lut, + unsigned int length); + + int (*plane_enable)(struct dispc_device *dispc, u32 hw_plane, + bool enable); + int (*plane_setup)(struct dispc_device *dispc, u32 hw_plane, + const struct tidss_plane_info *oi, + u32 hw_videoport); + + int (*vp_set_clk_rate)(struct dispc_device *dispc, + u32 hw_videoport, unsigned long rate); + int (*vp_enable_clk)(struct dispc_device *dispc, u32 hw_videoport); + void (*vp_disable_clk)(struct dispc_device *dispc, u32 hw_videoport); + + int (*runtime_suspend)(struct dispc_device *dispc); + int (*runtime_resume)(struct dispc_device *dispc); + + void (*remove)(struct dispc_device *dispc); + + int (*modeset_init)(struct dispc_device *dispc); +}; + +int dispc6_init(struct tidss_device *tidss); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_dispc6.c b/drivers/gpu/drm/tidss/tidss_dispc6.c new file mode 100644 index 000000000000..7772d4484a6e --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc6.c @@ -0,0 +1,1450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tidss_crtc.h" +#include "tidss_drv.h" +#include "tidss_encoder.h" +#include "tidss_plane.h" + +#include "tidss_dispc6.h" + +static const struct { + u32 fmt; + u32 port_width; +} dispc6_bus_formats[] = { + { MEDIA_BUS_FMT_RGB444_1X12, 12 }, + { MEDIA_BUS_FMT_RGB565_1X16, 16 }, + { MEDIA_BUS_FMT_RGB666_1X18, 18 }, + { MEDIA_BUS_FMT_RGB888_1X24, 24 }, +}; + +/* + * TRM gives bitfields as start:end, where start is the higher bit + * number. For example 7:0 + */ + +#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + + +#define REG_GET(dispc, idx, start, end) \ + FLD_GET(dispc6_read(dispc, idx), start, end) + +#define REG_FLD_MOD(dispc, idx, val, start, end) \ + dispc6_write(dispc, idx, FLD_MOD(dispc6_read(dispc, idx), val, start, end)) + +#define VID_REG_GET(dispc, plane, idx, start, end) \ + FLD_GET(dispc6_vid_read(dispc, plane, idx), start, end) + +#define VID_REG_FLD_MOD(dispc, plane, idx, val, start, end) \ + dispc6_vid_write(dispc, plane, idx, FLD_MOD(dispc6_vid_read(dispc, plane, idx), val, start, end)) + + +#define VP_REG_GET(dispc, vp, idx, start, end) \ + FLD_GET(dispc6_vp_read(dispc, vp, idx), start, end) + +#define VP_REG_FLD_MOD(dispc, vp, idx, val, start, end) \ + dispc6_vp_write(dispc, vp, idx, FLD_MOD(dispc6_vp_read(dispc, vp, idx), val, start, end)) + +struct dispc_features { + /* XXX should these come from the .dts? Min pclk is not feature of DSS IP */ + unsigned long min_pclk; + unsigned long max_pclk; +}; + +/* Note: 9MHz is a special allowed case, and is handled separately in the code */ +static const struct dispc_features k2g_dispc_feats = { + .min_pclk = 43750000, + .max_pclk = 150000000, +}; + +static const struct of_device_id dispc6_of_match[] = { + { .compatible = "ti,k2g-dss", .data = &k2g_dispc_feats, }, + {}, +}; + +struct dispc_device { + struct device *dev; + + void __iomem *base_cfg; + void __iomem *base_common; + void __iomem *base_vid1; + void __iomem *base_ovr1; + void __iomem *base_vp1; + + const struct dispc_features *feat; + + struct clk *fclk; + struct clk *vp_clk; + + bool is_enabled; + + u32 gamma_table[256]; + + struct tidss_device *tidss; +}; + +static void dispc6_write(struct dispc_device *dispc, u16 reg, u32 val) +{ + iowrite32(val, dispc->base_common + reg); +} + +static u32 dispc6_read(struct dispc_device *dispc, u16 reg) +{ + return ioread32(dispc->base_common + reg); +} + +static void dispc6_vid_write(struct dispc_device *dispc, + u32 hw_plane, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vid1; + + iowrite32(val, base + reg); +} + +static u32 dispc6_vid_read(struct dispc_device *dispc, + u32 hw_plane, u16 reg) +{ + void __iomem *base = dispc->base_vid1; + + return ioread32(base + reg); +} + +static void dispc6_ovr_write(struct dispc_device *dispc, + u32 hw_videoport, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_ovr1; + + iowrite32(val, base + reg); +} + +__maybe_unused +static u32 dispc6_ovr_read(struct dispc_device *dispc, + u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_ovr1; + + return ioread32(base + reg); +} + +static void dispc6_vp_write(struct dispc_device *dispc, + u32 hw_videoport, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vp1; + + iowrite32(val, base + reg); +} + +static u32 dispc6_vp_read(struct dispc_device *dispc, + u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_vp1; + + return ioread32(base + reg); +} + +static int dispc6_runtime_get(struct dispc_device *dispc) +{ + int r; + + dev_dbg(dispc->dev, "dispc_runtime_get\n"); + + r = pm_runtime_get_sync(dispc->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +static void dispc6_runtime_put(struct dispc_device *dispc) +{ + int r; + + dev_dbg(dispc->dev, "dispc_runtime_put\n"); + + r = pm_runtime_put_sync(dispc->dev); + WARN_ON(r < 0); +} + +static u64 dispc6_vp_irq_from_raw(u32 stat) +{ + u32 vp = 0; + u64 vp_stat = 0; + + if (stat & BIT(0)) + vp_stat |= DSS_IRQ_VP_FRAME_DONE(vp); + if (stat & BIT(1)) + vp_stat |= DSS_IRQ_VP_VSYNC_EVEN(vp); + if (stat & BIT(2)) + vp_stat |= DSS_IRQ_VP_VSYNC_ODD(vp); + if (stat & BIT(4)) + vp_stat |= DSS_IRQ_VP_SYNC_LOST(vp); + + return vp_stat; +} + +static u32 dispc6_vp_irq_to_raw(u64 vpstat) +{ + u32 vp = 0; + u32 stat = 0; + + if (vpstat & DSS_IRQ_VP_FRAME_DONE(vp)) + stat |= BIT(0); + if (vpstat & DSS_IRQ_VP_VSYNC_EVEN(vp)) + stat |= BIT(1); + if (vpstat & DSS_IRQ_VP_VSYNC_ODD(vp)) + stat |= BIT(2); + if (vpstat & DSS_IRQ_VP_SYNC_LOST(vp)) + stat |= BIT(4); + + return stat; +} + +static u64 dispc6_vid_irq_from_raw(u32 stat) +{ + uint plane = 0; + u64 vid_stat = 0; + + if (stat & BIT(0)) + vid_stat |= DSS_IRQ_PLANE_FIFO_UNDERFLOW(plane); + + return vid_stat; +} + +static u32 dispc6_vid_irq_to_raw(u64 vidstat) +{ + uint plane = 0; + u32 stat = 0; + + if (vidstat & DSS_IRQ_PLANE_FIFO_UNDERFLOW(plane)) + stat |= BIT(0); + + return stat; +} + + +static u64 dispc6_vp_read_irqstatus(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc6_vp_read(dispc, hw_videoport, DISPC_VP_IRQSTATUS); + + return dispc6_vp_irq_from_raw(stat); +} + +static void dispc6_vp_write_irqstatus(struct dispc_device *dispc, + u32 hw_videoport, + u64 vpstat) +{ + u32 stat = dispc6_vp_irq_to_raw(vpstat); + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_IRQSTATUS, stat); +} + +static u64 dispc6_vid_read_irqstatus(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc6_vid_read(dispc, hw_plane, DISPC_VID_IRQSTATUS); + + return dispc6_vid_irq_from_raw(stat); +} + +static void dispc6_vid_write_irqstatus(struct dispc_device *dispc, + u32 hw_plane, + u64 vidstat) +{ + u32 stat = dispc6_vid_irq_to_raw(vidstat); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_IRQSTATUS, stat); +} + + +static u64 dispc6_vp_read_irqenable(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc6_vp_read(dispc, hw_videoport, DISPC_VP_IRQENABLE); + + return dispc6_vp_irq_from_raw(stat); +} + +static void dispc6_vp_write_irqenable(struct dispc_device *dispc, + u32 hw_videoport, + u64 vpstat) +{ + u32 stat = dispc6_vp_irq_to_raw(vpstat); + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_IRQENABLE, stat); +} + +static u64 dispc6_vid_read_irqenable(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc6_vid_read(dispc, hw_plane, DISPC_VID_IRQENABLE); + + return dispc6_vid_irq_from_raw(stat); +} + +static void dispc6_vid_write_irqenable(struct dispc_device *dispc, + u32 hw_plane, + u64 vidstat) +{ + u32 stat = dispc6_vid_irq_to_raw(vidstat); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_IRQENABLE, stat); +} + + +static void dispc6_clear_irqstatus(struct dispc_device *dispc, u64 mask) +{ + dispc6_vp_write_irqstatus(dispc, 0, mask); + dispc6_vid_write_irqstatus(dispc, 0, mask); +} + +static u64 dispc6_read_and_clear_irqstatus(struct dispc_device *dispc) +{ + u64 stat = 0; + + // always clear the top level irqstatus + dispc6_write(dispc, DISPC_IRQSTATUS, + dispc6_read(dispc, DISPC_IRQSTATUS)); + + stat |= dispc6_vp_read_irqstatus(dispc, 0); + stat |= dispc6_vid_read_irqstatus(dispc, 0); + + dispc6_clear_irqstatus(dispc, stat); + + return stat; +} + +static u64 dispc6_read_irqenable(struct dispc_device *dispc) +{ + u64 stat = 0; + + stat |= dispc6_vp_read_irqenable(dispc, 0); + stat |= dispc6_vid_read_irqenable(dispc, 0); + + return stat; +} + +static void dispc6_write_irqenable(struct dispc_device *dispc, u64 mask) +{ + u64 old_mask = dispc6_read_irqenable(dispc); + + /* clear the irqstatus for newly enabled irqs */ + dispc6_clear_irqstatus(dispc, (mask ^ old_mask) & mask); + + dispc6_vp_write_irqenable(dispc, 0, mask); + dispc6_vid_write_irqenable(dispc, 0, mask); + + dispc6_write(dispc, DISPC_IRQENABLE_SET, (1 << 0) | (1 << 7)); + + /* flush posted write */ + dispc6_read_irqenable(dispc); +} + +static void dispc6_set_num_datalines(struct dispc_device *dispc, + u32 hw_videoport, int num_lines) +{ + int v; + + switch (num_lines) { + case 12: + v = 0; break; + case 16: + v = 1; break; + case 18: + v = 2; break; + case 24: + v = 3; break; + case 30: + v = 4; break; + case 36: + v = 5; break; + default: + WARN_ON(1); + v = 3; + break; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8); +} + +static void dispc6_vp_enable(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags) +{ + bool align, onoff, rf, ieo, ipc, ihs, ivs; + int i; + u32 port_width; + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + for (i = 0; i < ARRAY_SIZE(dispc6_bus_formats); ++i) { + if (dispc6_bus_formats[i].fmt != bus_fmt) + continue; + + port_width = dispc6_bus_formats[i].port_width; + break; + } + + if (WARN_ON(i == ARRAY_SIZE(dispc6_bus_formats))) + return; + + dispc6_set_num_datalines(dispc, hw_videoport, port_width); + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_H, + FLD_VAL(hsw - 1, 7, 0) | + FLD_VAL(hfp - 1, 19, 8) | + FLD_VAL(hbp - 1, 31, 20)); + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_V, + FLD_VAL(vsw - 1, 7, 0) | + FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20)); + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ivs = true; + else + ivs = false; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ihs = true; + else + ihs = false; + + if (bus_flags & DRM_BUS_FLAG_DE_LOW) + ieo = true; + else + ieo = false; + + if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + ipc = true; + else + ipc = false; + + /* always use the 'rf' setting */ + onoff = true; + + if (bus_flags & DRM_BUS_FLAG_SYNC_NEGEDGE) + rf = false; + else + rf = true; + + /* always use aligned syncs */ + align = true; + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ, + FLD_VAL(align, 18, 18) | + FLD_VAL(onoff, 17, 17) | + FLD_VAL(rf, 16, 16) | + FLD_VAL(ieo, 15, 15) | + FLD_VAL(ipc, 14, 14) | + FLD_VAL(ihs, 13, 13) | + FLD_VAL(ivs, 12, 12)); + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_SIZE_SCREEN, + FLD_VAL(mode->hdisplay - 1, 11, 0) | + FLD_VAL(mode->vdisplay - 1, 27, 16)); + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 0, 0); +} + +static void dispc6_vp_disable(struct dispc_device *dispc, u32 hw_videoport) +{ + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 0, 0, 0); +} + +static bool dispc6_vp_go_busy(struct dispc_device *dispc, + u32 hw_videoport) +{ + return VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5); +} + +static void dispc6_vp_go(struct dispc_device *dispc, + u32 hw_videoport) +{ + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 5, 5); +} + +static u16 c8_to_c12(u8 c8) +{ + u16 c12; + + c12 = c8 << 4; + + /* Replication logic: Copy c8 4 MSB to 4 LSB for full scale c12 */ + c12 = c8 >> 4; + + return c12; +} + +static u64 argb8888_to_argb12121212(u32 argb8888) +{ + u8 a, r, g, b; + u64 v; + + a = (argb8888 >> 24) & 0xff; + r = (argb8888 >> 16) & 0xff; + g = (argb8888 >> 8) & 0xff; + b = (argb8888 >> 0) & 0xff; + + v = ((u64)c8_to_c12(a) << 36) | ((u64)c8_to_c12(r) << 24) | + ((u64)c8_to_c12(g) << 12) | (u64)c8_to_c12(b); + + return v; +} + +static void dispc6_vp_setup(struct dispc_device *dispc, + u32 hw_videoport, + const struct tidss_vp_info *info) +{ + u64 v; + + v = argb8888_to_argb12121212(info->default_color); + + dispc6_ovr_write(dispc, 0, DISPC_OVR_DEFAULT_COLOR, v & 0xffffffff); + dispc6_ovr_write(dispc, 0, DISPC_OVR_DEFAULT_COLOR2, + (v >> 32) & 0xffff); +} + +static enum drm_mode_status dispc6_vp_check_mode(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_display_mode *mode) +{ + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + /* special case for 9MHz */ + if (mode->clock * 1000 < dispc->feat->min_pclk && mode->clock != 9000) + return MODE_CLOCK_LOW; + + if (mode->clock * 1000 > dispc->feat->max_pclk) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > 4096) + return MODE_BAD; + + if (mode->vdisplay > 4096) + return MODE_BAD; + + /* TODO: add interlace support */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096) + return MODE_BAD_HVALUE; + + if (vsw < 1 || vsw > 256 || + vfp < 0 || vfp > 4095 || + vbp < 0 || vbp > 4095) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +static int dispc6_vp_check_config(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags) +{ + enum drm_mode_status ok; + int i; + + ok = dispc6_vp_check_mode(dispc, hw_videoport, mode); + if (ok != MODE_OK) + return -EINVAL; + + + for (i = 0; i < ARRAY_SIZE(dispc6_bus_formats); ++i) { + if (dispc6_bus_formats[i].fmt == bus_fmt) + break; + } + + if (i == ARRAY_SIZE(dispc6_bus_formats)) + return -EINVAL; + + return 0; +} + +static int dispc6_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport) +{ + return clk_prepare_enable(dispc->vp_clk); +} + +static void dispc6_vp_disable_clk(struct dispc_device *dispc, + u32 hw_videoport) +{ + clk_disable_unprepare(dispc->vp_clk); +} + +static int dispc6_vp_set_clk_rate(struct dispc_device *dispc, + u32 hw_videoport, unsigned long rate) +{ + int r; + unsigned long new_rate; + + r = clk_set_rate(dispc->vp_clk, rate); + if (r) { + dev_err(dispc->dev, "Failed to set vp clk rate to %lu\n", + rate); + return r; + } + + new_rate = clk_get_rate(dispc->vp_clk); + + if (rate != new_rate) + dev_warn(dispc->dev, + "Failed to get exact pix clock %lu != %lu\n", + rate, new_rate); + + dev_dbg(dispc->dev, "New VP rate %lu Hz (requested %lu Hz)\n", + clk_get_rate(dispc->vp_clk), rate); + + return 0; +} + +/* CSC */ + +struct color_conv_coef { + int ry, rcb, rcr; + int gy, gcb, gcr; + int by, bcb, bcr; + int roffset, goffset, boffset; + bool full_range; +}; + +static void dispc6_vid_write_color_conv_coefs(struct dispc_device *dispc, + u32 hw_plane, + const struct color_conv_coef *ct) +{ +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + dispc6_vid_write(dispc, hw_plane, + DISPC_VID_CONV_COEF(0), CVAL(ct->rcr, ct->ry)); + dispc6_vid_write(dispc, hw_plane, + DISPC_VID_CONV_COEF(1), CVAL(ct->gy, ct->rcb)); + dispc6_vid_write(dispc, hw_plane, + DISPC_VID_CONV_COEF(2), CVAL(ct->gcb, ct->gcr)); + dispc6_vid_write(dispc, hw_plane, + DISPC_VID_CONV_COEF(3), CVAL(ct->bcr, ct->by)); + dispc6_vid_write(dispc, hw_plane, + DISPC_VID_CONV_COEF(4), CVAL(0, ct->bcb)); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_CONV_COEF(5), + FLD_VAL(ct->roffset, 15, 3) | + FLD_VAL(ct->goffset, 31, 19)); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_CONV_COEF(6), + FLD_VAL(ct->boffset, 15, 3)); + + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + ct->full_range, 11, 11); + +#undef CVAL +} + +static void dispc6_vid_csc_setup(struct dispc_device *dispc) +{ + /* YUV -> RGB, ITU-R BT.601, full range */ + const struct color_conv_coef coefs_yuv2rgb_bt601_full = { + 256, 0, 358, + 256, -88, -182, + 256, 452, 0, + 0, -2048, -2048, + true, + }; + + dispc6_vid_write_color_conv_coefs(dispc, 0, &coefs_yuv2rgb_bt601_full); +} + +static void dispc6_vid_csc_enable(struct dispc_device *dispc, + u32 hw_plane, bool enable) +{ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 9, 9); +} + +/* SCALER */ + +static u32 dispc6_calc_fir_inc(u32 in, u32 out) +{ + return (u32)div_u64(0x200000ull * in, out); +} + +struct dispc6_vid_fir_coefs { + s16 c2[16]; + s16 c1[16]; + u16 c0[9]; +}; + +static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_null = { + .c2 = { 0 }, + .c1 = { 0 }, + .c0 = { 512, 512, 512, 512, 512, 512, 512, 512, 256, }, +}; + +/* M=8, Upscale x >= 1 */ +static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m8 = { + .c2 = { 0, -4, -8, -16, -24, -32, -40, -48, 0, 2, 4, 6, 8, 6, 4, 2, }, + .c1 = { 0, 28, 56, 94, 132, 176, 220, 266, -56, -60, -64, -62, -60, -50, -40, -20, }, + .c0 = { 512, 506, 500, 478, 456, 424, 392, 352, 312, }, +}; + +/* 5-tap, M=22, Downscale Ratio 2.5 < x < 3 */ +static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m22_5tap = { + .c2 = { 16, 20, 24, 30, 36, 42, 48, 56, 0, 0, 0, 2, 4, 8, 12, 14, }, + .c1 = { 132, 140, 148, 156, 164, 172, 180, 186, 64, 72, 80, 88, 96, 104, 112, 122, }, + .c0 = { 216, 216, 216, 214, 212, 208, 204, 198, 192, }, +}; + +/* 3-tap, M=22, Downscale Ratio 2.5 < x < 3 */ +static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m22_3tap = { + .c1 = { 100, 118, 136, 156, 176, 196, 216, 236, 0, 10, 20, 30, 40, 54, 68, 84, }, + .c0 = { 312, 310, 308, 302, 296, 286, 276, 266, 256, }, +}; + +enum dispc6_vid_fir_coef_set { + DISPC6_VID_FIR_COEF_HORIZ, + DISPC6_VID_FIR_COEF_HORIZ_UV, + DISPC6_VID_FIR_COEF_VERT, + DISPC6_VID_FIR_COEF_VERT_UV, +}; + +static void dispc6_vid_write_fir_coefs(struct dispc_device *dispc, + u32 hw_plane, + enum dispc6_vid_fir_coef_set coef_set, + const struct dispc6_vid_fir_coefs *coefs) +{ + static const u16 c0_regs[] = { + [DISPC6_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H0, + [DISPC6_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H0_C, + [DISPC6_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V0, + [DISPC6_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V0_C, + }; + + static const u16 c12_regs[] = { + [DISPC6_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H12, + [DISPC6_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H12_C, + [DISPC6_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V12, + [DISPC6_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V12_C, + }; + + const u16 c0_base = c0_regs[coef_set]; + const u16 c12_base = c12_regs[coef_set]; + int phase; + + for (phase = 0; phase <= 8; ++phase) { + u16 reg = c0_base + phase * 4; + u16 c0 = coefs->c0[phase]; + + dispc6_vid_write(dispc, hw_plane, reg, c0); + } + + for (phase = 0; phase <= 15; ++phase) { + u16 reg = c12_base + phase * 4; + s16 c1, c2; + u32 c12; + + c1 = coefs->c1[phase]; + c2 = coefs->c2[phase]; + c12 = FLD_VAL(c1, 19, 10) | FLD_VAL(c2, 29, 20); + + dispc6_vid_write(dispc, hw_plane, reg, c12); + } +} + +static void dispc6_vid_write_scale_coefs(struct dispc_device *dispc, + u32 hw_plane) +{ + dispc6_vid_write_fir_coefs(dispc, hw_plane, DISPC6_VID_FIR_COEF_HORIZ, + &dispc6_fir_coefs_null); + dispc6_vid_write_fir_coefs(dispc, hw_plane, DISPC6_VID_FIR_COEF_HORIZ_UV, + &dispc6_fir_coefs_null); + dispc6_vid_write_fir_coefs(dispc, hw_plane, DISPC6_VID_FIR_COEF_VERT, + &dispc6_fir_coefs_null); + dispc6_vid_write_fir_coefs(dispc, hw_plane, DISPC6_VID_FIR_COEF_VERT_UV, + &dispc6_fir_coefs_null); +} + +static void dispc6_vid_set_scaling(struct dispc_device *dispc, + u32 hw_plane, + u32 orig_width, u32 orig_height, + u32 out_width, u32 out_height, + u32 fourcc) +{ + u32 in_w, in_h, in_w_uv, in_h_uv; + u32 fir_hinc, fir_vinc, fir_hinc_uv, fir_vinc_uv; + bool scale_x, scale_y; + bool five_taps = false; /* XXX always 3-tap for now */ + + in_w = in_w_uv = orig_width; + in_h = in_h_uv = orig_height; + + switch (fourcc) { + case DRM_FORMAT_NV12: + /* UV is subsampled by 2 horizontally and vertically */ + in_h_uv >>= 1; + in_w_uv >>= 1; + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + /* UV is subsampled by 2 horizontally */ + in_w_uv >>= 1; + break; + + default: + break; + } + + scale_x = in_w != out_width || in_w_uv != out_width; + scale_y = in_h != out_height || in_h_uv != out_height; + + /* HORIZONTAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, scale_x, 7, 7); + + /* VERTICAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, scale_y, 8, 8); + + /* Skip the rest if no scaling is used */ + if (!scale_x && !scale_y) + return; + + /* VERTICAL 5-TAPS */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, five_taps, 21, 21); + + /* FIR INC */ + + fir_hinc = dispc6_calc_fir_inc(in_w, out_width); + fir_vinc = dispc6_calc_fir_inc(in_h, out_height); + fir_hinc_uv = dispc6_calc_fir_inc(in_w_uv, out_width); + fir_vinc_uv = dispc6_calc_fir_inc(in_h_uv, out_height); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_FIRH, fir_hinc); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_FIRV, fir_vinc); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_FIRH2, fir_hinc_uv); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_FIRV2, fir_vinc_uv); + + dispc6_vid_write_scale_coefs(dispc, hw_plane); +} + +/* OTHER */ + +static const struct { + u32 fourcc; + u8 dss_code; + u8 bytespp; +} dispc6_color_formats[] = { + { DRM_FORMAT_ARGB4444, 0x0, 2, }, + { DRM_FORMAT_ABGR4444, 0x1, 2, }, + { DRM_FORMAT_RGBA4444, 0x2, 2, }, + + { DRM_FORMAT_RGB565, 0x3, 2, }, + { DRM_FORMAT_BGR565, 0x4, 2, }, + + { DRM_FORMAT_ARGB1555, 0x5, 2, }, + { DRM_FORMAT_ABGR1555, 0x6, 2, }, + + { DRM_FORMAT_ARGB8888, 0x7, 4, }, + { DRM_FORMAT_ABGR8888, 0x8, 4, }, + { DRM_FORMAT_RGBA8888, 0x9, 4, }, + { DRM_FORMAT_BGRA8888, 0xa, 4, }, + + { DRM_FORMAT_XRGB8888, 0x27, 4, }, + { DRM_FORMAT_XBGR8888, 0x28, 4, }, + { DRM_FORMAT_RGBX8888, 0x29, 4, }, + { DRM_FORMAT_BGRX8888, 0x2a, 4, }, + + { DRM_FORMAT_YUYV, 0x3e, 2, }, + { DRM_FORMAT_UYVY, 0x3f, 2, }, + + { DRM_FORMAT_NV12, 0x3d, 2, }, +}; + +static bool dispc6_fourcc_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_NV12: + return true; + default: + return false; + } +} + +static void dispc6_plane_set_pixel_format(struct dispc_device *dispc, + u32 hw_plane, u32 fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dispc6_color_formats); ++i) { + if (dispc6_color_formats[i].fourcc == fourcc) { + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + dispc6_color_formats[i].dss_code, + 6, 1); + return; + } + } + + WARN_ON(1); +} + +static int dispc6_fourcc_to_bytespp(u32 fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dispc6_color_formats); ++i) { + if (dispc6_color_formats[i].fourcc == fourcc) + return dispc6_color_formats[i].bytespp; + } + + WARN_ON(1); + return 4; +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + + WARN_ON(1); + return 0; +} + +static int dispc6_plane_setup(struct dispc_device *dispc, u32 hw_plane, + const struct tidss_plane_info *oi, + u32 hw_videoport) +{ + u32 fourcc = oi->fourcc; + int bytespp = dispc6_fourcc_to_bytespp(fourcc); + + dispc6_plane_set_pixel_format(dispc, hw_plane, fourcc); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_BA_0, oi->paddr); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_BA_1, oi->paddr); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_0, oi->p_uv_addr); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_1, oi->p_uv_addr); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_PICTURE_SIZE, + (oi->width - 1) | ((oi->height - 1) << 16)); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, + pixinc(1, bytespp)); + dispc6_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC, + pixinc(1 + oi->fb_width - oi->width, bytespp)); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_POSITION, + oi->pos_x | (oi->pos_y << 16)); + + dispc6_vid_write(dispc, hw_plane, DISPC_VID_SIZE, + (oi->out_width - 1) | ((oi->out_height - 1) << 16)); + + dispc6_vid_set_scaling(dispc, hw_plane, + oi->width, oi->height, + oi->out_width, oi->out_height, + fourcc); + + /* enable YUV->RGB color conversion */ + if (dispc6_fourcc_is_yuv(fourcc)) + dispc6_vid_csc_enable(dispc, hw_plane, true); + else + dispc6_vid_csc_enable(dispc, hw_plane, false); + + /* hw_videoport */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 0, 16, 14); + + return 0; +} + +static int dispc6_plane_enable(struct dispc_device *dispc, + u32 hw_plane, bool enable) +{ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 0, 0); + return 0; +} + +static u32 dispc6_vid_get_fifo_size(struct dispc_device *dispc, + u32 hw_plane) +{ + const u32 unit_size = 16; /* 128-bits */ + + return VID_REG_GET(dispc, hw_plane, DISPC_VID_BUF_SIZE_STATUS, 15, 0) * + unit_size; +} + +static void dispc6_vid_set_mflag_threshold(struct dispc_device *dispc, + u32 hw_plane, + u32 low, u32 high) +{ + dispc6_vid_write(dispc, hw_plane, DISPC_VID_MFLAG_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static void dispc6_mflag_setup(struct dispc_device *dispc) +{ + u32 hw_plane = 0; + const u32 unit_size = 16; /* 128-bits */ + u32 size = dispc6_vid_get_fifo_size(dispc, hw_plane); + u32 low, high; + + /* MFLAG_CTRL */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 1, 1, 0); + /* MFLAG_START */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 2, 2); + + /* + * Simulation team suggests below thesholds: + * HT = fifosize * 5 / 8; + * LT = fifosize * 4 / 8; + */ + + low = size * 4 / 8 / unit_size; + high = size * 5 / 8 / unit_size; + + dispc6_vid_set_mflag_threshold(dispc, hw_plane, low, high); +} + +static void dispc6_initial_config(struct dispc_device *dispc) +{ + dispc6_vid_csc_setup(dispc); + dispc6_mflag_setup(dispc); + + /* Enable the gamma Shadow bit-field */ + VP_REG_FLD_MOD(dispc, 0, DISPC_VP_CONFIG, 1, 2, 2); +} + +static int dispc6_init_features(struct dispc_device *dispc) +{ + const struct of_device_id *match; + + match = of_match_node(dispc6_of_match, dispc->dev->of_node); + if (!match) { + dev_err(dispc->dev, "Unsupported DISPC version\n"); + return -ENODEV; + } + + dispc->feat = match->data; + + return 0; +} + +static int dispc6_get_num_planes(struct dispc_device *dispc) +{ + return 1; +} + +static int dispc6_get_num_vps(struct dispc_device *dispc) +{ + return 1; +} + +static u32 dispc6_vp_gamma_size(struct dispc_device *dispc, + u32 hw_videoport) +{ + return ARRAY_SIZE(dispc->gamma_table); +} + +static void dispc6_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 *table = dispc->gamma_table; + uint hwlen = ARRAY_SIZE(dispc->gamma_table); + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + v |= i << 24; + + dispc6_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); + } +} + +static void dispc6_restore_gamma_tables(struct dispc_device *dispc) +{ + dev_dbg(dispc->dev, "%s()\n", __func__); + + dispc6_vp_write_gamma_table(dispc, 0); +} + +static const struct drm_color_lut dispc6_vp_gamma_default_lut[] = { + { .red = 0, .green = 0, .blue = 0, }, + { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, +}; + +static void dispc6_vp_set_gamma(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_color_lut *lut, + unsigned int length) +{ + u32 *table = dispc->gamma_table; + uint hwlen = ARRAY_SIZE(dispc->gamma_table); + static const uint hwbits = 8; + uint i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d, lut len %u, hw len %u\n", + __func__, hw_videoport, length, hwlen); + + if (lut == NULL || length < 2) { + lut = dispc6_vp_gamma_default_lut; + length = ARRAY_SIZE(dispc6_vp_gamma_default_lut); + } + + for (i = 0; i < length - 1; ++i) { + uint first = i * (hwlen - 1) / (length - 1); + uint last = (i + 1) * (hwlen - 1) / (length - 1); + uint w = last - first; + u16 r, g, b; + uint j; + + if (w == 0) + continue; + + for (j = 0; j <= w; j++) { + r = (lut[i].red * (w - j) + lut[i + 1].red * j) / w; + g = (lut[i].green * (w - j) + lut[i + 1].green * j) / w; + b = (lut[i].blue * (w - j) + lut[i + 1].blue * j) / w; + + r >>= 16 - hwbits; + g >>= 16 - hwbits; + b >>= 16 - hwbits; + + table[first + j] = (r << (hwbits * 2)) | + (g << hwbits) | b; + } + } + + if (dispc->is_enabled) + dispc6_vp_write_gamma_table(dispc, hw_videoport); +} + +static int dispc6_init_gamma_tables(struct dispc_device *dispc) +{ + dispc6_vp_set_gamma(dispc, 0, NULL, 0); + + return 0; +} + +static u32 dispc6_get_memory_bandwidth_limit(struct dispc_device *dispc) +{ + u32 limit = 0; + + /* Optional maximum memory bandwidth */ + of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth", + &limit); + + return limit; +} + +static const char *dispc6_plane_name(struct dispc_device *dispc, + u32 hw_plane) +{ + return "vid1"; +} + +static const char *dispc6_vp_name(struct dispc_device *dispc, + u32 hw_videoport) +{ + return "vp1"; +} + +static int dispc6_runtime_suspend(struct dispc_device *dispc) +{ + struct device *dev = dispc->dev; + + dev_dbg(dev, "suspend\n"); + + dispc->is_enabled = false; + + clk_disable_unprepare(dispc->fclk); + + return 0; +} + +static int dispc6_runtime_resume(struct dispc_device *dispc) +{ + struct device *dev = dispc->dev; + + dev_dbg(dev, "resume\n"); + + clk_prepare_enable(dispc->fclk); + + if (REG_GET(dispc, DISPC_SYSSTATUS, 0, 0) == 0) + dev_warn(dev, "DISPC FUNC RESET not done!\n"); + if (REG_GET(dispc, DISPC_SYSSTATUS, 1, 1) == 0) + dev_warn(dev, "DISPC VP RESET not done!\n"); + + dispc6_initial_config(dispc); + + dispc6_restore_gamma_tables(dispc); + + dispc->is_enabled = true; + + return 0; +} + +static int dispc6_modeset_init(struct dispc_device *dispc) +{ + struct tidss_device *tidss = dispc->tidss; + struct device *dev = tidss->dev; + const u32 hw_videoport = 0; + const u32 crtc_mask = 1; + const u32 hw_plane_id = 0; + struct drm_panel *panel; + struct drm_bridge *bridge; + u32 enc_type; + int ret; + struct tidss_plane *tplane; + struct tidss_crtc *tcrtc; + struct tidss_encoder *tenc; + struct device_node *epnode; + u32 fourccs[ARRAY_SIZE(dispc6_color_formats)]; + int i; + + /* first find if there is a connected panel/bridge */ + + ret = drm_of_find_panel_or_bridge(dev->of_node, hw_videoport, 0, &panel, &bridge); + if (ret) { + dev_dbg(dev, "no panel or bridge found\n"); + return ret; + } + + epnode = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (WARN_ON(!epnode)) + return -EINVAL; + + if (panel) { + dev_dbg(dev, "Setting up panel\n"); + + enc_type = DRM_MODE_ENCODER_DPI; + + bridge = devm_drm_panel_bridge_add(dev, panel, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(bridge)) { + dev_err(dev, "failed to set up panel bridge\n"); + return PTR_ERR(bridge); + } + } else { + enc_type = DRM_MODE_ENCODER_NONE; + } + + /* then create a plane, a crtc and an encoder for the panel/bridge */ + + for (i = 0; i < ARRAY_SIZE(dispc6_color_formats); ++i) + fourccs[i] = dispc6_color_formats[i].fourcc; + + tplane = tidss_plane_create(tidss, hw_plane_id, DRM_PLANE_TYPE_PRIMARY, + crtc_mask, fourccs, ARRAY_SIZE(fourccs)); + if (IS_ERR(tplane)) { + dev_err(tidss->dev, "plane create failed\n"); + return PTR_ERR(tplane); + } + + tidss->planes[tidss->num_planes++] = &tplane->plane; + + tcrtc = tidss_crtc_create(tidss, hw_videoport, &tplane->plane, epnode); + if (IS_ERR(tcrtc)) { + dev_err(tidss->dev, "crtc create failed\n"); + return PTR_ERR(tcrtc); + } + + tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc; + + tenc = tidss_encoder_create(tidss, enc_type, 1 << tcrtc->crtc.index); + if (IS_ERR(tenc)) { + dev_err(tidss->dev, "encoder create failed\n"); + return PTR_ERR(tenc); + } + + ret = drm_bridge_attach(&tenc->encoder, bridge, NULL); + if (ret) { + dev_err(tidss->dev, "bridge attach failed: %d\n", ret); + return ret; + } + + return 0; +} + +static void dispc6_remove(struct dispc_device *dispc); + +static const struct dispc_ops dispc6_ops = { + .read_and_clear_irqstatus = dispc6_read_and_clear_irqstatus, + .write_irqenable = dispc6_write_irqenable, + + .runtime_get = dispc6_runtime_get, + .runtime_put = dispc6_runtime_put, + + .get_num_planes = dispc6_get_num_planes, + .get_num_vps = dispc6_get_num_vps, + + .plane_name = dispc6_plane_name, + .vp_name = dispc6_vp_name, + + .get_memory_bandwidth_limit = dispc6_get_memory_bandwidth_limit, + + .vp_enable = dispc6_vp_enable, + .vp_disable = dispc6_vp_disable, + .vp_go_busy = dispc6_vp_go_busy, + .vp_go = dispc6_vp_go, + + .vp_setup = dispc6_vp_setup, + .vp_check_mode = dispc6_vp_check_mode, + .vp_check_config = dispc6_vp_check_config, + + .vp_gamma_size = dispc6_vp_gamma_size, + .vp_set_gamma = dispc6_vp_set_gamma, + + .plane_enable = dispc6_plane_enable, + .plane_setup = dispc6_plane_setup, + + .vp_set_clk_rate = dispc6_vp_set_clk_rate, + .vp_enable_clk = dispc6_vp_enable_clk, + .vp_disable_clk = dispc6_vp_disable_clk, + + .runtime_suspend = dispc6_runtime_suspend, + .runtime_resume = dispc6_runtime_resume, + + .remove = dispc6_remove, + + .modeset_init = dispc6_modeset_init, +}; + +static int dispc6_iomap_resource(struct platform_device *pdev, const char *name, + void __iomem **base) +{ + struct resource *res; + void __iomem *b; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + b = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(b)) { + dev_err(&pdev->dev, "cannot ioremap resource '%s'\n", name); + return PTR_ERR(b); + } + + *base = b; + + return 0; +} + +int dispc6_init(struct tidss_device *tidss) +{ + struct device *dev = tidss->dev; + struct platform_device *pdev = tidss->pdev; + struct dispc_device *dispc; + int r; + + dev_dbg(dev, "%s\n", __func__); + + dispc = devm_kzalloc(dev, sizeof(*dispc), GFP_KERNEL); + if (!dispc) + return -ENOMEM; + + dispc->tidss = tidss; + dispc->dev = dev; + + r = dispc6_init_features(dispc); + if (r) + goto err_free; + + r = dispc6_iomap_resource(pdev, "cfg", &dispc->base_cfg); + if (r) + goto err_free; + + r = dispc6_iomap_resource(pdev, "common", &dispc->base_common); + if (r) + goto err_free; + + r = dispc6_iomap_resource(pdev, "vid1", &dispc->base_vid1); + if (r) + goto err_free; + + r = dispc6_iomap_resource(pdev, "ovr1", &dispc->base_ovr1); + if (r) + goto err_free; + + r = dispc6_iomap_resource(pdev, "vp1", &dispc->base_vp1); + if (r) + goto err_free; + + dev_dbg(dev, "dispc6_bind: iores ok\n"); + + dispc->fclk = devm_clk_get(dev, "fck"); + if (IS_ERR(dispc->fclk)) { + dev_err(dev, "Failed to get fclk\n"); + r = PTR_ERR(dispc->fclk); + goto err_free; + } + + dispc->vp_clk = devm_clk_get(dev, "vp1"); + if (IS_ERR(dispc->vp_clk)) { + dev_err(dev, "Failed to get vp1 clk\n"); + r = PTR_ERR(dispc->vp_clk); + goto err_free; + } + + r = dispc6_init_gamma_tables(dispc); + if (r) + goto err_free; + + tidss->dispc_ops = &dispc6_ops; + tidss->dispc = dispc; + + dev_dbg(dev, "%s done\n", __func__); + + return 0; +err_free: + dev_err(dev, "%s failed: %d\n", __func__, r); + return r; +} + +static void dispc6_remove(struct dispc_device *dispc) +{ + struct device *dev = dispc->dev; + + dev_dbg(dev, "dispc6_unbind\n"); + + dispc->tidss->dispc_ops = NULL; + dispc->tidss->dispc = NULL; + + dev_dbg(dev, "dispc6_unbind done\n"); +} diff --git a/drivers/gpu/drm/tidss/tidss_dispc6.h b/drivers/gpu/drm/tidss/tidss_dispc6.h new file mode 100644 index 000000000000..80197c812acd --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc6.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_DISPC6_H__ +#define __TIDSS_DISPC6_H__ + +/* COMMON */ + +#define DISPC_REVISION 0x000 +#define DISPC_SYSCONFIG 0x004 +#define DISPC_SYSSTATUS 0x008 + +#define DISPC_IRQ_EOI 0x020 +#define DISPC_IRQSTATUS_RAW 0x024 +#define DISPC_IRQSTATUS 0x028 +#define DISPC_IRQENABLE_SET 0x02c +#define DISPC_IRQENABLE_CLR 0x030 +#define DISPC_IRQWAKEEN 0x034 + +#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x040 +#define DISPC_GLOBAL_BUFFER 0x044 +#define DISPC_BA0_FLIPIMMEDIATE_EN 0x048 + +#define DISPC_DBG_CONTROL 0x04c +#define DISPC_DBG_STATUS 0x050 + +#define DISPC_CLKGATING_DISABLE 0x054 + +/* VID */ + +#define DISPC_VID_ACCUH_0 0x0 +#define DISPC_VID_ACCUH_1 0x4 +#define DISPC_VID_ACCUH2_0 0x8 +#define DISPC_VID_ACCUH2_1 0xc + +#define DISPC_VID_ACCUV_0 0x10 +#define DISPC_VID_ACCUV_1 0x14 +#define DISPC_VID_ACCUV2_0 0x18 +#define DISPC_VID_ACCUV2_1 0x1c + +#define DISPC_VID_ATTRIBUTES 0x20 +#define DISPC_VID_ATTRIBUTES2 0x24 + +#define DISPC_VID_BA_0 0x28 +#define DISPC_VID_BA_1 0x2c +#define DISPC_VID_BA_UV_0 0x30 +#define DISPC_VID_BA_UV_1 0x34 +#define DISPC_VID_BUF_SIZE_STATUS 0x38 +#define DISPC_VID_BUF_THRESHOLD 0x3c + +#define DISPC_VID_CONV_COEF(n) (0x40 + (n) * 4) + +#define DISPC_VID_FIRH 0x5c +#define DISPC_VID_FIRH2 0x60 +#define DISPC_VID_FIRV 0x64 +#define DISPC_VID_FIRV2 0x68 + +#define DISPC_VID_FIR_COEFS_H0 0x6c +#define DISPC_VID_FIR_COEF_H0(phase) (0x6c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H0_C 0x90 +#define DISPC_VID_FIR_COEF_H0_C(phase) (0x90 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_H12 0xb4 +#define DISPC_VID_FIR_COEF_H12(phase) (0xb4 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H12_C 0xf4 +#define DISPC_VID_FIR_COEF_H12_C(phase) (0xf4 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V0 0x134 +#define DISPC_VID_FIR_COEF_V0(phase) (0x134 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V0_C 0x158 +#define DISPC_VID_FIR_COEF_V0_C(phase) (0x158 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V12 0x17c +#define DISPC_VID_FIR_COEF_V12(phase) (0x17c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V12_C 0x1bc +#define DISPC_VID_FIR_COEF_V12_C(phase) (0x1bc + (phase) * 4) + +#define DISPC_VID_IRQENABLE 0x200 +#define DISPC_VID_IRQSTATUS 0x204 + +#define DISPC_VID_MFLAG_THRESHOLD 0x208 +#define DISPC_VID_PICTURE_SIZE 0x20c +#define DISPC_VID_PIXEL_INC 0x210 +#define DISPC_VID_POSITION 0x214 +#define DISPC_VID_PRELOAD 0x218 +#define DISPC_VID_ROW_INC 0x21c +#define DISPC_VID_SIZE 0x220 + +/* OVR */ + +#define DISPC_OVR_DEFAULT_COLOR 0x08 +#define DISPC_OVR_DEFAULT_COLOR2 0x0c + +/* VP */ + +#define DISPC_VP_CONFIG 0x00 +#define DISPC_VP_CONTROL 0x04 +#define DISPC_VP_GAMMA_TABLE 0x20 +#define DISPC_VP_IRQENABLE 0x3c +#define DISPC_VP_IRQSTATUS 0x40 +#define DISPC_VP_POL_FREQ 0x4c +#define DISPC_VP_SIZE_SCREEN 0x50 +#define DISPC_VP_TIMING_H 0x54 +#define DISPC_VP_TIMING_V 0x58 + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c new file mode 100644 index 000000000000..8002766c4640 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tidss_dispc.h" +#include "tidss_drv.h" +#include "tidss_irq.h" +#include "tidss_kms.h" + +/* ----------------------------------------------------------------------------- + * Device Information + */ + +static const struct tidss_features tidss_k2g_feats = { + .dispc_init = dispc6_init, +}; + +static const struct of_device_id tidss_of_table[] = { + { .compatible = "ti,k2g-dss", .data = &tidss_k2g_feats }, + { } +}; + +DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); + +static struct drm_driver tidss_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME + | DRIVER_ATOMIC | DRIVER_HAVE_IRQ, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .dumb_create = drm_gem_cma_dumb_create, + .fops = &tidss_fops, + .name = "tidss", + .desc = "TI Keystone DSS", + .date = "20180215", + .major = 1, + .minor = 0, + + .irq_preinstall = tidss_irq_preinstall, + .irq_postinstall = tidss_irq_postinstall, + .irq_handler = tidss_irq_handler, + .irq_uninstall = tidss_irq_uninstall, +}; + +#ifdef CONFIG_PM +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int tidss_pm_runtime_suspend(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + return tidss->dispc_ops->runtime_suspend(tidss->dispc); +} + +static int tidss_pm_runtime_resume(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + int r; + + dev_dbg(dev, "%s\n", __func__); + + r = tidss->dispc_ops->runtime_resume(tidss->dispc); + if (r) + return r; + + tidss_irq_resume(tidss->ddev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tidss_suspend(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + struct drm_atomic_state *state; + + drm_kms_helper_poll_disable(tidss->ddev); + + console_lock(); + drm_fbdev_cma_set_suspend(tidss->fbdev, 1); + console_unlock(); + + state = drm_atomic_helper_suspend(tidss->ddev); + + if (IS_ERR(state)) { + console_lock(); + drm_fbdev_cma_set_suspend(tidss->fbdev, 0); + console_unlock(); + + drm_kms_helper_poll_enable(tidss->ddev); + + return PTR_ERR(state); + } + + tidss->saved_state = state; + + return 0; +} + +static int tidss_resume(struct device *dev) +{ + struct tidss_device *tidss = dev_get_drvdata(dev); + int ret = 0; + + if (tidss->saved_state) + ret = drm_atomic_helper_resume(tidss->ddev, tidss->saved_state); + + console_lock(); + drm_fbdev_cma_set_suspend(tidss->fbdev, 0); + console_unlock(); + + drm_kms_helper_poll_enable(tidss->ddev); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops tidss_pm_ops = { + .runtime_suspend = tidss_pm_runtime_suspend, + .runtime_resume = tidss_pm_runtime_resume, + SET_SYSTEM_SLEEP_PM_OPS(tidss_suspend, tidss_resume) +}; + +#endif /* CONFIG_PM */ + +/* ----------------------------------------------------------------------------- + * Platform driver + */ + +static int tidss_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tidss_device *tidss; + struct drm_device *ddev; + int ret; + int irq; + + dev_dbg(dev, "%s\n", __func__); + + tidss = devm_kzalloc(dev, sizeof(*tidss), GFP_KERNEL); + if (tidss == NULL) + return -ENOMEM; + + tidss->pdev = pdev; + tidss->dev = dev; + tidss->feat = of_device_get_match_data(dev); + + platform_set_drvdata(pdev, tidss); + + ddev = drm_dev_alloc(&tidss_driver, dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); + + tidss->ddev = ddev; + ddev->dev_private = tidss; + + pm_runtime_enable(dev); + + ret = tidss->feat->dispc_init(tidss); + if (ret) { + dev_err(dev, "failed to initialize dispc: %d\n", ret); + goto err_disable_pm; + } + +#ifndef CONFIG_PM_SLEEP + /* no PM, so force enable DISPC */ + tidss->dispc_ops->runtime_resume(tidss->dispc); +#endif + + ret = tidss_modeset_init(tidss); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to init DRM/KMS (%d)\n", ret); + goto err_runtime_suspend; + } + + irq = platform_get_irq(tidss->pdev, 0); + if (irq < 0) { + ret = irq; + dev_err(dev, "platform_get_irq failed: %d\n", ret); + goto err_modeset_cleanup; + } + + ret = drm_irq_install(ddev, irq); + if (ret) { + dev_err(dev, "drm_irq_install failed: %d\n", ret); + goto err_modeset_cleanup; + } + +#ifdef CONFIG_DRM_FBDEV_EMULATION + if (ddev->mode_config.num_connector) { + struct drm_fbdev_cma *fbdev; + + fbdev = drm_fbdev_cma_init(ddev, 32, + ddev->mode_config.num_connector); + if (IS_ERR(fbdev)) { + dev_err(tidss->dev, "fbdev init failed\n"); + ret = PTR_ERR(fbdev); + goto err_irq_uninstall; + } + + tidss->fbdev = fbdev; + } +#endif + + drm_kms_helper_poll_init(ddev); + + ret = drm_dev_register(ddev, 0); + if (ret) { + dev_err(dev, "failed to register DRM device\n"); + goto err_poll_fini; + } + + dev_dbg(dev, "%s done\n", __func__); + + return 0; + +err_poll_fini: + drm_kms_helper_poll_fini(ddev); + + if (tidss->fbdev) + drm_fbdev_cma_fini(tidss->fbdev); + + drm_atomic_helper_shutdown(ddev); + +#ifdef CONFIG_DRM_FBDEV_EMULATION +err_irq_uninstall: +#endif + drm_irq_uninstall(ddev); + +err_modeset_cleanup: + drm_mode_config_cleanup(ddev); + +err_runtime_suspend: +#ifndef CONFIG_PM_SLEEP + /* no PM, so force disable DISPC */ + tidss->dispc_ops->runtime_suspend(tidss->dispc); +#endif + + tidss->dispc_ops->remove(tidss->dispc); + +err_disable_pm: + pm_runtime_disable(dev); + + drm_dev_put(ddev); + + return ret; +} + +static int tidss_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tidss_device *tidss = platform_get_drvdata(pdev); + struct drm_device *ddev = tidss->ddev; + + dev_dbg(dev, "%s\n", __func__); + + drm_dev_unregister(ddev); + + drm_kms_helper_poll_fini(ddev); + + if (tidss->fbdev) + drm_fbdev_cma_fini(tidss->fbdev); + + drm_atomic_helper_shutdown(ddev); + + drm_irq_uninstall(ddev); + + drm_mode_config_cleanup(ddev); + +#ifndef CONFIG_PM_SLEEP + /* no PM, so force disable DISPC */ + tidss->dispc_ops->runtime_suspend(tidss->dispc); +#endif + + tidss->dispc_ops->remove(tidss->dispc); + + pm_runtime_disable(dev); + + drm_dev_put(ddev); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; +} + +MODULE_DEVICE_TABLE(of, tidss_of_table); + +static struct platform_driver tidss_platform_driver = { + .probe = tidss_probe, + .remove = tidss_remove, + .driver = { + .name = "tidss", +#ifdef CONFIG_PM + .pm = &tidss_pm_ops, +#endif + .of_match_table = tidss_of_table, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(tidss_platform_driver); + +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("TI Keystone DSS Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h new file mode 100644 index 000000000000..fd1e767e9308 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_drv.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_DRV_H__ +#define __TIDSS_DRV_H__ + +#include + +struct tidss_device { + struct device *dev; /* Underlying DSS device */ + struct platform_device *pdev; /* Underlying DSS platform device */ + struct drm_device *ddev; /* DRM device for DSS */ + + struct drm_fbdev_cma *fbdev; + + struct dispc_device *dispc; + const struct dispc_ops *dispc_ops; + + const struct tidss_features *feat; + + u32 num_crtcs; + struct drm_crtc *crtcs[8]; + + u32 num_planes; + struct drm_plane *planes[8]; + + spinlock_t wait_lock; /* protects the irq masks */ + u64 irq_mask; /* enabled irqs in addition to wait_list */ + u64 irq_uf_mask; /* underflow irq bits for all planes */ + + struct drm_atomic_state *saved_state; +}; + +struct tidss_features { + int (*dispc_init)(struct tidss_device *tidss); +}; + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c new file mode 100644 index 000000000000..fb7bc3fc0fe8 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include + +#include +#include +#include +#include +#include + +#include "tidss_drv.h" +#include "tidss_encoder.h" +#include "tidss_crtc.h" + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static void tidss_encoder_disable(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + + dev_dbg(ddev->dev, "%s\n", __func__); + +} + +static void tidss_encoder_enable(struct drm_encoder *encoder) +{ + struct drm_device *ddev = encoder->dev; + + dev_dbg(ddev->dev, "%s\n", __func__); + +} + +static int tidss_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_device *ddev = encoder->dev; + struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state); + struct drm_display_info *di = &conn_state->connector->display_info; + + dev_dbg(ddev->dev, "%s\n", __func__); + + // XXX any cleaner way to set bus format and flags? + tcrtc_state->bus_format = di->bus_formats[0]; + tcrtc_state->bus_flags = di->bus_flags; + + return 0; +} + +static void tidss_encoder_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_device *ddev = encoder->dev; + + dev_dbg(ddev->dev, "%s\n", __func__); + +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .atomic_mode_set = tidss_encoder_mode_set, + .disable = tidss_encoder_disable, + .enable = tidss_encoder_enable, + .atomic_check = tidss_encoder_atomic_check, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +struct tidss_encoder *tidss_encoder_create(struct tidss_device *tidss, + u32 encoder_type, u32 possible_crtcs) +{ + struct tidss_encoder *tenc; + struct drm_encoder *enc; + int ret; + + tenc = devm_kzalloc(tidss->dev, sizeof(*tenc), GFP_KERNEL); + if (!tenc) + return ERR_PTR(-ENOMEM); + + enc = &tenc->encoder; + enc->possible_crtcs = possible_crtcs; + + ret = drm_encoder_init(tidss->ddev, enc, &encoder_funcs, + encoder_type, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_encoder_helper_add(enc, &encoder_helper_funcs); + + dev_dbg(tidss->dev, "Encoder create done\n"); + + return tenc; +} diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h new file mode 100644 index 000000000000..f1bd05a3f611 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_encoder.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_ENCODER_H__ +#define __TIDSS_ENCODER_H__ + +#include + +struct tidss_device; +struct tidss_crtc; + +struct tidss_encoder { + struct drm_encoder encoder; +}; + +struct tidss_encoder *tidss_encoder_create(struct tidss_device *tidss, + u32 encoder_type, u32 possible_crtcs); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_irq.c b/drivers/gpu/drm/tidss/tidss_irq.c new file mode 100644 index 000000000000..07c4e1f0924e --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_irq.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include + +#include "tidss_irq.h" +#include "tidss_drv.h" +#include "tidss_dispc.h" +#include "tidss_crtc.h" +#include "tidss_plane.h" + +/* call with wait_lock and dispc runtime held */ +static void tidss_irq_update(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + assert_spin_locked(&tidss->wait_lock); + + tidss->dispc_ops->write_irqenable(tidss->dispc, tidss->irq_mask); +} + +void tidss_irq_enable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss->irq_mask |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport); + tidss_irq_update(ddev); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} + +void tidss_irq_disable_vblank(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss->irq_mask &= ~(DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport)); + tidss_irq_update(ddev); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} + +static void tidss_irq_fifo_underflow(struct tidss_device *tidss, + u64 irqstatus) +{ + static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + unsigned int i; + u64 masked; + + spin_lock(&tidss->wait_lock); + masked = irqstatus & tidss->irq_uf_mask & tidss->irq_mask; + spin_unlock(&tidss->wait_lock); + + if (!masked) + return; + + if (!__ratelimit(&_rs)) + return; + + DRM_ERROR("FIFO underflow on "); + + for (i = 0; i < DSS_MAX_PLANES; ++i) { + if (masked & DSS_IRQ_PLANE_FIFO_UNDERFLOW(i)) + pr_cont("%u:%s ", i, + tidss->dispc_ops->plane_name(tidss->dispc, i)); + } + + pr_cont("(%016llx)\n", irqstatus); +} + +static void tidss_irq_ocp_error_handler(struct drm_device *ddev, + u64 irqstatus) +{ + if (irqstatus & DSS_IRQ_DEVICE_OCP_ERR) + dev_err_ratelimited(ddev->dev, "OCP error\n"); +} + +irqreturn_t tidss_irq_handler(int irq, void *arg) +{ + struct drm_device *ddev = (struct drm_device *) arg; + struct tidss_device *tidss = ddev->dev_private; + unsigned int id; + u64 irqstatus; + + if (WARN_ON(!ddev->irq_enabled)) + return IRQ_NONE; + + irqstatus = tidss->dispc_ops->read_and_clear_irqstatus(tidss->dispc); + + for (id = 0; id < tidss->num_crtcs; id++) { + struct drm_crtc *crtc = tidss->crtcs[id]; + struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); + u32 hw_videoport = tcrtc->hw_videoport; + + if (irqstatus & (DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) | + DSS_IRQ_VP_VSYNC_ODD(hw_videoport))) + tidss_crtc_vblank_irq(crtc); + + if (irqstatus & (DSS_IRQ_VP_FRAME_DONE(hw_videoport))) + tidss_crtc_framedone_irq(crtc); + + if (irqstatus & DSS_IRQ_VP_SYNC_LOST(hw_videoport)) + tidss_crtc_error_irq(crtc, irqstatus); + } + + tidss_irq_ocp_error_handler(ddev, irqstatus); + tidss_irq_fifo_underflow(tidss, irqstatus); + + return IRQ_HANDLED; +} + +void tidss_irq_preinstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + spin_lock_init(&tidss->wait_lock); + + tidss->dispc_ops->runtime_get(tidss->dispc); + + tidss->dispc_ops->write_irqenable(tidss->dispc, 0); + tidss->dispc_ops->read_and_clear_irqstatus(tidss->dispc); + + tidss->dispc_ops->runtime_put(tidss->dispc); +} + +int tidss_irq_postinstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + unsigned int i; + unsigned long flags; + + tidss->dispc_ops->runtime_get(tidss->dispc); + + spin_lock_irqsave(&tidss->wait_lock, flags); + + tidss->irq_mask = DSS_IRQ_DEVICE_OCP_ERR; + + tidss->irq_uf_mask = 0; + for (i = 0; i < tidss->num_planes; ++i) { + struct tidss_plane *tplane = to_tidss_plane(tidss->planes[i]); + + tidss->irq_uf_mask |= DSS_IRQ_PLANE_FIFO_UNDERFLOW(tplane->hw_plane_id); + } + tidss->irq_mask |= tidss->irq_uf_mask; + + for (i = 0; i < tidss->num_crtcs; ++i) { + struct tidss_crtc *tcrtc = to_tidss_crtc(tidss->crtcs[i]); + + tidss->irq_mask |= DSS_IRQ_VP_SYNC_LOST(tcrtc->hw_videoport); + + tidss->irq_mask |= DSS_IRQ_VP_FRAME_DONE(tcrtc->hw_videoport); + } + + tidss_irq_update(ddev); + + spin_unlock_irqrestore(&tidss->wait_lock, flags); + + tidss->dispc_ops->runtime_put(tidss->dispc); + + return 0; +} + +void tidss_irq_uninstall(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + + tidss->dispc_ops->runtime_get(tidss->dispc); + tidss->dispc_ops->write_irqenable(tidss->dispc, 0); + tidss->dispc_ops->runtime_put(tidss->dispc); +} + +void tidss_irq_resume(struct drm_device *ddev) +{ + struct tidss_device *tidss = ddev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&tidss->wait_lock, flags); + tidss_irq_update(ddev); + spin_unlock_irqrestore(&tidss->wait_lock, flags); +} diff --git a/drivers/gpu/drm/tidss/tidss_irq.h b/drivers/gpu/drm/tidss/tidss_irq.h new file mode 100644 index 000000000000..e1507298c9d8 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_irq.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_IRQ_H__ +#define __TIDSS_IRQ_H__ + +#include + +struct drm_crtc; +struct drm_device; + +void tidss_irq_enable_vblank(struct drm_crtc *crtc); +void tidss_irq_disable_vblank(struct drm_crtc *crtc); + +void tidss_irq_preinstall(struct drm_device *ddev); +int tidss_irq_postinstall(struct drm_device *ddev); +void tidss_irq_uninstall(struct drm_device *ddev); +irqreturn_t tidss_irq_handler(int irq, void *arg); + +void tidss_irq_resume(struct drm_device *ddev); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c new file mode 100644 index 000000000000..cd003f1569cd --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include + +#include "tidss_crtc.h" +#include "tidss_drv.h" +#include "tidss_encoder.h" +#include "tidss_kms.h" +#include "tidss_plane.h" + +static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state) +{ + struct drm_device *ddev = old_state->dev; + struct tidss_device *tidss = ddev->dev_private; + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss->dispc_ops->runtime_get(tidss->dispc); + + drm_atomic_helper_commit_modeset_disables(ddev, old_state); + drm_atomic_helper_commit_planes(ddev, old_state, 0); + drm_atomic_helper_commit_modeset_enables(ddev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_wait_for_flip_done(ddev, old_state); + + drm_atomic_helper_cleanup_planes(ddev, old_state); + + tidss->dispc_ops->runtime_put(tidss->dispc); +} + +static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = { + .atomic_commit_tail = tidss_atomic_commit_tail, +}; + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int tidss_modeset_init(struct tidss_device *tidss) +{ + struct drm_device *ddev = tidss->ddev; + int ret; + int i; + + dev_dbg(tidss->dev, "%s\n", __func__); + + drm_mode_config_init(ddev); + + ddev->mode_config.min_width = 8; + ddev->mode_config.min_height = 8; + ddev->mode_config.max_width = 8096; + ddev->mode_config.max_height = 8096; + ddev->mode_config.funcs = &mode_config_funcs; + ddev->mode_config.helper_private = &mode_config_helper_funcs; + + ret = tidss->dispc_ops->modeset_init(tidss->dispc); + if (ret) + return ret; + + ret = drm_vblank_init(ddev, tidss->num_crtcs); + if (ret) + return ret; + + /* Start with vertical blanking interrupt reporting disabled. */ + for (i = 0; i < tidss->num_crtcs; ++i) + drm_crtc_vblank_reset(tidss->crtcs[i]); + + drm_mode_config_reset(ddev); + + dev_dbg(tidss->dev, "%s done\n", __func__); + + return 0; +} diff --git a/drivers/gpu/drm/tidss/tidss_kms.h b/drivers/gpu/drm/tidss/tidss_kms.h new file mode 100644 index 000000000000..927b57b05827 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_kms.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_KMS_H__ +#define __TIDSS_KMS_H__ + +struct tidss_device; + +int tidss_modeset_init(struct tidss_device *rcdu); + +#endif diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c new file mode 100644 index 000000000000..539c3ae706de --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tidss_crtc.h" +#include "tidss_drv.h" +#include "tidss_plane.h" + +static int tidss_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_device *ddev = plane->dev; + struct drm_crtc_state *crtc_state; + int ret; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->crtc) { + /* + * The visible field is not reset by the DRM core but only + * updated by drm_plane_helper_check_state(), set it manually. + */ + state->visible = false; + return 0; + } + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + /* XXX TODO: check scaling via dispc_ops */ + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + 0, + INT_MAX, + true, true); + if (ret < 0) + return ret; + + if (!state->visible) + return 0; + + return 0; +} + +static void tidss_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_plane *tplane = to_tidss_plane(plane); + struct tidss_plane_info info; + int ret; + u32 hw_videoport; + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + uint32_t x, y; + struct drm_gem_cma_object *gem; + + dev_dbg(ddev->dev, "%s\n", __func__); + + if (!state->visible) { + tidss->dispc_ops->plane_enable(tidss->dispc, tplane->hw_plane_id, + false); + return; + } + + hw_videoport = to_tidss_crtc(state->crtc)->hw_videoport; + + memset(&info, 0, sizeof(info)); + + info.fourcc = fb->format->format; + info.pos_x = state->crtc_x; + info.pos_y = state->crtc_y; + info.out_width = state->crtc_w; + info.out_height = state->crtc_h; + info.width = state->src_w >> 16; + info.height = state->src_h >> 16; + info.zorder = state->zpos; + + x = state->src_x >> 16; + y = state->src_y >> 16; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + + info.paddr = gem->paddr + fb->offsets[0] + x * fb->format->cpp[0] + + y * fb->pitches[0]; + + info.fb_width = fb->pitches[0] / fb->format->cpp[0]; + + if (fb->format->num_planes == 2) { + gem = drm_fb_cma_get_gem_obj(fb, 1); + + info.p_uv_addr = gem->paddr + + fb->offsets[0] + + (x * fb->format->cpp[0] / fb->format->hsub) + + (y * fb->pitches[0] / fb->format->vsub); + } + + ret = tidss->dispc_ops->plane_setup(tidss->dispc, tplane->hw_plane_id, + &info, hw_videoport); + + if (ret) { + dev_err(plane->dev->dev, "Failed to setup plane %d\n", + tplane->hw_plane_id); + tidss->dispc_ops->plane_enable(tidss->dispc, tplane->hw_plane_id, + false); + return; + } + + tidss->dispc_ops->plane_enable(tidss->dispc, tplane->hw_plane_id, true); +} + +static void tidss_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct tidss_device *tidss = ddev->dev_private; + struct tidss_plane *tplane = to_tidss_plane(plane); + + dev_dbg(ddev->dev, "%s\n", __func__); + + tidss->dispc_ops->plane_enable(tidss->dispc, tplane->hw_plane_id, false); +} + +static const struct drm_plane_helper_funcs tidss_plane_helper_funcs = { + .atomic_check = tidss_plane_atomic_check, + .atomic_update = tidss_plane_atomic_update, + .atomic_disable = tidss_plane_atomic_disable, +}; + +static const struct drm_plane_funcs tidss_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .destroy = drm_plane_cleanup, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, + u32 hw_plane_id, u32 plane_type, + u32 crtc_mask, const u32 *formats, + u32 num_formats) +{ + enum drm_plane_type type; + uint32_t possible_crtcs; + uint num_planes = tidss->dispc_ops->get_num_planes(tidss->dispc); + int ret; + struct tidss_plane *tplane; + + tplane = devm_kzalloc(tidss->dev, sizeof(*tplane), GFP_KERNEL); + if (!tplane) + return ERR_PTR(-ENOMEM); + + tplane->hw_plane_id = hw_plane_id; + + possible_crtcs = crtc_mask; + type = plane_type; + + ret = drm_universal_plane_init(tidss->ddev, &tplane->plane, + possible_crtcs, + &tidss_plane_funcs, + formats, num_formats, + NULL, type, NULL); + if (ret < 0) + return ERR_PTR(ret); + + drm_plane_helper_add(&tplane->plane, &tidss_plane_helper_funcs); + if (num_planes > 1) + drm_plane_create_zpos_property(&tplane->plane, hw_plane_id, 0, + num_planes - 1); + + return tplane; +} diff --git a/drivers/gpu/drm/tidss/tidss_plane.h b/drivers/gpu/drm/tidss/tidss_plane.h new file mode 100644 index 000000000000..d865e14cb5f9 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_plane.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Tomi Valkeinen + */ + +#ifndef __TIDSS_PLANE_H__ +#define __TIDSS_PLANE_H__ + +#include "tidss_dispc.h" + +#define to_tidss_plane(p) container_of((p), struct tidss_plane, plane) + +struct tidss_plane { + struct drm_plane plane; + + u32 hw_plane_id; + +}; + +struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, + u32 hw_plane_id, u32 plane_type, + u32 crtc_mask, const u32 *formats, + u32 num_formats); +#endif From patchwork Mon Jun 18 13:22:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138942 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949472lji; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) X-Google-Smtp-Source: ADUXVKKqV9/4lEsswV5twKhM88kcA0/S5t8VFqnKY6NIMyh6masiJiixVb46Y3PBlPJ03xWnbjRV X-Received: by 2002:a17:902:7248:: with SMTP id c8-v6mr14191915pll.128.1529328191565; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328191; cv=none; d=google.com; s=arc-20160816; b=R1S9KBmqlQvh36Qem7wr752puTHv0GLZpeGzERqkTJ8WVZaxI3AxsnGXP8UYySWUI3 tpgcSFEe9u3yWCXK5YFO7Bj+tvZOB09o9BTaxuaugOLG4Kq2KhWCtcc32SiD6GcRK2oc yGfNBGzsdk3pfUxHyBvUMMHPMoWPY2hHEKIu7mYKbCuYXOARxNb4ST+ryg7syMG7LHlf i6RWtXOMvVkfA5U59BgR/+6oxwM45tcaFMFjEjecEZB2xI07BA4wEoHTqf6cW6NK3Ei7 HCOY2B+5SFE5/UR6e2EIy34Bv2DohyLZUvcpF4cwD+OdC3p6GHKo9kQbyS/h2B31Rx5v calQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=NWFgaS5GF3luuVQTt0iW8jY+IB4DWu3axMAJ/4ekZDw=; b=TMcybc2nIMcnn0G7B/ZSeGBzAdiZIiipNZyty4Ca0++AZRU+Be3jAMbIBulmsVHvOk gkLng0QA0mFZ3P43eGPR+tpuS/OSJFSWy7Ya/yFo+hexcRcipDOb744IVxGSYxLCjgkk wjwAhAR1FJETh6UAvOBvkrv8MEdssP0KOc3v/D4smuL7fcor2w3v7W70uxPkMWf4TpHO snF7ZYCgPh/LLfbRQQd4X4HSa0shcHNGrx+VjYdTjcQKMeHZ2pdD4tJsQ7mOdQRmuwuZ JXvFpvdAOpbGO3VlEshHviyWOzwBl5tjUzWFJzdgLDiRbNHj9/FZoSleKPPK18jol9lK ATCQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=QrMv1r8s; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t66-v6si11868193pgc.6.2018.06.18.06.23.11; Mon, 18 Jun 2018 06:23:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=QrMv1r8s; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933182AbeFRNXJ (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:09 -0400 Received: from fllnx209.ext.ti.com ([198.47.19.16]:26383 "EHLO fllnx209.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933501AbeFRNXH (ORCPT ); Mon, 18 Jun 2018 09:23:07 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by fllnx209.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDN3bd018686; Mon, 18 Jun 2018 08:23:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328183; bh=KBSO72lWiOk6cLp14pr3LuZhIXbiyufE4GNv1Y/9Nx0=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=QrMv1r8s5Hsc3wi5S8zF5bkealgs0DUgl3h4yM75gFjtc6tird/RUJAXQjNAs7aKh C0q4gqpeWmsjeh8uzB1macCaHvkpel9NafbvxI9IT58RgN2zpgrBNeMydF99uMbX1b f9lnlZdzbhXQxtQDoyi1Tj7Sm/sx7WJ1eezRR/OU= Received: from DLEE113.ent.ti.com (dlee113.ent.ti.com [157.170.170.24]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDN36i017946; Mon, 18 Jun 2018 08:23:03 -0500 Received: from DLEE110.ent.ti.com (157.170.170.21) by DLEE113.ent.ti.com (157.170.170.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:02 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DLEE110.ent.ti.com (157.170.170.21) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:02 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMows002369; Mon, 18 Jun 2018 08:23:01 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen Subject: [RFC PATCHv2 5/9] drm/tidss: Add dispc7 for DSS7 support Date: Mon, 18 Jun 2018 16:22:38 +0300 Message-ID: <20180618132242.8673-6-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Jyri Sarha This patch adds support for DSS7 to tidss driver. The DSS7 is a new DSS version found Texas Instruments Keystone AM65x SoC. DSS7 has many similarities to DSS6, but it is different enough to warrant a separate backend driver to keep the code simpler. The DSS7 on AM65x SoC has two video ports and two video pipeline (planes). Only one of the them - called VID - supports scaling, the other is called a VIDL (video lite pipeline). In addition to traditional MIPI DPI output AM65x supports OpenLDI video output. Signed-off-by: Jyri Sarha Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/tidss/Makefile | 1 + drivers/gpu/drm/tidss/tidss_dispc.h | 1 + drivers/gpu/drm/tidss/tidss_dispc7.c | 1708 ++++++++++++++++++++++++++ drivers/gpu/drm/tidss/tidss_dispc7.h | 185 +++ drivers/gpu/drm/tidss/tidss_drv.c | 5 + 5 files changed, 1900 insertions(+) create mode 100644 drivers/gpu/drm/tidss/tidss_dispc7.c create mode 100644 drivers/gpu/drm/tidss/tidss_dispc7.h -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/gpu/drm/tidss/Makefile b/drivers/gpu/drm/tidss/Makefile index ee6b24db0441..444a7f28f1f2 100644 --- a/drivers/gpu/drm/tidss/Makefile +++ b/drivers/gpu/drm/tidss/Makefile @@ -6,6 +6,7 @@ tidss-y := tidss_drv.o \ tidss_plane.o \ tidss_encoder.o \ tidss_dispc6.o \ + tidss_dispc7.o \ tidss_irq.o obj-$(CONFIG_DRM_TIDSS) += tidss.o diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 38ee3d75404e..05f4c7f9eb68 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -141,5 +141,6 @@ struct dispc_ops { }; int dispc6_init(struct tidss_device *tidss); +int dispc7_init(struct tidss_device *tidss); #endif diff --git a/drivers/gpu/drm/tidss/tidss_dispc7.c b/drivers/gpu/drm/tidss/tidss_dispc7.c new file mode 100644 index 000000000000..d3efbf673d0f --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc7.c @@ -0,0 +1,1708 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tidss_drv.h" +#include "tidss_crtc.h" +#include "tidss_plane.h" +#include "tidss_encoder.h" + +#include "tidss_dispc.h" +#include "tidss_dispc7.h" + +static const struct dispc7_features dispc7_am6_feats = { + .min_pclk = 1000, + .max_pclk = 200000000, + + .num_vps = 2, + .vp_name = { "vp1", "vp2" }, + .ovr_name = { "ovr1", "ovr2" }, + .vpclk_name = { "vp1", "vp2" }, + .vp_enc_type = { DRM_MODE_ENCODER_LVDS, DRM_MODE_ENCODER_DPI }, + + .num_planes = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ + .vid_name = { "vid", "vidl1" }, + .vid_lite = { false, true, }, +}; + +static const struct of_device_id dispc7_of_table[] = { + { .compatible = "ti,am6-dss", .data = &dispc7_am6_feats }, + { } +}; + +enum dispc7_oldi_mode { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 }; + +struct dispc7_bus_format { + u32 bus_fmt; + u32 data_width; + bool oldi; + enum dispc7_oldi_mode oldi_mode; +}; + +static const struct dispc7_bus_format dispc7_bus_formats[] = { + { MEDIA_BUS_FMT_RGB444_1X12, 12, false, 0 }, + { MEDIA_BUS_FMT_RGB565_1X16, 16, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X18, 18, false, 0 }, + { MEDIA_BUS_FMT_RGB888_1X24, 24, false, 0 }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, true, SPWG_18 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, true, SPWG_24 }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, true, JEIDA_24 }, +}; + +/* + * TRM gives bitfields as start:end, where start is the higher bit + * number. For example 7:0 + */ + +#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define REG_GET(dispc, idx, start, end) \ + FLD_GET(dispc7_read(dispc, idx), start, end) + +#define REG_FLD_MOD(dispc, idx, val, start, end) \ + dispc7_write(dispc, idx, FLD_MOD(dispc7_read(dispc, idx), val, start, end)) + +#define VID_REG_GET(dispc, hw_plane, idx, start, end) \ + FLD_GET(dispc7_vid_read(dispc, hw_plane, idx), start, end) + +#define VID_REG_FLD_MOD(dispc, hw_plane, idx, val, start, end) \ + dispc7_vid_write(dispc, hw_plane, idx, FLD_MOD(dispc7_vid_read(dispc, hw_plane, idx), val, start, end)) + +#define VP_REG_GET(dispc, vp, idx, start, end) \ + FLD_GET(dispc7_vp_read(dispc, vp, idx), start, end) + +#define VP_REG_FLD_MOD(dispc, vp, idx, val, start, end) \ + dispc7_vp_write(dispc, vp, idx, FLD_MOD(dispc7_vp_read(dispc, vp, idx), val, start, end)) + +#define OVR_REG_GET(dispc, ovr, idx, start, end) \ + FLD_GET(dispc7_ovr_read(dispc, ovr, idx), start, end) + +#define OVR_REG_FLD_MOD(dispc, ovr, idx, val, start, end) \ + dispc7_ovr_write(dispc, ovr, idx, FLD_MOD(dispc7_ovr_read(dispc, ovr, idx), val, start, end)) + +struct dss_vp_data { + u32 gamma_table[256]; + bool oldi; +}; + +struct dss_plane_data { + uint zorder; + uint hw_videoport; +}; + +struct dispc_device { + struct tidss_device *tidss; + struct device *dev; + + void __iomem *base_common; + void __iomem *base_vid[DISPC7_MAX_PLANES]; + void __iomem *base_ovr[DISPC7_MAX_PORTS]; + void __iomem *base_vp[DISPC7_MAX_PORTS]; + + struct regmap *syscon; + + struct clk *vp_clk[DISPC7_MAX_PORTS]; + + const struct dispc7_features *feat; + + struct clk *fclk; + + bool is_enabled; + + struct dss_vp_data vp_data[DISPC7_MAX_PORTS]; + + struct dss_plane_data plane_data[DISPC7_MAX_PLANES]; +}; + + +static void dispc7_write(struct dispc_device *dispc, u16 reg, u32 val) +{ + iowrite32(val, dispc->base_common + reg); +} + +static u32 dispc7_read(struct dispc_device *dispc, u16 reg) +{ + return ioread32(dispc->base_common + reg); +} + +static void dispc7_vid_write(struct dispc_device *dispc, u32 hw_plane, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vid[hw_plane]; + + iowrite32(val, base + reg); +} + +static u32 dispc7_vid_read(struct dispc_device *dispc, u32 hw_plane, u16 reg) +{ + void __iomem *base = dispc->base_vid[hw_plane]; + + return ioread32(base + reg); +} + +static void dispc7_ovr_write(struct dispc_device *dispc, u32 hw_videoport, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_ovr[hw_videoport]; + + iowrite32(val, base + reg); +} + +static u32 dispc7_ovr_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_ovr[hw_videoport]; + + return ioread32(base + reg); +} + +static void dispc7_vp_write(struct dispc_device *dispc, u32 hw_videoport, u16 reg, u32 val) +{ + void __iomem *base = dispc->base_vp[hw_videoport]; + + iowrite32(val, base + reg); +} + +static u32 dispc7_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) +{ + void __iomem *base = dispc->base_vp[hw_videoport]; + + return ioread32(base + reg); +} + + +static int dispc7_runtime_get(struct dispc_device *dispc) +{ + int r; + + dev_dbg(dispc->dev, "%s\n", __func__); + + r = pm_runtime_get_sync(dispc->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +static void dispc7_runtime_put(struct dispc_device *dispc) +{ + int r; + + dev_dbg(dispc->dev, "%s\n", __func__); + + r = pm_runtime_put_sync(dispc->dev); + WARN_ON(r < 0); +} + +static u64 dispc7_vp_irq_from_raw(u32 stat, u32 hw_videoport) +{ + u64 vp_stat = 0; + + if (stat & BIT(0)) + vp_stat |= DSS_IRQ_VP_FRAME_DONE(hw_videoport); + if (stat & BIT(1)) + vp_stat |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport); + if (stat & BIT(2)) + vp_stat |= DSS_IRQ_VP_VSYNC_ODD(hw_videoport); + if (stat & BIT(4)) + vp_stat |= DSS_IRQ_VP_SYNC_LOST(hw_videoport); + + return vp_stat; +} + +static u32 dispc7_vp_irq_to_raw(u64 vpstat, u32 hw_videoport) +{ + u32 stat = 0; + + if (vpstat & DSS_IRQ_VP_FRAME_DONE(hw_videoport)) + stat |= BIT(0); + if (vpstat & DSS_IRQ_VP_VSYNC_EVEN(hw_videoport)) + stat |= BIT(1); + if (vpstat & DSS_IRQ_VP_VSYNC_ODD(hw_videoport)) + stat |= BIT(2); + if (vpstat & DSS_IRQ_VP_SYNC_LOST(hw_videoport)) + stat |= BIT(4); + + return stat; +} + +static u64 dispc7_vid_irq_from_raw(u32 stat, u32 hw_plane) +{ + u64 vid_stat = 0; + + if (stat & BIT(0)) + vid_stat |= DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane); + + return vid_stat; +} + +static u32 dispc7_vid_irq_to_raw(u64 vidstat, u32 hw_plane) +{ + u32 stat = 0; + + if (vidstat & DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane)) + stat |= BIT(0); + + return stat; +} + +static u64 dispc7_vp_read_irqstatus(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc7_read(dispc, DISPC_VP_IRQSTATUS(hw_videoport)); + + return dispc7_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc7_vp_write_irqstatus(struct dispc_device *dispc, + u32 hw_videoport, u64 vpstat) +{ + u32 stat = dispc7_vp_irq_to_raw(vpstat, hw_videoport); + + dispc7_write(dispc, DISPC_VP_IRQSTATUS(hw_videoport), stat); +} + +static u64 dispc7_vid_read_irqstatus(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc7_read(dispc, DISPC_VID_IRQSTATUS(hw_plane)); + + return dispc7_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc7_vid_write_irqstatus(struct dispc_device *dispc, + u32 hw_plane, u64 vidstat) +{ + u32 stat = dispc7_vid_irq_to_raw(vidstat, hw_plane); + + dispc7_write(dispc, DISPC_VID_IRQSTATUS(hw_plane), stat); +} + +static u64 dispc7_vp_read_irqenable(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 stat = dispc7_read(dispc, DISPC_VP_IRQENABLE(hw_videoport)); + + return dispc7_vp_irq_from_raw(stat, hw_videoport); +} + +static void dispc7_vp_write_irqenable(struct dispc_device *dispc, + u32 hw_videoport, u64 vpstat) +{ + u32 stat = dispc7_vp_irq_to_raw(vpstat, hw_videoport); + + dispc7_write(dispc, DISPC_VP_IRQENABLE(hw_videoport), stat); +} + + +static u64 dispc7_vid_read_irqenable(struct dispc_device *dispc, + u32 hw_plane) +{ + u32 stat = dispc7_read(dispc, DISPC_VID_IRQENABLE(hw_plane)); + + return dispc7_vid_irq_from_raw(stat, hw_plane); +} + +static void dispc7_vid_write_irqenable(struct dispc_device *dispc, + u32 hw_plane, u64 vidstat) +{ + u32 stat = dispc7_vid_irq_to_raw(vidstat, hw_plane); + + dispc7_write(dispc, DISPC_VID_IRQENABLE(hw_plane), stat); +} + +static void dispc7_clear_irqstatus(struct dispc_device *dispc, u64 clearmask) +{ + uint i; + u32 top_clear = 0; + + for (i = 0; i < dispc->feat->num_vps; ++i) { + if (clearmask & DSS_IRQ_VP_MASK(i)) { + dispc7_vp_write_irqstatus(dispc, i, clearmask); + top_clear |= BIT(i); + } + } + for (i = 0; i < dispc->feat->num_planes; ++i) { + if (clearmask & DSS_IRQ_PLANE_MASK(i)) { + dispc7_vid_write_irqstatus(dispc, i, clearmask); + top_clear |= BIT(4 + i); + } + } + dispc7_write(dispc, DISPC_IRQSTATUS, top_clear); + + /* Flush posted writes */ + dispc7_read(dispc, DISPC_IRQSTATUS); +} + +static u64 dispc7_read_and_clear_irqstatus(struct dispc_device *dispc) +{ + u64 status = 0; + uint i; + + for (i = 0; i < dispc->feat->num_vps; ++i) + status |= dispc7_vp_read_irqstatus(dispc, i); + + for (i = 0; i < dispc->feat->num_planes; ++i) + status |= dispc7_vid_read_irqstatus(dispc, i); + + dispc7_clear_irqstatus(dispc, status); + + return status; +} + +static u64 dispc7_read_irqenable(struct dispc_device *dispc) +{ + u64 enable = 0; + uint i; + + for (i = 0; i < dispc->feat->num_vps; ++i) + enable |= dispc7_vp_read_irqenable(dispc, i); + + for (i = 0; i < dispc->feat->num_planes; ++i) + enable |= dispc7_vid_read_irqenable(dispc, i); + + return enable; +} + +static void dispc7_write_irqenable(struct dispc_device *dispc, u64 mask) +{ + uint i; + u32 main_enable = 0, main_disable = 0; + u64 old_mask; + + old_mask = dispc7_read_irqenable(dispc); + + /* clear the irqstatus for newly enabled irqs */ + dispc7_clear_irqstatus(dispc, (old_mask ^ mask) & mask); + + for (i = 0; i < dispc->feat->num_vps; ++i) { + dispc7_vp_write_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_VP_MASK(i)) + main_enable |= BIT(i); /* VP IRQ */ + else + main_disable |= BIT(i); /* VP IRQ */ + } + + for (i = 0; i < dispc->feat->num_planes; ++i) { + dispc7_vid_write_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_PLANE_MASK(i)) + main_enable |= BIT(i + 4); /* VID IRQ */ + else + main_disable |= BIT(i + 4); /* VID IRQ */ + } + + if (main_enable) + dispc7_write(dispc, DISPC_IRQENABLE_SET, main_enable); + + if (main_disable) + dispc7_write(dispc, DISPC_IRQENABLE_CLR, main_disable); + + /* Flush posted writes */ + dispc7_read(dispc, DISPC_IRQENABLE_SET); +} + + +static void dispc7_oldi_tx_power(struct dispc_device *dispc, bool power) +{ + u32 val = power ? 0 : CTRLMMR0P1_OLDI_PWRDN_TX; + + regmap_update_bits(dispc->syscon, CTRLMMR0P1_OLDI_DAT0_IO_CTRL, + CTRLMMR0P1_OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->syscon, CTRLMMR0P1_OLDI_DAT1_IO_CTRL, + CTRLMMR0P1_OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->syscon, CTRLMMR0P1_OLDI_DAT2_IO_CTRL, + CTRLMMR0P1_OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->syscon, CTRLMMR0P1_OLDI_DAT3_IO_CTRL, + CTRLMMR0P1_OLDI_PWRDN_TX, val); + regmap_update_bits(dispc->syscon, CTRLMMR0P1_OLDI_CLK_IO_CTRL, + CTRLMMR0P1_OLDI_PWRDN_TX, val); +} + +static void dispc7_set_num_datalines(struct dispc_device *dispc, + u32 hw_videoport, int num_lines) +{ + int v; + + switch (num_lines) { + case 12: + v = 0; break; + case 16: + v = 1; break; + case 18: + v = 2; break; + case 24: + v = 3; break; + case 30: + v = 4; break; + case 36: + v = 5; break; + default: + WARN_ON(1); + v = 3; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8); +} + +static void dispc7_enable_oldi(struct dispc_device *dispc, u32 hw_videoport, + const struct dispc7_bus_format *fmt) +{ + u32 oldi_cfg = 0; + u32 oldi_reset_bit = BIT(5 + hw_videoport); + int count = 0; + + /* + * On am6 DUALMODESYNC, MASTERSLAVE, MODE, and SRC are set + * statically to 0. + */ + + if (fmt->data_width == 24) + oldi_cfg |= BIT(8); /* MSB */ + else if (fmt->data_width != 18) + dev_warn(dispc->dev, "%s: %d port width not supported\n", + __func__, fmt->data_width); + + oldi_cfg |= BIT(7); /* DEPOL */ + + oldi_cfg = FLD_MOD(oldi_cfg, fmt->oldi_mode, 3, 1); + + oldi_cfg |= BIT(12); /* SOFTRST */ + + oldi_cfg |= BIT(0); /* ENABLE */ + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, oldi_cfg); + + while (!(oldi_reset_bit & dispc7_read(dispc, DSS_SYSSTATUS)) && + count < 10000) + count++; + + if (!(oldi_reset_bit & dispc7_read(dispc, DSS_SYSSTATUS))) + dev_warn(dispc->dev, "%s: timeout waiting OLDI reset done\n", + __func__); +} + +static void dispc7_vp_enable(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags) +{ + bool align, onoff, rf, ieo, ipc, ihs, ivs; + int i; + const struct dispc7_bus_format *fmt = NULL; + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + for (i = 0; i < ARRAY_SIZE(dispc7_bus_formats); ++i) { + if (dispc7_bus_formats[i].bus_fmt != bus_fmt) + continue; + + fmt = &dispc7_bus_formats[i]; + break; + } + + if (WARN_ON(!fmt)) + return; + + dispc7_set_num_datalines(dispc, hw_videoport, fmt->data_width); + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_H, + FLD_VAL(hsw - 1, 7, 0) | + FLD_VAL(hfp - 1, 19, 8) | + FLD_VAL(hbp - 1, 31, 20)); + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_V, + FLD_VAL(vsw - 1, 7, 0) | + FLD_VAL(vfp, 19, 8) | + FLD_VAL(vbp, 31, 20)); + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ivs = true; + else + ivs = false; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ihs = true; + else + ihs = false; + + if (bus_flags & DRM_BUS_FLAG_DE_LOW) + ieo = true; + else + ieo = false; + + if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + ipc = true; + else + ipc = false; + + /* always use the 'rf' setting */ + onoff = true; + + if (bus_flags & DRM_BUS_FLAG_SYNC_NEGEDGE) + rf = false; + else + rf = true; + + /* always use aligned syncs */ + align = true; + + /* always use DE_HIGH for OLDI */ + if (fmt->oldi) + ieo = false; + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ, + FLD_VAL(align, 18, 18) | + FLD_VAL(onoff, 17, 17) | + FLD_VAL(rf, 16, 16) | + FLD_VAL(ieo, 15, 15) | + FLD_VAL(ipc, 14, 14) | + FLD_VAL(ihs, 13, 13) | + FLD_VAL(ivs, 12, 12)); + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_SIZE_SCREEN, + FLD_VAL(mode->hdisplay - 1, 11, 0) | + FLD_VAL(mode->vdisplay - 1, 27, 16)); + + + if (fmt->oldi) { + dispc7_oldi_tx_power(dispc, true); + + dispc7_enable_oldi(dispc, hw_videoport, fmt); + + dispc->vp_data[hw_videoport].oldi = true; + } + + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 0, 0); +} + +static void dispc7_vp_disable(struct dispc_device *dispc, u32 hw_videoport) +{ + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 0, 0, 0); + + if (dispc->vp_data[hw_videoport].oldi) { + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0); + + dispc7_oldi_tx_power(dispc, false); + + dispc->vp_data[hw_videoport].oldi = false; + } +} + +static bool dispc7_vp_go_busy(struct dispc_device *dispc, + u32 hw_videoport) +{ + return VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5); +} + +static void dispc7_vp_go(struct dispc_device *dispc, u32 hw_videoport) +{ + VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 5, 5); +} + +static u16 c8_to_c12(u8 c8) +{ + u16 c12; + + c12 = c8 << 4; + + /* Replication logic: Copy c8 4 MSB to 4 LSB for full scale c12 */ + c12 |= c8 >> 4; + + return c12; +} + +static u64 argb8888_to_argb12121212(u32 argb8888) +{ + u8 a, r, g, b; + u64 v; + + a = (argb8888 >> 24) & 0xff; + r = (argb8888 >> 16) & 0xff; + g = (argb8888 >> 8) & 0xff; + b = (argb8888 >> 0) & 0xff; + + v = ((u64)c8_to_c12(a) << 36) | ((u64)c8_to_c12(r) << 24) | + ((u64)c8_to_c12(g) << 12) | (u64)c8_to_c12(b); + + return v; +} + +static void dispc7_vp_setup(struct dispc_device *dispc, u32 hw_videoport, + const struct tidss_vp_info *info) +{ + u64 v; + + v = argb8888_to_argb12121212(info->default_color); + + dispc7_ovr_write(dispc, hw_videoport, + DISPC_OVR_DEFAULT_COLOR, v & 0xffffffff); + dispc7_ovr_write(dispc, hw_videoport, + DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff); +} + +static enum drm_mode_status dispc7_vp_check_mode(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_display_mode *mode) +{ + u32 hsw, hfp, hbp, vsw, vfp, vbp; + + if (mode->clock * 1000 < dispc->feat->min_pclk) + return MODE_CLOCK_LOW; + + if (mode->clock * 1000 > dispc->feat->max_pclk) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > 4096) + return MODE_BAD; + + if (mode->vdisplay > 4096) + return MODE_BAD; + + /* TODO: add interlace support */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + hbp = mode->htotal - mode->hsync_end; + + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + vbp = mode->vtotal - mode->vsync_end; + + if (hsw < 1 || hsw > 256 || + hfp < 1 || hfp > 4096 || + hbp < 1 || hbp > 4096) + return MODE_BAD_HVALUE; + + if (vsw < 1 || vsw > 256 || + vfp < 0 || vfp > 4095 || + vbp < 0 || vbp > 4095) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +static int dispc7_vp_check_config(struct dispc_device *dispc, u32 hw_videoport, + const struct drm_display_mode *mode, + u32 bus_fmt, u32 bus_flags) +{ + enum drm_mode_status ok; + int i; + + ok = dispc7_vp_check_mode(dispc, hw_videoport, mode); + if (ok != MODE_OK) + return -EINVAL; + + + for (i = 0; i < ARRAY_SIZE(dispc7_bus_formats); ++i) { + if (dispc7_bus_formats[i].bus_fmt == bus_fmt) + break; + } + + if (i == ARRAY_SIZE(dispc7_bus_formats)) + return -EINVAL; + + return 0; +} + +static int dispc7_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport) +{ + return clk_prepare_enable(dispc->vp_clk[hw_videoport]); +} + +static void dispc7_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport) +{ + clk_disable_unprepare(dispc->vp_clk[hw_videoport]); +} + +static int dispc7_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport, + unsigned long rate) +{ + int r; + unsigned long new_rate; + + r = clk_set_rate(dispc->vp_clk[hw_videoport], rate); + if (r) { + dev_err(dispc->dev, "Failed to set vp%d clk rate to %lu\n", + hw_videoport, rate); + return r; + } + + new_rate = clk_get_rate(dispc->vp_clk[hw_videoport]); + + if (rate != new_rate) + dev_warn(dispc->dev, + "Failed to get exact pix clock %lu != %lu\n", + rate, new_rate); + + dev_dbg(dispc->dev, "New VP%d rate %lu Hz (requested %lu Hz)\n", + hw_videoport, clk_get_rate(dispc->vp_clk[hw_videoport]), rate); + + return 0; +} + +/* CSC */ + +struct color_conv_coef { + int ry, rcb, rcr; + int gy, gcb, gcr; + int by, bcb, bcr; + int roffset, goffset, boffset; + bool full_range; +}; + +static void dispc7_vid_write_color_conv_coefs(struct dispc_device *dispc, + u32 hw_plane, + const struct color_conv_coef *ct) +{ +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) + + dispc7_vid_write(dispc, hw_plane, + DISPC_VID_CSC_COEF(0), CVAL(ct->rcr, ct->ry)); + dispc7_vid_write(dispc, hw_plane, + DISPC_VID_CSC_COEF(1), CVAL(ct->gy, ct->rcb)); + dispc7_vid_write(dispc, hw_plane, + DISPC_VID_CSC_COEF(2), CVAL(ct->gcb, ct->gcr)); + dispc7_vid_write(dispc, hw_plane, + DISPC_VID_CSC_COEF(3), CVAL(ct->bcr, ct->by)); + dispc7_vid_write(dispc, hw_plane, + DISPC_VID_CSC_COEF(4), CVAL(0, ct->bcb)); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_CSC_COEF(5), + FLD_VAL(ct->roffset, 15, 3) | + FLD_VAL(ct->goffset, 31, 19)); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_CSC_COEF(6), + FLD_VAL(ct->boffset, 15, 3)); + + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + ct->full_range, 11, 11); + +#undef CVAL +} + +static void dispc7_vid_csc_setup(struct dispc_device *dispc) +{ + /* YUV -> RGB, ITU-R BT.601, full range */ + const struct color_conv_coef yuv2rgb_bt601_full = { + 256, 0, 358, + 256, -88, -182, + 256, 452, 0, + 0, -2048, -2048, + true, + }; + int i; + + for (i = 0; i < dispc->feat->num_planes; i++) + dispc7_vid_write_color_conv_coefs(dispc, i, + &yuv2rgb_bt601_full); +} + +static void dispc7_vid_csc_enable(struct dispc_device *dispc, u32 hw_plane, + bool enable) +{ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 9, 9); +} + +/* SCALER */ + +static u32 dispc7_calc_fir_inc(uint in, uint out) +{ + return (u32)div_u64(0x200000ull * in, out); +} + +struct dispc7_vid_fir_coefs { + s16 c2[16]; + s16 c1[16]; + u16 c0[9]; +}; + +static const struct dispc7_vid_fir_coefs dispc7_fir_coefs_null = { + .c2 = { 0 }, + .c1 = { 0 }, + .c0 = { 512, 512, 512, 512, 512, 512, 512, 512, 256, }, +}; + +/* M=8, Upscale x >= 1 */ +static const struct dispc7_vid_fir_coefs dispc7_fir_coefs_m8 = { + .c2 = { 0, -4, -8, -16, -24, -32, -40, -48, 0, 2, 4, 6, 8, 6, 4, 2, }, + .c1 = { 0, 28, 56, 94, 132, 176, 220, 266, -56, -60, -64, -62, -60, -50, -40, -20, }, + .c0 = { 512, 506, 500, 478, 456, 424, 392, 352, 312, }, +}; + +/* 5-tap, M=22, Downscale Ratio 2.5 < x < 3 */ +static const struct dispc7_vid_fir_coefs dispc7_fir_coefs_m22_5tap = { + .c2 = { 16, 20, 24, 30, 36, 42, 48, 56, 0, 0, 0, 2, 4, 8, 12, 14, }, + .c1 = { 132, 140, 148, 156, 164, 172, 180, 186, 64, 72, 80, 88, 96, 104, 112, 122, }, + .c0 = { 216, 216, 216, 214, 212, 208, 204, 198, 192, }, +}; + +/* 3-tap, M=22, Downscale Ratio 2.5 < x < 3 */ +static const struct dispc7_vid_fir_coefs dispc7_fir_coefs_m22_3tap = { + .c1 = { 100, 118, 136, 156, 176, 196, 216, 236, 0, 10, 20, 30, 40, 54, 68, 84, }, + .c0 = { 312, 310, 308, 302, 296, 286, 276, 266, 256, }, +}; + +enum dispc7_vid_fir_coef_set { + DISPC7_VID_FIR_COEF_HORIZ, + DISPC7_VID_FIR_COEF_HORIZ_UV, + DISPC7_VID_FIR_COEF_VERT, + DISPC7_VID_FIR_COEF_VERT_UV, +}; + +static void dispc7_vid_write_fir_coefs(struct dispc_device *dispc, + u32 hw_plane, + enum dispc7_vid_fir_coef_set coef_set, + const struct dispc7_vid_fir_coefs *coefs) +{ + static const u16 c0_regs[] = { + [DISPC7_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H0, + [DISPC7_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H0_C, + [DISPC7_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V0, + [DISPC7_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V0_C, + }; + + static const u16 c12_regs[] = { + [DISPC7_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H12, + [DISPC7_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H12_C, + [DISPC7_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V12, + [DISPC7_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V12_C, + }; + + const u16 c0_base = c0_regs[coef_set]; + const u16 c12_base = c12_regs[coef_set]; + int phase; + + for (phase = 0; phase <= 8; ++phase) { + u16 reg = c0_base + phase * 4; + u16 c0 = coefs->c0[phase]; + + dispc7_vid_write(dispc, hw_plane, reg, c0); + } + + for (phase = 0; phase <= 15; ++phase) { + u16 reg = c12_base + phase * 4; + s16 c1, c2; + u32 c12; + + c1 = coefs->c1[phase]; + c2 = coefs->c2[phase]; + c12 = FLD_VAL(c1, 19, 10) | FLD_VAL(c2, 29, 20); + + dispc7_vid_write(dispc, hw_plane, reg, c12); + } +} + +static void dispc7_vid_write_scale_coefs(struct dispc_device *dispc, + u32 hw_plane) +{ + dispc7_vid_write_fir_coefs(dispc, hw_plane, DISPC7_VID_FIR_COEF_HORIZ, &dispc7_fir_coefs_null); + dispc7_vid_write_fir_coefs(dispc, hw_plane, DISPC7_VID_FIR_COEF_HORIZ_UV, &dispc7_fir_coefs_null); + dispc7_vid_write_fir_coefs(dispc, hw_plane, DISPC7_VID_FIR_COEF_VERT, &dispc7_fir_coefs_null); + dispc7_vid_write_fir_coefs(dispc, hw_plane, DISPC7_VID_FIR_COEF_VERT_UV, &dispc7_fir_coefs_null); +} + +static void dispc7_vid_set_scaling(struct dispc_device *dispc, + u32 hw_plane, + uint orig_width, uint orig_height, + uint out_width, uint out_height, + u32 fourcc) +{ + uint in_w, in_h, in_w_uv, in_h_uv; + uint fir_hinc, fir_vinc, fir_hinc_uv, fir_vinc_uv; + bool scale_x, scale_y; + bool five_taps = false; /* XXX always 3-tap for now */ + + in_w = in_w_uv = orig_width; + in_h = in_h_uv = orig_height; + + switch (fourcc) { + case DRM_FORMAT_NV12: + /* UV is subsampled by 2 horizontally and vertically */ + in_h_uv >>= 1; + in_w_uv >>= 1; + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + /* UV is subsampled by 2 horizontally */ + in_w_uv >>= 1; + break; + + default: + break; + } + + scale_x = in_w != out_width || in_w_uv != out_width; + scale_y = in_h != out_height || in_h_uv != out_height; + + /* HORIZONTAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, scale_x, 7, 7); + + /* VERTICAL RESIZE ENABLE */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, scale_y, 8, 8); + + /* Skip the rest if no scaling is used */ + if (!scale_x && !scale_y) + return; + + /* VERTICAL 5-TAPS */ + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + five_taps, 21, 21); + + /* FIR INC */ + + fir_hinc = dispc7_calc_fir_inc(in_w, out_width); + fir_vinc = dispc7_calc_fir_inc(in_h, out_height); + fir_hinc_uv = dispc7_calc_fir_inc(in_w_uv, out_width); + fir_vinc_uv = dispc7_calc_fir_inc(in_h_uv, out_height); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_FIRH, fir_hinc); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_FIRV, fir_vinc); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_FIRH2, fir_hinc_uv); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_FIRV2, fir_vinc_uv); + + dispc7_vid_write_scale_coefs(dispc, hw_plane); +} + +/* OTHER */ + +static const struct { + u32 fourcc; + u8 dss_code; + u8 bytespp; +} dispc7_color_formats[] = { + { DRM_FORMAT_ARGB4444, 0x0, 2, }, + { DRM_FORMAT_ABGR4444, 0x1, 2, }, + { DRM_FORMAT_RGBA4444, 0x2, 2, }, + + { DRM_FORMAT_RGB565, 0x3, 2, }, + { DRM_FORMAT_BGR565, 0x4, 2, }, + + { DRM_FORMAT_ARGB1555, 0x5, 2, }, + { DRM_FORMAT_ABGR1555, 0x6, 2, }, + + { DRM_FORMAT_ARGB8888, 0x7, 4, }, + { DRM_FORMAT_ABGR8888, 0x8, 4, }, + { DRM_FORMAT_RGBA8888, 0x9, 4, }, + { DRM_FORMAT_BGRA8888, 0xa, 4, }, + + { DRM_FORMAT_RGB888, 0xb, 3, }, + { DRM_FORMAT_BGR888, 0xc, 3, }, + + { DRM_FORMAT_ARGB2101010, 0xe, 4, }, + { DRM_FORMAT_ABGR2101010, 0xf, 4, }, + { DRM_FORMAT_RGBA1010102, 0x10, 4, }, + { DRM_FORMAT_BGRA1010102, 0x11, 4, }, + + { DRM_FORMAT_XRGB4444, 0x20, 2, }, + { DRM_FORMAT_XBGR4444, 0x21, 2, }, + { DRM_FORMAT_RGBX4444, 0x22, 2, }, + + { DRM_FORMAT_ARGB1555, 0x25, 2, }, + { DRM_FORMAT_ABGR1555, 0x26, 2, }, + + { DRM_FORMAT_XRGB8888, 0x27, 4, }, + { DRM_FORMAT_XBGR8888, 0x28, 4, }, + { DRM_FORMAT_RGBX8888, 0x29, 4, }, + { DRM_FORMAT_BGRX8888, 0x2a, 4, }, + + { DRM_FORMAT_XRGB2101010, 0x2e, 4, }, + { DRM_FORMAT_XBGR2101010, 0x2f, 4, }, + { DRM_FORMAT_RGBX1010102, 0x30, 4, }, + { DRM_FORMAT_BGRX1010102, 0x31, 4, }, + + { DRM_FORMAT_YUYV, 0x3e, 2, }, + { DRM_FORMAT_UYVY, 0x3f, 2, }, + + { DRM_FORMAT_NV12, 0x3d, 2, }, +}; + +static bool dispc7_fourcc_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_NV12: + return true; + default: + return false; + } +} + +static void dispc7_plane_set_pixel_format(struct dispc_device *dispc, + u32 hw_plane, u32 fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dispc7_color_formats); ++i) { + if (dispc7_color_formats[i].fourcc == fourcc) { + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, + dispc7_color_formats[i].dss_code, + 6, 1); + return; + } + } + + WARN_ON(1); +} + +static int dispc7_fourcc_to_bytespp(u32 fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dispc7_color_formats); ++i) { + if (dispc7_color_formats[i].fourcc == fourcc) + return dispc7_color_formats[i].bytespp; + } + + WARN_ON(1); + return 4; +} + +static s32 pixinc(int pixels, u8 ps) +{ + if (pixels == 1) + return 1; + else if (pixels > 1) + return 1 + (pixels - 1) * ps; + else if (pixels < 0) + return 1 - (-pixels + 1) * ps; + + WARN_ON(1); + return 0; +} + +static int dispc7_plane_setup(struct dispc_device *dispc, u32 hw_plane, + const struct tidss_plane_info *pi, + u32 hw_videoport) +{ + bool lite = dispc->feat->vid_lite[hw_plane]; + u32 fourcc = pi->fourcc; + int bytespp = dispc7_fourcc_to_bytespp(fourcc); + + if (dispc7_fourcc_is_yuv(fourcc) && (pi->width & 1)) { + dev_err(dispc->dev, "non even input width %d for YUV format\n", + pi->width); + return -EINVAL; + } + + dispc7_plane_set_pixel_format(dispc, hw_plane, fourcc); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_0, pi->paddr & 0xffffffff); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_0, (u64)pi->paddr >> 32); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_1, pi->paddr & 0xffffffff); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_1, (u64)pi->paddr >> 32); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_0, pi->p_uv_addr & 0xffffffff); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_EXT_0, (u64)pi->p_uv_addr >> 32); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_1, pi->p_uv_addr & 0xffffffff); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_BA_UV_EXT_1, (u64)pi->p_uv_addr >> 32); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_PICTURE_SIZE, + (pi->width - 1) | ((pi->height - 1) << 16)); + + dispc7_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, + pixinc(1, bytespp)); + dispc7_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC, + pixinc(1 + pi->fb_width - pi->width, bytespp)); + + if (fourcc == DRM_FORMAT_NV12) + dispc7_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC_UV, + pixinc(1 + pi->fb_width - pi->width, bytespp)); + + if (!lite) { + dispc7_vid_write(dispc, hw_plane, DISPC_VID_SIZE, + (pi->out_width - 1) | + ((pi->out_height - 1) << 16)); + + dispc7_vid_set_scaling(dispc, hw_plane, + pi->width, pi->height, + pi->out_width, pi->out_height, + fourcc); + } + + /* enable YUV->RGB color conversion */ + if (dispc7_fourcc_is_yuv(fourcc)) + dispc7_vid_csc_enable(dispc, hw_plane, true); + else + dispc7_vid_csc_enable(dispc, hw_plane, false); + + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(pi->zorder), + hw_plane, 4, 1); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(pi->zorder), + pi->pos_x, 17, 6); + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(pi->zorder), + pi->pos_y, 30, 19); + + dispc->plane_data[hw_plane].zorder = pi->zorder; + dispc->plane_data[hw_plane].hw_videoport = hw_videoport; + + return 0; +} + +static int dispc7_plane_enable(struct dispc_device *dispc, + u32 hw_plane, bool enable) +{ + OVR_REG_FLD_MOD(dispc, dispc->plane_data[hw_plane].hw_videoport, + DISPC_OVR_ATTRIBUTES(dispc->plane_data[hw_plane].zorder), + !!enable, 0, 0); + VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 0, 0); + return 0; +} + +static u32 dispc7_vid_get_fifo_size(struct dispc_device *dispc, + u32 hw_plane) +{ + const u32 unit_size = 16; /* 128-bits */ + + return VID_REG_GET(dispc, hw_plane, DISPC_VID_BUF_SIZE_STATUS, 15, 0) * + unit_size; +} + +static void dispc7_vid_set_mflag_threshold(struct dispc_device *dispc, + u32 hw_plane, uint low, uint high) +{ + dispc7_vid_write(dispc, hw_plane, DISPC_VID_MFLAG_THRESHOLD, + FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); +} + +static void __maybe_unused dispc7_mflag_setup(struct dispc_device *dispc) +{ + u32 hw_plane = 0; + const u32 unit_size = 16; /* 128-bits */ + u32 size = dispc7_vid_get_fifo_size(dispc, hw_plane); + u32 low, high; + + /* MFLAG_CTRL = MFLAGFORCE */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 1, 1, 0); + /* MFLAG_START = MFLAGNORMALSTARTMODE */ + REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); + + /* + * Simulation team suggests below thesholds: + * HT = fifosize * 5 / 8; + * LT = fifosize * 4 / 8; + */ + + low = size * 4 / 8 / unit_size; + high = size * 5 / 8 / unit_size; + + dispc7_vid_set_mflag_threshold(dispc, hw_plane, low, high); +} + +static void dispc7_vp_init(struct dispc_device *dispc) +{ + unsigned int i; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + /* Enable the gamma Shadow bit-field for all VPs*/ + for (i = 0; i < dispc->feat->num_vps; i++) + VP_REG_FLD_MOD(dispc, i, DISPC_VP_CONFIG, 1, 2, 2); +} + +static void dispc7_initial_config(struct dispc_device *dispc) +{ + dispc7_vid_csc_setup(dispc); + //dispc7_mflag_setup(dispc); + dispc7_vp_init(dispc); +} + +static int dispc7_get_num_planes(struct dispc_device *dispc) +{ + return dispc->feat->num_planes; +} + +static int dispc7_get_num_vps(struct dispc_device *dispc) +{ + return dispc->feat->num_vps; +} + +static u32 dispc7_vp_gamma_size(struct dispc_device *dispc, + u32 hw_videoport) +{ + return ARRAY_SIZE(dispc->vp_data[hw_videoport].gamma_table); +} + +static void dispc7_vp_write_gamma_table(struct dispc_device *dispc, + u32 hw_videoport) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + uint hwlen = ARRAY_SIZE(dispc->vp_data[hw_videoport].gamma_table); + unsigned int i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); + + for (i = 0; i < hwlen; ++i) { + u32 v = table[i]; + + v |= i << 24; + + dispc7_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); + } +} + +static void dispc7_restore_gamma_tables(struct dispc_device *dispc) +{ + unsigned int i; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + for (i = 0; i < dispc->feat->num_vps; i++) + dispc7_vp_write_gamma_table(dispc, i); +} + +static const struct drm_color_lut dispc7_vp_gamma_default_lut[] = { + { .red = 0, .green = 0, .blue = 0, }, + { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, +}; + +static void dispc7_vp_set_gamma(struct dispc_device *dispc, + u32 hw_videoport, + const struct drm_color_lut *lut, + unsigned int length) +{ + u32 *table = dispc->vp_data[hw_videoport].gamma_table; + uint hwlen = ARRAY_SIZE(dispc->vp_data[hw_videoport].gamma_table); + static const uint hwbits = 8; + uint i; + + dev_dbg(dispc->dev, "%s: hw_videoport %d, lut len %u, hw len %u\n", + __func__, hw_videoport, length, hwlen); + + if (lut == NULL || length < 2) { + lut = dispc7_vp_gamma_default_lut; + length = ARRAY_SIZE(dispc7_vp_gamma_default_lut); + } + + for (i = 0; i < length - 1; ++i) { + uint first = i * (hwlen - 1) / (length - 1); + uint last = (i + 1) * (hwlen - 1) / (length - 1); + uint w = last - first; + u16 r, g, b; + uint j; + + if (w == 0) + continue; + + for (j = 0; j <= w; j++) { + r = (lut[i].red * (w - j) + lut[i + 1].red * j) / w; + g = (lut[i].green * (w - j) + lut[i + 1].green * j) / w; + b = (lut[i].blue * (w - j) + lut[i + 1].blue * j) / w; + + r >>= 16 - hwbits; + g >>= 16 - hwbits; + b >>= 16 - hwbits; + + table[first + j] = (r << (hwbits * 2)) | + (g << hwbits) | b; + } + } + + if (dispc->is_enabled) + dispc7_vp_write_gamma_table(dispc, hw_videoport); +} + +static int dispc7_init_gamma_tables(struct dispc_device *dispc) +{ + unsigned int i; + + dev_dbg(dispc->dev, "%s()\n", __func__); + + for (i = 0; i < dispc->feat->num_vps; i++) + dispc7_vp_set_gamma(dispc, i, NULL, 0); + + return 0; +} + +static const char *dispc7_plane_name(struct dispc_device *dispc, u32 hw_plane) +{ + if (WARN_ON(hw_plane >= dispc->feat->num_planes)) + return "ERROR"; + + return dispc->feat->vid_name[hw_plane]; +} + +static const char *dispc7_vp_name(struct dispc_device *dispc, u32 hw_videoport) +{ + if (WARN_ON(hw_videoport >= dispc->feat->num_vps)) + return "ERROR"; + + return dispc->feat->vp_name[hw_videoport]; +} + +static int dispc7_runtime_suspend(struct dispc_device *dispc) +{ + dev_dbg(dispc->dev, "suspend\n"); + + dispc->is_enabled = false; + + clk_disable_unprepare(dispc->fclk); + + return 0; +} + +static int dispc7_runtime_resume(struct dispc_device *dispc) +{ + dev_dbg(dispc->dev, "resume\n"); + + clk_prepare_enable(dispc->fclk); + + if (REG_GET(dispc, DSS_SYSSTATUS, 0, 0) == 0) + dev_warn(dispc->dev, "DSS FUNC RESET not done!\n"); + + dev_dbg(dispc->dev, "OMAP DSS7 rev 0x%x\n", + dispc7_read(dispc, DSS_REVISION)); + + dev_dbg(dispc->dev, "VP RESETDONE %d,%d,%d\n", + REG_GET(dispc, DSS_SYSSTATUS, 1, 1), + REG_GET(dispc, DSS_SYSSTATUS, 2, 2), + REG_GET(dispc, DSS_SYSSTATUS, 3, 3)); + + dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n", + REG_GET(dispc, DSS_SYSSTATUS, 5, 5), + REG_GET(dispc, DSS_SYSSTATUS, 6, 6), + REG_GET(dispc, DSS_SYSSTATUS, 7, 7)); + + dev_dbg(dispc->dev, "DISPC IDLE %d\n", + REG_GET(dispc, DSS_SYSSTATUS, 9, 9)); + + dispc7_initial_config(dispc); + + dispc7_restore_gamma_tables(dispc); + + dispc->is_enabled = true; + + return 0; +} + +static int dispc7_modeset_init(struct dispc_device *dispc) +{ + struct tidss_device *tidss = dispc->tidss; + struct device *dev = tidss->dev; + u32 fourccs[ARRAY_SIZE(dispc7_color_formats)]; + int i; + + struct pipe { + u32 hw_videoport; + struct drm_bridge *bridge; + u32 enc_type; + struct device_node *epnode; + }; + + u32 max_vps = dispc->feat->num_vps; + u32 max_planes = dispc->feat->num_planes; + + struct pipe pipes[DISPC7_MAX_PORTS]; + u32 num_pipes = 0; + u32 crtc_mask; + + for (i = 0; i < ARRAY_SIZE(fourccs); ++i) + fourccs[i] = dispc7_color_formats[i].fourcc; + + /* first find all the connected panels & bridges */ + + for (i = 0; i < max_vps; i++) { + struct drm_panel *panel; + struct drm_bridge *bridge; + u32 enc_type = DRM_MODE_ENCODER_NONE; + int ret; + + ret = drm_of_find_panel_or_bridge(dev->of_node, i, 0, + &panel, &bridge); + if (ret == -ENODEV) { + dev_dbg(dev, "no panel/bridge for port %d\n", i); + continue; + } else if (ret) { + dev_dbg(dev, "port %d probe returned %d\n", i, ret); + return ret; + } + + if (panel) { + u32 conn_type; + + dev_dbg(dev, "Setting up panel for port %d\n", i); + + enc_type = dispc->feat->vp_enc_type[i]; + switch (enc_type) { + case DRM_MODE_ENCODER_LVDS: + conn_type = DRM_MODE_CONNECTOR_LVDS; + break; + case DRM_MODE_ENCODER_DPI: + conn_type = DRM_MODE_CONNECTOR_DPI; + break; + default: + conn_type = DRM_MODE_CONNECTOR_Unknown; + break; + } + + bridge = devm_drm_panel_bridge_add(dev, panel, + conn_type); + if (IS_ERR(bridge)) { + dev_err(dev, "failed to set up panel bridge for port %d\n", i); + return PTR_ERR(bridge); + } + } + + pipes[num_pipes].hw_videoport = i; + pipes[num_pipes].bridge = bridge; + pipes[num_pipes].enc_type = enc_type; + pipes[num_pipes].epnode = + of_graph_get_endpoint_by_regs(dev->of_node, i, 0); + if (WARN_ON(!pipes[num_pipes].epnode)) + return -EINVAL; + + num_pipes++; + } + + /* all planes can be on any crtc */ + crtc_mask = (1 << num_pipes) - 1; + + /* then create a plane, a crtc and an encoder for each panel/bridge */ + + for (i = 0; i < num_pipes; ++i) { + struct tidss_plane *tplane; + struct tidss_crtc *tcrtc; + struct tidss_encoder *tenc; + u32 hw_plane_id = tidss->num_planes; + int ret; + + tplane = tidss_plane_create(tidss, hw_plane_id, + DRM_PLANE_TYPE_PRIMARY, crtc_mask, + fourccs, ARRAY_SIZE(fourccs)); + if (IS_ERR(tplane)) { + dev_err(tidss->dev, "plane create failed\n"); + return PTR_ERR(tplane); + } + + tidss->planes[tidss->num_planes++] = &tplane->plane; + + tcrtc = tidss_crtc_create(tidss, pipes[i].hw_videoport, + &tplane->plane, pipes[i].epnode); + if (IS_ERR(tcrtc)) { + dev_err(tidss->dev, "crtc create failed\n"); + return PTR_ERR(tcrtc); + } + + tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc; + + tenc = tidss_encoder_create(tidss, pipes[i].enc_type, + 1 << tcrtc->crtc.index); + if (IS_ERR(tenc)) { + dev_err(tidss->dev, "encoder create failed\n"); + return PTR_ERR(tenc); + } + + ret = drm_bridge_attach(&tenc->encoder, pipes[i].bridge, NULL); + if (ret) { + dev_err(tidss->dev, "bridge attach failed: %d\n", ret); + return ret; + } + } + + /* create overlay planes of the leftover planes */ + + while (tidss->num_planes < max_planes) { + struct tidss_plane *tplane; + u32 hw_plane_id = tidss->num_planes; + + tplane = tidss_plane_create(tidss, hw_plane_id, + DRM_PLANE_TYPE_OVERLAY, crtc_mask, + fourccs, ARRAY_SIZE(fourccs)); + + if (IS_ERR(tplane)) { + dev_err(tidss->dev, "plane create failed\n"); + return PTR_ERR(tplane); + } + + tidss->planes[tidss->num_planes++] = &tplane->plane; + } + + return 0; +} + +static void dispc7_remove(struct dispc_device *dispc) +{ + struct device *dev = dispc->dev; + + dev_dbg(dev, "%s\n", __func__); + + dispc->tidss->dispc_ops = NULL; + dispc->tidss->dispc = NULL; +} + +static const struct dispc_ops dispc7_ops = { + .read_and_clear_irqstatus = dispc7_read_and_clear_irqstatus, + .write_irqenable = dispc7_write_irqenable, + + .runtime_get = dispc7_runtime_get, + .runtime_put = dispc7_runtime_put, + + .get_num_planes = dispc7_get_num_planes, + .get_num_vps = dispc7_get_num_vps, + + .plane_name = dispc7_plane_name, + .vp_name = dispc7_vp_name, + + .vp_enable = dispc7_vp_enable, + .vp_disable = dispc7_vp_disable, + .vp_go_busy = dispc7_vp_go_busy, + .vp_go = dispc7_vp_go, + + .vp_setup = dispc7_vp_setup, + .vp_check_mode = dispc7_vp_check_mode, + .vp_check_config = dispc7_vp_check_config, + + .vp_gamma_size = dispc7_vp_gamma_size, + .vp_set_gamma = dispc7_vp_set_gamma, + + .plane_enable = dispc7_plane_enable, + .plane_setup = dispc7_plane_setup, + + .vp_set_clk_rate = dispc7_vp_set_clk_rate, + .vp_enable_clk = dispc7_vp_enable_clk, + .vp_disable_clk = dispc7_vp_disable_clk, + + .runtime_suspend = dispc7_runtime_suspend, + .runtime_resume = dispc7_runtime_resume, + + .remove = dispc7_remove, + + .modeset_init = dispc7_modeset_init, +}; + +static int dispc7_iomap_resource(struct platform_device *pdev, const char *name, + void __iomem **base) +{ + struct resource *res; + void __iomem *b; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(&pdev->dev, "cannot get mem resource '%s'\n", name); + return -EINVAL; + } + + b = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(b)) { + dev_err(&pdev->dev, "cannot ioremap resource '%s'\n", name); + return PTR_ERR(b); + } + + *base = b; + + return 0; +} + +int dispc7_init(struct tidss_device *tidss) +{ + struct device *dev = tidss->dev; + struct platform_device *pdev = tidss->pdev; + struct dispc_device *dispc; + const struct dispc7_features *feat; + int r = 0; + uint i; + + dev_info(dev, "%s\n", __func__); + + feat = of_match_device(dispc7_of_table, dev)->data; + + dispc = devm_kzalloc(dev, sizeof(*dispc), GFP_KERNEL); + if (!dispc) + return -ENOMEM; + + dispc->tidss = tidss; + dispc->dev = dev; + dispc->feat = feat; + + r = dispc7_iomap_resource(pdev, "common", &dispc->base_common); + if (r) + return r; + + for (i = 0; i < dispc->feat->num_planes; i++) { + r = dispc7_iomap_resource(pdev, dispc->feat->vid_name[i], + &dispc->base_vid[i]); + dev_dbg(dev, "%s: %u %s %d\n", __func__, + i, dispc->feat->vid_name[i], r); + if (r) + return r; + } + + for (i = 0; i < dispc->feat->num_vps; i++) { + struct clk *clk; + + r = dispc7_iomap_resource(pdev, dispc->feat->ovr_name[i], + &dispc->base_ovr[i]); + dev_dbg(dev, "%s: %u %s %d\n", __func__, + i, dispc->feat->ovr_name[i], r); + if (r) + return r; + + r = dispc7_iomap_resource(pdev, dispc->feat->vp_name[i], + &dispc->base_vp[i]); + dev_dbg(dev, "%s: %u %s %d\n", __func__, + i, dispc->feat->vp_name[i], r); + if (r) + return r; + + clk = devm_clk_get(dev, dispc->feat->vpclk_name[i]); + if (IS_ERR(clk)) { + dev_err(dev, "%s: Failed to get clk %s:%ld\n", __func__, + dispc->feat->vpclk_name[i], PTR_ERR(clk)); + return PTR_ERR(clk); + } + dispc->vp_clk[i] = clk; + } + + dispc->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(dispc->syscon)) { + dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n", + __func__, PTR_ERR(dispc->syscon)); + return PTR_ERR(dispc->syscon); + } + + dispc->fclk = devm_clk_get(dev, "fck"); + if (IS_ERR(dispc->fclk)) { + dev_err(dev, "Failed to get fclk\n"); + return PTR_ERR(dispc->fclk); + } + dev_dbg(dev, "DSS fclk %lu Hz\n", clk_get_rate(dispc->fclk)); + + r = dispc7_init_gamma_tables(dispc); + if (r) + return r; + + tidss->dispc_ops = &dispc7_ops; + tidss->dispc = dispc; + + return 0; +} diff --git a/drivers/gpu/drm/tidss/tidss_dispc7.h b/drivers/gpu/drm/tidss/tidss_dispc7.h new file mode 100644 index 000000000000..0f898aaa94c5 --- /dev/null +++ b/drivers/gpu/drm/tidss/tidss_dispc7.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha + */ + +#ifndef __TIDSS_DISPC7_H +#define __TIDSS_DISPC7_H + +#define DISPC7_MAX_PORTS 4 +#define DISPC7_MAX_PLANES 4 + +struct dispc7_features { + /* XXX should these come from the .dts? Min pclk is not feature of DSS IP */ + unsigned long min_pclk; + unsigned long max_pclk; + + u32 num_vps; + const char *vp_name[DISPC7_MAX_PORTS]; /* Should match dt reg names */ + const char *ovr_name[DISPC7_MAX_PORTS]; /* Should match dt reg names */ + const char *vpclk_name[DISPC7_MAX_PORTS]; /* Should match dt clk names */ + const u32 vp_enc_type[DISPC7_MAX_PORTS]; + u32 num_planes; + const char *vid_name[DISPC7_MAX_PLANES]; /* Should match dt reg names */ + bool vid_lite[DISPC7_MAX_PLANES]; +}; + +#define DSS_REVISION 0x4 +#define DSS_SYSCONFIG 0x8 +#define DSS_SYSSTATUS 0x20 +#define DISPC_IRQ_EOI 0x24 +#define DISPC_IRQSTATUS_RAW 0x28 +#define DISPC_IRQSTATUS 0x2c +#define DISPC_IRQENABLE_SET 0x30 +#define DISPC_IRQENABLE_CLR 0x40 +#define DISPC_VID_IRQENABLE(n) (0x44 + (n) * 4) +#define DISPC_VID_IRQSTATUS(n) (0x58 + (n) * 4) +#define DISPC_VP_IRQENABLE(n) (0x70 + (n) * 4) +#define DISPC_VP_IRQSTATUS(n) (0x7c + (n) * 4) + +#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x90 +#define DISPC_GLOBAL_OUTPUT_ENABLE 0x94 +#define DISPC_GLOBAL_BUFFER 0x98 +#define DSS_CBA_CFG 0x9c +#define DISPC_DBG_CONTROL 0xa0 +#define DISPC_DBG_STATUS 0xa4 +#define DISPC_CLKGATING_DISABLE 0xa8 +#define DISPC_SECURE_DISABLE 0xac + +/* COMMON1 not implemented */ + +/* VID */ + +#define DISPC_VID_ACCUH_0 0x0 +#define DISPC_VID_ACCUH_1 0x4 +#define DISPC_VID_ACCUH2_0 0x8 +#define DISPC_VID_ACCUH2_1 0xc +#define DISPC_VID_ACCUV_0 0x10 +#define DISPC_VID_ACCUV_1 0x14 +#define DISPC_VID_ACCUV2_0 0x18 +#define DISPC_VID_ACCUV2_1 0x1c +#define DISPC_VID_ATTRIBUTES 0x20 +#define DISPC_VID_ATTRIBUTES2 0x24 +#define DISPC_VID_BA_0 0x28 +#define DISPC_VID_BA_1 0x2c +#define DISPC_VID_BA_UV_0 0x30 +#define DISPC_VID_BA_UV_1 0x34 +#define DISPC_VID_BUF_SIZE_STATUS 0x38 +#define DISPC_VID_BUF_THRESHOLD 0x3c +#define DISPC_VID_CSC_COEF(n) (0x40 + (n) * 4) + +#define DISPC_VID_FIRH 0x5c +#define DISPC_VID_FIRH2 0x60 +#define DISPC_VID_FIRV 0x64 +#define DISPC_VID_FIRV2 0x68 + +#define DISPC_VID_FIR_COEFS_H0 0x6c +#define DISPC_VID_FIR_COEF_H0(phase) (0x6c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H0_C 0x90 +#define DISPC_VID_FIR_COEF_H0_C(phase) (0x90 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_H12 0xb4 +#define DISPC_VID_FIR_COEF_H12(phase) (0xb4 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_H12_C 0xf4 +#define DISPC_VID_FIR_COEF_H12_C(phase) (0xf4 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V0 0x134 +#define DISPC_VID_FIR_COEF_V0(phase) (0x134 + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V0_C 0x158 +#define DISPC_VID_FIR_COEF_V0_C(phase) (0x158 + (phase) * 4) + +#define DISPC_VID_FIR_COEFS_V12 0x17c +#define DISPC_VID_FIR_COEF_V12(phase) (0x17c + (phase) * 4) +#define DISPC_VID_FIR_COEFS_V12_C 0x1bc +#define DISPC_VID_FIR_COEF_V12_C(phase) (0x1bc + (phase) * 4) + +#define DISPC_VID_GLOBAL_ALPHA 0x1fc +#define DISPC_VID_MFLAG_THRESHOLD 0x208 +#define DISPC_VID_PICTURE_SIZE 0x20c +#define DISPC_VID_PIXEL_INC 0x210 +#define DISPC_VID_PRELOAD 0x218 +#define DISPC_VID_ROW_INC 0x21c +#define DISPC_VID_SIZE 0x220 +#define DISPC_VID_BA_EXT_0 0x22c +#define DISPC_VID_BA_EXT_1 0x230 +#define DISPC_VID_BA_UV_EXT_0 0x234 +#define DISPC_VID_BA_UV_EXT_1 0x238 +#define DISPC_VID_CSC_COEF7 0x23c +#define DISPC_VID_ROW_INC_UV 0x248 +#define DISPC_VID_CLUT 0x260 +#define DISPC_VID_SAFETY_ATTRIBUTES 0x2a0 +#define DISPC_VID_SAFETY_CAPT_SIGNATURE 0x2a4 +#define DISPC_VID_SAFETY_POSITION 0x2a8 +#define DISPC_VID_SAFETY_REF_SIGNATURE 0x2ac +#define DISPC_VID_SAFETY_SIZE 0x2b0 +#define DISPC_VID_SAFETY_LFSR_SEED 0x2b4 +#define DISPC_VID_LUMAKEY 0x2b8 + +/* OVR */ + +#define DISPC_OVR_CONFIG 0x0 +#define DISPC_OVR_DEFAULT_COLOR 0x8 +#define DISPC_OVR_DEFAULT_COLOR2 0xc +#define DISPC_OVR_TRANS_COLOR_MAX 0x10 +#define DISPC_OVR_TRANS_COLOR_MAX2 0x14 +#define DISPC_OVR_TRANS_COLOR_MIN 0x18 +#define DISPC_OVR_TRANS_COLOR_MIN2 0x1c +#define DISPC_OVR_ATTRIBUTES(n) (0x20 + (n) * 4) + +/* VP */ + +#define DISPC_VP_CONFIG 0x0 +#define DISPC_VP_CONTROL 0x4 +#define DISPC_VP_CSC_COEF0 0x8 +#define DISPC_VP_CSC_COEF1 0xc +#define DISPC_VP_CSC_COEF2 0x10 +#define DISPC_VP_DATA_CYCLE_0 0x14 +#define DISPC_VP_DATA_CYCLE_1 0x18 +#define DISPC_VP_DATA_CYCLE_2 0x1c +#define DISPC_VP_LINE_NUMBER 0x44 +#define DISPC_VP_POL_FREQ 0x4c +#define DISPC_VP_SIZE_SCREEN 0x50 +#define DISPC_VP_TIMING_H 0x54 +#define DISPC_VP_TIMING_V 0x58 +#define DISPC_VP_CSC_COEF3 0x5c +#define DISPC_VP_CSC_COEF4 0x60 +#define DISPC_VP_CSC_COEF5 0x64 +#define DISPC_VP_CSC_COEF6 0x68 +#define DISPC_VP_CSC_COEF7 0x6c +#define DISPC_VP_SAFETY_ATTRIBUTES_0 0x70 +#define DISPC_VP_SAFETY_ATTRIBUTES_1 0x74 +#define DISPC_VP_SAFETY_ATTRIBUTES_2 0x78 +#define DISPC_VP_SAFETY_ATTRIBUTES_3 0x7c +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_0 0x90 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_1 0x94 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_2 0x98 +#define DISPC_VP_SAFETY_CAPT_SIGNATURE_3 0x9c +#define DISPC_VP_SAFETY_POSITION_0 0xb0 +#define DISPC_VP_SAFETY_POSITION_1 0xb4 +#define DISPC_VP_SAFETY_POSITION_2 0xb8 +#define DISPC_VP_SAFETY_POSITION_3 0xbc +#define DISPC_VP_SAFETY_REF_SIGNATURE_0 0xd0 +#define DISPC_VP_SAFETY_REF_SIGNATURE_1 0xd4 +#define DISPC_VP_SAFETY_REF_SIGNATURE_2 0xd8 +#define DISPC_VP_SAFETY_REF_SIGNATURE_3 0xdc +#define DISPC_VP_SAFETY_SIZE_0 0xf0 +#define DISPC_VP_SAFETY_SIZE_1 0xf4 +#define DISPC_VP_SAFETY_SIZE_2 0xf8 +#define DISPC_VP_SAFETY_SIZE_3 0xfc +#define DISPC_VP_SAFETY_LFSR_SEED 0x110 +#define DISPC_VP_GAMMA_TABLE 0x120 +#define DISPC_VP_DSS_OLDI_CFG 0x160 +#define DISPC_VP_DSS_OLDI_STATUS 0x164 +#define DISPC_VP_DSS_OLDI_LB 0x168 + +/* CTRL_MMR0 access trough syscon */ +#define CTRLMMR0P1_OLDI_DAT0_IO_CTRL 0x1E0 +#define CTRLMMR0P1_OLDI_DAT1_IO_CTRL 0x1E4 +#define CTRLMMR0P1_OLDI_DAT2_IO_CTRL 0x1E8 +#define CTRLMMR0P1_OLDI_DAT3_IO_CTRL 0x1EC +#define CTRLMMR0P1_OLDI_CLK_IO_CTRL 0x1F0 + +#define CTRLMMR0P1_OLDI_PWRDN_TX BIT(8) + +#endif /* __TIDSS_DISPC7_H */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 8002766c4640..0411ef952a7f 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -30,8 +30,13 @@ static const struct tidss_features tidss_k2g_feats = { .dispc_init = dispc6_init, }; +static const struct tidss_features tidss_am6_feats = { + .dispc_init = dispc7_init, +}; + static const struct of_device_id tidss_of_table[] = { { .compatible = "ti,k2g-dss", .data = &tidss_k2g_feats }, + { .compatible = "ti,am6-dss", .data = &tidss_am6_feats }, { } }; From patchwork Mon Jun 18 13:22:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138941 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949502lji; Mon, 18 Jun 2018 06:23:13 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJPSu4zPmeR7PsvghNDbJzZqXbQSLGL0Ac/qwxf8Qo8kWUSTZxgIxRIKWOcvbU2rxU1yoJv X-Received: by 2002:a62:859c:: with SMTP id m28-v6mr13589243pfk.42.1529328192883; Mon, 18 Jun 2018 06:23:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328192; cv=none; d=google.com; s=arc-20160816; b=jm7W4iAziDqSn0V7HrD/FiG/J92ywPiGAqdd7KS9iddW/Kqt7aysKC2hLPhn8S84pG 3pJ6mpU7bTxbNoNYolCfWz3ATF7UzpH1wUm9HyQU+QVRxN1JNQkax1U12NjskqID+mI6 479HpIp61+qR2NBU45tzZMrT7q8SzCX0+GiqSuwnOBqDB1nNrPgwkPIWxJJ8XbAGdr/e WDqUj/cBJeaXTZ5I0d//J2+KJdupWLJOjF9g/8P4o43tNCNZUJhZjxzLCFDxf2cNffqk qRp0t4l1GTFcSUlnCbO1E6lCaxSBX4vIXc6OLTVxTOxWomKK1PzINjg+RmQ1DXXmWAtJ ubzA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=g2pl5wO/YDG8utjbVuFXbnw11ScAsU8pncDwYeZErxI=; b=x035yxHTvAVmdKTAaThuMfsf4tM6QXR+fNKyp2dp5TDzkahkRmjyYhbWwwoycefr4y FKYA1nmFYUOM7uhf0u4Nl8RMRZWgthSM8PnJFwrD8PDNSqA00EFBfupKbTKyTH5jFL1f NdQWN40urb8d/YIppxh46KqnhxsFskn70QlCzvIB6icrrnXJzPBjAGrYvoiroz7KPG0A Foo1BBuVhhRauGT8WLvMLk9C/IJhldg/5IN1XnFJBD/dLFV4A+2eyDdxB5ZPmqMVGvnh mDiZ+9FF3loR5opoZIIBRTywu47z9QdeoQg8drJUspfoHHsmVQeR3D+ZODsAzLz+gyEG 7a5Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=gVpFfMwz; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id t66-v6si11868193pgc.6.2018.06.18.06.23.12; Mon, 18 Jun 2018 06:23:12 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=gVpFfMwz; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934260AbeFRNXK (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:10 -0400 Received: from fllnx209.ext.ti.com ([198.47.19.16]:26390 "EHLO fllnx209.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934243AbeFRNXI (ORCPT ); Mon, 18 Jun 2018 09:23:08 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by fllnx209.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDN5sJ018690; Mon, 18 Jun 2018 08:23:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328185; bh=1gaRG+HfTlNbWcH4JQYawrHSMrmHvltzCv2oLFmhFPI=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=gVpFfMwzyZ1YfgIFCUabDbxnBhMiU4CJGC6FqZlKxy+5CAqzc/oNjz0N0C8nSpb5f LVdh9tKJcb6aShTI4KdMPmh3Y0dyXDCtvgkGslnmV0pELTVy4TzqvKUfATPVhyBPIO rCosQb6/lKhEVOWkUr4kbIRWSR7i+OFr+tmvIN3Q= Received: from DLEE108.ent.ti.com (dlee108.ent.ti.com [157.170.170.38]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDN5AM003770; Mon, 18 Jun 2018 08:23:05 -0500 Received: from DLEE103.ent.ti.com (157.170.170.33) by DLEE108.ent.ti.com (157.170.170.38) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:04 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DLEE103.ent.ti.com (157.170.170.33) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:04 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowt002369; Mon, 18 Jun 2018 08:23:03 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen Subject: [RFC PATCHv2 6/9] MAINTAINERS: add entry for tidss Date: Mon, 18 Jun 2018 16:22:39 +0300 Message-ID: <20180618132242.8673-7-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Add entry for tidss DRM driver. Signed-off-by: Tomi Valkeinen --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/MAINTAINERS b/MAINTAINERS index 9c125f705f78..87671b77398e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4780,6 +4780,15 @@ S: Maintained F: drivers/gpu/drm/omapdrm/ F: Documentation/devicetree/bindings/display/ti/ +DRM DRIVERS FOR TI KEYSTONE +M: Tomi Valkeinen +R: Jyri Sarha +L: dri-devel@lists.freedesktop.org +S: Maintained +F: drivers/gpu/drm/tidss/ +F: Documentation/devicetree/bindings/display/ti/ti,k2g-dss.txt +F: Documentation/devicetree/bindings/display/ti/ti,am6-dss.txt + DRM DRIVERS FOR VC4 M: Eric Anholt T: git git://github.com/anholt/linux From patchwork Mon Jun 18 13:22:40 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138946 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949816lji; Mon, 18 Jun 2018 06:23:30 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJltJ32biv2TNfkaB8YyyijtU+haupuzUOyGzdKpVVhwbnxqd5g0ugqdTIMG9ZSfKfv3F8l X-Received: by 2002:a65:504a:: with SMTP id k10-v6mr11040421pgo.151.1529328210540; Mon, 18 Jun 2018 06:23:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328210; cv=none; d=google.com; s=arc-20160816; b=xHBfyUpG1uI4x/tQJ4qqgfAPuaErBN++izrYkjMn8zTaYsT6yXIkrcLqX7Fo7+1AwI bZA31Xz6Hgtic8Wm0aKpQ5rRp4tAbauJInizGkSL0/JdfOMngNVXZWtRR1RCPLMHgs7i z7Q6veRWEHl/RoA1ZmPdIRxc87HnTJcKIlyl6R4mhfY6gKa8e7j/KIA5GL4vNYsR4mzC rM61udrOFsXzAYOUM/bZ9plGK/43qERIH0WSyvvEfgsF8mY/T/+5pDi/3xh6BLvttr+C UMR//SqJNm8xQ3XPhufnd7DBFJEjlulRy0DFVFS/W4Nw2U2R60ddexrmGkDNyz02hsLx mCYQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=8X+GcvcSDw9ZP4Pp1Qibhwg9TO4PML8R7ve2F7u/hJM=; b=ZfBF5E4TKZx8fEmtoccTs0hInzTJuIfiMRdAD40wrWVu/L878xg1WSlRx1bViKrwW5 7fQnripkHCZ/aJxETB6JnoRb+8XvUW0NodC9FJ/7FIES+WnxPjpXU13BE3ilMiyYvb1n jqD+8t7mpGVSCVI490AHhaw8pL+WL2ogS+7rqUyAPHbOzq/Pu/jdKrhpOXZKXHQ8DRr1 0i+mtlES9DB2uqCYMXJwzd/B+helrhtkYiOVQcChR5tptQlBZ0znfVpf3dMzMG/LEx4+ DlyteqCgQaZS2teUAeQrgNoeCsd0VD8GiB7WFfx4l5omWgL0kJmvc0vybqt96Qp0LNwY atvA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=LHxs1qoh; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s1-v6si12371160pga.296.2018.06.18.06.23.30; Mon, 18 Jun 2018 06:23:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=LHxs1qoh; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934264AbeFRNXM (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:12 -0400 Received: from lelnx193.ext.ti.com ([198.47.27.77]:60755 "EHLO lelnx193.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933524AbeFRNXK (ORCPT ); Mon, 18 Jun 2018 09:23:10 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by lelnx193.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDN7gv022250; Mon, 18 Jun 2018 08:23:07 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328187; bh=pYQWOa8PgLWUtWWiEuvqcZDeoMGi5jB2vu06N9RsHGE=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=LHxs1qohquCwW0ovCGr5+IUkriOKZ4z75zt9p+vY0RaqlxSb1u1i3o7nln7I/jzAn pApk4i8ImAuX0Pnbw0Ci5Bj1dlz8JC2VQdiqfSKn2QNPPtxgrd8A4A3szsK3f2dIXK uIZ/OXuL10UFRFOqSkUbBMhm0O7yScSEPvKzBhLI= Received: from DLEE109.ent.ti.com (dlee109.ent.ti.com [157.170.170.41]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDN7Pc018063; Mon, 18 Jun 2018 08:23:07 -0500 Received: from DLEE100.ent.ti.com (157.170.170.30) by DLEE109.ent.ti.com (157.170.170.41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:06 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DLEE100.ent.ti.com (157.170.170.30) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:06 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowu002369; Mon, 18 Jun 2018 08:23:05 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen , Thierry Reding Subject: [RFC PATCHv2 7/9] drm/panel: simple: add newhaven, nhd-4.3-480272ef-atxl LCD Date: Mon, 18 Jun 2018 16:22:40 +0300 Message-ID: <20180618132242.8673-8-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Add support for newhaven,nhd-4.3-480272ef-atxl to panel-simple. Signed-off-by: Tomi Valkeinen Cc: Thierry Reding --- .../panel/newhaven,nhd-4.3-480272ef-atxl.txt | 7 +++++ drivers/gpu/drm/panel/panel-simple.c | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt b/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt new file mode 100644 index 000000000000..e78292b1a131 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt @@ -0,0 +1,7 @@ +Newhaven Display International 480 x 272 TFT LCD panel + +Required properties: +- compatible: should be "newhaven,nhd-4.3-480272ef-atxl" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index cbf1ab404ee7..5d00c7d403b3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2069,6 +2069,32 @@ static const struct panel_desc winstar_wf35ltiacd = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct drm_display_mode newhaven_nhd_43_480272ef_atxl_mode = { + .clock = 9000, + .hdisplay = 480, + .hsync_start = 480 + 2, + .hsync_end = 480 + 2 + 41, + .htotal = 480 + 2 + 41 + 2, + .vdisplay = 272, + .vsync_start = 272 + 2, + .vsync_end = 272 + 2 + 10, + .vtotal = 272 + 2 + 10 + 2, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc newhaven_nhd_43_480272ef_atxl = { + .modes = &newhaven_nhd_43_480272ef_atxl_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 95, + .height = 54, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | DRM_BUS_FLAG_SYNC_POSEDGE, +}; + static const struct of_device_id platform_of_match[] = { { .compatible = "ampire,am-480272h3tmqw-t01h", @@ -2286,6 +2312,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "winstar,wf35ltiacd", .data = &winstar_wf35ltiacd, + }, { + .compatible = "newhaven,nhd-4.3-480272ef-atxl", + .data = &newhaven_nhd_43_480272ef_atxl, }, { /* sentinel */ } From patchwork Mon Jun 18 13:22:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138945 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949747lji; Mon, 18 Jun 2018 06:23:25 -0700 (PDT) X-Google-Smtp-Source: ADUXVKKp5U3W3yCSeIeI+/Cmlt/DHrhsUgi4n8GdcOLW9JOcvaY6DjldtLdzqsE5qJ08kQUCxpXe X-Received: by 2002:a17:902:b590:: with SMTP id a16-v6mr14466401pls.225.1529328205849; Mon, 18 Jun 2018 06:23:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328205; cv=none; d=google.com; s=arc-20160816; b=VvKXWhXF1+7WgGiXFFflbMNFfic/YQdAvwqW95PMXfuFrWdl7Il5pxjWQn2ug/ezeL sreflcMXg2uYPA1MaDmhbeiZF7fmDn9wH7jTGZ9bkMJs3mqKAtrzEefNct6HmMEjTWvA giJc6WJoWOOnAFnwxrUCGwcSiHeObQtY8XghdEuGuxBdn7tQcn3EweB3fq1Apvi2QSTu gk3waqK97HdSxfbD5Y56Wmswz+zD12erZESJyOKA17C2gZlb5XeZcGYwx1LkJ3YEN/l2 tnufSD9dcmgd2nZgV6DkvhwbjBQer+9lOHdDEloyZytvPrdYfaMoOD50K5kkfjbgA6tr HAuA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=NSyGdZMceF6+POGWoKWdhlswwMr9HZHjnbS0kqD37hY=; b=F5U/Df1GMrWfADJ+7p/R1mfe88iWHUuVrunPeQ73vKqJFAifR4H+O6fcYL2aFwtXGC 7YD2AYN7hq8rSZJvqJv064H2s6AX5OIiUARJVnlZfxJL8itPyjBw5CeK8VMtGx78cvH0 R4/JhEvubfQB0UzLXZoGYMSGREnJBmOhB8eVhkuF/s9J0ev6KGKa2mkKtm2JOxTe1Som PaGfZwTbftr0x/hijtUK2Fyue9fpjuC7/VEIx25KdFtmPyjftKXe/ur0yM17iRv2Nz4Z o7PY+TChN32KWGXx1VM+UkEMTT+Ond78lDv04DVys+Wjt9Z3Y21JD7Z5/K/ZaS02cEfI uK2g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=vGR9tvQ6; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s1-v6si12371160pga.296.2018.06.18.06.23.25; Mon, 18 Jun 2018 06:23:25 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=vGR9tvQ6; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934348AbeFRNXR (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:17 -0400 Received: from fllnx210.ext.ti.com ([198.47.19.17]:13079 "EHLO fllnx210.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934323AbeFRNXO (ORCPT ); Mon, 18 Jun 2018 09:23:14 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by fllnx210.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDN92X021053; Mon, 18 Jun 2018 08:23:09 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328189; bh=pVrpZWbaTovkwsiJRntKs1wEm4dZleiivaEnYwkjRtA=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=vGR9tvQ6u2Wz5ues6IRP2ftx8HYdH3nrq7ATt4L8BAcXxyrkIS2XT6rGN0/YBmUcu ZC7xPDu3/NaxfpfsobQRxalEaEqCwVMCVHaaFCakHedb7CIboAYUh074IZFH4f3nbY HMKcaZszY/ohDbkR61crWJwF4YilxO/sB7ZjZEMA= Received: from DFLE100.ent.ti.com (dfle100.ent.ti.com [10.64.6.21]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDN91H003892; Mon, 18 Jun 2018 08:23:09 -0500 Received: from DFLE111.ent.ti.com (10.64.6.32) by DFLE100.ent.ti.com (10.64.6.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:09 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE111.ent.ti.com (10.64.6.32) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:09 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMowv002369; Mon, 18 Jun 2018 08:23:07 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen , Subject: [RFC PATCHv2 8/9] ARM: dts: keystone-k2g: add DSS node Date: Mon, 18 Jun 2018 16:22:41 +0300 Message-ID: <20180618132242.8673-9-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Add DSS node to k2g.dtsi. Signed-off-by: Tomi Valkeinen Cc: devicetree@vger.kernel.org --- arch/arm/boot/dts/keystone-k2g.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/arm/boot/dts/keystone-k2g.dtsi b/arch/arm/boot/dts/keystone-k2g.dtsi index da78c0034427..d761fe7d46e7 100644 --- a/arch/arm/boot/dts/keystone-k2g.dtsi +++ b/arch/arm/boot/dts/keystone-k2g.dtsi @@ -609,5 +609,26 @@ reg = <0x21010000 0x200>; interrupts = ; }; + + dss: dss@02540000 { + compatible = "ti,k2g-dss"; + + reg = <0x02540000 0x400>, + <0x02550000 0x1000>, + <0x02557000 0x1000>, + <0x0255a800 0x100>, + <0x0255ac00 0x100>; + + reg-names = "cfg", "common", "vid1", "ovr1", "vp1"; + + clocks = <&k2g_clks 0x2 0>, <&k2g_clks 0x2 1>; + clock-names = "fck", "vp1"; + + power-domains = <&k2g_pds 0x2>; + + interrupts = ; + + status = "disabled"; + }; }; }; From patchwork Mon Jun 18 13:22:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 138944 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp3949670lji; Mon, 18 Jun 2018 06:23:21 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLduD+MrmGWZ/37Qt6QXwGVAhWWTQt0oFb50UDy0GyOI+oLt1UHuCCzo68i9j02dA+z6B8i X-Received: by 2002:a63:715d:: with SMTP id b29-v6mr11217985pgn.325.1529328201820; Mon, 18 Jun 2018 06:23:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529328201; cv=none; d=google.com; s=arc-20160816; b=fTUsW1GnLpsB/QSE3Vc5yRGkakfCPjDsAGpFw+jRyOur1EHtRbMaBAOXSgb+Kd1bwr M0c+gcq8vPZ5Ruz4nEC/tplWSyZUqLPJRX0YCWZh3OPYGHCjE1g94F8CIy+PTlZlIu01 xPyix9YJvjGRtjbI1ukmv8br0v/lUOCpwR+AiKcmb7vlvfi2M22APKxEG8yq4ZUxbWgl UOHEJ5+6NOrvFQMQeg2DUSmxB627oehtXWbe03xu6bnPDdEdN7+On7of4c8LLlPxdOpX +NtOrtJPmdw/55rG0w5NoQRi72kiaU6WgVomUzxRcjusCjbxvkYp52Efc8B6MIpNkaUA UxzQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=bhC2sns5Rq989PFI5KTlJFYu7vMXPh9YzugK8dnmIxE=; b=LpMJ9CDnT2uW3wjv1mxzDhjy8/AMNao0gSbmE9XjMj7z9p/qlO5G8ZJ00cuvlR9O6+ YbZGENiaGM9dLvmziiVKZLNRPaYl9aF+ZnSHW+Dobg4luJcWMLg+XZpSEJVhjn5mANY1 agrbKKtPH1HQgLAW3lvJZd33BuSrwxbPqqWN6bsg3BLuLnZnPGOpHtUoRGZ38cxsASbG iH9JQ6k2neBJMt1C8ChCNpUwhHCvXMeR/bwqa3cdZt5vyOJuIr1UExIBejnucM2cc+xf jU9Y2L3NnWT5gjkBri+srOFLQWkLnbmAYp28a3V4LIj/6WiRfEdaFadU1A58wBmODRaF wIHQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=TirJILFU; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id ay6-v6si14871742plb.210.2018.06.18.06.23.21; Mon, 18 Jun 2018 06:23:21 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@ti.com header.s=ti-com-17Q1 header.b=TirJILFU; spf=pass (google.com: best guess record for domain of linux-omap-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-omap-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933517AbeFRNXS (ORCPT + 5 others); Mon, 18 Jun 2018 09:23:18 -0400 Received: from lelnx194.ext.ti.com ([198.47.27.80]:30660 "EHLO lelnx194.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934324AbeFRNXO (ORCPT ); Mon, 18 Jun 2018 09:23:14 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by lelnx194.ext.ti.com (8.15.1/8.15.1) with ESMTP id w5IDNBH9007164; Mon, 18 Jun 2018 08:23:11 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1529328191; bh=EcmSsxFFUD8Fv6VjBHRKTBtQf4zH7J7nQKOYOGRGbMM=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=TirJILFUzjWfrFavCLhqP9FliCRIXaVyJey/coHHcFeK6a8+iXSCuo7Ii5aTjHl6e DE+ZWPcuPUmJw2rNUlWVZjTqUuOe/fDSMy/YfSSqnDIG5v9csNfPrZd6bc8cwf+/vV vorEafS6eJWae2s62XHZ1yIDfjk7rF/ezSAIKAZw= Received: from DFLE112.ent.ti.com (dfle112.ent.ti.com [10.64.6.33]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDNBPT018126; Mon, 18 Jun 2018 08:23:11 -0500 Received: from DFLE104.ent.ti.com (10.64.6.25) by DFLE112.ent.ti.com (10.64.6.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1466.3; Mon, 18 Jun 2018 08:23:11 -0500 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE104.ent.ti.com (10.64.6.25) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.1466.3 via Frontend Transport; Mon, 18 Jun 2018 08:23:11 -0500 Received: from deskari.lan (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id w5IDMoww002369; Mon, 18 Jun 2018 08:23:09 -0500 From: Tomi Valkeinen To: , Laurent Pinchart , CC: Jyri Sarha , Peter Ujfalusi , Benoit Parrot , Tomi Valkeinen , Subject: [RFC PATCHv2 9/9] ARM: dts: keystone-k2g-evm: add LCD and HDMI displays Date: Mon, 18 Jun 2018 16:22:42 +0300 Message-ID: <20180618132242.8673-10-tomi.valkeinen@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618132242.8673-1-tomi.valkeinen@ti.com> References: <20180618132242.8673-1-tomi.valkeinen@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org K2G EVM has an SiI902x HDMI encoder on the board, and a separately purchasable LCD which can be attached to the board. Only one of these displays can be used at a time, and two dip-switches need to be changed when switching to another display. The HDMI support is added to the base k2g-evm.dts file, and the LCD support is added as a separate k2g-evm-lcd.dts file. The user must choose one of the dtbs, depending on which display he wants to use. Signed-off-by: Tomi Valkeinen Cc: devicetree@vger.kernel.org --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/keystone-k2g-evm-lcd.dts | 80 ++++++++++++++++++++ arch/arm/boot/dts/keystone-k2g-evm.dts | 87 ++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 arch/arm/boot/dts/keystone-k2g-evm-lcd.dts -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 7e2424957809..11bb3ba22bdf 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -222,6 +222,7 @@ dtb-$(CONFIG_ARCH_KEYSTONE) += \ keystone-k2l-evm.dtb \ keystone-k2e-evm.dtb \ keystone-k2g-evm.dtb \ + keystone-k2g-evm-lcd.dtb \ keystone-k2g-ice.dtb dtb-$(CONFIG_MACH_KIRKWOOD) += \ kirkwood-b3.dtb \ diff --git a/arch/arm/boot/dts/keystone-k2g-evm-lcd.dts b/arch/arm/boot/dts/keystone-k2g-evm-lcd.dts new file mode 100644 index 000000000000..be0498010e71 --- /dev/null +++ b/arch/arm/boot/dts/keystone-k2g-evm-lcd.dts @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Source for K2G EVM with LCD Display + * + * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include "keystone-k2g-evm.dts" +#include + +/ { + lcd0: display { + compatible = "newhaven,nhd-4.3-480272ef-atxl", "panel-dpi"; + label = "lcd"; + + backlight = <&lcd_bl>; + + panel-timing { + clock-frequency = <9000000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <2>; + hback-porch = <2>; + hsync-len = <41>; + vfront-porch = <2>; + vback-porch = <2>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <1>; + }; + + port { + lcd_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + + lcd_bl: backlight { + compatible = "pwm-backlight"; + pwms = <&ecap0 0 50000 PWM_POLARITY_INVERTED>; + brightness-levels = <0 32 64 96 128 160 192 224 255>; + default-brightness-level = <8>; + }; +}; + +&i2c1 { + edt-ft5306@38 { + status = "okay"; + compatible = "edt,edt-ft5306", "edt,edt-ft5x06"; + reg = <0x38>; + + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5306_ts_pins>; + + interrupt-parent = <&gpio1>; + interrupts = <42 IRQ_TYPE_EDGE_FALLING>; + + touchscreen-size-x = <480>; + touchscreen-size-y = <272>; + }; +}; + +&k2g_pinctrl { + edt_ft5306_ts_pins: edt_ft5306_ts_pins { + pinctrl-single,pins = < + K2G_CORE_IOPAD(0x1364) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE3) /* pr1_pru1_gpo16.gpio1_42 */ + >; + }; +}; + +&dpi_out { + remote-endpoint = <&lcd_in>; +}; + +&sii9022 { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/keystone-k2g-evm.dts b/arch/arm/boot/dts/keystone-k2g-evm.dts index 6a4657799b99..b232e3b30d76 100644 --- a/arch/arm/boot/dts/keystone-k2g-evm.dts +++ b/arch/arm/boot/dts/keystone-k2g-evm.dts @@ -81,6 +81,13 @@ >; }; + i2c1_pins: pinmux_i2c1_pins { + pinctrl-single,pins = < + K2G_CORE_IOPAD(0x1384) (BUFFER_CLASS_B | PIN_PULLUP | MUX_MODE0) /* i2c1_scl.i2c1_scl */ + K2G_CORE_IOPAD(0x1388) (BUFFER_CLASS_B | PIN_PULLUP | MUX_MODE0) /* i2c1_sda.i2c1_sda */ + >; + }; + ecap0_pins: ecap0_pins { pinctrl-single,pins = < K2G_CORE_IOPAD(0x1374) (BUFFER_CLASS_B | MUX_MODE4) /* pr1_mdio_data.ecap0_in_apwm0_out */ @@ -114,6 +121,46 @@ K2G_CORE_IOPAD(0x11f0) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE0) /* uart2_txd.uart2_txd */ >; }; + + vout_pins: pinmux_vout_pins { + pinctrl-single,pins = < + K2G_CORE_IOPAD(0x1078) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata23.dssdata23 */ + K2G_CORE_IOPAD(0x107c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata22.dssdata22 */ + K2G_CORE_IOPAD(0x1080) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata21.dssdata21 */ + K2G_CORE_IOPAD(0x1084) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata20.dssdata20 */ + K2G_CORE_IOPAD(0x1088) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata19.dssdata19 */ + K2G_CORE_IOPAD(0x108c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata18.dssdata18 */ + K2G_CORE_IOPAD(0x1090) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata17.dssdata17 */ + K2G_CORE_IOPAD(0x1094) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata16.dssdata16 */ + K2G_CORE_IOPAD(0x1098) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata15.dssdata15 */ + K2G_CORE_IOPAD(0x109c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata14.dssdata14 */ + K2G_CORE_IOPAD(0x10a0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata13.dssdata13 */ + K2G_CORE_IOPAD(0x10a4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata12.dssdata12 */ + K2G_CORE_IOPAD(0x10a8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata11.dssdata11 */ + K2G_CORE_IOPAD(0x10ac) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata10.dssdata10 */ + K2G_CORE_IOPAD(0x10b0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata9.dssdata9 */ + K2G_CORE_IOPAD(0x10b4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata8.dssdata8 */ + K2G_CORE_IOPAD(0x10b8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata7.dssdata7 */ + K2G_CORE_IOPAD(0x10bc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata6.dssdata6 */ + K2G_CORE_IOPAD(0x10c0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata5.dssdata5 */ + K2G_CORE_IOPAD(0x10c4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata4.dssdata4 */ + K2G_CORE_IOPAD(0x10c8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata3.dssdata3 */ + K2G_CORE_IOPAD(0x10cc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata2.dssdata2 */ + K2G_CORE_IOPAD(0x10d0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata1.dssdata1 */ + K2G_CORE_IOPAD(0x10d4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata0.dssdata0 */ + K2G_CORE_IOPAD(0x10d8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssvsync.dssvsync */ + K2G_CORE_IOPAD(0x10dc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dsshsync.dsshsync */ + K2G_CORE_IOPAD(0x10e0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dsspclk.dsspclk */ + K2G_CORE_IOPAD(0x10e4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssde.dssde */ + K2G_CORE_IOPAD(0x10e8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssfid.dssfid */ + >; + }; + + sii9022_pins: sii9022_pins { + pinctrl-single,pins = < + K2G_CORE_IOPAD(0x1338) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE3) /* pr1_pru1_gpo5.gpio1_31 */ + >; + }; }; &uart0 { @@ -268,3 +315,43 @@ pinctrl-0 = <&uart2_pins>; status = "okay"; }; + +&dss { + pinctrl-names = "default"; + pinctrl-0 = <&vout_pins>; + status = "ok"; + + port { + dpi_out: endpoint { + remote-endpoint = <&sii9022_in>; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + clock-frequency = <400000>; + + sii9022: sii9022@3b { + compatible = "sil,sii9022"; + reg = <0x3b>; + + pinctrl-names = "default"; + pinctrl-0 = <&sii9022_pins>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sii9022_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + }; +};