From nobody Fri Oct 3 00:00:52 2025 Received: from lelvem-ot02.ext.ti.com (lelvem-ot02.ext.ti.com [198.47.23.235]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADC0E2459D7; Tue, 9 Sep 2025 08:08:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.235 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405300; cv=none; b=f4lOTccEYGm7YrZ+VoIJaFQw8BeoTu0ehBQ/gt4YlVpqq6wNilqheWNfF9sURmuWRteU9zmujtV1BVxuCe7S4YoRjBeuCeGrcgBL/t7XTu8TJvASs23Jf40a0xezmlxF7Dh8z2bGkioZfcLux/ey4UtU+ziJ2rRpuGstAoDonYY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405300; c=relaxed/simple; bh=rQQ/NYH0XT2y0bcBu9GEl4snCB+RMlEz2waML4P5UOk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LCYYFfkbcjaD+J+zR7IyjjYX1LkY/+x4YN9t0ugv7RTOw4JRhZTF/ARgEjSZG65NRtAopl5Cr3QXjKzQ7t1qJDTxUdkgVbh6wRE7gigrH+GUWPl8JSboday9Yvn8P2MWlBqHRN9oKMz6DsVMJ7Ox2eYDvpn1h5OcqdPLTkaGN7g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=X/FOFqdi; arc=none smtp.client-ip=198.47.23.235 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="X/FOFqdi" Received: from fllvem-sh03.itg.ti.com ([10.64.41.86]) by lelvem-ot02.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58988ASD293692; Tue, 9 Sep 2025 03:08:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757405290; bh=uiTBbXv7BZK5wK0Y/uh/hpSNWpRN8sahZSuucIpfXwY=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=X/FOFqdi3ngQkU8I8WoMO+2TTpvG5z5FKcdRhNrACdw/mYzTcMt1gZtpX7QcI9tWi N25jlQS34JK+c6asgQP39m9nqJ0BgqYrWZjRrWZj0xCsS92qgh0HRDNGy1uEgH2ob1 zH+o+3RFGLKfxXpAYgf1+mIZ6AQ5k1BjdSL0HN9k= Received: from DFLE114.ent.ti.com (dfle114.ent.ti.com [10.64.6.35]) by fllvem-sh03.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58988Ao93630155 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 9 Sep 2025 03:08:10 -0500 Received: from DFLE107.ent.ti.com (10.64.6.28) by DFLE114.ent.ti.com (10.64.6.35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Tue, 9 Sep 2025 03:08:09 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) 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.2507.55 via Frontend Transport; Tue, 9 Sep 2025 03:08:09 -0500 Received: from abhilash-HP.dhcp.ti.com (abhilash-hp.dhcp.ti.com [10.24.68.198]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58987soE2319864; Tue, 9 Sep 2025 03:08:05 -0500 From: Yemike Abhilash Chandra To: , , , , CC: , , , , , , , , Subject: [PATCH V3 1/5] Revert "media: platform: ti: Remove unused vpdma_update_dma_addr" Date: Tue, 9 Sep 2025 13:37:14 +0530 Message-ID: <20250909080718.1381758-2-y-abhilashchandra@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250909080718.1381758-1-y-abhilashchandra@ti.com> References: <20250909080718.1381758-1-y-abhilashchandra@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" This reverts commit 9314891df119442a6ec1518b3d872c330e2bf1a1. We're adding support for TI VIP driver, so this is no longer unused. Signed-off-by: Yemike Abhilash Chandra --- drivers/media/platform/ti/vpe/vpdma.c | 32 +++++++++++++++++++++++++++ drivers/media/platform/ti/vpe/vpdma.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform= /ti/vpe/vpdma.c index bb8a8bd7980c..da90d7f03f82 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -552,6 +552,38 @@ EXPORT_SYMBOL(vpdma_submit_descs); =20 static void dump_dtd(struct vpdma_dtd *dtd); =20 +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx) +{ + struct vpdma_dtd *dtd =3D list->buf.addr; + dma_addr_t write_desc_addr; + int offset; + + dtd +=3D idx; + vpdma_unmap_desc_buf(vpdma, &list->buf); + + dtd->start_addr =3D dma_addr; + + /* Calculate write address from the offset of write_dtd from start + * of the list->buf + */ + offset =3D (void *)write_dtd - list->buf.addr; + write_desc_addr =3D list->buf.dma_addr + offset; + + if (drop) + dtd->desc_write_addr =3D dtd_desc_write_addr(write_desc_addr, + 1, 1, 0); + else + dtd->desc_write_addr =3D dtd_desc_write_addr(write_desc_addr, + 1, 0, 0); + + vpdma_map_desc_buf(vpdma, &list->buf); + + dump_dtd(dtd); +} +EXPORT_SYMBOL(vpdma_update_dma_addr); + void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, u32 width, u32 height) { diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform= /ti/vpe/vpdma.h index e4d7941c6207..393fcbb3cb40 100644 --- a/drivers/media/platform/ti/vpe/vpdma.h +++ b/drivers/media/platform/ti/vpe/vpdma.h @@ -222,6 +222,9 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list); int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *l= ist, int list_num); bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx); =20 /* VPDMA hardware list funcs */ int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); --=20 2.34.1 From nobody Fri Oct 3 00:00:52 2025 Received: from lelvem-ot02.ext.ti.com (lelvem-ot02.ext.ti.com [198.47.23.235]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A99F624BC0A; Tue, 9 Sep 2025 08:08:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.235 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405304; cv=none; b=PklFVBokZLKqpnvREoPqB4W66u0rVZpgIKLkLMMElduTqMQJta53GH51xZ4KfbnTGnG7kdzEiOQlJi6EFIXgECK5FiTNMyE9Bc9A419bcg6ltXsQvccrtBjtuBeSGRx5JRD+rK7aIESPHBeyioClQN8ExfRh4zDq2RCzRIIO9o8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405304; c=relaxed/simple; bh=Ne4C+BMqQcVIJtkfC1mDxaOD4y5ksTNyWClXK+XeqMY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gpzEGErIZJgnnScz3ik5Zzahwdpb5d1v63WOluuDVrNCzuB3rF3McIwXdpdJgHwrP2YUd+F9OL2aTuYnZPZOryNPqyMudpmAn4okQ7imSUVmQOe1sxX5vEuixTACCsNovW9RfC5qZail6ogp64SVvFqVM5UKXhk17V6yVtsMkRw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=RGIf/KBE; arc=none smtp.client-ip=198.47.23.235 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="RGIf/KBE" Received: from lelvem-sh01.itg.ti.com ([10.180.77.71]) by lelvem-ot02.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58988GXl293703; Tue, 9 Sep 2025 03:08:16 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757405296; bh=0IQVsxUtSmCznRSWhfq5hVJWByYGmYfsvUXUUhaD7Ho=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=RGIf/KBEMP6Zf17bpv6zouLO0BTz/fnhc5fKuBjQ0c95ObQlN6KmM95kbXmsc446F gMzqz7SuUjxx22h5SMs0GpuuMBUutzN5VojLsVmm045LIzJczRvZtyzFjtQDMlObog FQkcX2+27/KmlourK1eW2T5/9BnfGHC/mmanV8HU= Received: from DLEE115.ent.ti.com (dlee115.ent.ti.com [157.170.170.26]) by lelvem-sh01.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58988FXL3008637 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 9 Sep 2025 03:08:15 -0500 Received: from DLEE105.ent.ti.com (157.170.170.35) 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.2507.55; Tue, 9 Sep 2025 03:08:15 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DLEE105.ent.ti.com (157.170.170.35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Tue, 9 Sep 2025 03:08:15 -0500 Received: from abhilash-HP.dhcp.ti.com (abhilash-hp.dhcp.ti.com [10.24.68.198]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58987soF2319864; Tue, 9 Sep 2025 03:08:11 -0500 From: Yemike Abhilash Chandra To: , , , , CC: , , , , , , , , Subject: [PATCH V3 2/5] media: platform: ti: Add kerneldoc for vpdma_update_dma_addr() Date: Tue, 9 Sep 2025 13:37:15 +0530 Message-ID: <20250909080718.1381758-3-y-abhilashchandra@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250909080718.1381758-1-y-abhilashchandra@ti.com> References: <20250909080718.1381758-1-y-abhilashchandra@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" Add kerneldoc for vpdma_update_dma_addr() function. Signed-off-by: Yemike Abhilash Chandra --- drivers/media/platform/ti/vpe/vpdma.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform= /ti/vpe/vpdma.c index da90d7f03f82..b79b7e8479f9 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -552,6 +552,21 @@ EXPORT_SYMBOL(vpdma_submit_descs); =20 static void dump_dtd(struct vpdma_dtd *dtd); =20 +/** + * vpdma_update_dma_addr() - update DMA address in a descriptor + * @vpdma: VPDMA device context + * @list: vpdma desc list to which we add this descriptor + * @dma_addr: new DMA address to program into the descriptor + * @write_dtd: descriptor pointer used to compute write-back address + * @drop: if true, set the drop bit in the write descriptor + * @idx: index of the descriptor in the list to update + * + * Updates dma addresses of the descriptor at @idx in @list. + * This allows reusing an existing descriptor list with a new buffer + * address, instead of rebuilding the list, which is needed when + * multiple clients share the same VPDMA engine. The list buffer is + * unmapped before the update and remapped after. + */ void vpdma_update_dma_addr(struct vpdma_data *vpdma, struct vpdma_desc_list *list, dma_addr_t dma_addr, void *write_dtd, int drop, int idx) --=20 2.34.1 From nobody Fri Oct 3 00:00:52 2025 Received: from lelvem-ot02.ext.ti.com (lelvem-ot02.ext.ti.com [198.47.23.235]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D4418246BB0; Tue, 9 Sep 2025 08:08:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.235 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405310; cv=none; b=W3snIUiMRvE0ApUsRwfBRkes+AiqxpCR2ItbeDXgXCj4bGT+/Hir6M4onA6eEcfPqzPaGbIOeLZl5xCMuimNKu6i12fKHIx6uMQxHPT8ojiBozSSiQ7m/U8OIUhWnYSvhl9dlWneVSNYK5vbiEsMy6X2YMpKToABTmV4AfGCeas= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405310; c=relaxed/simple; bh=89w6MEXCIhsoRMtf3xdonsIzzsSUSv4uhS13qrhfDQo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gzfZApM1zkNKXXSf72fud2x7/+dmubd3GRbnbybxRYn/3CZdpUHmsrSuyZWANq1cbiwyEA2p/Qn9v8G3pz8OoTpV2xiKTilhoL0ttJ13xMxZLPyFN6+m+E/Rw912wicgOpg9ID4KPzVyKQ76KiLo7CsSDdwP0jR9kPnBu+qjc0g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=WPLxeCFB; arc=none smtp.client-ip=198.47.23.235 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="WPLxeCFB" Received: from fllvem-sh04.itg.ti.com ([10.64.41.54]) by lelvem-ot02.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58988M11293721; Tue, 9 Sep 2025 03:08:22 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757405302; bh=zAoKSSA3eOpGoSDC2uDEsYycNqZ4whp7kQEppdqyx30=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=WPLxeCFBFFec+Q523cP7gR5ZZwRfVB8742Ga0F+Q2g8qe4kb4FIMc0RwqKuC4InMz jy1Lu1lmoPzSxC0/jufr67a0FS8MA7HjC1Hkot/XXUbVYU5ng2R0MigJnQ0kJE+Fl0 7yWL7VOI0VvcLZLS0mBxtdPFMc57wIIEG+4PAjaA= Received: from DLEE106.ent.ti.com (dlee106.ent.ti.com [157.170.170.36]) by fllvem-sh04.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58988MxD205175 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 9 Sep 2025 03:08:22 -0500 Received: from DLEE108.ent.ti.com (157.170.170.38) by DLEE106.ent.ti.com (157.170.170.36) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Tue, 9 Sep 2025 03:08:21 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) 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.2507.55 via Frontend Transport; Tue, 9 Sep 2025 03:08:21 -0500 Received: from abhilash-HP.dhcp.ti.com (abhilash-hp.dhcp.ti.com [10.24.68.198]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58987soG2319864; Tue, 9 Sep 2025 03:08:17 -0500 From: Yemike Abhilash Chandra To: , , , , CC: , , , , , , , , Subject: [PATCH V3 3/5] media: platform: ti: Switch to EXPORT_SYMBOL_GPL() Date: Tue, 9 Sep 2025 13:37:16 +0530 Message-ID: <20250909080718.1381758-4-y-abhilashchandra@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250909080718.1381758-1-y-abhilashchandra@ti.com> References: <20250909080718.1381758-1-y-abhilashchandra@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" Switch to GPL version of EXPORT_SYMBOL for vpdma_update_dma_addr() and Fix a small alignment issue while at it. Signed-off-by: Yemike Abhilash Chandra --- drivers/media/platform/ti/vpe/vpdma.c | 7 ++++--- drivers/media/platform/ti/vpe/vpdma.h | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform= /ti/vpe/vpdma.c index b79b7e8479f9..29ee1918bc1c 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -568,8 +568,9 @@ static void dump_dtd(struct vpdma_dtd *dtd); * unmapped before the update and remapped after. */ void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx) + struct vpdma_desc_list *list, + dma_addr_t dma_addr, + void *write_dtd, int drop, int idx) { struct vpdma_dtd *dtd =3D list->buf.addr; dma_addr_t write_desc_addr; @@ -597,7 +598,7 @@ void vpdma_update_dma_addr(struct vpdma_data *vpdma, =20 dump_dtd(dtd); } -EXPORT_SYMBOL(vpdma_update_dma_addr); +EXPORT_SYMBOL_GPL(vpdma_update_dma_addr); =20 void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, u32 width, u32 height) diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform= /ti/vpe/vpdma.h index 393fcbb3cb40..5b3a0cd49a3c 100644 --- a/drivers/media/platform/ti/vpe/vpdma.h +++ b/drivers/media/platform/ti/vpe/vpdma.h @@ -223,8 +223,8 @@ int vpdma_submit_descs(struct vpdma_data *vpdma, struct= vpdma_desc_list *list, int list_num); bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx); + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx); =20 /* VPDMA hardware list funcs */ int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); --=20 2.34.1 From nobody Fri Oct 3 00:00:52 2025 Received: from fllvem-ot04.ext.ti.com (fllvem-ot04.ext.ti.com [198.47.19.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 347AE25484D; Tue, 9 Sep 2025 08:08:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405323; cv=none; b=ay/dZ5grNGb21oWQ1A+LcDS5MLAWLfj6oR4nx+F5UVn/X84IkDBuhFXrcZZMCzvdWMM4hpjlMinywLp6/JtVGuGLJdN9NPlj1t5Yx+/VOP4pJipxi/MLqERXleAT9L0or69DqoGrLUqs8gOzkg4xearqg+rerVuIk92bMKrKYVs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405323; c=relaxed/simple; bh=vZSM/yrb2BZgSqr6rmUzjORK9UQE9yavIWDxwKGqGpQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=IIqsR0dxszl4WXd0ALX3ZIFN/am/utm4AFgkN4k82xXm0u3h5o/EYXe1kAslO2xKYoXmDT5x9dwvmvd0vX/0knYEEV+rZN7CTVuPew8qmgd1zzrUNi+ubkFow4tzxHWNuyd22U2W8ZTJFe+RNZTnyqRMR5a5pTQ4yDWfajtvUgw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=QjTmnlbb; arc=none smtp.client-ip=198.47.19.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="QjTmnlbb" Received: from lelvem-sh02.itg.ti.com ([10.180.78.226]) by fllvem-ot04.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58988Rnp243223; Tue, 9 Sep 2025 03:08:27 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757405307; bh=wEFs+iFSAROEggAA7jjqs/gos9b4R+SgN7qg4D29q/c=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=QjTmnlbbBt2oqdGuQYjciIK+jQRRLYINYRpMcjQCFzcYz1S/nVuXmWZWoJp1rgsgD l8gDaZnX29/Y+qaynSGaBIF8eA9z+DmsyIY0pNbQ/PsrFD130xlSC9wrlQNSo3S1wd bYss72UlnBXj9rxxNAg34fbw8t5/kAvBICgY7tDQ= Received: from DLEE111.ent.ti.com (dlee111.ent.ti.com [157.170.170.22]) by lelvem-sh02.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58988RJC3566735 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 9 Sep 2025 03:08:27 -0500 Received: from DLEE102.ent.ti.com (157.170.170.32) by DLEE111.ent.ti.com (157.170.170.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55; Tue, 9 Sep 2025 03:08:27 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DLEE102.ent.ti.com (157.170.170.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Tue, 9 Sep 2025 03:08:27 -0500 Received: from abhilash-HP.dhcp.ti.com (abhilash-hp.dhcp.ti.com [10.24.68.198]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58987soH2319864; Tue, 9 Sep 2025 03:08:23 -0500 From: Yemike Abhilash Chandra To: , , , , CC: , , , , , , , , , Sukrut Bellary Subject: [PATCH V3 4/5] dt-bindings: media: ti: vpe: Add support for Video Input Port Date: Tue, 9 Sep 2025 13:37:17 +0530 Message-ID: <20250909080718.1381758-5-y-abhilashchandra@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250909080718.1381758-1-y-abhilashchandra@ti.com> References: <20250909080718.1381758-1-y-abhilashchandra@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" From: Dale Farnsworth Add device tree bindings for the Video Input Port. Video Input Port (VIP) can be found on devices such as DRA7xx and provides a parallel interface to a video source such as a sensor or TV decoder. Signed-off-by: Dale Farnsworth Signed-off-by: Benoit Parrot Signed-off-by: Sukrut Bellary Signed-off-by: Yemike Abhilash Chandra --- Change log: Changes in v3: - Remove redundant labels in bindings - Remove minItems in interrupts and mandate exactly 2 interrupts using items - Rename phandle from ti,vip-clk-polarity to ti,ctrl-module and explain why= this is required by the device - Make the phandle verifiable instead of just descriptive - Drop entire sensor node from example DT - Fix ports hierarchy using appropriate references and descriptions - Use generic node names - Add two new properties ti,vip-pixel-mux and ti,vip-channels with appropri= ate types and descriptions .../devicetree/bindings/media/ti,vip.yaml | 178 ++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 179 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/ti,vip.yaml diff --git a/Documentation/devicetree/bindings/media/ti,vip.yaml b/Document= ation/devicetree/bindings/media/ti,vip.yaml new file mode 100644 index 000000000000..c0bce44725db --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti,vip.yaml @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/ti,vip.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DRA7x VIDEO INPUT PORT (VIP). + +maintainers: + - Yemike Abhilash Chandra + +description: |- + Video Input Port (VIP) can be found on devices such as DRA7xx and + provides the system interface and the processing capability to + connect parallel image-sensor as well as BT.656/1120 capable encoder + chip to DRA7x device. + + Each VIP instance supports 2 independently configurable external + video input capture slices (Slice 0 and Slice 1) each providing + up to two video input ports (Port A and Port B). + +properties: + compatible: + enum: + - ti,dra7-vip + + reg: + items: + - description: The VIP main register region + - description: Video Data Parser (PARSER) register region for Slice0 + - description: Color Space Conversion (CSC) register region for Slic= e0 + - description: Scaler (SC) register region for Slice0 + - description: Video Data Parser (PARSER) register region for Slice1 + - description: Color Space Conversion (CSC) register region for Slic= e1 + - description: Scaler (SC) register region for Slice1 + - description: Video Port Direct Memory Access (VPDMA) register regi= on + + reg-names: + items: + - const: vip + - const: parser0 + - const: csc0 + - const: sc0 + - const: parser1 + - const: csc1 + - const: sc1 + - const: vpdma + + interrupts: + items: + - description: IRQ index 0 is used for Slice0 interrupts + - description: IRQ index 1 is used for Slice1 interrupts + + ti,ctrl-module: + description: + Reference to the device control module that provides clock-edge + inversion control for VIP ports. These controls allow the + VIP to sample pixel data on the correct clock edge. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to device control module + - description: offset to the CTRL_CORE_SMA_SW_1 register + - description: Bit field to slice 0 port A + - description: Bit field to slice 0 port B + - description: Bit field to slice 1 port A + - description: Bit field to slice 1 port B + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@[0-3]$': + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: | + Each VIP instance supports 2 independently configurable external= video + input capture slices (Slice 0 and Slice 1) each providing up to = two video + input ports (Port A and Port B). These ports represent the follo= wing + port@0 -> Slice 0 Port A + port@1 -> Slice 0 Port B + port@2 -> Slice 1 Port A + port@3 -> Slice 1 Port B + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-width: + enum: [8, 16, 24] + default: 8 + + ti,vip-pixel-mux: + type: boolean + description: + In BT656/1120 mode, this will enable pixel-muxing if + the number of channels is either 1, 2 or 4. If this + property is present then pixel-muxing is enabled + otherwise it will use line-muxing. + + ti,vip-channels: + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 1 + maxItems: 16 + description: + In BT656/1120 mode, list of channel ids to be captured. + If the property is not present then 1 channel is assumed. + + remote-endpoint: true + +required: + - compatible + - reg + - reg-names + - interrupts + - ti,ctrl-module + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + vip1: video@48970000 { + compatible =3D "ti,dra7-vip"; + reg =3D <0x48970000 0x114>, + <0x48975500 0xD8>, + <0x48975700 0x18>, + <0x48975800 0x80>, + <0x48975a00 0xD8>, + <0x48975c00 0x18>, + <0x48975d00 0x80>, + <0x4897d000 0x400>; + reg-names =3D "vip", + "parser0", + "csc0", + "sc0", + "parser1", + "csc1", + "sc1", + "vpdma"; + interrupts =3D , + ; + ti,ctrl-module =3D <&scm_conf 0x534 0x0 0x2 0x1 0x3>; + + ports { + #address-cells =3D <1>; + #size-cells =3D <0>; + + vin1a: port@0 { + reg =3D <0>; + + vin1a_ep: endpoint { + remote-endpoint =3D <&camera1>; + hsync-active =3D <1>; + vsync-active =3D <1>; + pclk-sample =3D <0>; + bus-width =3D <8>; + }; + }; + vin1b: port@1 { + reg =3D <1>; + }; + vin2a: port@2 { + reg =3D <2>; + }; + vin2b: port@3 { + reg =3D <3>; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index d7175c38c6d1..83ca0cfd0565 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25564,6 +25564,7 @@ S: Maintained W: http://linuxtv.org/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ F: Documentation/devicetree/bindings/media/ti,cal.yaml +F: Documentation/devicetree/bindings/media/ti,vip.yaml F: Documentation/devicetree/bindings/media/ti,vpe.yaml F: drivers/media/platform/ti/cal/ F: drivers/media/platform/ti/vpe/ --=20 2.34.1 From nobody Fri Oct 3 00:00:52 2025 Received: from fllvem-ot03.ext.ti.com (fllvem-ot03.ext.ti.com [198.47.19.245]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 106F4246774; Tue, 9 Sep 2025 08:08:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.245 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405336; cv=none; b=RKrnM6bLmWqAg9HgAshlAG2rnGoXnDZeSuOIJeLtxr1JuSoNpCjX9y/s5anY8LH1BcPs9abGD5XUO4JAxdRCvxT5oYgFZx/fxKLRLfBW5Yeg5iXV8MlDxvN4KpsxAMMNXObd9lJQ2/kDZAJ/c77fycca7f8ZnS2dWh4HQyj2s1w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757405336; c=relaxed/simple; bh=Ywsy+IGmTUeWdsrPYZXVloqC0/UDZGTzpxoi7F9Knm4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EhfC/WA4wS50gZ9+PSR5ts9pj2P2Fa+xDkhN/CLpQQaMQAoQC+ibPj0Uao2o+hXUhA3hEDOT7tQrz3POuooUWbVlhvOewnS40YzhXt8+iG65TZ15CsPQyD4IERFz7XA+hcIfX13Aye373T+GYAlozPPmalnuVlxtIonEoXBCc2Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=rGr7vn47; arc=none smtp.client-ip=198.47.19.245 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="rGr7vn47" Received: from lelvem-sh02.itg.ti.com ([10.180.78.226]) by fllvem-ot03.ext.ti.com (8.15.2/8.15.2) with ESMTP id 58988YMo3981405; Tue, 9 Sep 2025 03:08:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1757405314; bh=KIfh6RxtHR+yByTOI5aqf1meEWQckoXnCFIy2db6cl4=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=rGr7vn47Plpn9YvsuA4fD8MCchaKyEhcsSCG1Z/YBvxIqCwt9jSVRLwOoDdxxAiru Vhh3ySnmxvTakEJZn40k2OLgHmMfLWmSfE51oG0eGE7RHj+75AgOXe6doHsddm7kHq kqxt0M3tPWq+iCcgFwAuUWLcWkvXwz0LDnu7lvNQ= Received: from DLEE115.ent.ti.com (dlee115.ent.ti.com [157.170.170.26]) by lelvem-sh02.itg.ti.com (8.18.1/8.18.1) with ESMTPS id 58988YgZ3566767 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA256 bits=128 verify=FAIL); Tue, 9 Sep 2025 03:08:34 -0500 Received: from DLEE112.ent.ti.com (157.170.170.23) 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.2507.55; Tue, 9 Sep 2025 03:08:33 -0500 Received: from lelvem-mr05.itg.ti.com (10.180.75.9) by DLEE112.ent.ti.com (157.170.170.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.55 via Frontend Transport; Tue, 9 Sep 2025 03:08:33 -0500 Received: from abhilash-HP.dhcp.ti.com (abhilash-hp.dhcp.ti.com [10.24.68.198]) by lelvem-mr05.itg.ti.com (8.18.1/8.18.1) with ESMTP id 58987soI2319864; Tue, 9 Sep 2025 03:08:29 -0500 From: Yemike Abhilash Chandra To: , , , , CC: , , , , , , , , , Sukrut Bellary Subject: [PATCH V3 5/5] media: ti-vpe: Add the VIP driver Date: Tue, 9 Sep 2025 13:37:18 +0530 Message-ID: <20250909080718.1381758-6-y-abhilashchandra@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250909080718.1381758-1-y-abhilashchandra@ti.com> References: <20250909080718.1381758-1-y-abhilashchandra@ti.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Content-Type: text/plain; charset="utf-8" From: Dale Farnsworth VIP stands for Video Input Port; it can be found on devices such as DRA7xx and provides a parallel interface to a video source such as a sensor or TV decoder. Each VIP can support two inputs (slices) and an SoC can be configured with a variable number of VIPs. Each slice ican support two ports, each connected to it's own sub-device. Signed-off-by: Dale Farnsworth Signed-off-by: Benoit Parrot Signed-off-by: Sukrut Bellary Signed-off-by: Yemike Abhilash Chandra --- Changelog: Changes in v3: - Use dev_err_probe() instead of dev_err() - Remove functionality that identifies instance ID using label - Do not fill bus_info in vip_querycap - Change "camera" to "Camera" in vip_enuminput - Add vb2_is_busy() check in vip_s_std function - If the input matches the current video standard in vip_s_std, then return - Remove compose since the hardware does not support it - Add check for s->target in vip_s_selection function - Drop wait_prepare/wait_finish callbacks - Remove v4l2_dbg prints that only log the called ioctl drivers/media/platform/ti/Kconfig | 13 + drivers/media/platform/ti/vpe/Makefile | 2 + drivers/media/platform/ti/vpe/vip.c | 3768 ++++++++++++++++++++++++ drivers/media/platform/ti/vpe/vip.h | 724 +++++ 4 files changed, 4507 insertions(+) create mode 100644 drivers/media/platform/ti/vpe/vip.c create mode 100644 drivers/media/platform/ti/vpe/vip.h diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/= Kconfig index 3bc4aa35887e..7cd1539ab84a 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -41,6 +41,19 @@ config VIDEO_TI_CAL_MC default. Note that this behavior can be overridden via module parameter 'mc_api'. =20 +config VIDEO_TI_VIP + tristate "TI Video Input Port" + default n + depends on VIDEO_DEV && SOC_DRA7XX + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + select VIDEO_TI_VPDMA + select VIDEO_TI_SC + select VIDEO_TI_CSC + help + Driver support for VIP module on certain TI SoC's + VIP =3D Video Input Port. + # Mem2mem drivers =20 config VIDEO_TI_VPE diff --git a/drivers/media/platform/ti/vpe/Makefile b/drivers/media/platfor= m/ti/vpe/Makefile index 3fadfe084f87..fbb0dec5a30e 100644 --- a/drivers/media/platform/ti/vpe/Makefile +++ b/drivers/media/platform/ti/vpe/Makefile @@ -3,10 +3,12 @@ obj-$(CONFIG_VIDEO_TI_VPE) +=3D ti-vpe.o obj-$(CONFIG_VIDEO_TI_VPDMA) +=3D ti-vpdma.o obj-$(CONFIG_VIDEO_TI_SC) +=3D ti-sc.o obj-$(CONFIG_VIDEO_TI_CSC) +=3D ti-csc.o +obj-$(CONFIG_VIDEO_TI_VIP) +=3D ti-vip.o =20 ti-vpe-y :=3D vpe.o ti-vpdma-y :=3D vpdma.o ti-sc-y :=3D sc.o ti-csc-y :=3D csc.o +ti-vip-y :=3D vip.o =20 ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) +=3D -DDEBUG diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/t= i/vpe/vip.c new file mode 100644 index 000000000000..0183fe3efe4b --- /dev/null +++ b/drivers/media/platform/ti/vpe/vip.c @@ -0,0 +1,3768 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI VIP capture driver + * + * Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com/ + * David Griego, + * Dale Farnsworth, + * Yemike Abhilash Chandra, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "vip.h" + +#define VIP_MODULE_NAME "vip" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-8)"); + +/* + * Minimum and maximum frame sizes + */ +#define MIN_W 128 +#define MIN_H 128 +#define MAX_W 2048 +#define MAX_H 1536 + +/* + * Required alignments + */ +#define S_ALIGN 0 /* multiple of 1 */ +#define H_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN 1 /* multiple of 2 */ +#define L_ALIGN 7 /* multiple of 128, line stride, 16 bytes */ + +/* + * Need a descriptor entry for each of up to 15 outputs, + * and up to 2 control transfers. + */ +#define VIP_DESC_LIST_SIZE (17 * sizeof(struct vpdma_dtd)) + +/* + * port flag bits + */ +#define FLAG_INTERLACED BIT(4) +#define FLAG_MULT_PORT BIT(6) +#define FLAG_MULT_ANC BIT(7) + +#define VIP_VPDMA_FIFO_SIZE 2 +#define VIP_DROPQ_SIZE 3 + +/* + * Define indices into the srce_info tables + */ + +#define VIP_SRCE_MULT_PORT 0 +#define VIP_SRCE_MULT_ANC 1 +#define VIP_SRCE_LUMA 2 +#define VIP_SRCE_CHROMA 3 +#define VIP_SRCE_RGB 4 + +#define reg_read(dev, offset) ioread32((dev)->base + (offset)) +#define reg_write(dev, offset, val) iowrite32((val), (dev)->base + (offset= )) + +#define GET_OFFSET_TOP(port, obj, reg) \ + ((obj)->res->start - (port)->dev->res->start + (reg)) + +#define VIP_SET_MMR_ADB_HDR(port, hdr, regs, offset_a) \ + VPDMA_SET_MMR_ADB_HDR((port)->mmr_adb, vip_mmr_adb, hdr, regs, offset_a) + +/* + * These represent the module resets bit for slice 1 + * Upon detecting slice2 we simply left shift by 1 + */ +#define VIP_DP_RST BIT(16) +#define VIP_CSC_RST BIT(20) +#define VIP_SC_RST BIT(22) + +#define VIP_PARSER_PORT(p) (VIP_PARSER_PORTA_0 + ((p) * 0x8U)) +#define VIP_PARSER_EXTRA_PORT(p) (VIP_PARSER_PORTA_1 + ((p) * 0x8U)) +#define VIP_PARSER_CROP_H_PORT(p) \ + (VIP_PARSER_PORTA_EXTRA4 + ((p) * 0x10U)) +#define VIP_PARSER_CROP_V_PORT(p) \ + (VIP_PARSER_PORTA_EXTRA5 + ((p) * 0x10U)) +#define VIP_PARSER_STOP_IMM_PORT(p) (VIP_PARSER_PORTA_EXTRA6 + ((p) * 0x4U= )) + +#define PARSER_IRQ_MASK (VIP_PORTA_OUTPUT_FIFO_YUV | \ + VIP_PORTB_OUTPUT_FIFO_YUV) + +/* + * The srce_info structure contains per-srce data. + */ +struct vip_srce_info { + u8 base_channel; /* the VPDMA channel number */ + u8 vb_index; /* input frame f, f-1, f-2 index */ + u8 vb_part; /* identifies section of co-planar formats */ +}; + +static struct vip_srce_info srce_info[5] =3D { + [VIP_SRCE_MULT_PORT] =3D { + .base_channel =3D VIP1_CHAN_NUM_MULT_PORT_A_SRC0, + .vb_index =3D 0, + .vb_part =3D VIP_CHROMA, + }, + [VIP_SRCE_MULT_ANC] =3D { + .base_channel =3D VIP1_CHAN_NUM_MULT_ANC_A_SRC0, + .vb_index =3D 0, + .vb_part =3D VIP_LUMA, + }, + [VIP_SRCE_LUMA] =3D { + .base_channel =3D VIP1_CHAN_NUM_PORT_A_LUMA, + .vb_index =3D 1, + .vb_part =3D VIP_LUMA, + }, + [VIP_SRCE_CHROMA] =3D { + .base_channel =3D VIP1_CHAN_NUM_PORT_A_CHROMA, + .vb_index =3D 1, + .vb_part =3D VIP_CHROMA, + }, + [VIP_SRCE_RGB] =3D { + .base_channel =3D VIP1_CHAN_NUM_PORT_A_RGB, + .vb_part =3D VIP_LUMA, + }, +}; + +static struct vip_fmt vip_formats[VIP_MAX_ACTIVE_FMT] =3D { + { + .fourcc =3D V4L2_PIX_FMT_NV12, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 1, + .vpdma_fmt =3D { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_UYVY, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_YUYV, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_VYUY, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_YVYU, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_RGB24, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_RGB32, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_BGR24, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_BGR32, + .code =3D MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_RGB24, + .code =3D MEDIA_BUS_FMT_RGB888_1X24, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_RGB32, + .code =3D MEDIA_BUS_FMT_ARGB8888_1X32, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_SBGGR8, + .code =3D MEDIA_BUS_FMT_SBGGR8_1X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_SGBRG8, + .code =3D MEDIA_BUS_FMT_SGBRG8_1X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_SGRBG8, + .code =3D MEDIA_BUS_FMT_SGRBG8_1X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc =3D V4L2_PIX_FMT_SRGGB8, + .code =3D MEDIA_BUS_FMT_SRGGB8_1X8, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + /* V4L2 currently only defines one 16 bit variant */ + .fourcc =3D V4L2_PIX_FMT_SBGGR16, + .code =3D MEDIA_BUS_FMT_SBGGR16_1X16, + .coplanar =3D 0, + .vpdma_fmt =3D { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16], + }, + }, +}; + +/* + * DMA address/data block for the shadow registers + */ +struct vip_mmr_adb { + struct vpdma_adb_hdr sc_hdr0; + u32 sc_regs0[7]; + u32 sc_pad0[1]; + struct vpdma_adb_hdr sc_hdr8; + u32 sc_regs8[6]; + u32 sc_pad8[2]; + struct vpdma_adb_hdr sc_hdr17; + u32 sc_regs17[9]; + u32 sc_pad17[3]; + struct vpdma_adb_hdr csc_hdr; + u32 csc_regs[6]; + u32 csc_pad[2]; +}; + +/* + * Function prototype declarations + */ +static int alloc_port(struct vip_dev *, int); +static void free_port(struct vip_port *); +static int vip_setup_parser(struct vip_port *port); +static int vip_setup_scaler(struct vip_stream *stream); +static void vip_enable_parser(struct vip_port *port, bool on); +static void vip_reset_parser(struct vip_port *port, bool on); +static void vip_parser_stop_imm(struct vip_port *port, bool on); +static void stop_dma(struct vip_stream *stream, bool clear_list); +static int vip_load_vpdma_list_fifo(struct vip_stream *stream); +static inline bool is_scaler_available(struct vip_port *port); +static inline bool allocate_scaler(struct vip_port *port); +static inline void free_scaler(struct vip_port *port); +static bool is_csc_available(struct vip_port *port); +static bool allocate_csc(struct vip_port *port, + enum vip_csc_state csc_direction); +static void free_csc(struct vip_port *port); + +/* initialize v4l2_format_info member in vip_formats array */ +static void vip_init_format_info(struct device *dev) +{ + struct vip_fmt *fmt; + int i; + + for (i =3D 0; i < ARRAY_SIZE(vip_formats); i++) { + fmt =3D &vip_formats[i]; + fmt->finfo =3D v4l2_format_info(fmt->fourcc); + } +} + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] =3D (unsigned char)(fmt & 0xff); + code[1] =3D (unsigned char)((fmt >> 8) & 0xff); + code[2] =3D (unsigned char)((fmt >> 16) & 0xff); + code[3] =3D (unsigned char)((fmt >> 24) & 0xff); + code[4] =3D '\0'; + + return code; +} + +/* + * Find our format description corresponding to the passed v4l2_format + */ +static struct vip_fmt *find_port_format_by_pix(struct vip_port *port, + u32 pixelformat) +{ + struct vip_fmt *fmt; + unsigned int index; + + for (index =3D 0; index < port->num_active_fmt; index++) { + fmt =3D port->active_fmt[index]; + if (fmt->fourcc =3D=3D pixelformat) + return fmt; + } + + return NULL; +} + +static struct vip_fmt *find_port_format_by_code(struct vip_port *port, + u32 code) +{ + struct vip_fmt *fmt; + unsigned int index; + + for (index =3D 0; index < port->num_active_fmt; index++) { + fmt =3D port->active_fmt[index]; + if (fmt->code =3D=3D code) + return fmt; + } + + return NULL; +} + +inline struct vip_port *notifier_to_vip_port(struct v4l2_async_notifier *n) +{ + return container_of(n, struct vip_port, notifier); +} + +static bool vip_is_mbuscode_yuv(u32 code) +{ + return ((code & 0xff00) =3D=3D 0x2000); +} + +static bool vip_is_mbuscode_rgb(u32 code) +{ + return ((code & 0xff00) =3D=3D 0x1000); +} + +static bool vip_is_mbuscode_raw(u32 code) +{ + return ((code & 0xff00) =3D=3D 0x3000); +} + +/* + * This is not an accurate conversion but it is only used to + * assess if color conversion is needed. + */ +static u32 vip_mbus_code_to_fourcc(u32 code) +{ + if (vip_is_mbuscode_rgb(code)) + return V4L2_PIX_FMT_RGB24; + + if (vip_is_mbuscode_yuv(code)) + return V4L2_PIX_FMT_UYVY; + + return V4L2_PIX_FMT_SBGGR8; +} + +static enum vip_csc_state +vip_csc_direction(u32 src_code, const struct v4l2_format_info *dfinfo) +{ + if (vip_is_mbuscode_yuv(src_code) && v4l2_is_format_rgb(dfinfo)) + return VIP_CSC_Y2R; + else if (vip_is_mbuscode_rgb(src_code) && v4l2_is_format_yuv(dfinfo)) + return VIP_CSC_R2Y; + else + return VIP_CSC_NA; +} + +/* + * Insert a masked field into a 32-bit field + */ +static void insert_field(u32 *valp, u32 field, u32 mask, int shift) +{ + u32 val =3D *valp; + + val &=3D ~(mask << shift); + val |=3D (field & mask) << shift; + *valp =3D val; +} + +/* + * Set the headers for all of the address/data block structures. + */ +static void init_adb_hdrs(struct vip_port *port) +{ + VIP_SET_MMR_ADB_HDR(port, sc_hdr0, sc_regs0, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC0)); + VIP_SET_MMR_ADB_HDR(port, sc_hdr8, sc_regs8, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC8)); + VIP_SET_MMR_ADB_HDR(port, sc_hdr17, sc_regs17, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC17)); + VIP_SET_MMR_ADB_HDR(port, csc_hdr, csc_regs, + GET_OFFSET_TOP(port, port->dev->csc, CSC_CSC00)); + +}; + +static void vip_module_toggle(struct vip_dev *dev, uint32_t module, bool o= n) +{ + u32 val =3D 0; + + val =3D reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id =3D=3D VIP_SLICE2) + module <<=3D 1; + + if (on) + val |=3D module; + else + val &=3D ~module; + + reg_write(dev, VIP_CLK_RESET, val); +} + +/* + * Enable or disable the VIP clocks + */ +static void vip_set_clock_enable(struct vip_dev *dev, bool on) +{ + u32 val =3D 0; + + val =3D reg_read(dev, VIP_CLK_ENABLE); + if (on) { + val |=3D VIP_VPDMA_CLK_ENABLE; + if (dev->slice_id =3D=3D VIP_SLICE1) + val |=3D VIP_VIP1_DATA_PATH_CLK_ENABLE; + else + val |=3D VIP_VIP2_DATA_PATH_CLK_ENABLE; + } else { + if (dev->slice_id =3D=3D VIP_SLICE1) + val &=3D ~VIP_VIP1_DATA_PATH_CLK_ENABLE; + else + val &=3D ~VIP_VIP2_DATA_PATH_CLK_ENABLE; + + /* Both VIP are disabled then shutdown VPDMA also */ + if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE | + VIP_VIP2_DATA_PATH_CLK_ENABLE))) + val =3D 0; + } + + reg_write(dev, VIP_CLK_ENABLE, val); +} + +/* This helper function is used to enable the clock early on to + * enable vpdma firmware loading before the slice device are created + */ +static void vip_shared_set_clock_enable(struct vip_shared *shared, bool on) +{ + u32 val =3D 0; + + val =3D VIP_VIP1_DATA_PATH_CLK_ENABLE | VIP_VPDMA_CLK_ENABLE; + + reg_write(shared, VIP_CLK_ENABLE, val); +} + +static void vip_top_reset(struct vip_dev *dev) +{ + u32 val =3D 0; + + val =3D reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id =3D=3D VIP_SLICE1) + insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP1_DATA_PATH_RESET_SHIFT); + else + insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP2_DATA_PATH_RESET_SHIFT); + + reg_write(dev, VIP_CLK_RESET, val); + + usleep_range(200, 250); + + val =3D reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id =3D=3D VIP_SLICE1) + insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP1_DATA_PATH_RESET_SHIFT); + else + insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP2_DATA_PATH_RESET_SHIFT); + reg_write(dev, VIP_CLK_RESET, val); +} + +static void vip_top_vpdma_reset(struct vip_shared *shared) +{ + u32 val; + + val =3D reg_read(shared, VIP_CLK_RESET); + insert_field(&val, 1, VIP_VPDMA_CLK_RESET_MASK, + VIP_VPDMA_CLK_RESET_SHIFT); + reg_write(shared, VIP_CLK_RESET, val); + + usleep_range(200, 250); + + val =3D reg_read(shared, VIP_CLK_RESET); + insert_field(&val, 0, VIP_VPDMA_CLK_RESET_MASK, + VIP_VPDMA_CLK_RESET_SHIFT); + reg_write(shared, VIP_CLK_RESET, val); +} + +static void vip_set_pclk_invert(struct vip_port *port) +{ + struct vip_ctrl_module *ctrl =3D port->dev->syscon; + struct vip_dev *dev =3D port->dev; + u32 index; + /* + * When the VIP parser is configured to so that the pixel clock + * is to be sampled at falling edge, the pixel clock needs to be + * inverted before it is given to the VIP module. This is done + * by setting a bit in the CTRL_CORE_SMA_SW1 register. + */ + + index =3D 2 * port->dev->slice_id + port->port_id; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: slice%d:port%d -> index: %d\n", _= _func__, + port->dev->slice_id, port->port_id, index); + + if (ctrl->syscon_pol) + regmap_update_bits(ctrl->syscon_pol, + ctrl->syscon_offset, + ctrl->syscon_bit_field[index], + ctrl->syscon_bit_field[index]); +} + +static void vip_set_data_interface(struct vip_port *port, + enum data_interface_modes mode) +{ + u32 val =3D 0; + + insert_field(&val, mode, VIP_DATA_INTERFACE_MODE_MASK, + VIP_DATA_INTERFACE_MODE_SHFT); + + reg_write(port->dev->parser, VIP_PARSER_MAIN_CFG, val); +} + +static void vip_set_slice_path(struct vip_dev *dev, + enum data_path_select data_path, u32 path_val) +{ + u32 val =3D 0; + int data_path_reg; + + data_path_reg =3D VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id; + + switch (data_path) { + case ALL_FIELDS_DATA_SELECT: + val |=3D path_val; + break; + case VIP_CSC_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_CSC_SRC_SELECT_MASK, + VIP_CSC_SRC_SELECT_SHFT); + break; + case VIP_SC_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_SC_SRC_SELECT_MASK, + VIP_SC_SRC_SELECT_SHFT); + break; + case VIP_RGB_SRC_DATA_SELECT: + val |=3D (path_val) ? VIP_RGB_SRC_SELECT : 0; + break; + case VIP_RGB_OUT_LO_DATA_SELECT: + val |=3D (path_val) ? VIP_RGB_OUT_LO_SRC_SELECT : 0; + break; + case VIP_RGB_OUT_HI_DATA_SELECT: + val |=3D (path_val) ? VIP_RGB_OUT_HI_SRC_SELECT : 0; + break; + case VIP_CHR_DS_1_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_DS1_SRC_SELECT_MASK, + VIP_DS1_SRC_SELECT_SHFT); + break; + case VIP_CHR_DS_2_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_DS2_SRC_SELECT_MASK, + VIP_DS2_SRC_SELECT_SHFT); + break; + case VIP_MULTI_CHANNEL_DATA_SELECT: + val |=3D (path_val) ? VIP_MULTI_CHANNEL_SELECT : 0; + break; + case VIP_CHR_DS_1_DATA_BYPASS: + val |=3D (path_val) ? VIP_DS1_BYPASS : 0; + break; + case VIP_CHR_DS_2_DATA_BYPASS: + val |=3D (path_val) ? VIP_DS2_BYPASS : 0; + break; + default: + v4l2_err(&dev->v4l2_dev, "%s: data_path 0x%x is not valid\n", + __func__, data_path); + return; + } + insert_field(&val, data_path, VIP_DATAPATH_SELECT_MASK, + VIP_DATAPATH_SELECT_SHFT); + reg_write(dev, data_path_reg, val); + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: DATA_PATH_SELECT(%08X): %08X\n", = __func__, + data_path_reg, reg_read(dev, data_path_reg)); +} + +/* + * Return the vip_stream structure for a given struct file + */ +static inline struct vip_stream *file2stream(struct file *file) +{ + return video_drvdata(file); +} + +/* + * Append a destination descriptor to the current descriptor list, + * setting up dma to the given srce. + */ +static int add_out_dtd(struct vip_stream *stream, int srce_type) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vip_srce_info *sinfo =3D &srce_info[srce_type]; + struct v4l2_rect *c_rect =3D &port->c_rect; + struct vip_fmt *fmt =3D port->fmt; + int channel, plane =3D 0; + int max_width, max_height; + dma_addr_t dma_addr =3D 0; + u32 flags; + u32 width =3D stream->width; + + channel =3D sinfo->base_channel; + + switch (srce_type) { + case VIP_SRCE_MULT_PORT: + case VIP_SRCE_MULT_ANC: + if (port->port_id =3D=3D VIP_PORTB) + channel +=3D VIP_CHAN_MULT_PORTB_OFFSET; + channel +=3D stream->stream_id; + flags =3D 0; + break; + case VIP_SRCE_CHROMA: + plane =3D 1; + fallthrough; + case VIP_SRCE_LUMA: + if (port->port_id =3D=3D VIP_PORTB) { + if (port->scaler && !port->fmt->coplanar) + /* + * In this case Port A Chroma channel + * is used to carry Port B scaled YUV422 + */ + channel +=3D 1; + else + channel +=3D VIP_CHAN_YUV_PORTB_OFFSET; + } + flags =3D port->flags; + break; + case VIP_SRCE_RGB: + if (port->port_id =3D=3D VIP_PORTB || + (port->port_id =3D=3D VIP_PORTA && + port->csc =3D=3D VIP_CSC_NA && + v4l2_is_format_rgb(port->fmt->finfo))) + /* + * RGB sensor only connect to Y_LO + * channel i.e. port B channel. + */ + channel +=3D VIP_CHAN_RGB_PORTB_OFFSET; + flags =3D port->flags; + break; + default: + v4l2_err(&dev->v4l2_dev, "%s: srce_type 0x%x is not valid\n", + __func__, srce_type); + return -1; + } + + if (dev->slice_id =3D=3D VIP_SLICE2) + channel +=3D VIP_CHAN_VIP2_OFFSET; + + if (port->fmt->vpdma_fmt[0] =3D=3D &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) { + /* + * Special case since we are faking a YUV422 16bit format + * to have the vpdma perform the needed byte swap + * we need to adjust the pixel width accordingly + * otherwise the parser will attempt to collect more pixels + * then available and the vpdma transfer will exceed the + * allocated frame buffer. + */ + width >>=3D 1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting wi= dth to %d\n", + __func__, width); + } + + /* + * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1 + */ + + if (dev->slice_id =3D=3D VIP_SLICE1) { + vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1, + width, stream->height); + + max_width =3D MAX_OUT_WIDTH_REG1; + max_height =3D MAX_OUT_HEIGHT_REG1; + } else if (dev->slice_id =3D=3D VIP_SLICE2) { + vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2, + width, stream->height); + + max_width =3D MAX_OUT_WIDTH_REG2; + max_height =3D MAX_OUT_HEIGHT_REG2; + } + + /* + * Mark this channel to be cleared while cleaning up resources + * This will make sure that an abort descriptor for this channel + * would be submitted to VPDMA causing any ongoing transaction to be + * aborted and cleanup the VPDMA FSM for this channel + */ + stream->vpdma_channels[channel] =3D 1; + + vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width, + stream->bytesperline, c_rect, + fmt->vpdma_fmt[plane], dma_addr, + max_width, max_height, channel, flags); + return 0; +} + +/* + * add_stream_dtds - prepares and starts DMA for pending transfers + */ +static void add_stream_dtds(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + int srce_type; + + if (port->flags & FLAG_MULT_PORT) + srce_type =3D VIP_SRCE_MULT_PORT; + else if (port->flags & FLAG_MULT_ANC) + srce_type =3D VIP_SRCE_MULT_ANC; + else if (v4l2_is_format_rgb(port->fmt->finfo)) + srce_type =3D VIP_SRCE_RGB; + else + srce_type =3D VIP_SRCE_LUMA; + + add_out_dtd(stream, srce_type); + + if (srce_type =3D=3D VIP_SRCE_LUMA && port->fmt->coplanar) + add_out_dtd(stream, VIP_SRCE_CHROMA); +} + +static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser =3D dev->parser; + u32 reg_addr =3D VIP_INT0_ENABLE0_SET + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val =3D (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Enable Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_MASK, ~PARSER_IRQ_MASK); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_enable_list_complete_irq(dev->shared->vpdma, + irq_num, list_num, true); +} + +static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser =3D dev->parser; + u32 reg_addr =3D VIP_INT0_ENABLE0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val =3D (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Disable all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_MASK, 0xffffffff); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_enable_list_complete_irq(dev->shared->vpdma, + irq_num, list_num, false); +} + +static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser =3D dev->parser; + u32 reg_addr =3D VIP_INT0_STATUS0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val =3D (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Clear all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_CLR, 0xffffffff); + reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id); +} + +static void populate_desc_list(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + unsigned int list_length; + + stream->desc_next =3D stream->desc_list.buf.addr; + add_stream_dtds(stream); + + list_length =3D stream->desc_next - stream->desc_list.buf.addr; + vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); +} + +/* + * start_dma - adds descriptors to the dma list and submits them. + * Should be called after a new vb is queued and on a vpdma list + * completion interrupt. + */ +static void start_dma(struct vip_stream *stream, struct vip_buffer *buf) +{ + struct vip_dev *dev =3D stream->port->dev; + struct vpdma_data *vpdma =3D dev->shared->vpdma; + int list_num =3D stream->list_num; + dma_addr_t dma_addr; + int drop_data; + + if (vpdma_list_busy(vpdma, list_num)) { + v4l2_err(&dev->v4l2_dev, "vpdma list busy, cannot post\n"); + return; /* nothing to do */ + } + + if (buf) { + dma_addr =3D vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + drop_data =3D 0; + v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: vb2 buf idx:%d, dma_addr:%pad\n", + __func__, buf->vb.vb2_buf.index, &dma_addr); + } else { + dma_addr =3D 0; + drop_data =3D 1; + v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: dropped\n", __func__); + } + + vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list, + dma_addr, stream->write_desc, drop_data, 0); + + if (stream->port->fmt->coplanar) { + dma_addr +=3D stream->bytesperline * stream->height; + vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list, + dma_addr, stream->write_desc + 1, + drop_data, 1); + } + + vpdma_submit_descs(dev->shared->vpdma, + &stream->desc_list, stream->list_num); +} + +static void vip_schedule_next_buffer(struct vip_stream *stream) +{ + struct vip_dev *dev =3D stream->port->dev; + struct vip_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&stream->vidq)) { + v4l2_dbg(4, debug, &dev->v4l2_dev, "Dropping frame\n"); + if (list_empty(&stream->dropq)) { + v4l2_err(&dev->v4l2_dev, "No dropq buffer left!"); + spin_unlock_irqrestore(&dev->slock, flags); + return; + } + buf =3D list_entry(stream->dropq.next, + struct vip_buffer, list); + + buf->drop =3D true; + list_move_tail(&buf->list, &stream->post_bufs); + buf =3D NULL; + } else { + buf =3D list_entry(stream->vidq.next, + struct vip_buffer, list); + buf->drop =3D false; + list_move_tail(&buf->list, &stream->post_bufs); + v4l2_dbg(4, debug, &dev->v4l2_dev, "added next buffer\n"); + } + + spin_unlock_irqrestore(&dev->slock, flags); + start_dma(stream, buf); +} + +static void vip_process_buffer_complete(struct vip_stream *stream) +{ + struct vip_dev *dev =3D stream->port->dev; + struct vip_buffer *buf; + struct vb2_v4l2_buffer *vb =3D NULL; + unsigned long flags, fld; + + buf =3D list_first_entry(&stream->post_bufs, struct vip_buffer, list); + + if (stream->port->flags & FLAG_INTERLACED) { + vpdma_unmap_desc_buf(dev->shared->vpdma, + &stream->desc_list.buf); + + fld =3D dtd_get_field(stream->write_desc); + stream->field =3D fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; + + vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + } + + if (buf) { + v4l2_dbg(4, debug, &dev->v4l2_dev, "vip buffer complete 0x%x, 0x%x\n", + (unsigned int)buf, buf->drop); + + vb =3D &buf->vb; + vb->sequence =3D stream->sequence; + vb->vb2_buf.timestamp =3D ktime_get_ns(); + vb->field =3D V4L2_FIELD_NONE; + + if (buf->drop) { + spin_lock_irqsave(&dev->slock, flags); + list_move_tail(&buf->list, &stream->dropq); + spin_unlock_irqrestore(&dev->slock, flags); + } else { + spin_lock_irqsave(&dev->slock, flags); + list_del(&buf->list); + spin_unlock_irqrestore(&dev->slock, flags); + vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); + } + } else { + v4l2_err(&dev->v4l2_dev, "%s: buf is null!!!\n", __func__); + return; + } + + stream->sequence++; +} + +static int vip_reset_vpdma(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vip_buffer *buf; + unsigned long flags; + + stop_dma(stream, false); + + spin_lock_irqsave(&dev->slock, flags); + /* requeue all active buffers in the opposite order */ + while (!list_empty(&stream->post_bufs)) { + buf =3D list_last_entry(&stream->post_bufs, + struct vip_buffer, list); + list_del(&buf->list); + if (buf->drop =3D=3D 1) { + list_add_tail(&buf->list, &stream->dropq); + v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing drop buffer on dropq\n"); + } else { + list_add(&buf->list, &stream->vidq); + v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing vb2 buf idx:%d on vidq\n= ", + buf->vb.vb2_buf.index); + } + } + spin_unlock_irqrestore(&dev->slock, flags); + + /* Make sure the desc_list is unmapped */ + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + + return 0; +} + +static void vip_overflow_recovery_work(struct work_struct *work) +{ + struct vip_stream *stream =3D container_of(work, struct vip_stream, + recovery_work); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + + v4l2_err(&dev->v4l2_dev, "%s: Port %c\n", __func__, + port->port_id =3D=3D VIP_PORTA ? 'A' : 'B'); + + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + + /* 1. Set VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMMEDIATELY */ + /* 2. Set VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMMEDIATELY */ + vip_parser_stop_imm(port, 1); + + /* 3. Clear VIP_PORT_A[8] ENABLE */ + /* + * 4. Set VIP_PORT_A[7] CLR_ASYNC_FIFO_RD + * Set VIP_PORT_A[6] CLR_ASYNC_FIFO_WR + */ + vip_enable_parser(port, false); + + /* 5. Set VIP_PORT_A[23] SW_RESET */ + vip_reset_parser(port, 1); + + /* + * 6. Reset other VIP modules + * For each module used downstream of VIP_PARSER, write 1 to the + * bit location of the VIP_CLKC_RST register which is connected + * to VIP_PARSER + */ + vip_module_toggle(dev, VIP_DP_RST, true); + + usleep_range(200, 250); + + /* + * 7. Abort VPDMA channels + * Write to list attribute to stop list 0 + * Write to list address register location of abort list + * Write to list attribute register list 0 and size of abort list + */ + vip_reset_vpdma(stream); + + /* 8. Clear VIP_PORT_A[23] SW_RESET */ + vip_reset_parser(port, 0); + + /* + * 9. Un-reset other VIP modules + * For each module used downstream of VIP_PARSER, write 0 to + * the bit location of the VIP_CLKC_RST register which is + * connected to VIP_PARSER + */ + vip_module_toggle(dev, VIP_DP_RST, false); + + /* 10. (Delay) */ + /* 11. SC coeff downloaded (if VIP_SCALER is being used) */ + vip_setup_scaler(stream); + + /* 12. (Delay) */ + /* the above are not needed here yet */ + + populate_desc_list(stream); + stream->num_recovery++; + if (stream->num_recovery < 5) { + /* Reload the vpdma */ + vip_load_vpdma_list_fifo(stream); + + enable_irqs(dev, dev->slice_id, stream->list_num); + vip_schedule_next_buffer(stream); + + /* 13. Clear VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMM */ + /* 14. Clear VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMM */ + + vip_parser_stop_imm(port, 0); + + /* 15. Set VIP_PORT_A[8] ENABLE */ + /* + * 16. Clear VIP_PORT_A[7] CLR_ASYNC_FIFO_RD + * Clear VIP_PORT_A[6] CLR_ASYNC_FIFO_WR + */ + vip_enable_parser(port, true); + } else { + v4l2_err(&dev->v4l2_dev, "%s: num_recovery limit exceeded leaving disabl= ed\n", + __func__); + } +} + +static void handle_parser_irqs(struct vip_dev *dev) +{ + struct vip_parser_data *parser =3D dev->parser; + struct vip_port *porta =3D dev->ports[VIP_PORTA]; + struct vip_port *portb =3D dev->ports[VIP_PORTB]; + struct vip_stream *stream =3D NULL; + u32 irq_stat =3D reg_read(parser, VIP_PARSER_FIQ_STATUS); + int i; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: FIQ_STATUS: 0x%08x\n", __func__, = irq_stat); + + /* Clear all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_CLR, irq_stat); + reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0); + + #ifdef DEBUG + if (irq_stat & VIP_PORTA_VDET) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_VDET\n"); + if (irq_stat & VIP_PORTB_VDET) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_VDET\n"); + if (irq_stat & VIP_PORTA_ASYNC_FIFO_OF) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_ASYNC_FIFO_OF\n"); + if (irq_stat & VIP_PORTB_ASYNC_FIFO_OF) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_ASYNC_FIFO_OF\n"); + if (irq_stat & VIP_PORTA_OUTPUT_FIFO_YUV) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_YUV\n"); + if (irq_stat & VIP_PORTA_OUTPUT_FIFO_ANC) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_ANC\n"); + if (irq_stat & VIP_PORTB_OUTPUT_FIFO_YUV) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_YUV\n"); + if (irq_stat & VIP_PORTB_OUTPUT_FIFO_ANC) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_ANC\n"); + if (irq_stat & VIP_PORTA_CONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CONN\n"); + if (irq_stat & VIP_PORTA_DISCONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_DISCONN\n"); + if (irq_stat & VIP_PORTB_CONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CONN\n"); + if (irq_stat & VIP_PORTB_DISCONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_DISCONN\n"); + if (irq_stat & VIP_PORTA_SRC0_SIZE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_SRC0_SIZE\n"); + if (irq_stat & VIP_PORTB_SRC0_SIZE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_SRC0_SIZE\n"); + if (irq_stat & VIP_PORTA_YUV_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_YUV_PROTO_VIOLATION\n"); + if (irq_stat & VIP_PORTA_ANC_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_ANC_PROTO_VIOLATION\n"); + if (irq_stat & VIP_PORTB_YUV_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_YUV_PROTO_VIOLATION\n"); + if (irq_stat & VIP_PORTB_ANC_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_ANC_PROTO_VIOLATION\n"); + if (irq_stat & VIP_PORTA_CFG_DISABLE_COMPLETE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CFG_DISABLE_COMPLETE\n"); + if (irq_stat & VIP_PORTB_CFG_DISABLE_COMPLETE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CFG_DISABLE_COMPLETE\n"); + #endif + + if (irq_stat & (VIP_PORTA_ASYNC_FIFO_OF | + VIP_PORTA_OUTPUT_FIFO_YUV | + VIP_PORTA_OUTPUT_FIFO_ANC)) { + for (i =3D 0; i < VIP_CAP_STREAMS_PER_PORT; i++) { + if (porta->cap_streams[i] && + porta->cap_streams[i]->port->port_id =3D=3D + porta->port_id) { + stream =3D porta->cap_streams[i]; + break; + } + } + if (stream) { + disable_irqs(dev, dev->slice_id, + stream->list_num); + schedule_work(&stream->recovery_work); + return; + } + } + if (irq_stat & (VIP_PORTB_ASYNC_FIFO_OF | + VIP_PORTB_OUTPUT_FIFO_YUV | + VIP_PORTB_OUTPUT_FIFO_ANC)) { + for (i =3D 0; i < VIP_CAP_STREAMS_PER_PORT; i++) { + if (portb->cap_streams[i] && + portb->cap_streams[i]->port->port_id =3D=3D + portb->port_id) { + stream =3D portb->cap_streams[i]; + break; + } + } + if (stream) { + disable_irqs(dev, dev->slice_id, + stream->list_num); + schedule_work(&stream->recovery_work); + return; + } + } +} + +static irqreturn_t vip_irq(int irq_vip, void *data) +{ + struct vip_dev *dev =3D (struct vip_dev *)data; + struct vpdma_data *vpdma; + struct vip_stream *stream; + int list_num; + int irq_num =3D dev->slice_id; + u32 irqst, irqst_saved, reg_addr; + + if (!dev->shared) + return IRQ_HANDLED; + + vpdma =3D dev->shared->vpdma; + reg_addr =3D VIP_INT0_STATUS0 + + VIP_INTC_INTX_OFFSET * irq_num; + irqst_saved =3D reg_read(dev->shared, reg_addr); + irqst =3D irqst_saved; + + v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n", + irq_vip, irq_num, irqst); + if (irqst) { + if (irqst & (VIP_VIP1_PARSER_INT << (irq_num * 1))) { + irqst &=3D ~(VIP_VIP1_PARSER_INT << (irq_num * 1)); + handle_parser_irqs(dev); + } + + for (list_num =3D 0; irqst && (list_num < 8); list_num++) { + /* Check for LIST_COMPLETE IRQ */ + if (!(irqst & (1 << list_num * 2))) + continue; + + v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d: handling LIST%d_COMPLETE\n", + irq_num, list_num); + + stream =3D vpdma_hwlist_get_priv(vpdma, list_num); + if (!stream || stream->list_num !=3D list_num) { + v4l2_err(&dev->v4l2_dev, "IRQ occurred for unused list"); + continue; + } + + vpdma_clear_list_stat(vpdma, irq_num, list_num); + + vip_process_buffer_complete(stream); + + vip_schedule_next_buffer(stream); + + irqst &=3D ~((1 << list_num * 2)); + } + } + + /* Acknowledge that we are done with all interrupts */ + reg_write(dev->shared, VIP_INTC_E0I, 1 << irq_num); + + /* Clear handled events from status register */ + reg_addr =3D VIP_INT0_STATUS0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + reg_write(dev->shared, reg_addr, irqst_saved); + + return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int vip_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, VIP_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card)); + return 0; +} + +static int vip_enuminput(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vip_stream *stream =3D file2stream(file); + + if (inp->index) + return -EINVAL; + + inp->type =3D V4L2_INPUT_TYPE_CAMERA; + inp->std =3D stream->vfd->tvnorms; + sprintf(inp->name, "Camera %u", stream->vfd->num); + + return 0; +} + +static int vip_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i =3D 0; + return 0; +} + +static int vip_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i !=3D 0) + return -EINVAL; + return 0; +} + +static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + + *std =3D stream->vfd->tvnorms; + v4l2_subdev_call(port->subdev, video, querystd, std); + return 0; +} + +static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + + *std =3D stream->vfd->tvnorms; + v4l2_subdev_call(port->subdev, video, g_std, std); + + return 0; +} + +static int vip_s_std(struct file *file, void *fh, v4l2_std_id std) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + + if (stream->vfd->tvnorms =3D=3D std) + return 0; + + if (vb2_is_busy(&stream->vb_vidq)) + return -EBUSY; + + if (!(std & stream->vfd->tvnorms)) + return -EINVAL; + + v4l2_subdev_call(port->subdev, video, s_std, std); + return 0; +} + +static int vip_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_fmt *fmt; + + if (f->index >=3D port->num_active_fmt) + return -EINVAL; + + fmt =3D port->active_fmt[f->index]; + f->pixelformat =3D fmt->fourcc; + + return 0; +} + +static int vip_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vip_fmt *fmt; + int ret; + struct v4l2_subdev_frame_size_enum fse =3D { + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + .pad =3D 0, + }; + + fmt =3D find_port_format_by_pix(port, f->pixel_format); + if (!fmt) + return -EINVAL; + + fse.index =3D f->index; + fse.code =3D fmt->code; + ret =3D v4l2_subdev_call(port->subdev, pad, enum_frame_size, NULL, &fse); + if (ret) + return -EINVAL; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: index: %d code: %x W:[%d,%d] H:[%= d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + f->type =3D V4L2_FRMSIZE_TYPE_DISCRETE; + f->discrete.width =3D fse.max_width; + f->discrete.height =3D fse.max_height; + + return 0; +} + +static int vip_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie =3D { + .index =3D f->index, + .width =3D f->width, + .height =3D f->height, + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt =3D find_port_format_by_pix(port, f->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code =3D fmt->code; + ret =3D v4l2_subdev_call(port->subdev, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + f->type =3D V4L2_FRMIVAL_TYPE_DISCRETE; + f->discrete =3D fie.interval; + + return 0; +} + +static int vip_g_parm(struct file *file, void *fh, struct v4l2_streamparm = *a) +{ + struct vip_stream *stream =3D video_drvdata(file); + struct vip_port *port =3D stream->port; + + return v4l2_g_parm_cap(video_devdata(file), port->subdev, a); +} + +static int vip_s_parm(struct file *file, void *fh, struct v4l2_streamparm = *a) +{ + struct vip_stream *stream =3D video_drvdata(file); + struct vip_port *port =3D stream->port; + + return v4l2_s_parm_cap(video_devdata(file), port->subdev, a); +} + +static int vip_calc_format_size(struct vip_port *port, + struct vip_fmt *fmt, + struct v4l2_format *f) +{ + enum v4l2_field *field; + unsigned int stride; + struct vip_dev *dev =3D port->dev; + + if (!fmt) { + v4l2_dbg(2, debug, &dev->v4l2_dev, + "no vip_fmt format provided!\n"); + return -EINVAL; + } + + field =3D &f->fmt.pix.field; + if (*field =3D=3D V4L2_FIELD_ANY) + *field =3D V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE !=3D *field && V4L2_FIELD_ALTERNATE !=3D *field) + return -EINVAL; + + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, W_ALIGN, + &f->fmt.pix.height, MIN_H, MAX_H, H_ALIGN, + S_ALIGN); + + stride =3D f->fmt.pix.width * (fmt->vpdma_fmt[0]->depth >> 3); + f->fmt.pix.bytesperline =3D ALIGN(stride, VPDMA_STRIDE_ALIGN); + + f->fmt.pix.sizeimage =3D f->fmt.pix.height * f->fmt.pix.bytesperline; + if (fmt->coplanar) { + f->fmt.pix.sizeimage +=3D f->fmt.pix.height * + f->fmt.pix.bytesperline * + fmt->vpdma_fmt[VIP_CHROMA]->depth >> 3; + } + + f->fmt.pix.colorspace =3D V4L2_COLORSPACE_SRGB; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "calc_format_size: fourcc:%s size: %dx= %d bpl:%d img_size:%d\n", + fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static inline bool vip_is_size_dma_aligned(u32 bpp, u32 width) +{ + return ((width * bpp) =3D=3D ALIGN(width * bpp, VPDMA_STRIDE_ALIGN)); +} + +static int vip_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vip_fmt *fmt; + u32 best_width, best_height, largest_width, largest_height; + int ret, found; + enum vip_csc_state csc_direction; + struct v4l2_subdev_frame_size_enum fse =3D { + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + .pad =3D 0, + }; + + fmt =3D find_port_format_by_pix(port, f->fmt.pix.pixelformat); + if (!fmt) { + /* Just get the first one enumerated */ + fmt =3D port->active_fmt[0]; + f->fmt.pix.pixelformat =3D fmt->fourcc; + } + + csc_direction =3D vip_csc_direction(fmt->code, fmt->finfo); + if (csc_direction !=3D VIP_CSC_NA) { + if (!is_csc_available(port)) { + v4l2_dbg(2, debug, &dev->v4l2_dev, + "CSC not available for Fourcc format (0x%08x).\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt =3D port->active_fmt[0]; + f->fmt.pix.pixelformat =3D fmt->fourcc; + /* re-evaluate the csc_direction here */ + csc_direction =3D vip_csc_direction(fmt->code, + fmt->finfo); + } else { + v4l2_dbg(3, debug, &dev->v4l2_dev, "CSC active on Port %c: going %s\n", + port->port_id =3D=3D VIP_PORTA ? 'A' : 'B', + (csc_direction =3D=3D VIP_CSC_Y2R) ? "Y2R" : "R2Y"); + } + } + + /* + * Given that sensors might support multiple mbus code we need + * to use the one that matches the requested pixel format + */ + port->try_mbus_framefmt =3D port->mbus_framefmt; + port->try_mbus_framefmt.code =3D fmt->code; + + /* check for/find a valid width/height */ + ret =3D 0; + found =3D false; + best_width =3D 0; + best_height =3D 0; + largest_width =3D 0; + largest_height =3D 0; + + fse.code =3D fmt->code; + for (fse.index =3D 0; ; fse.index++) { + u32 bpp =3D fmt->vpdma_fmt[0]->depth >> 3; + + ret =3D v4l2_subdev_call(port->subdev, pad, + enum_frame_size, NULL, &fse); + if (ret) + break; + + if (!vip_is_size_dma_aligned(bpp, fse.max_width)) + continue; + + if (fse.max_width >=3D largest_width && + fse.max_height >=3D largest_height) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new larger: %= dx%d\n", + fse.index, fse.max_width, fse.max_height); + largest_width =3D fse.max_width; + largest_height =3D fse.max_height; + } + + if (fse.max_width >=3D f->fmt.pix.width && + fse.max_height >=3D f->fmt.pix.height) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found at least larg= er: %dx%d\n", + fse.index, fse.max_width, fse.max_height); + + if (!best_width || + ((abs(best_width - f->fmt.pix.width) >=3D + abs(fse.max_width - f->fmt.pix.width)) && + (abs(best_height - f->fmt.pix.height) >=3D + abs(fse.max_height - f->fmt.pix.height)))) { + best_width =3D fse.max_width; + best_height =3D fse.max_height; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new best: %d= x%d\n", + fse.index, fse.max_width, + fse.max_height); + } + } + + if (f->fmt.pix.width =3D=3D fse.max_width && + f->fmt.pix.height =3D=3D fse.max_height) { + found =3D true; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct match:= %dx%d\n", + fse.index, fse.max_width, + fse.max_height); + break; + } + + if (f->fmt.pix.width >=3D fse.min_width && + f->fmt.pix.width <=3D fse.max_width && + f->fmt.pix.height >=3D fse.min_height && + f->fmt.pix.height <=3D fse.max_height) { + found =3D true; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct range = match: %dx%d\n", + fse.index, fse.max_width, + fse.max_height); + break; + } + } + + if (found) { + port->try_mbus_framefmt.width =3D f->fmt.pix.width; + port->try_mbus_framefmt.height =3D f->fmt.pix.height; + /* No need to check for scaling */ + goto calc_size; + } else if (f->fmt.pix.width > largest_width) { + port->try_mbus_framefmt.width =3D largest_width; + port->try_mbus_framefmt.height =3D largest_height; + } else if (best_width) { + port->try_mbus_framefmt.width =3D best_width; + port->try_mbus_framefmt.height =3D best_height; + } else { + /* use existing values as default */ + } + + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt best subdev size: %dx%d\n", + port->try_mbus_framefmt.width, + port->try_mbus_framefmt.height); + + if (is_scaler_available(port) && + csc_direction !=3D VIP_CSC_Y2R && + !vip_is_mbuscode_raw(fmt->code) && + f->fmt.pix.height <=3D port->try_mbus_framefmt.height && + port->try_mbus_framefmt.height <=3D SC_MAX_PIXEL_HEIGHT && + port->try_mbus_framefmt.width <=3D SC_MAX_PIXEL_WIDTH) { + /* + * Scaler is only accessible if the dst colorspace is YUV. + * As the input to the scaler must be in YUV mode only. + * + * Scaling up is allowed only horizontally. + */ + unsigned int hratio, vratio, width_align, height_align; + u32 bpp =3D fmt->vpdma_fmt[0]->depth >> 3; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler active on Port %c: requesting= %dx%d\n", + port->port_id =3D=3D VIP_PORTA ? 'A' : 'B', + f->fmt.pix.width, f->fmt.pix.height); + + /* Just make sure everything is properly aligned */ + width_align =3D ALIGN(f->fmt.pix.width * bpp, VPDMA_STRIDE_ALIGN); + width_align /=3D bpp; + height_align =3D ALIGN(f->fmt.pix.height, 2); + + f->fmt.pix.width =3D width_align; + f->fmt.pix.height =3D height_align; + + hratio =3D f->fmt.pix.width * 1000 / + port->try_mbus_framefmt.width; + vratio =3D f->fmt.pix.height * 1000 / + port->try_mbus_framefmt.height; + if (hratio < 125) { + f->fmt.pix.width =3D port->try_mbus_framefmt.width / 8; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Horizontal scaling ratio out of ran= ge adjusting -> %d\n", + f->fmt.pix.width); + } + + if (vratio < 188) { + f->fmt.pix.height =3D port->try_mbus_framefmt.height / 4; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Vertical scaling ratio out of range= adjusting -> %d\n", + f->fmt.pix.height); + } + v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler: got %dx%d\n", + f->fmt.pix.width, f->fmt.pix.height); + } else { + /* use existing values as default */ + f->fmt.pix.width =3D port->try_mbus_framefmt.width; + f->fmt.pix.height =3D port->try_mbus_framefmt.height; + } + +calc_size: + /* That we have a fmt calculate imagesize and bytesperline */ + return vip_calc_format_size(port, fmt, f); +} + +static int vip_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + + /* Use last known values or defaults */ + f->fmt.pix.width =3D stream->width; + f->fmt.pix.height =3D stream->height; + f->fmt.pix.pixelformat =3D port->fmt->fourcc; + f->fmt.pix.field =3D stream->sup_field; + f->fmt.pix.colorspace =3D V4L2_COLORSPACE_SRGB; + f->fmt.pix.bytesperline =3D stream->bytesperline; + f->fmt.pix.sizeimage =3D stream->sizeimage; + + return 0; +} + +static int vip_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct v4l2_subdev_format sfmt; + struct v4l2_mbus_framefmt *mf; + enum vip_csc_state csc_direction; + int ret; + + if (vb2_is_busy(&stream->vb_vidq)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret =3D vip_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + /* + * Check if we need the scaler or not + * + * Since on previous S_FMT call the scaler might have been + * allocated if it is not needed in this instance we will + * attempt to free it just in case. + * + * free_scaler() is harmless unless the current port + * allocated it. + */ + if (f->fmt.pix.width =3D=3D port->try_mbus_framefmt.width && + f->fmt.pix.height =3D=3D port->try_mbus_framefmt.height) + free_scaler(port); + else + allocate_scaler(port); + + port->fmt =3D find_port_format_by_pix(port, + f->fmt.pix.pixelformat); + stream->width =3D f->fmt.pix.width; + stream->height =3D f->fmt.pix.height; + stream->bytesperline =3D f->fmt.pix.bytesperline; + stream->sizeimage =3D f->fmt.pix.sizeimage; + stream->sup_field =3D f->fmt.pix.field; + stream->field =3D f->fmt.pix.field; + + port->c_rect.left =3D 0; + port->c_rect.top =3D 0; + port->c_rect.width =3D stream->width; + port->c_rect.height =3D stream->height; + + /* + * Check if we need the csc unit or not + * + * Since on previous S_FMT call, the csc might have been + * allocated if it is not needed in this instance we will + * attempt to free it just in case. + * + * free_csc() is harmless unless the current port + * allocated it. + */ + csc_direction =3D vip_csc_direction(port->fmt->code, port->fmt->finfo); + if (csc_direction =3D=3D VIP_CSC_NA) + free_csc(port); + else + allocate_csc(port, csc_direction); + + if (stream->sup_field =3D=3D V4L2_FIELD_ALTERNATE) + port->flags |=3D FLAG_INTERLACED; + else + port->flags &=3D ~FLAG_INTERLACED; + + memset(&sfmt, 0, sizeof(sfmt)); + mf =3D &sfmt.format; + v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code); + /* Make sure to use the subdev size found in the try_fmt */ + mf->width =3D port->try_mbus_framefmt.width; + mf->height =3D port->try_mbus_framefmt.height; + + sfmt.which =3D V4L2_SUBDEV_FORMAT_ACTIVE; + sfmt.pad =3D 0; + ret =3D v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt); + if (ret) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "set_fmt failed in subdev\n"); + return ret; + } + + /* Save it */ + port->mbus_framefmt =3D *mf; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "s_fmt subdev fmt mbus_code: %04X size= : %dx%d\n", + port->mbus_framefmt.code, + port->mbus_framefmt.width, port->mbus_framefmt.height); + + return 0; +} + +/* + * Does the exact opposite of set_fmt_params + * It makes sure the DataPath register is sane after tear down + */ +static void unset_fmt_params(struct vip_stream *stream) +{ + struct vip_dev *dev =3D stream->port->dev; + struct vip_port *port =3D stream->port; + + stream->sequence =3D 0; + stream->field =3D V4L2_FIELD_TOP; + + if (port->csc =3D=3D VIP_CSC_Y2R) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + /* We are done */ + return; + } else if (port->csc =3D=3D VIP_CSC_R2Y) { + if (port->scaler && port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } + } else if (port->scaler) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } + } else if (port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } + } else { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } + } + /* We are done */ + return; + } else if (v4l2_is_format_rgb(port->fmt->finfo)) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + /* We are done */ + return; + } + + if (port->scaler && port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + } + } else if (port->scaler) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } + } else if (port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + } else { + /* + * We undo all data path setting except for the multi + * stream case. + * Because we cannot disrupt other on-going capture if only + * one stream is terminated the other might still be going + */ + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } +} + +/* + * Set the registers that are modified when the video format changes. + */ +static void set_fmt_params(struct vip_stream *stream) +{ + struct vip_dev *dev =3D stream->port->dev; + struct vip_port *port =3D stream->port; + + stream->sequence =3D 0; + stream->field =3D V4L2_FIELD_TOP; + + if (port->csc =3D=3D VIP_CSC_Y2R) { + port->flags &=3D ~FLAG_MULT_PORT; + /* Set alpha component in background color */ + vpdma_set_bg_color(dev->shared->vpdma, + (struct vpdma_data_format *) + port->fmt->vpdma_fmt[0], + 0xff); + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 1); + } else { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1); + } + /* We are done */ + return; + } else if (port->csc =3D=3D VIP_CSC_R2Y) { + port->flags &=3D ~FLAG_MULT_PORT; + if (port->scaler && port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, + VIP_SC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 1); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } else { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + } + } else if (port->scaler) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, + VIP_SC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 1); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } else { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + } + } else if (port->fmt->coplanar) { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 2); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } else { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + } + } else { + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, + 2); + vip_set_slice_path(dev, + VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, + VIP_RGB_OUT_HI_DATA_SELECT, + 0); + } else { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + } + } + /* We are done */ + return; + } else if (v4l2_is_format_rgb(port->fmt->finfo)) { + port->flags &=3D ~FLAG_MULT_PORT; + /* Set alpha component in background color */ + vpdma_set_bg_color(dev->shared->vpdma, + (struct vpdma_data_format *) + port->fmt->vpdma_fmt[0], + 0xff); + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1); + } else { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + } + /* We are done */ + return; + } + + if (port->scaler && port->fmt->coplanar) { + port->flags &=3D ~FLAG_MULT_PORT; + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + } + } else if (port->scaler) { + port->flags &=3D ~FLAG_MULT_PORT; + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } + } else if (port->fmt->coplanar) { + port->flags &=3D ~FLAG_MULT_PORT; + if (port->port_id =3D=3D VIP_PORTA) { + vip_set_slice_path(dev, + VIP_CHR_DS_1_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, + VIP_CHR_DS_2_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, + VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + } else { + port->flags |=3D FLAG_MULT_PORT; + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } +} + +static int vip_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + if (s->type !=3D V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + struct vip_stream *stream =3D file2stream(file); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left =3D 0; + s->r.top =3D 0; + s->r.width =3D stream->width; + s->r.height =3D stream->height; + return 0; + + case V4L2_SEL_TGT_CROP: + s->r =3D stream->port->c_rect; + return 0; + } + + return -EINVAL; +} + +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int vip_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + if (s->type !=3D V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target !=3D V4L2_SEL_TGT_CROP) + return -EINVAL; + + struct vip_stream *stream =3D file2stream(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct v4l2_rect r =3D s->r; + + v4l_bound_align_image(&r.width, 0, stream->width, 0, + &r.height, 0, stream->height, 0, 0); + + r.left =3D clamp_t(unsigned int, r.left, 0, stream->width - r.width); + r.top =3D clamp_t(unsigned int, r.top, 0, stream->height - r.height); + + if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + return -ERANGE; + + s->r =3D r; + stream->port->c_rect =3D r; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "cropped (%d,%d)/%dx%d of %dx%d\n", + r.left, r.top, r.width, r.height, + stream->width, stream->height); + + return 0; +} + +static const struct v4l2_ioctl_ops vip_ioctl_ops =3D { + .vidioc_querycap =3D vip_querycap, + .vidioc_enum_input =3D vip_enuminput, + .vidioc_g_input =3D vip_g_input, + .vidioc_s_input =3D vip_s_input, + + .vidioc_querystd =3D vip_querystd, + .vidioc_g_std =3D vip_g_std, + .vidioc_s_std =3D vip_s_std, + + .vidioc_enum_fmt_vid_cap =3D vip_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap =3D vip_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap =3D vip_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap =3D vip_s_fmt_vid_cap, + + .vidioc_enum_frameintervals =3D vip_enum_frameintervals, + .vidioc_enum_framesizes =3D vip_enum_framesizes, + .vidioc_s_parm =3D vip_s_parm, + .vidioc_g_parm =3D vip_g_parm, + .vidioc_g_selection =3D vip_g_selection, + .vidioc_s_selection =3D vip_s_selection, + .vidioc_reqbufs =3D vb2_ioctl_reqbufs, + .vidioc_create_bufs =3D vb2_ioctl_create_bufs, + .vidioc_prepare_buf =3D vb2_ioctl_prepare_buf, + .vidioc_querybuf =3D vb2_ioctl_querybuf, + .vidioc_qbuf =3D vb2_ioctl_qbuf, + .vidioc_dqbuf =3D vb2_ioctl_dqbuf, + .vidioc_expbuf =3D vb2_ioctl_expbuf, + + .vidioc_streamon =3D vb2_ioctl_streamon, + .vidioc_streamoff =3D vb2_ioctl_streamoff, + .vidioc_log_status =3D v4l2_ctrl_log_status, + .vidioc_subscribe_event =3D v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event =3D v4l2_event_unsubscribe, +}; + +/* + * Videobuf operations + */ +static int vip_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vq); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + unsigned int size =3D stream->sizeimage; + + if (vq->max_num_buffers + *nbuffers < 3) + *nbuffers =3D 3 - vq->max_num_buffers; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size =3D sizes[0]; + } + + *nplanes =3D 1; + sizes[0] =3D size; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "get %d buffer(s) of size %d each.\n", + *nbuffers, sizes[0]); + + return 0; +} + +static int vip_buf_prepare(struct vb2_buffer *vb) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vb->vb2_queue); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + + if (vb2_plane_size(vb, 0) < stream->sizeimage) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)stream->sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, stream->sizeimage); + + return 0; +} + +static void vip_buf_queue(struct vb2_buffer *vb) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vb->vb2_queue); + struct vip_dev *dev =3D stream->port->dev; + struct vip_buffer *buf =3D container_of(vb, struct vip_buffer, + vb.vb2_buf); + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &stream->vidq); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void return_buffers(struct vb2_queue *vq, int state) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vq); + struct vip_dev *dev =3D stream->port->dev; + struct vip_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + /* release all active buffers */ + while (!list_empty(&stream->post_bufs)) { + buf =3D list_entry(stream->post_bufs.next, + struct vip_buffer, list); + list_del(&buf->list); + if (buf->drop =3D=3D 1) + list_add_tail(&buf->list, &stream->dropq); + else + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + while (!list_empty(&stream->vidq)) { + buf =3D list_entry(stream->vidq.next, struct vip_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + INIT_LIST_HEAD(&stream->post_bufs); + INIT_LIST_HEAD(&stream->vidq); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +static int vip_setup_scaler(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct sc_data *sc =3D dev->sc; + struct csc_data *csc =3D dev->csc; + struct vpdma_data *vpdma =3D dev->shared->vpdma; + struct vip_mmr_adb *mmr_adb =3D port->mmr_adb.addr; + int list_num =3D stream->list_num; + int timeout =3D 500; + struct v4l2_format dst_f; + struct v4l2_format src_f; + + memset(&src_f, 0, sizeof(src_f)); + src_f.type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_fill_pix_format(&src_f.fmt.pix, &port->mbus_framefmt); + src_f.fmt.pix.pixelformat =3D vip_mbus_code_to_fourcc(port->fmt->code); + + dst_f =3D src_f; + dst_f.fmt.pix.pixelformat =3D port->fmt->fourcc; + dst_f.fmt.pix.width =3D stream->width; + dst_f.fmt.pix.height =3D stream->height; + + /* if scaler not associated with this port then skip */ + if (port->scaler) { + sc_set_hs_coeffs(sc, port->sc_coeff_h.addr, + port->mbus_framefmt.width, + port->c_rect.width); + sc_set_vs_coeffs(sc, port->sc_coeff_v.addr, + port->mbus_framefmt.height, + port->c_rect.height); + sc_config_scaler(sc, &mmr_adb->sc_regs0[0], + &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], + port->mbus_framefmt.width, + port->mbus_framefmt.height, + port->c_rect.width, + port->c_rect.height); + port->load_mmrs =3D true; + } + + /* if csc not associated with this port then skip */ + if (port->csc) { + csc_set_coeff(csc, &mmr_adb->csc_regs[0], + &src_f, &dst_f); + + port->load_mmrs =3D true; + } + + /* If coeff are already loaded then skip */ + if (!sc->load_coeff_v && !sc->load_coeff_h && !port->load_mmrs) + return 0; + + if (vpdma_list_busy(vpdma, list_num)) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: List %d is busy\n", + __func__, list_num); + } + + /* Make sure we start with a clean list */ + vpdma_reset_desc_list(&stream->desc_list); + + /* config descriptors */ + if (port->load_mmrs) { + vpdma_map_desc_buf(vpdma, &port->mmr_adb); + vpdma_add_cfd_adb(&stream->desc_list, CFD_MMR_CLIENT, + &port->mmr_adb); + + port->load_mmrs =3D false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added mmr_adb config desc\n"); + } + + if (sc->loaded_coeff_h !=3D port->sc_coeff_h.dma_addr || + sc->load_coeff_h) { + vpdma_map_desc_buf(vpdma, &port->sc_coeff_h); + vpdma_add_cfd_block(&stream->desc_list, + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, + &port->sc_coeff_h, 0); + + sc->loaded_coeff_h =3D port->sc_coeff_h.dma_addr; + sc->load_coeff_h =3D false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_h config desc\n"); + } + + if (sc->loaded_coeff_v !=3D port->sc_coeff_v.dma_addr || + sc->load_coeff_v) { + vpdma_map_desc_buf(vpdma, &port->sc_coeff_v); + vpdma_add_cfd_block(&stream->desc_list, + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, + &port->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + + sc->loaded_coeff_v =3D port->sc_coeff_v.dma_addr; + sc->load_coeff_v =3D false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_v config desc\n"); + } + v4l2_dbg(3, debug, stream, "CFD_SC_CLIENT %d slice_id: %d\n", + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, dev->slice_id); + + vpdma_map_desc_buf(vpdma, &stream->desc_list.buf); + v4l2_dbg(3, debug, &dev->v4l2_dev, "Submitting desc on list# %d\n", list_= num); + vpdma_submit_descs(vpdma, &stream->desc_list, list_num); + + while (vpdma_list_busy(vpdma, list_num) && timeout--) + usleep_range(1000, 1100); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->mmr_adb); + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_h); + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_v); + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + + vpdma_reset_desc_list(&stream->desc_list); + + if (timeout <=3D 0) { + v4l2_err(&dev->v4l2_dev, "Timed out setting up scaler through VPDMA list= \n"); + return -EBUSY; + } + + return 0; +} + +static int vip_load_vpdma_list_fifo(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vpdma_data *vpdma =3D dev->shared->vpdma; + int list_num =3D stream->list_num; + struct vip_buffer *buf; + unsigned long flags; + int timeout, i; + + if (vpdma_list_busy(dev->shared->vpdma, stream->list_num)) + return -EBUSY; + + for (i =3D 0; i < VIP_VPDMA_FIFO_SIZE; i++) { + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&stream->vidq)) { + v4l2_err(&dev->v4l2_dev, "No buffer left!"); + spin_unlock_irqrestore(&dev->slock, flags); + return -EINVAL; + } + + buf =3D list_entry(stream->vidq.next, + struct vip_buffer, list); + buf->drop =3D false; + + list_move_tail(&buf->list, &stream->post_bufs); + spin_unlock_irqrestore(&dev->slock, flags); + + v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: start_dma vb2 buf idx:%d\n", + __func__, buf->vb.vb2_buf.index); + start_dma(stream, buf); + + timeout =3D 500; + while (vpdma_list_busy(vpdma, list_num) && timeout--) + usleep_range(1000, 1100); + + if (timeout <=3D 0) { + v4l2_err(&dev->v4l2_dev, "Timed out loading VPDMA list fifo\n"); + return -EBUSY; + } + } + return 0; +} + +static int vip_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vq); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + int ret; + + ret =3D vip_setup_scaler(stream); + if (ret) + goto err; + + /* + * Make sure the scaler is configured before the datapath is + * enabled. The scaler can only load the coefficient + * parameters when it is idle. If the scaler path is enabled + * and video data is being received then the VPDMA transfer will + * stall indefinetely. + */ + set_fmt_params(stream); + ret =3D vip_setup_parser(port); + if (ret) + goto err; + + if (port->subdev) { + ret =3D v4l2_subdev_call(port->subdev, video, s_stream, 1); + if (ret) { + v4l2_err(&dev->v4l2_dev, "stream on failed in subdev\n"); + goto err; + } + } + + stream->sequence =3D 0; + stream->field =3D V4L2_FIELD_TOP; + populate_desc_list(stream); + + ret =3D vip_load_vpdma_list_fifo(stream); + if (ret) + goto err; + + stream->num_recovery =3D 0; + + clear_irqs(dev, dev->slice_id, stream->list_num); + enable_irqs(dev, dev->slice_id, stream->list_num); + vip_schedule_next_buffer(stream); + vip_parser_stop_imm(port, false); + vip_enable_parser(port, true); + + return 0; + +err: + return_buffers(vq, VB2_BUF_STATE_QUEUED); + return ret; +} + +/* + * Abort streaming and wait for last buffer + */ +static void vip_stop_streaming(struct vb2_queue *vq) +{ + struct vip_stream *stream =3D vb2_get_drv_priv(vq); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + int ret; + + vip_parser_stop_imm(port, true); + vip_enable_parser(port, false); + unset_fmt_params(stream); + + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + + if (port->subdev) { + ret =3D v4l2_subdev_call(port->subdev, video, s_stream, 0); + if (ret) + v4l2_err(&dev->v4l2_dev, "Failed to stop subdev stream"); + } + + stop_dma(stream, true); + + return_buffers(vq, VB2_BUF_STATE_ERROR); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + vpdma_reset_desc_list(&stream->desc_list); +} + +static const struct vb2_ops vip_video_qops =3D { + .queue_setup =3D vip_queue_setup, + .buf_prepare =3D vip_buf_prepare, + .buf_queue =3D vip_buf_queue, + .start_streaming =3D vip_start_streaming, + .stop_streaming =3D vip_stop_streaming, +}; + +static int vip_init_dev(struct vip_dev *dev) +{ + if (dev->num_ports !=3D 0) + goto done; + + vip_set_clock_enable(dev, 1); + vip_module_toggle(dev, VIP_SC_RST, false); + vip_module_toggle(dev, VIP_CSC_RST, false); +done: + dev->num_ports++; + + return 0; +} + +static inline bool is_scaler_available(struct vip_port *port) +{ + if (port->endpoint.bus_type =3D=3D V4L2_MBUS_PARALLEL) + if (port->dev->sc_assigned =3D=3D VIP_NOT_ASSIGNED || + port->dev->sc_assigned =3D=3D port->port_id) + return true; + return false; +} + +static inline bool allocate_scaler(struct vip_port *port) +{ + if (is_scaler_available(port)) { + if (port->dev->sc_assigned =3D=3D VIP_NOT_ASSIGNED || + port->dev->sc_assigned =3D=3D port->port_id) { + port->dev->sc_assigned =3D port->port_id; + port->scaler =3D true; + return true; + } + } + return false; +} + +static inline void free_scaler(struct vip_port *port) +{ + if (port->dev->sc_assigned =3D=3D port->port_id) { + port->dev->sc_assigned =3D VIP_NOT_ASSIGNED; + port->scaler =3D false; + } +} + +static bool is_csc_available(struct vip_port *port) +{ + if (port->endpoint.bus_type =3D=3D V4L2_MBUS_PARALLEL) + if (port->dev->csc_assigned =3D=3D VIP_NOT_ASSIGNED || + port->dev->csc_assigned =3D=3D port->port_id) + return true; + return false; +} + +static bool allocate_csc(struct vip_port *port, + enum vip_csc_state csc_direction) +{ + struct vip_dev *dev =3D port->dev; + /* Is CSC needed? */ + if (csc_direction !=3D VIP_CSC_NA) { + if (is_csc_available(port)) { + port->dev->csc_assigned =3D port->port_id; + port->csc =3D csc_direction; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc allocated: dir: %d\n", + __func__, csc_direction); + return true; + } + } + return false; +} + +static void free_csc(struct vip_port *port) +{ + struct vip_dev *dev =3D port->dev; + + if (port->dev->csc_assigned =3D=3D port->port_id) { + port->dev->csc_assigned =3D VIP_NOT_ASSIGNED; + port->csc =3D VIP_CSC_NA; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc freed\n", + __func__); + } +} + +static int vip_init_port(struct vip_port *port) +{ + struct vip_dev *dev =3D port->dev; + int ret; + struct vip_fmt *fmt; + struct v4l2_subdev_format sd_fmt =3D { + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + .pad =3D 0, + }; + struct v4l2_mbus_framefmt *mbus_fmt =3D &sd_fmt.format; + + if (port->num_streams !=3D 0) + goto done; + + ret =3D vip_init_dev(port->dev); + if (ret) + goto done; + + /* Get subdevice current frame format */ + ret =3D v4l2_subdev_call(port->subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) + v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port get_fmt failed in subdev: = (%d)\n", + ret); + + /* try to find one that matches */ + fmt =3D find_port_format_by_code(port, mbus_fmt->code); + if (!fmt) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "subdev default mbus_fmt %04x is not = matched.\n", + mbus_fmt->code); + /* if all else fails just pick the first one */ + fmt =3D port->active_fmt[0]; + + mbus_fmt->code =3D fmt->code; + sd_fmt.which =3D V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad =3D 0; + ret =3D v4l2_subdev_call(port->subdev, pad, set_fmt, + NULL, &sd_fmt); + if (ret) + v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port set_fmt failed in subdev:= (%d)\n", + ret); + } + + /* Assign current format */ + port->fmt =3D fmt; + port->mbus_framefmt =3D *mbus_fmt; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: g_mbus_fmt subdev mbus_code: %04X= fourcc:%s size: %dx%d\n", + __func__, fmt->code, + fourcc_to_str(fmt->fourcc), + mbus_fmt->width, mbus_fmt->height); + + if (mbus_fmt->field =3D=3D V4L2_FIELD_ALTERNATE) + port->flags |=3D FLAG_INTERLACED; + else + port->flags &=3D ~FLAG_INTERLACED; + + port->c_rect.left =3D 0; + port->c_rect.top =3D 0; + port->c_rect.width =3D mbus_fmt->width; + port->c_rect.height =3D mbus_fmt->height; + + ret =3D vpdma_alloc_desc_buf(&port->sc_coeff_h, SC_COEF_SRAM_SIZE); + if (ret !=3D 0) + return ret; + + ret =3D vpdma_alloc_desc_buf(&port->sc_coeff_v, SC_COEF_SRAM_SIZE); + if (ret !=3D 0) + goto free_sc_h; + + ret =3D vpdma_alloc_desc_buf(&port->mmr_adb, sizeof(struct vip_mmr_adb)); + if (ret !=3D 0) + goto free_sc_v; + + init_adb_hdrs(port); + + vip_enable_parser(port, false); +done: + port->num_streams++; + return 0; + +free_sc_v: + vpdma_free_desc_buf(&port->sc_coeff_v); +free_sc_h: + vpdma_free_desc_buf(&port->sc_coeff_h); + return ret; +} + +static int vip_init_stream(struct vip_stream *stream) +{ + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + struct vip_fmt *fmt; + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_format f; + int ret; + + ret =3D vip_init_port(port); + if (ret !=3D 0) + return ret; + + fmt =3D port->fmt; + mbus_fmt =3D &port->mbus_framefmt; + + memset(&f, 0, sizeof(f)); + + /* Properly calculate the sizeimage and bytesperline values. */ + v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt); + f.fmt.pix.pixelformat =3D fmt->fourcc; + ret =3D vip_calc_format_size(port, fmt, &f); + if (ret) + return ret; + + stream->width =3D f.fmt.pix.width; + stream->height =3D f.fmt.pix.height; + stream->sup_field =3D f.fmt.pix.field; + stream->bytesperline =3D f.fmt.pix.bytesperline; + stream->sizeimage =3D f.fmt.pix.sizeimage; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream fourcc:%s size: %dx%d bpl= :%d img_size:%d\n", + fourcc_to_str(f.fmt.pix.pixelformat), + f.fmt.pix.width, f.fmt.pix.height, + f.fmt.pix.bytesperline, f.fmt.pix.sizeimage); + v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream vpdma data type: 0x%02X\n= ", + port->fmt->vpdma_fmt[0]->data_type); + + ret =3D vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE, + VPDMA_LIST_TYPE_NORMAL); + + if (ret !=3D 0) + return ret; + + stream->write_desc =3D (struct vpdma_dtd *)stream->desc_list.buf.addr + + 15; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n", + __func__, &stream); + + return 0; +} + +static void vip_release_dev(struct vip_dev *dev) +{ + /* + * On last close, disable clocks to conserve power + */ + + if (--dev->num_ports =3D=3D 0) { + /* reset the scaler module */ + vip_module_toggle(dev, VIP_SC_RST, true); + vip_module_toggle(dev, VIP_CSC_RST, true); + vip_set_clock_enable(dev, 0); + } +} + +static int vip_set_crop_parser(struct vip_port *port) +{ + struct vip_dev *dev =3D port->dev; + struct vip_parser_data *parser =3D dev->parser; + u32 hcrop =3D 0, vcrop =3D 0; + u32 width =3D port->mbus_framefmt.width; + + if (port->fmt->vpdma_fmt[0] =3D=3D &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) { + /* + * Special case since we are faking a YUV422 16bit format + * to have the vpdma perform the needed byte swap + * we need to adjust the pixel width accordingly + * otherwise the parser will attempt to collect more pixels + * then available and the vpdma transfer will exceed the + * allocated frame buffer. + */ + width >>=3D 1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting wi= dth to %d\n", + __func__, width); + } + + /* + * Set Parser Crop parameters to source size otherwise + * scaler and colorspace converter will yield garbage. + */ + hcrop =3D VIP_ACT_BYPASS; + insert_field(&hcrop, 0, VIP_ACT_SKIP_NUMPIX_MASK, + VIP_ACT_SKIP_NUMPIX_SHFT); + insert_field(&hcrop, width, + VIP_ACT_USE_NUMPIX_MASK, VIP_ACT_USE_NUMPIX_SHFT); + reg_write(parser, VIP_PARSER_CROP_H_PORT(port->port_id), hcrop); + + insert_field(&vcrop, 0, VIP_ACT_SKIP_NUMLINES_MASK, + VIP_ACT_SKIP_NUMLINES_SHFT); + insert_field(&vcrop, port->mbus_framefmt.height, + VIP_ACT_USE_NUMLINES_MASK, VIP_ACT_USE_NUMLINES_SHFT); + reg_write(parser, VIP_PARSER_CROP_V_PORT(port->port_id), vcrop); + + return 0; +} + +static int vip_setup_parser(struct vip_port *port) +{ + struct vip_dev *dev =3D port->dev; + struct vip_parser_data *parser =3D dev->parser; + struct v4l2_fwnode_endpoint *endpoint =3D &port->endpoint; + struct vip_bt656_bus *bt656_ep =3D &port->bt656_endpoint; + int iface, sync_type; + u32 flags =3D 0, config0; + + /* Reset the port */ + vip_reset_parser(port, true); + usleep_range(200, 250); + vip_reset_parser(port, false); + + config0 =3D reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (endpoint->bus_type =3D=3D V4L2_MBUS_BT656) { + flags =3D endpoint->bus.parallel.flags; + iface =3D DUAL_8B_INTERFACE; + + /* + * Ideally, this should come from subdev + * port->fmt can be anything once CSC is enabled + */ + if (vip_is_mbuscode_rgb(port->fmt->code)) { + sync_type =3D EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444; + } else { + switch (bt656_ep->num_channels) { + case 4: + sync_type =3D EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422; + break; + case 2: + sync_type =3D EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422; + break; + case 1: + sync_type =3D EMBEDDED_SYNC_SINGLE_YUV422; + break; + default: + sync_type =3D + EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422; + } + if (bt656_ep->pixmux =3D=3D 0) + sync_type =3D + EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422; + } + + } else if (endpoint->bus_type =3D=3D V4L2_MBUS_PARALLEL) { + flags =3D endpoint->bus.parallel.flags; + + switch (endpoint->bus.parallel.bus_width) { + case 24: + iface =3D SINGLE_24B_INTERFACE; + break; + case 16: + iface =3D SINGLE_16B_INTERFACE; + break; + case 8: + default: + iface =3D DUAL_8B_INTERFACE; + } + + if (vip_is_mbuscode_rgb(port->fmt->code)) + sync_type =3D DISCRETE_SYNC_SINGLE_RGB_24B; + else + sync_type =3D DISCRETE_SYNC_SINGLE_YUV422; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + config0 |=3D VIP_HSYNC_POLARITY; + else if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + config0 &=3D ~VIP_HSYNC_POLARITY; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + config0 |=3D VIP_VSYNC_POLARITY; + else if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + config0 &=3D ~VIP_VSYNC_POLARITY; + + config0 &=3D ~VIP_USE_ACTVID_HSYNC_ONLY; + config0 |=3D VIP_ACTVID_POLARITY; + config0 |=3D VIP_DISCRETE_BASIC_MODE; + + } else { + v4l2_err(&dev->v4l2_dev, "Device doesn't support CSI2"); + return -EINVAL; + } + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) { + vip_set_pclk_invert(port); + config0 |=3D VIP_PIXCLK_EDGE_POLARITY; + } else { + config0 &=3D ~VIP_PIXCLK_EDGE_POLARITY; + } + + config0 |=3D ((sync_type & VIP_SYNC_TYPE_MASK) << VIP_SYNC_TYPE_SHFT); + + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); + + vip_set_data_interface(port, iface); + vip_set_crop_parser(port); + + return 0; +} + +static void vip_enable_parser(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev =3D port->dev; + struct vip_parser_data *parser =3D dev->parser; + + config0 =3D reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (on) { + config0 |=3D VIP_PORT_ENABLE; + config0 &=3D ~(VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR); + } else { + config0 &=3D ~VIP_PORT_ENABLE; + config0 |=3D (VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR); + } + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); +} + +static void vip_reset_parser(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev =3D port->dev; + struct vip_parser_data *parser =3D dev->parser; + + config0 =3D reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (on) + config0 |=3D VIP_SW_RESET; + else + config0 &=3D ~VIP_SW_RESET; + + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); +} + +static void vip_parser_stop_imm(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev =3D port->dev; + struct vip_parser_data *parser =3D dev->parser; + + config0 =3D reg_read(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id)); + + if (on) + config0 =3D 0xffffffff; + else + config0 =3D 0; + + reg_write(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id), config0); +} + +static void vip_release_stream(struct vip_stream *stream) +{ + struct vip_dev *dev =3D stream->port->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n", + __func__, &stream); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + vpdma_free_desc_buf(&stream->desc_list.buf); + vpdma_free_desc_list(&stream->desc_list); +} + +static void vip_release_port(struct vip_port *port) +{ + struct vip_dev *dev =3D port->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: port instance %pa\n", + __func__, &port); + + vpdma_free_desc_buf(&port->mmr_adb); + vpdma_free_desc_buf(&port->sc_coeff_h); + vpdma_free_desc_buf(&port->sc_coeff_v); +} + +static void stop_dma(struct vip_stream *stream, bool clear_list) +{ + struct vip_dev *dev =3D stream->port->dev; + int ch, size =3D 0; + + /* Create a list of channels to be cleared */ + for (ch =3D 0; ch < VPDMA_MAX_CHANNELS; ch++) { + if (stream->vpdma_channels[ch] =3D=3D 1) { + stream->vpdma_channels_to_abort[size++] =3D ch; + v4l2_dbg(2, debug, &dev->v4l2_dev, "Clear channel no: %d\n", ch); + } + } + + /* Clear all the used channels for the list */ + vpdma_list_cleanup(dev->shared->vpdma, stream->list_num, + stream->vpdma_channels_to_abort, size); + + if (clear_list) + for (ch =3D 0; ch < VPDMA_MAX_CHANNELS; ch++) + stream->vpdma_channels[ch] =3D 0; +} + +static int vip_open(struct file *file) +{ + struct vip_stream *stream =3D video_drvdata(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + int ret =3D 0; + + mutex_lock(&dev->mutex); + + ret =3D v4l2_fh_open(file); + if (ret) { + v4l2_err(&dev->v4l2_dev, "v4l2_fh_open failed\n"); + goto unlock; + } + + /* + * If this is the first open file. + * Then initialize hw module. + */ + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (vip_init_stream(stream)) + ret =3D -ENODEV; +unlock: + mutex_unlock(&dev->mutex); + return ret; +} + +static int vip_release(struct file *file) +{ + struct vip_stream *stream =3D video_drvdata(file); + struct vip_port *port =3D stream->port; + struct vip_dev *dev =3D port->dev; + bool fh_singular; + int ret; + + mutex_lock(&dev->mutex); + + /* Save the singular status before we call the clean-up helper */ + fh_singular =3D v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret =3D _vb2_fop_release(file, NULL); + + free_csc(port); + free_scaler(port); + + /* + * If this is the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) { + vip_release_stream(stream); + + if (--port->num_streams =3D=3D 0) { + vip_release_port(port); + vip_release_dev(port->dev); + } + } + + mutex_unlock(&dev->mutex); + + return ret; +} + +/* + * File operations + */ +static const struct v4l2_file_operations vip_fops =3D { + .owner =3D THIS_MODULE, + .open =3D vip_open, + .release =3D vip_release, + .read =3D vb2_fop_read, + .poll =3D vb2_fop_poll, + .unlocked_ioctl =3D video_ioctl2, + .mmap =3D vb2_fop_mmap, +}; + +static struct video_device vip_videodev =3D { + .name =3D VIP_MODULE_NAME, + .fops =3D &vip_fops, + .ioctl_ops =3D &vip_ioctl_ops, + .minor =3D -1, + .release =3D video_device_release, + .tvnorms =3D V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .device_caps =3D V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE, +}; + +static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type) +{ + struct vip_stream *stream; + struct vip_dev *dev =3D port->dev; + struct vb2_queue *q; + struct video_device *vfd; + struct vip_buffer *buf; + struct list_head *pos, *tmp; + int ret, i; + + stream =3D kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->port =3D port; + stream->stream_id =3D stream_id; + stream->vfl_type =3D vfl_type; + port->cap_streams[stream_id] =3D stream; + + stream->list_num =3D vpdma_hwlist_alloc(dev->shared->vpdma, stream); + if (stream->list_num < 0) { + v4l2_err(&dev->v4l2_dev, "Could not get VPDMA hwlist"); + ret =3D -ENODEV; + goto do_free_stream; + } + + INIT_LIST_HEAD(&stream->post_bufs); + + /* + * Initialize queue + */ + q =3D &stream->vb_vidq; + q->type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes =3D VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv =3D stream; + q->buf_struct_size =3D sizeof(struct vip_buffer); + q->ops =3D &vip_video_qops; + q->mem_ops =3D &vb2_dma_contig_memops; + q->timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock =3D &dev->mutex; + q->min_queued_buffers =3D 2; + q->dev =3D dev->v4l2_dev.dev; + + ret =3D vb2_queue_init(q); + if (ret) + goto do_free_hwlist; + + INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work); + + INIT_LIST_HEAD(&stream->vidq); + + /* Allocate/populate Drop queue entries */ + INIT_LIST_HEAD(&stream->dropq); + for (i =3D 0; i < VIP_DROPQ_SIZE; i++) { + buf =3D kzalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) { + ret =3D -ENOMEM; + goto do_free_dropq; + } + buf->drop =3D true; + list_add(&buf->list, &stream->dropq); + } + + vfd =3D video_device_alloc(); + if (!vfd) + goto do_free_dropq; + *vfd =3D vip_videodev; + vfd->v4l2_dev =3D &dev->v4l2_dev; + vfd->queue =3D q; + + vfd->lock =3D &dev->mutex; + video_set_drvdata(vfd, stream); + stream->vfd =3D vfd; + + ret =3D video_register_device(vfd, vfl_type, -1); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto do_free_vfd; + } + + v4l2_info(&dev->v4l2_dev, "device registered as %s\n", + video_device_node_name(vfd)); + return 0; + +do_free_vfd: + video_device_release(vfd); +do_free_dropq: + list_for_each_safe(pos, tmp, &stream->dropq) { + buf =3D list_entry(pos, + struct vip_buffer, list); + v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n"); + list_del(pos); + kfree(buf); + } +do_free_hwlist: + vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); +do_free_stream: + kfree(stream); + return ret; +} + +static void free_stream(struct vip_stream *stream) +{ + struct vip_dev *dev; + struct vip_buffer *buf; + struct list_head *pos, *q; + + if (!stream) + return; + + dev =3D stream->port->dev; + /* Free up the Drop queue */ + list_for_each_safe(pos, q, &stream->dropq) { + buf =3D list_entry(pos, + struct vip_buffer, list); + v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n"); + list_del(pos); + kfree(buf); + } + + video_unregister_device(stream->vfd); + vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); + stream->port->cap_streams[stream->stream_id] =3D NULL; + kfree(stream); +} + +static int get_subdev_active_format(struct vip_port *port, + struct v4l2_subdev *subdev) +{ + struct vip_fmt *fmt; + struct vip_dev *dev =3D port->dev; + struct v4l2_subdev_mbus_code_enum mbus_code; + int ret =3D 0; + unsigned int k, i, j; + enum vip_csc_state csc; + + /* Enumerate sub device formats and enable all matching local formats */ + port->num_active_fmt =3D 0; + for (k =3D 0, i =3D 0; (ret !=3D -EINVAL); k++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index =3D k; + mbus_code.which =3D V4L2_SUBDEV_FORMAT_ACTIVE; + ret =3D v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "subdev %s: code: %04x idx: %d\n", + subdev->name, mbus_code.code, k); + + for (j =3D 0; j < ARRAY_SIZE(vip_formats); j++) { + fmt =3D &vip_formats[j]; + if (mbus_code.code !=3D fmt->code) + continue; + + /* + * When the port is configured for BT656 + * then none of the downstream unit can be used. + * So here we need to skip all format requiring + * either CSC or CHR_DS + */ + csc =3D vip_csc_direction(fmt->code, fmt->finfo); + if (port->endpoint.bus_type =3D=3D V4L2_MBUS_BT656 && + (csc !=3D VIP_CSC_NA || fmt->coplanar)) + continue; + + port->active_fmt[i] =3D fmt; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "matched fourcc: %s: code: %04x idx: %d\n", + fourcc_to_str(fmt->fourcc), fmt->code, i); + port->num_active_fmt =3D ++i; + } + } + + if (i =3D=3D 0) { + v4l2_err(&dev->v4l2_dev, "No suitable format reported by subdev %s\n", + subdev->name); + return -EINVAL; + } + return 0; +} + +static int alloc_port(struct vip_dev *dev, int id) +{ + struct vip_port *port; + + if (dev->ports[id]) + return -EINVAL; + + port =3D devm_kzalloc(&dev->pdev->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + dev->ports[id] =3D port; + port->dev =3D dev; + port->port_id =3D id; + port->num_streams =3D 0; + return 0; +} + +static void free_port(struct vip_port *port) +{ + if (!port) + return; + + v4l2_async_nf_unregister(&port->notifier); + v4l2_async_nf_cleanup(&port->notifier); + free_stream(port->cap_streams[0]); +} + +static int get_field(u32 value, u32 mask, int shift) +{ + return (value & (mask << shift)) >> shift; +} + +static int vip_probe_complete(struct platform_device *pdev); +static void vip_vpdma_fw_cb(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "VPDMA firmware loaded\n"); + + if (pdev->dev.of_node) + vip_probe_complete(pdev); +} + +static int vip_create_streams(struct vip_port *port, + struct v4l2_subdev *subdev) +{ + struct v4l2_mbus_config_parallel *bus; + struct vip_bt656_bus *bt656_ep; + int i; + + for (i =3D 0; i < VIP_CAP_STREAMS_PER_PORT; i++) + free_stream(port->cap_streams[i]); + + if (get_subdev_active_format(port, subdev)) + return -ENODEV; + + port->subdev =3D subdev; + + if (port->endpoint.bus_type =3D=3D V4L2_MBUS_PARALLEL) { + port->flags |=3D FLAG_MULT_PORT; + port->num_streams_configured =3D 1; + alloc_stream(port, 0, VFL_TYPE_VIDEO); + } else if (port->endpoint.bus_type =3D=3D V4L2_MBUS_BT656) { + port->flags |=3D FLAG_MULT_PORT; + bus =3D &port->endpoint.bus.parallel; + bt656_ep =3D &port->bt656_endpoint; + port->num_streams_configured =3D bt656_ep->num_channels; + for (i =3D 0; i < bt656_ep->num_channels; i++) { + if (bt656_ep->channels[i] >=3D 16) + continue; + alloc_stream(port, bt656_ep->channels[i], VFL_TYPE_VIDEO); + } + } + return 0; +} + +static int vip_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct vip_port *port =3D notifier_to_vip_port(notifier); + int ret; + + if (port->subdev) { + v4l2_info(&port->dev->v4l2_dev, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + v4l2_info(&port->dev->v4l2_dev, "Port %c: Using subdev %s for capture\n", + port->port_id =3D=3D VIP_PORTA ? 'A' : 'B', subdev->name); + + ret =3D vip_create_streams(port, subdev); + if (ret) + return ret; + + return 0; +} + +static int vip_async_complete(struct v4l2_async_notifier *notifier) +{ + return 0; +} + +static const struct v4l2_async_notifier_operations vip_async_ops =3D { + .bound =3D vip_async_bound, + .complete =3D vip_async_complete, +}; + +static struct fwnode_handle * +fwnode_graph_get_next_endpoint_by_regs(const struct fwnode_handle *fwnode, + int port_reg, int reg) +{ + return of_fwnode_handle(of_graph_get_endpoint_by_regs(to_of_node(fwnode), + port_reg, reg)); +} + +static int vip_register_subdev_notif(struct vip_port *port, + struct fwnode_handle *ep) +{ + struct v4l2_async_notifier *notifier =3D &port->notifier; + struct fwnode_handle *subdev; + struct v4l2_fwnode_endpoint *vep; + struct vip_bt656_bus *bt656_vep; + struct v4l2_async_connection *asd; + int ret, rval; + struct vip_dev *dev =3D port->dev; + + vep =3D &port->endpoint; + bt656_vep =3D &port->bt656_endpoint; + + subdev =3D fwnode_graph_get_remote_port_parent(ep); + if (!subdev) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "can't get remote parent\n"); + return -EINVAL; + } + + ret =3D v4l2_fwnode_endpoint_parse(ep, vep); + if (ret) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "Failed to parse endpoint:\n"); + fwnode_handle_put(subdev); + return -EINVAL; + } + + if (vep->bus_type =3D=3D V4L2_MBUS_BT656) { + if (fwnode_property_present(ep, "ti,vip-pixel-mux")) + bt656_vep->pixmux =3D 1; + else + bt656_vep->pixmux =3D 0; + v4l2_dbg(3, debug, &dev->v4l2_dev, "ti,vip-pixel-mux %u\n", bt656_vep->p= ixmux); + + bt656_vep->num_channels =3D 0; + rval =3D fwnode_property_read_u8_array(ep, "ti,vip-channels", + NULL, 0); + if (rval > 0) { + bt656_vep->num_channels =3D + min_t(int, ARRAY_SIZE(bt656_vep->channels), + rval); + + fwnode_property_read_u8_array(ep, "ti,vip-channels", + bt656_vep->channels, + bt656_vep->num_channels); + } + v4l2_dbg(3, debug, &dev->v4l2_dev, "ti,vip-channels %u\n", bt656_vep->nu= m_channels); + } + + v4l2_async_nf_init(notifier, &port->dev->shared->v4l2_dev); + + asd =3D v4l2_async_nf_add_fwnode(notifier, subdev, struct v4l2_async_conn= ection); + if (IS_ERR(asd)) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "Error adding asd\n"); + fwnode_handle_put(subdev); + v4l2_async_nf_cleanup(notifier); + return -EINVAL; + } + + notifier->ops =3D &vip_async_ops; + ret =3D v4l2_async_nf_register(notifier); + if (ret) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "Error registering async notifier\n"); + v4l2_async_nf_cleanup(notifier); + ret =3D -EINVAL; + } + + return ret; +} + +static int vip_endpoint_scan(struct platform_device *pdev) +{ + struct device_node *parent =3D pdev->dev.of_node; + struct device_node *ep =3D NULL; + int count =3D 0, p; + + for (p =3D 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { + ep =3D of_graph_get_endpoint_by_regs(parent, p, 0); + if (!ep) + continue; + + count++; + of_node_put(ep); + } + + return count; +} + +static int vip_probe_complete(struct platform_device *pdev) +{ + struct vip_shared *shared =3D platform_get_drvdata(pdev); + struct vip_ctrl_module *ctrl =3D NULL; + struct vip_port *port; + struct vip_dev *dev; + struct device_node *parent =3D pdev->dev.of_node; + struct fwnode_handle *ep =3D NULL; + struct of_phandle_args args; + int ret, i, slice_id, port_id, p; + + /* Allocate ctrl before using it */ + ctrl =3D devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + if (parent && of_property_present(parent, "ti,ctrl-module")) { + ctrl->syscon_pol =3D syscon_regmap_lookup_by_phandle(parent, + "ti,ctrl-module"); + if (IS_ERR(ctrl->syscon_pol)) + return dev_err_probe(&pdev->dev, PTR_ERR(ctrl->syscon_pol), + "Failed to get ti,ctrl-module regmap\n"); + } + + ret =3D of_parse_phandle_with_fixed_args(parent, "ti,ctrl-module", + 5, 0, &args); + + if (ret) { + dev_err(&pdev->dev, "failed to parse ti,ctrl-module\n"); + return ret; + } + + ctrl->syscon_offset =3D args.args[0]; + + for (i =3D 0; i < ARRAY_SIZE(ctrl->syscon_bit_field); i++) + ctrl->syscon_bit_field[i] =3D args.args[i + 1]; + + for (p =3D 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { + ep =3D fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent), + p, 0); + if (!ep) + continue; + + switch (p) { + case 0: + slice_id =3D VIP_SLICE1; port_id =3D VIP_PORTA; + break; + case 1: + slice_id =3D VIP_SLICE2; port_id =3D VIP_PORTA; + break; + case 2: + slice_id =3D VIP_SLICE1; port_id =3D VIP_PORTB; + break; + case 3: + slice_id =3D VIP_SLICE2; port_id =3D VIP_PORTB; + break; + default: + dev_err(&pdev->dev, "Unknown port reg=3D<%d>\n", p); + continue; + } + + ret =3D alloc_port(shared->devs[slice_id], port_id); + if (ret < 0) + continue; + + dev =3D shared->devs[slice_id]; + dev->syscon =3D ctrl; + port =3D dev->ports[port_id]; + + vip_register_subdev_notif(port, ep); + fwnode_handle_put(ep); + } + return 0; +} + +static int vip_probe_slice(struct platform_device *pdev, int slice) +{ + struct vip_shared *shared =3D platform_get_drvdata(pdev); + struct vip_dev *dev; + struct vip_parser_data *parser; + int ret; + + dev =3D devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->irq =3D platform_get_irq(pdev, slice); + if (dev->irq < 0) + return dev->irq; + + ret =3D devm_request_irq(&pdev->dev, dev->irq, vip_irq, + 0, VIP_MODULE_NAME, dev); + if (ret < 0) + return -ENOMEM; + + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + dev->slice_id =3D slice; + dev->pdev =3D pdev; + dev->res =3D shared->res; + dev->base =3D shared->base; + dev->v4l2_dev =3D shared->v4l2_dev; + + dev->shared =3D shared; + shared->devs[slice] =3D dev; + + vip_top_reset(dev); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + + parser =3D devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL); + if (!parser) + return PTR_ERR(parser); + + parser->res =3D platform_get_resource_byname(pdev, + IORESOURCE_MEM, + (slice =3D=3D 0) ? + "parser0" : + "parser1"); + parser->base =3D devm_ioremap_resource(&pdev->dev, parser->res); + if (IS_ERR(parser->base)) + return PTR_ERR(parser->base); + + parser->pdev =3D pdev; + dev->parser =3D parser; + + dev->sc_assigned =3D VIP_NOT_ASSIGNED; + dev->sc =3D sc_create(pdev, (slice =3D=3D 0) ? "sc0" : "sc1"); + if (IS_ERR(dev->sc)) + return PTR_ERR(dev->sc); + + dev->csc_assigned =3D VIP_NOT_ASSIGNED; + dev->csc =3D csc_create(pdev, (slice =3D=3D 0) ? "csc0" : "csc1"); + if (IS_ERR(dev->sc)) + return PTR_ERR(dev->sc); + + return 0; +} + +static int vip_probe(struct platform_device *pdev) +{ + struct vip_shared *shared; + struct pinctrl *pinctrl; + int ret, slice =3D VIP_SLICE1; + u32 tmp, pid; + + /* If there are no endpoint defined there is nothing to do */ + if (!vip_endpoint_scan(pdev)) { + dev_err(&pdev->dev, "%s: No sensor", __func__); + return -ENODEV; + } + + ret =3D dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "32-bit consistent DMA enable failed\n"); + return ret; + } + + shared =3D devm_kzalloc(&pdev->dev, sizeof(*shared), GFP_KERNEL); + if (!shared) + return -ENOMEM; + + shared->res =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, "vip"); + shared->base =3D devm_ioremap_resource(&pdev->dev, shared->res); + if (IS_ERR(shared->base)) + return PTR_ERR(shared->base); + + vip_init_format_info(&pdev->dev); + + pinctrl =3D devm_pinctrl_get_select_default(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + ret =3D pm_runtime_get_sync(&pdev->dev); + if (ret) + goto err_runtime_disable; + + /* Make sure H/W module has the right functionality */ + pid =3D reg_read(shared, VIP_PID); + tmp =3D get_field(pid, VIP_PID_FUNC_MASK, VIP_PID_FUNC_SHIFT); + + if (tmp !=3D VIP_PID_FUNC) { + dev_info(&pdev->dev, "vip: unexpected PID function: 0x%x\n", + tmp); + ret =3D -ENODEV; + goto err_runtime_put; + } + + ret =3D v4l2_device_register(&pdev->dev, &shared->v4l2_dev); + if (ret) + goto err_runtime_put; + + /* enable clocks, so the firmware will load properly */ + vip_shared_set_clock_enable(shared, 1); + vip_top_vpdma_reset(shared); + + platform_set_drvdata(pdev, shared); + + v4l2_ctrl_handler_init(&shared->ctrl_handler, 11); + shared->v4l2_dev.ctrl_handler =3D &shared->ctrl_handler; + + for (slice =3D VIP_SLICE1; slice < VIP_NUM_SLICES; slice++) { + ret =3D vip_probe_slice(pdev, slice); + if (ret) { + dev_err(&pdev->dev, "Creating slice failed"); + goto err_dev_unreg; + } + } + + shared->vpdma =3D &shared->vpdma_data; + ret =3D vpdma_create(pdev, shared->vpdma, vip_vpdma_fw_cb); + if (ret) { + dev_err(&pdev->dev, "Creating VPDMA failed"); + goto err_dev_unreg; + } + + return 0; + +err_dev_unreg: + v4l2_ctrl_handler_free(&shared->ctrl_handler); + v4l2_device_unregister(&shared->v4l2_dev); +err_runtime_put: + pm_runtime_put_sync(&pdev->dev); +err_runtime_disable: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void vip_remove(struct platform_device *pdev) +{ + struct vip_shared *shared =3D platform_get_drvdata(pdev); + struct vip_dev *dev; + int slice; + + for (slice =3D 0; slice < VIP_NUM_SLICES; slice++) { + dev =3D shared->devs[slice]; + if (!dev) + continue; + + free_port(dev->ports[VIP_PORTA]); + free_port(dev->ports[VIP_PORTB]); + } + + v4l2_ctrl_handler_free(&shared->ctrl_handler); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + +#if defined(CONFIG_OF) +static const struct of_device_id vip_of_match[] =3D { + { + .compatible =3D "ti,dra7-vip", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, vip_of_match); +#endif + +static struct platform_driver vip_pdrv =3D { + .probe =3D vip_probe, + .remove =3D vip_remove, + .driver =3D { + .name =3D VIP_MODULE_NAME, + .of_match_table =3D of_match_ptr(vip_of_match), + }, +}; + +module_platform_driver(vip_pdrv); + +MODULE_DESCRIPTION("TI VIP driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/ti/vpe/vip.h b/drivers/media/platform/t= i/vpe/vip.h new file mode 100644 index 000000000000..445eeff26c08 --- /dev/null +++ b/drivers/media/platform/ti/vpe/vip.h @@ -0,0 +1,724 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TI VIP capture driver + * + * Copyright (C) 2025 Texas Instruments Incorpated - http://www.ti.com/ + * David Griego, + * Dale Farnsworth, + * Yemike Abhilash Chandra, + */ + +#ifndef __TI_VIP_H +#define __TI_VIP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vpdma.h" +#include "vpdma_priv.h" +#include "sc.h" +#include "csc.h" + +#define VIP_INSTANCE1 1 +#define VIP_INSTANCE2 2 +#define VIP_INSTANCE3 3 + +#define VIP_SLICE1 0 +#define VIP_SLICE2 1 +#define VIP_NUM_SLICES 2 + +/* + * Additional client identifiers used for VPDMA configuration descriptors + */ +#define VIP_SLICE1_CFD_SC_CLIENT 7 +#define VIP_SLICE2_CFD_SC_CLIENT 8 + +#define VIP_PORTA 0 +#define VIP_PORTB 1 +#define VIP_NUM_PORTS 2 + +#define VIP_MAX_PLANES 2 +#define VIP_LUMA 0 +#define VIP_CHROMA 1 + +#define VIP_CAP_STREAMS_PER_PORT 16 +#define VIP_VBI_STREAMS_PER_PORT 16 + +#define VIP_MAX_SUBDEV 5 +/* + * This value needs to be at least as large as the number of entry in + * vip_formats[]. + * When vip_formats[] is modified make sure to adjust this value also. + */ +#define VIP_MAX_ACTIVE_FMT 16 +/* + * Colorspace conversion unit can be in one of 3 modes: + * NA - Not Available on this port + * Y2R - Needed for YUV to RGB on this port + * R2Y - Needed for RGB to YUV on this port + */ +enum vip_csc_state { + VIP_CSC_NA =3D 0, + VIP_CSC_Y2R, + VIP_CSC_R2Y, +}; + +/* buffer for one video frame */ +struct vip_buffer { + /* common v4l buffer stuff */ + struct vb2_v4l2_buffer vb; + struct list_head list; + bool drop; +}; + +/* + * struct vip_fmt - VIP media bus format information + * @fourcc: V4L2 pixel format FCC identifier + * @code: V4L2 media bus format code + * @colorspace: V4L2 colorspace identifier + * @coplanar: 1 if unpacked Luma and Chroma, 0 otherwise (packed/interleav= ed) + * @vpdma_fmt: VPDMA data format per plane. + * @finfo: Cache v4l2_format_info for associated fourcc + */ +struct vip_fmt { + u32 fourcc; + u32 code; + u32 colorspace; + u8 coplanar; + const struct vpdma_data_format *vpdma_fmt[VIP_MAX_PLANES]; + const struct v4l2_format_info *finfo; +}; + +/* + * The vip_parser_data structures contains the memory mapped + * info to access the parser registers. + */ +struct vip_parser_data { + void __iomem *base; + struct resource *res; + + struct platform_device *pdev; +}; + +/* + * The vip_shared structure contains data that is shared by both + * the VIP1 and VIP2 slices. + */ +struct vip_shared { + struct list_head list; + struct resource *res; + void __iomem *base; + struct vpdma_data vpdma_data; + struct vpdma_data *vpdma; + struct v4l2_device v4l2_dev; + struct vip_dev *devs[VIP_NUM_SLICES]; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * The vip_bt656_bus structure contains vip specific bt656 bus data. + */ +struct vip_bt656_bus { + unsigned char num_channels; + unsigned char pixmux; + unsigned char channels[16]; +}; + +struct vip_ctrl_module { + struct regmap *syscon_pol; + u32 syscon_offset; + u32 syscon_bit_field[4]; +}; + +/* + * There are two vip_dev structure, one for each vip slice: VIP1 & VIP2. + */ +struct vip_dev { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + struct vip_shared *shared; + struct resource *res; + struct vip_ctrl_module *syscon; + int instance_id; + int slice_id; + int num_ports; /* count of open ports */ + struct mutex mutex; + /* protects access to stream buffer queues */ + spinlock_t slock; + + int irq; + void __iomem *base; + + struct vip_port *ports[VIP_NUM_PORTS]; + + char name[16]; + /* parser data handle */ + struct vip_parser_data *parser; + /* scaler data handle */ + struct sc_data *sc; + /* scaler port assignation */ + int sc_assigned; + /* csc data handle */ + struct csc_data *csc; + /* csc port assignation */ + int csc_assigned; +}; + +/* + * There are two vip_port structures for each vip_dev, one for port A + * and one for port B. + */ +struct vip_port { + struct vip_dev *dev; + int port_id; + + unsigned int flags; + struct v4l2_rect c_rect; /* crop rectangle */ + struct v4l2_mbus_framefmt mbus_framefmt; + struct v4l2_mbus_framefmt try_mbus_framefmt; + + char name[16]; + struct vip_fmt *fmt; /* current format info */ + /* Number of channels/streams configured */ + int num_streams_configured; + int num_streams; /* count of open streams */ + struct vip_stream *cap_streams[VIP_CAP_STREAMS_PER_PORT]; + + struct v4l2_async_notifier notifier; + struct v4l2_subdev *subdev; + struct v4l2_fwnode_endpoint endpoint; + struct vip_bt656_bus bt656_endpoint; + struct vip_fmt *active_fmt[VIP_MAX_ACTIVE_FMT]; + int num_active_fmt; + /* have new shadow reg values */ + bool load_mmrs; + /* shadow reg addr/data block */ + struct vpdma_buf mmr_adb; + /* h coeff buffer */ + struct vpdma_buf sc_coeff_h; + /* v coeff buffer */ + struct vpdma_buf sc_coeff_v; + /* Show if scaler resource is available on this port */ + bool scaler; + /* Show the csc resource state on this port */ + enum vip_csc_state csc; +}; + +/* + * When handling multiplexed video, there can be multiple streams for each + * port. The vip_stream structure holds per-stream data. + */ +struct vip_stream { + struct video_device *vfd; + struct vip_port *port; + int stream_id; + int list_num; + int vfl_type; + char name[16]; + struct work_struct recovery_work; + int num_recovery; + enum v4l2_field field; /* current field */ + unsigned int sequence; /* current frame/field seq */ + enum v4l2_field sup_field; /* supported field value */ + unsigned int width; /* frame width */ + unsigned int height; /* frame height */ + unsigned int bytesperline; /* bytes per line in memory */ + unsigned int sizeimage; /* image size in memory */ + struct list_head vidq; /* incoming vip_bufs queue */ + struct list_head dropq; /* drop vip_bufs queue */ + struct list_head post_bufs; /* vip_bufs to be DMAed */ + /* Maintain a list of used channels - Needed for VPDMA cleanup */ + int vpdma_channels[VPDMA_MAX_CHANNELS]; + int vpdma_channels_to_abort[VPDMA_MAX_CHANNELS]; + struct vpdma_desc_list desc_list; /* DMA descriptor list */ + struct vpdma_dtd *write_desc; + /* next unused desc_list addr */ + void *desc_next; + struct vb2_queue vb_vidq; +}; + +/* + * VIP Enumerations + */ +enum data_path_select { + ALL_FIELDS_DATA_SELECT =3D 0, + VIP_CSC_SRC_DATA_SELECT, + VIP_SC_SRC_DATA_SELECT, + VIP_RGB_SRC_DATA_SELECT, + VIP_RGB_OUT_LO_DATA_SELECT, + VIP_RGB_OUT_HI_DATA_SELECT, + VIP_CHR_DS_1_SRC_DATA_SELECT, + VIP_CHR_DS_2_SRC_DATA_SELECT, + VIP_MULTI_CHANNEL_DATA_SELECT, + VIP_CHR_DS_1_DATA_BYPASS, + VIP_CHR_DS_2_DATA_BYPASS, +}; + +enum data_interface_modes { + SINGLE_24B_INTERFACE =3D 0, + SINGLE_16B_INTERFACE =3D 1, + DUAL_8B_INTERFACE =3D 2, +}; + +enum sync_types { + EMBEDDED_SYNC_SINGLE_YUV422 =3D 0, + EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422 =3D 1, + EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422 =3D 2, + EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422 =3D 3, + DISCRETE_SYNC_SINGLE_YUV422 =3D 4, + EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444 =3D 5, + DISCRETE_SYNC_SINGLE_RGB_24B =3D 10, +}; + +#define VIP_NOT_ASSIGNED -1 + +/* + * Register offsets and field selectors + */ +#define VIP_PID_FUNC 0xf02 + +#define VIP_PID 0x0000 +#define VIP_PID_MINOR_MASK 0x3f +#define VIP_PID_MINOR_SHIFT 0 +#define VIP_PID_CUSTOM_MASK 0x03 +#define VIP_PID_CUSTOM_SHIFT 6 +#define VIP_PID_MAJOR_MASK 0x07 +#define VIP_PID_MAJOR_SHIFT 8 +#define VIP_PID_RTL_MASK 0x1f +#define VIP_PID_RTL_SHIFT 11 +#define VIP_PID_FUNC_MASK 0xfff +#define VIP_PID_FUNC_SHIFT 16 +#define VIP_PID_SCHEME_MASK 0x03 +#define VIP_PID_SCHEME_SHIFT 30 + +#define VIP_SYSCONFIG 0x0010 +#define VIP_SYSCONFIG_IDLE_MASK 0x03 +#define VIP_SYSCONFIG_IDLE_SHIFT 2 +#define VIP_SYSCONFIG_STANDBY_MASK 0x03 +#define VIP_SYSCONFIG_STANDBY_SHIFT 4 +#define VIP_FORCE_IDLE_MODE 0 +#define VIP_NO_IDLE_MODE 1 +#define VIP_SMART_IDLE_MODE 2 +#define VIP_SMART_IDLE_WAKEUP_MODE 3 +#define VIP_FORCE_STANDBY_MODE 0 +#define VIP_NO_STANDBY_MODE 1 +#define VIP_SMART_STANDBY_MODE 2 +#define VIP_SMART_STANDBY_WAKEUP_MODE 3 + +#define VIP_INTC_INTX_OFFSET 0x0020 + +#define VIP_INT0_STATUS0_RAW_SET 0x0020 +#define VIP_INT0_STATUS0_RAW VIP_INT0_STATUS0_RAW_SET +#define VIP_INT0_STATUS0_CLR 0x0028 +#define VIP_INT0_STATUS0 VIP_INT0_STATUS0_CLR +#define VIP_INT0_ENABLE0_SET 0x0030 +#define VIP_INT0_ENABLE0 VIP_INT0_ENABLE0_SET +#define VIP_INT0_ENABLE0_CLR 0x0038 +#define VIP_INT0_LIST0_COMPLETE BIT(0) +#define VIP_INT0_LIST0_NOTIFY BIT(1) +#define VIP_INT0_LIST1_COMPLETE BIT(2) +#define VIP_INT0_LIST1_NOTIFY BIT(3) +#define VIP_INT0_LIST2_COMPLETE BIT(4) +#define VIP_INT0_LIST2_NOTIFY BIT(5) +#define VIP_INT0_LIST3_COMPLETE BIT(6) +#define VIP_INT0_LIST3_NOTIFY BIT(7) +#define VIP_INT0_LIST4_COMPLETE BIT(8) +#define VIP_INT0_LIST4_NOTIFY BIT(9) +#define VIP_INT0_LIST5_COMPLETE BIT(10) +#define VIP_INT0_LIST5_NOTIFY BIT(11) +#define VIP_INT0_LIST6_COMPLETE BIT(12) +#define VIP_INT0_LIST6_NOTIFY BIT(13) +#define VIP_INT0_LIST7_COMPLETE BIT(14) +#define VIP_INT0_LIST7_NOTIFY BIT(15) +#define VIP_INT0_DESCRIPTOR BIT(16) +#define VIP_VIP1_PARSER_INT BIT(20) +#define VIP_VIP2_PARSER_INT BIT(21) + +#define VIP_INT0_STATUS1_RAW_SET 0x0024 +#define VIP_INT0_STATUS1_RAW VIP_INT0_STATUS0_RAW_SET +#define VIP_INT0_STATUS1_CLR 0x002c +#define VIP_INT0_STATUS1 VIP_INT0_STATUS0_CLR +#define VIP_INT0_ENABLE1_SET 0x0034 +#define VIP_INT0_ENABLE1 VIP_INT0_ENABLE0_SET +#define VIP_INT0_ENABLE1_CLR 0x003c +#define VIP_INT0_ENABLE1_STAT 0x004c +#define VIP_INT0_CHANNEL_GROUP0 BIT(0) +#define VIP_INT0_CHANNEL_GROUP1 BIT(1) +#define VIP_INT0_CHANNEL_GROUP2 BIT(2) +#define VIP_INT0_CHANNEL_GROUP3 BIT(3) +#define VIP_INT0_CHANNEL_GROUP4 BIT(4) +#define VIP_INT0_CHANNEL_GROUP5 BIT(5) +#define VIP_INT0_CLIENT BIT(7) +#define VIP_VIP1_DS1_UV_ERROR_INT BIT(22) +#define VIP_VIP1_DS2_UV_ERROR_INT BIT(23) +#define VIP_VIP2_DS1_UV_ERROR_INT BIT(24) +#define VIP_VIP2_DS2_UV_ERROR_INT BIT(25) + +#define VIP_INTC_E0I 0x00a0 + +#define VIP_CLK_ENABLE 0x0100 +#define VIP_VPDMA_CLK_ENABLE BIT(0) +#define VIP_VIP1_DATA_PATH_CLK_ENABLE BIT(16) +#define VIP_VIP2_DATA_PATH_CLK_ENABLE BIT(17) + +#define VIP_CLK_RESET 0x0104 +#define VIP_VPDMA_RESET BIT(0) +#define VIP_VPDMA_CLK_RESET_MASK 0x1 +#define VIP_VPDMA_CLK_RESET_SHIFT 0 +#define VIP_DATA_PATH_CLK_RESET_MASK 0x1 +#define VIP_VIP1_DATA_PATH_RESET_SHIFT 16 +#define VIP_VIP2_DATA_PATH_RESET_SHIFT 17 +#define VIP_VIP1_DATA_PATH_RESET BIT(16) +#define VIP_VIP2_DATA_PATH_RESET BIT(17) +#define VIP_VIP1_PARSER_RESET BIT(18) +#define VIP_VIP2_PARSER_RESET BIT(19) +#define VIP_VIP1_CSC_RESET BIT(20) +#define VIP_VIP2_CSC_RESET BIT(21) +#define VIP_VIP1_SC_RESET BIT(22) +#define VIP_VIP2_SC_RESET BIT(23) +#define VIP_VIP1_DS1_RESET BIT(25) +#define VIP_VIP2_DS1_RESET BIT(26) +#define VIP_VIP1_DS2_RESET BIT(27) +#define VIP_VIP2_DS2_RESET BIT(28) +#define VIP_MAIN_RESET BIT(31) + +#define VIP_VIP1_DATA_PATH_SELECT 0x010c +#define VIP_VIP2_DATA_PATH_SELECT 0x0110 +#define VIP_CSC_SRC_SELECT_MASK 0x07 +#define VIP_CSC_SRC_SELECT_SHFT 0 +#define VIP_SC_SRC_SELECT_MASK 0x07 +#define VIP_SC_SRC_SELECT_SHFT 3 +#define VIP_RGB_SRC_SELECT BIT(6) +#define VIP_RGB_OUT_LO_SRC_SELECT BIT(7) +#define VIP_RGB_OUT_HI_SRC_SELECT BIT(8) +#define VIP_DS1_SRC_SELECT_MASK 0x07 +#define VIP_DS1_SRC_SELECT_SHFT 9 +#define VIP_DS2_SRC_SELECT_MASK 0x07 +#define VIP_DS2_SRC_SELECT_SHFT 12 +#define VIP_MULTI_CHANNEL_SELECT BIT(15) +#define VIP_DS1_BYPASS BIT(16) +#define VIP_DS2_BYPASS BIT(17) +#define VIP_TESTPORT_B_SELECT BIT(26) +#define VIP_TESTPORT_A_SELECT BIT(27) +#define VIP_DATAPATH_SELECT_MASK 0x0f +#define VIP_DATAPATH_SELECT_SHFT 28 + +#define VIP1_PARSER_REG_OFFSET 0x5500 +#define VIP2_PARSER_REG_OFFSET 0x5a00 + +#define VIP_PARSER_MAIN_CFG 0x0000 +#define VIP_DATA_INTERFACE_MODE_MASK 0x03 +#define VIP_DATA_INTERFACE_MODE_SHFT 0 +#define VIP_CLIP_BLANK BIT(4) +#define VIP_CLIP_ACTIVE BIT(5) + +#define VIP_PARSER_PORTA_0 0x0004 +#define VIP_PARSER_PORTB_0 0x000c +#define VIP_SYNC_TYPE_MASK 0x0f +#define VIP_SYNC_TYPE_SHFT 0 +#define VIP_CTRL_CHANNEL_SEL_MASK 0x03 +#define VIP_CTRL_CHANNEL_SEL_SHFT 4 +#define VIP_ASYNC_FIFO_WR BIT(6) +#define VIP_ASYNC_FIFO_RD BIT(7) +#define VIP_PORT_ENABLE BIT(8) +#define VIP_FID_POLARITY BIT(9) +#define VIP_PIXCLK_EDGE_POLARITY BIT(10) +#define VIP_HSYNC_POLARITY BIT(11) +#define VIP_VSYNC_POLARITY BIT(12) +#define VIP_ACTVID_POLARITY BIT(13) +#define VIP_FID_DETECT_MODE BIT(14) +#define VIP_USE_ACTVID_HSYNC_ONLY BIT(15) +#define VIP_FID_SKEW_PRECOUNT_MASK 0x3f +#define VIP_FID_SKEW_PRECOUNT_SHFT 16 +#define VIP_DISCRETE_BASIC_MODE BIT(22) +#define VIP_SW_RESET BIT(23) +#define VIP_FID_SKEW_POSTCOUNT_MASK 0x3f +#define VIP_FID_SKEW_POSTCOUNT_SHFT 24 +#define VIP_ANALYZER_2X4X_SRCNUM_POS BIT(30) +#define VIP_ANALYZER_FVH_ERR_COR_EN BIT(31) + +#define VIP_PARSER_PORTA_1 0x0008 +#define VIP_PARSER_PORTB_1 0x0010 +#define VIP_SRC0_NUMLINES_MASK 0x0fff +#define VIP_SRC0_NUMLINES_SHFT 0 +#define VIP_ANC_CHAN_SEL_8B_MASK 0x03 +#define VIP_ANC_CHAN_SEL_8B_SHFT 13 +#define VIP_SRC0_NUMPIX_MASK 0x0fff +#define VIP_SRC0_NUMPIX_SHFT 16 +#define VIP_REPACK_SEL_MASK 0x07 +#define VIP_REPACK_SEL_SHFT 28 + +#define VIP_PARSER_FIQ_MASK 0x0014 +#define VIP_PARSER_FIQ_CLR 0x0018 +#define VIP_PARSER_FIQ_STATUS 0x001c +#define VIP_PORTA_VDET BIT(0) +#define VIP_PORTB_VDET BIT(1) +#define VIP_PORTA_ASYNC_FIFO_OF BIT(2) +#define VIP_PORTB_ASYNC_FIFO_OF BIT(3) +#define VIP_PORTA_OUTPUT_FIFO_YUV BIT(4) +#define VIP_PORTA_OUTPUT_FIFO_ANC BIT(6) +#define VIP_PORTB_OUTPUT_FIFO_YUV BIT(7) +#define VIP_PORTB_OUTPUT_FIFO_ANC BIT(9) +#define VIP_PORTA_CONN BIT(10) +#define VIP_PORTA_DISCONN BIT(11) +#define VIP_PORTB_CONN BIT(12) +#define VIP_PORTB_DISCONN BIT(13) +#define VIP_PORTA_SRC0_SIZE BIT(14) +#define VIP_PORTB_SRC0_SIZE BIT(15) +#define VIP_PORTA_YUV_PROTO_VIOLATION BIT(16) +#define VIP_PORTA_ANC_PROTO_VIOLATION BIT(17) +#define VIP_PORTB_YUV_PROTO_VIOLATION BIT(18) +#define VIP_PORTB_ANC_PROTO_VIOLATION BIT(19) +#define VIP_PORTA_CFG_DISABLE_COMPLETE BIT(20) +#define VIP_PORTB_CFG_DISABLE_COMPLETE BIT(21) + +#define VIP_PARSER_PORTA_SOURCE_FID 0x0020 +#define VIP_PARSER_PORTA_ENCODER_FID 0x0024 +#define VIP_PARSER_PORTB_SOURCE_FID 0x0028 +#define VIP_PARSER_PORTB_ENCODER_FID 0x002c + +#define VIP_PARSER_PORTA_SRC0_SIZE 0x0030 +#define VIP_PARSER_PORTB_SRC0_SIZE 0x0070 +#define VIP_SOURCE_HEIGHT_MASK 0x0fff +#define VIP_SOURCE_HEIGHT_SHFT 0 +#define VIP_SOURCE_WIDTH_MASK 0x0fff +#define VIP_SOURCE_WIDTH_SHFT 16 + +#define VIP_PARSER_PORTA_VDET_VEC 0x00b0 +#define VIP_PARSER_PORTB_VDET_VEC 0x00b4 + +#define VIP_PARSER_PORTA_EXTRA2 0x00b8 +#define VIP_PARSER_PORTB_EXTRA2 0x00c8 +#define VIP_ANC_SKIP_NUMPIX_MASK 0x0fff +#define VIP_ANC_SKIP_NUMPIX_SHFT 0 +#define VIP_ANC_BYPASS BIT(15) +#define VIP_ANC_USE_NUMPIX_MASK 0x0fff +#define VIP_ANC_USE_NUMPIX_SHFT 16 +#define VIP_ANC_TARGET_SRCNUM_MASK 0x0f +#define VIP_ANC_TARGET_SRCNUM_SHFT 28 + +#define VIP_PARSER_PORTA_EXTRA3 0x00bc +#define VIP_PARSER_PORTB_EXTRA3 0x00cc +#define VIP_ANC_SKIP_NUMLINES_MASK 0x0fff +#define VIP_ANC_SKIP_NUMLINES_SHFT 0 +#define VIP_ANC_USE_NUMLINES_MASK 0x0fff +#define VIP_ANC_USE_NUMLINES_SHFT 16 + +#define VIP_PARSER_PORTA_EXTRA4 0x00c0 +#define VIP_PARSER_PORTB_EXTRA4 0x00d0 +#define VIP_ACT_SKIP_NUMPIX_MASK 0x0fff +#define VIP_ACT_SKIP_NUMPIX_SHFT 0 +#define VIP_ACT_BYPASS BIT(15) +#define VIP_ACT_USE_NUMPIX_MASK 0x0fff +#define VIP_ACT_USE_NUMPIX_SHFT 16 +#define VIP_ACT_TARGET_SRCNUM_MASK 0x0f +#define VIP_ACT_TARGET_SRCNUM_SHFT 28 + +#define VIP_PARSER_PORTA_EXTRA5 0x00c4 +#define VIP_PARSER_PORTB_EXTRA5 0x00d4 +#define VIP_ACT_SKIP_NUMLINES_MASK 0x0fff +#define VIP_ACT_SKIP_NUMLINES_SHFT 0 +#define VIP_ACT_USE_NUMLINES_MASK 0x0fff +#define VIP_ACT_USE_NUMLINES_SHFT 16 + +#define VIP_PARSER_PORTA_EXTRA6 0x00d8 +#define VIP_PARSER_PORTB_EXTRA6 0x00dc +#define VIP_ANC_SRCNUM_STOP_IMM_SHFT 0 +#define VIP_YUV_SRCNUM_STOP_IMM_SHFT 16 + +#define VIP_CSC_CSC00 0x0200 +#define VIP_CSC_A0_MASK 0x1fff +#define VIP_CSC_A0_SHFT 0 +#define VIP_CSC_B0_MASK 0x1fff +#define VIP_CSC_B0_SHFT 16 + +#define VIP_CSC_CSC01 0x0204 +#define VIP_CSC_C0_MASK 0x1fff +#define VIP_CSC_C0_SHFT 0 +#define VIP_CSC_A1_MASK 0x1fff +#define VIP_CSC_A1_SHFT 16 + +#define VIP_CSC_CSC02 0x0208 +#define VIP_CSC_B1_MASK 0x1fff +#define VIP_CSC_B1_SHFT 0 +#define VIP_CSC_C1_MASK 0x1fff +#define VIP_CSC_C1_SHFT 16 + +#define VIP_CSC_CSC03 0x020c +#define VIP_CSC_A2_MASK 0x1fff +#define VIP_CSC_A2_SHFT 0 +#define VIP_CSC_B2_MASK 0x1fff +#define VIP_CSC_B2_SHFT 16 + +#define VIP_CSC_CSC04 0x0210 +#define VIP_CSC_C2_MASK 0x1fff +#define VIP_CSC_C2_SHFT 0 +#define VIP_CSC_D0_MASK 0x0fff +#define VIP_CSC_D0_SHFT 16 + +#define VIP_CSC_CSC05 0x0214 +#define VIP_CSC_D1_MASK 0x0fff +#define VIP_CSC_D1_SHFT 0 +#define VIP_CSC_D2_MASK 0x0fff +#define VIP_CSC_D2_SHFT 16 +#define VIP_CSC_BYPASS BIT(28) + +#define VIP_SC_MP_SC0 0x0300 +#define VIP_INTERLACE_O BIT(0) +#define VIP_LINEAR BIT(1) +#define VIP_SC_BYPASS BIT(2) +#define VIP_INVT_FID BIT(3) +#define VIP_USE_RAV BIT(4) +#define VIP_ENABLE_EV BIT(5) +#define VIP_AUTH_HS BIT(6) +#define VIP_DCM_2X BIT(7) +#define VIP_DCM_4X BIT(8) +#define VIP_HP_BYPASS BIT(9) +#define VIP_INTERLACE_I BIT(10) +#define VIP_ENABLE_SIN2_VER_INTP BIT(11) +#define VIP_Y_PK_EN BIT(14) +#define VIP_TRIM BIT(15) +#define VIP_SELFGEN_FID BIT(16) + +#define VIP_SC_MP_SC1 0x0304 +#define VIP_ROW_ACC_INC_MASK 0x07ffffff +#define VIP_ROW_ACC_INC_SHFT 0 + +#define VIP_SC_MP_SC2 0x0308 +#define VIP_ROW_ACC_OFFSET_MASK 0x0fffffff +#define VIP_ROW_ACC_OFFSET_SHFT 0 + +#define VIP_SC_MP_SC3 0x030c +#define VIP_ROW_ACC_OFFSET_B_MASK 0x0fffffff +#define VIP_ROW_ACC_OFFSET_B_SHFT 0 + +#define VIP_SC_MP_SC4 0x0310 +#define VIP_TAR_H_MASK 0x07ff +#define VIP_TAR_H_SHFT 0 +#define VIP_TAR_W_MASK 0x07ff +#define VIP_TAR_W_SHFT 12 +#define VIP_LIN_ACC_INC_U_MASK 0x07 +#define VIP_LIN_ACC_INC_U_SHFT 24 +#define VIP_NLIN_ACC_INIT_U_MASK 0x07 +#define VIP_NLIN_ACC_INIT_U_SHFT 28 + +#define VIP_SC_MP_SC5 0x0314 +#define VIP_SRC_H_MASK 0x03ff +#define VIP_SRC_H_SHFT 0 +#define VIP_SRC_W_MASK 0x07ff +#define VIP_SRC_W_SHFT 12 +#define VIP_NLIN_ACC_INC_U_MASK 0x07 +#define VIP_NLIN_ACC_INC_U_SHFT 24 + +#define VIP_SC_MP_SC6 0x0318 +#define VIP_ROW_ACC_INIT_RAV_MASK 0x03ff +#define VIP_ROW_ACC_INIT_RAV_SHFT 0 +#define VIP_ROW_ACC_INIT_RAV_B_MASK 0x03ff +#define VIP_ROW_ACC_INIT_RAV_B_SHFT 10 + +#define VIP_SC_MP_SC8 0x0320 +#define VIP_NLIN_LEFT_MASK 0x07ff +#define VIP_NLIN_LEFT_SHFT 0 +#define VIP_NLIN_RIGHT_MASK 0x07ff +#define VIP_NLIN_RIGHT_SHFT 12 + +#define VIP_SC_MP_SC9 0x0324 +#define VIP_LIN_ACC_INC VIP_SC_MP_SC9 + +#define VIP_SC_MP_SC10 0x0328 +#define VIP_NLIN_ACC_INIT VIP_SC_MP_SC10 + +#define VIP_SC_MP_SC11 0x032c +#define VIP_NLIN_ACC_INC VIP_SC_MP_SC11 + +#define VIP_SC_MP_SC12 0x0330 +#define VIP_COL_ACC_OFFSET_MASK 0x01ffffff +#define VIP_COL_ACC_OFFSET_SHFT 0 + +#define VIP_SC_MP_SC13 0x0334 +#define VIP_SC_FACTOR_RAV_MASK 0x03ff +#define VIP_SC_FACTOR_RAV_SHFT 0 +#define VIP_CHROMA_INTP_THR_MASK 0x03ff +#define VIP_CHROMA_INTP_THR_SHFT 12 +#define VIP_DELTA_CHROMA_THR_MASK 0x0f +#define VIP_DELTA_CHROMA_THR_SHFT 24 + +#define VIP_SC_MP_SC17 0x0344 +#define VIP_EV_THR_MASK 0x03ff +#define VIP_EV_THR_SHFT 12 +#define VIP_DELTA_LUMA_THR_MASK 0x0f +#define VIP_DELTA_LUMA_THR_SHFT 24 +#define VIP_DELTA_EV_THR_MASK 0x0f +#define VIP_DELTA_EV_THR_SHFT 28 + +#define VIP_SC_MP_SC18 0x0348 +#define VIP_HS_FACTOR_MASK 0x03ff +#define VIP_HS_FACTOR_SHFT 0 +#define VIP_CONF_DEFAULT_MASK 0x01ff +#define VIP_CONF_DEFAULT_SHFT 16 + +#define VIP_SC_MP_SC19 0x034c +#define VIP_HPF_COEFF0_MASK 0xff +#define VIP_HPF_COEFF0_SHFT 0 +#define VIP_HPF_COEFF1_MASK 0xff +#define VIP_HPF_COEFF1_SHFT 8 +#define VIP_HPF_COEFF2_MASK 0xff +#define VIP_HPF_COEFF2_SHFT 16 +#define VIP_HPF_COEFF3_MASK 0xff +#define VIP_HPF_COEFF3_SHFT 23 + +#define VIP_SC_MP_SC20 0x0350 +#define VIP_HPF_COEFF4_MASK 0xff +#define VIP_HPF_COEFF4_SHFT 0 +#define VIP_HPF_COEFF5_MASK 0xff +#define VIP_HPF_COEFF5_SHFT 8 +#define VIP_HPF_NORM_SHFT_MASK 0x07 +#define VIP_HPF_NORM_SHFT_SHFT 16 +#define VIP_NL_LIMIT_MASK 0x1ff +#define VIP_NL_LIMIT_SHFT 20 + +#define VIP_SC_MP_SC21 0x0354 +#define VIP_NL_LO_THR_MASK 0x01ff +#define VIP_NL_LO_THR_SHFT 0 +#define VIP_NL_LO_SLOPE_MASK 0xff +#define VIP_NL_LO_SLOPE_SHFT 16 + +#define VIP_SC_MP_SC22 0x0358 +#define VIP_NL_HI_THR_MASK 0x01ff +#define VIP_NL_HI_THR_SHFT 0 +#define VIP_NL_HI_SLOPE_SH_MASK 0x07 +#define VIP_NL_HI_SLOPE_SH_SHFT 16 + +#define VIP_SC_MP_SC23 0x035c +#define VIP_GRADIENT_THR_MASK 0x07ff +#define VIP_GRADIENT_THR_SHFT 0 +#define VIP_GRADIENT_THR_RANGE_MASK 0x0f +#define VIP_GRADIENT_THR_RANGE_SHFT 12 +#define VIP_MIN_GY_THR_MASK 0xff +#define VIP_MIN_GY_THR_SHFT 16 +#define VIP_MIN_GY_THR_RANGE_MASK 0x0f +#define VIP_MIN_GY_THR_RANGE_SHFT 28 + +#define VIP_SC_MP_SC24 0x0360 +#define VIP_ORG_H_MASK 0x07ff +#define VIP_ORG_H_SHFT 0 +#define VIP_ORG_W_MASK 0x07ff +#define VIP_ORG_W_SHFT 16 + +#define VIP_SC_MP_SC25 0x0364 +#define VIP_OFF_H_MASK 0x07ff +#define VIP_OFF_H_SHFT 0 +#define VIP_OFF_W_MASK 0x07ff +#define VIP_OFF_W_SHFT 16 + +#define VIP_VPDMA_REG_OFFSET 0xd000 + +#endif --=20 2.34.1