Received: by 10.223.148.5 with SMTP id 5csp7107373wrq; Thu, 18 Jan 2018 00:43:17 -0800 (PST) X-Google-Smtp-Source: ACJfBosuNb5qJ5EMVfHYyMq515WYTkEOxmP9bJC9ZU8KyHmGZzCwMTstSDrshV5p35jiLYsC2him X-Received: by 10.98.56.150 with SMTP id f144mr16332660pfa.167.1516264997578; Thu, 18 Jan 2018 00:43:17 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516264997; cv=none; d=google.com; s=arc-20160816; b=olQLJgz3k4fz/r3xyWiog9d9bdItE5mVDGprglHL0OqMpFZfwarrP3+JWFctkrXzYn 9puRbuxfjW8VD+LEthOdUJ7ymJfyCccBbTysN6YJ20NoMCVcdJVWg2X3VYKDFQQD0IT+ Z9na7OiBN8fkOkENqdJ/zOB2bHB9llXVhmQqDyMwj3fyKRSzprk3oR+fFurvsohFtDas YWMCRvcPBXYWmED1CXwpCQ8T9QtAd7JEla/1GgfOvy/+NrJhhfcgYNdIeeXMCl0Savwx SUuMpbkoVD2gJcIDPk0HEw+8XzmX4qd78CrKJRPzEPB5AG8t28piNNsoV3UtiwHqiFcj dYHA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:spamdiagnosticmetadata :spamdiagnosticoutput:mime-version:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature:arc-authentication-results; bh=194ohF6d/tLGIthg/+Px5sj17DaE6YKlQwdQhFyKupY=; b=aQZGWo3GcZfkdc77valcSC1XtdjPOUKBGWKHFCwK8TpCx7ixFfMZWmDbUoy8rWOWC7 bqSWXoLsKEgny1rN9vJvrzJ+cvH8cTVEQZ8FsHi6ank/pVPSL769iEE0u21XqXMnrFgI CBPZnVILIwNXkhXqNqJG3KXzJ4lR0AvHSm3P1AY7JfIbWcmq6KtvAJf2CEaeIfqQLH4A 0gUpZuqszhUx4v3vNzOhYPPwWN5Ou240WfSatHoZXsHr3zAj7umXOmnr7onxjKDB9prk zNrEoiZXeRWy5kMtspXjIk+cwzEsFAP5R1zOvjkkS/ZeMIESc9IMCIU3oFGpvMxBY9pM nqxQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@Sony.onmicrosoft.com header.s=selector1-Sony-onmicrosoft-com header.b=akzBUGak; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e12si5367412pgq.581.2018.01.18.00.43.02; Thu, 18 Jan 2018 00:43:17 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@Sony.onmicrosoft.com header.s=selector1-Sony-onmicrosoft-com header.b=akzBUGak; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755030AbeARImX (ORCPT + 99 others); Thu, 18 Jan 2018 03:42:23 -0500 Received: from mail-sn1nam01on0093.outbound.protection.outlook.com ([104.47.32.93]:51760 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754847AbeARImU (ORCPT ); Thu, 18 Jan 2018 03:42:20 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Sony.onmicrosoft.com; s=selector1-Sony-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=194ohF6d/tLGIthg/+Px5sj17DaE6YKlQwdQhFyKupY=; b=akzBUGakVygeuXVhIYWzLLNQd/gIXfDtehPAA84xW9HrNviMbA7BekghASxfmfUiQPJGQILxXG5Nze8pgrYSLOQIEqVbgcvTAuEOltHo7qq69m8+r2PjEmwDJk9x+XdPYQTbWH7Xq+QI8DYTuxxCwy7XQ4N8zOLI62/ovh7z0x4= Received: from CY4PR13CA0096.namprd13.prod.outlook.com (10.171.162.34) by SN1PR13MB0463.namprd13.prod.outlook.com (10.163.201.157) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.428.9; Thu, 18 Jan 2018 08:42:17 +0000 Received: from BL2NAM02FT022.eop-nam02.prod.protection.outlook.com (2a01:111:f400:7e46::208) by CY4PR13CA0096.outlook.office365.com (2603:10b6:903:152::34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.428.9 via Frontend Transport; Thu, 18 Jan 2018 08:42:17 +0000 Received-SPF: Pass (protection.outlook.com: domain of sony.com designates 117.103.190.43 as permitted sender) receiver=protection.outlook.com; client-ip=117.103.190.43; helo=jp.sony.com; Received: from jp.sony.com (117.103.190.43) by BL2NAM02FT022.mail.protection.outlook.com (10.152.77.153) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.20.428.12 via Frontend Transport; Thu, 18 Jan 2018 08:42:17 +0000 Received: from JPYOKXHT102.jp.sony.com (117.103.191.49) by JPYOKXEG103.jp.sony.com (117.103.190.43) with Microsoft SMTP Server (TLS) id 14.3.361.1; Thu, 18 Jan 2018 08:42:04 +0000 Received: from localhost.localdomain (43.25.41.74) by JPYOKXHT102.jp.sony.com (117.103.191.49) with Microsoft SMTP Server (TLS) id 14.3.361.1; Thu, 18 Jan 2018 08:42:03 +0000 From: To: , , CC: , , , , , , , Subject: [PATCH v5 02/12] [media] cxd2880-spi: Add support for CXD2880 SPI interface Date: Thu, 18 Jan 2018 17:46:10 +0900 Message-ID: <20180118084610.20967-1-Yasunari.Takiguchi@sony.com> X-Mailer: git-send-email 2.15.1 In-Reply-To: <20180118084016.20689-1-Yasunari.Takiguchi@sony.com> References: <20180118084016.20689-1-Yasunari.Takiguchi@sony.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [43.25.41.74] X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:117.103.190.43;IPV:NLI;CTRY:JP;EFV:NLI;SFV:NSPM;SFS:(10019020)(376002)(346002)(39860400002)(39380400002)(396003)(2980300002)(438002)(189003)(199004)(5660300001)(2950100002)(956003)(6666003)(5890100001)(76176011)(2906002)(6346003)(2876002)(50466002)(48376002)(107886003)(4326008)(26005)(7636002)(316002)(54906003)(110136005)(16526018)(72206003)(16586007)(8936002)(478600001)(6116002)(3846002)(86152003)(106466001)(39060400002)(305945005)(1076002)(106002)(47776003)(50226002)(7736002)(356003)(86362001)(66066001)(8676002)(51416003)(246002)(575784001)(59450400001)(36756003)(2201001)(49486002);DIR:OUT;SFP:1102;SCL:1;SRVR:SN1PR13MB0463;H:jp.sony.com;FPR:;SPF:Pass;PTR:jpyokxeg103.jp.sony.com;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BL2NAM02FT022;1:E2AVp+pUKQnrGU+8FCdV7Lq6DadGgqraPmmPPd8uNGaFSmU/l49hCh5dwtuuoFN8AFhBUcfAR3aUILj6SGNZMSHmMyD8ogBxrjq7GHTQ8J5kA8JCSURiBdlO3u6gXU5f X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 1184d6ba-49cf-4151-a3fa-08d55e4f61b6 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(7020095)(4652020)(4534125)(4602075)(4627221)(201703031133081)(201702281549075)(5600026)(4604075)(4608076)(2017052603307)(7193020);SRVR:SN1PR13MB0463; X-Microsoft-Exchange-Diagnostics: 1;SN1PR13MB0463;3:94UWAsqo2h8d0rgQDfh+nVwe2c7CzQiy7vLMJwZTcymfue2vb93Kr66MbLN5j60oXRyA15MSahfeoGP30K3szj2kmK53zLDUMlAvP7poHJ3HoNY2sG3V2/tQZquSHDcrtkHqAhoYCO/mkWElrMDDPIsC4F8jiN4eVdbMulKNNJqYNjVZNJuTZtKH6AqY7NYGra3gzA2/Dsge5RX5MB8xzRM+jtDlx+bfYDsTme+aERnxl5x4+Dg0l85uudYgxLKxbCe7hFKP9RlR0mTj7FENSSZOlqueSyN3bGamV0IEfV2xZddj7aVYr+0cDzZFjUfG7+QXNLBCJSHTUTwVLZ+wwFPmg7EwtFwA2RjU+gxznxs=;25:vEcVncM5MidLN4cz0tWYAjLiB9cu5HJ9lLkrhF7FvD/1r2nFqxj7G6fAG+NQ/NR1urtJ5fRQzw/4/Ws8cCO7B5HNEE6uJWXEZ/COHkcHVs/meK2menmyLEyNbvaiGhPKCN0tRwXVxgKyGRs98Aba6/nXXOysdQ3O/+oJIFe2lbnS3RadVbFzwY5PrCaBROI7Fa6nTHcAXYis4Rrjy90qaNn4it/owzcayt67naGkE30Ah/mR+jxFunsL+N/5QlkSyTqPEKKlT/P9qmpJH3iew2ijGDQV3sBmczJ515daT+Yl7plsd1kXJ1zrgCt46HiZrm5cnMD4TJdnVJR7W6BX9w== X-MS-TrafficTypeDiagnostic: SN1PR13MB0463: X-Microsoft-Exchange-Diagnostics: 1;SN1PR13MB0463;31:5O38LMClefFLxJF/AoCsICWTGn1dVdJS+D9pwA9GGTEPBpmGd8JsTwoCsfFeGsD+Zk8/mZXZDjIsc45kiVA8iSRNb/4IWYVA3lnKmfIpyxB1GYH/JLsQNu/4iVkrAd6zr8IBsev9H6EqXw75nXdoIHFT8lIeaANIvkMdA/xmvWr8pnAZI2zCKqW+tNCvLp8ei4S0nVjt10tXjM/OPiGhz5+9QzQW7R+AVwuLmNqH9O0=;20:ZryL1gAqdHU8hYrayUF8dcI3BmZMQVMJOzi/CaIz4ydy452l1lz8CCgcPJVX+a7mrbI+3gHVQAetz1ebOKZMQ06BM8EkE8w1jYyXnHVhAMTDoocu1Yl5PRnUJ+C8J1jBbDgyxPCOCn4Qh6PcwMvFmNQp5egknldq+Xwdy0fUBhuCxgYDQlQX0I9df9TyxibnQbpwbGP/I3MYZTSoUJmVtUP6iCfBE4l6eJEemwg6tu7+dpHe7Yt6ppNCNn5o2FLkCenay+BgREI3zuCuodOvhJr+GuA+fQ3snp3IDKPi4+H/Uy/EgYI52SijJr5miMiELZhFHcsy8txUyNr0n9Kgf6T5rLO7pFoe2GuabfGuLIorhf1qBedXK7cXK1vJ1PgUoWc7AG30Va+ytD6BSM/EgVfsGJ3wpRAi80Z7Gpk0/25us45ObBDEyMuZ+5UsOKai1HrZulqKhgWkNL6UAFUQQV8yvnw76bXHRfpAIy4opl4/l28v53qn0KsTO2BE8DNI X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(182409339516656)(21532816269658); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040492)(2401047)(8121501046)(5005006)(3002001)(10201501046)(93006095)(93004095)(3231046)(11241501184)(2400058)(944501161)(6055026)(6041279)(20161123564045)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123562045)(20161123558120)(6072148)(201708071742011);SRVR:SN1PR13MB0463;BCL:0;PCL:0;RULEID:(100000803101)(100110400095);SRVR:SN1PR13MB0463; X-Microsoft-Exchange-Diagnostics: 1;SN1PR13MB0463;4:kT/IsQv/TJJYihlbOgSGRMYO9aDl1dLYCQHLyLRnDwy4VxtEshc/PQ91my6LjzaiifrA6UEcIA81l72N6mOwuBbu8ti+4I1H0H5fnNKeKsW1m+pwyNXDNm0V+IrkRqosmQpiEhVhLlPBD2PCQJgxTmmGimLX5IUF1xAF/P2gSr3HxM6rs+Z75DmUxmfiYwhgsAjZVxEWLOrIZXqU8LZi6SzInIbt+spbf5V6SHtHzbQUmlRpV9YUbPEMnpdytJNGsRC0h3ooI6h+CKv1vQCOREDTY3VdCq9Gh3GKa+UrEiWqR+J3e/Z/TeJokCgKG6s6DnK2eRPwOCUA8k9SA3EuZlUOZAa1MxxEVDdaUwGzrZ4= X-Forefront-PRVS: 05568D1FF7 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;SN1PR13MB0463;23:jncIZoil9pKUc6BudlB5Fq2OUQnJUYWMKO+tx32h0?= =?us-ascii?Q?HbvfjdNvzxKxAmMzFtUmutjFkbuDMkcAHpK9th6/Yl+gqUfoeU/K6KDVq9Zn?= =?us-ascii?Q?GHy8zbNrfF9YT1tlLGWj6Xxe9Jfhhqvz4wu52KsFb8zCP4JH1MmjmNQfo5JD?= =?us-ascii?Q?23dVpwiS5HXG5Bd3nVswFD5eL3Q2g+43Qli+dRinkr0wJbb6+utOOCd6WoZa?= =?us-ascii?Q?9OxoWPdlSmIbMF3UbhPXD3pPOrPvmU9bizzEj3e4fK0LT/eXXnCop3dNxJE7?= =?us-ascii?Q?soVcELC8unxZL3fPlVKQXOtuB4o73gsmY9lkWptSFxrbDiGdkNTPkSZLj+fU?= =?us-ascii?Q?SN3MMrFz7B6hLnIPuFRu22ZY6/e1vl2jcxP/5Sko6rptMoaEfQCOe5TT8+8n?= =?us-ascii?Q?gfLHoP1F6TLYeQA53CePz5qAde0LkXiwRyYtogJLwUvwcdajfvVje3fwgrwk?= =?us-ascii?Q?DV2Q75p0VVF0Jd8n4N9T3FrFH7MnwnHM39BFQm7eWLVuK+YXe/Cv79gXSPZO?= =?us-ascii?Q?eAkPW+Qej5hAyY5tX2KJgkISyJBR6WOk9zM0ir3O4Us9gC62RcTyQpKDsZYq?= =?us-ascii?Q?L+xcHjQ0jMO21i1bMTfIGHKC6vSiNzrQLjzMsISv4M0zAa8y4oHxShXAxOz/?= =?us-ascii?Q?QdKKj1xSDMuanGnmeSP8BpCGlIVHs1n9aXr7qxpUF4t6jruHH+dok5O6i9JZ?= =?us-ascii?Q?MR3owZMjvvttyb+VPksw+LrplE1mjdoLXbcQ8Fuot8V273XsqKC7L5FDEqat?= =?us-ascii?Q?pGKTvOqLqLZig9++urVx+KJ/x+REuYaoaj6gnP/RmcxLrFBQk5i5Vw+SnaKj?= =?us-ascii?Q?a3PXDhnBmNMB0jfAF/Gm2eN7D4ziVqeZ18LOofsHn8CuWuomplZp4WCTQrJ+?= =?us-ascii?Q?ud5OPmDN05EEcSOcaXvtSuQltcnV0WU0AeLLVH3LmMZyE32JAZx9XRgzFYgI?= =?us-ascii?Q?Ry+PtRgZR3hD1Gy9ZiRNPjqgeURbhd39EQEkghOZVXkqzI2Fh3358gK8Lqaz?= =?us-ascii?Q?RdumWlv4PSv92ovK/QR3y5O+bA6+/hqu3xPZsj7N2pdJe1SX1K+rr94tpyVG?= =?us-ascii?Q?6AR2JTjo+QSGqrcSU1bHdur5oBUCe/znF0Y/3Rp8WR/Vx3+hp03wRxk5CKDt?= =?us-ascii?Q?H6P6olAeSayiLa8SCigdnkYteLKq9HeOgRXHAmBoeY337vFu421S2pBLBguk?= =?us-ascii?Q?udtJFxbEFv/l57Kq1QQ3Jr6wYgkGpbIXSG0ilY8ZTTS787umkjL/qHcNA=3D?= =?us-ascii?Q?=3D?= X-Microsoft-Exchange-Diagnostics: 1;SN1PR13MB0463;6:381mfwjuYWYLz5VqS4Ld4Qli8eiDNbyBdJ//hKSPMRt6jCof6m9OnHP3MlcESio66c3szUXkRdDgurgmuQkxx9CkvgEp0v0NIVllgfdRk4QCzncdgMcp2kfr/Cu4xkYf0XlZeE+N2pok2nRnjlGs8T6HBa1Nt6tqkavJaclAWP5CwqUShqMov8br9E0nFvtZwtM1z+jSvxq/z6lD3CkCEaF8+yGkLEARCethMdA2QS1QcLJFqcCJmrHVdJmP4C5hFRUqOFzh+1Rb46dJ4NSJARDhhDph/jI8qotQSDqx99b9iPco3UIVi318UCAUBe4Yv79+069LQYB0ofrveW+/mqU+uPRkn6Crft5RZftnNJk=;5:MdpfJN6XO+rTkCSSWBJuBUekDFCM7dH3DHH0GbwQghh2gSWsQuMtJ5ZXecLO5CArS6YkMW892E+PvgsKbcr/u82YZj2yw5HYRsqGLrDV4iS68nYF0kwrcM++HS8rm63oLYfkKCSsQESE7ZvIhUz5Noo3DZhH0r+jsQwY+o0Hi8E=;24:xIlpC5CTRy6Gd17ND3L+NoSn9AVmyHmfJ4OlAK1VXm0kyZIfIgb2jyKPfJErjRAcdWYfXoUkxgDxcHd1z69vPryompLtx84WbdbeScYxcuc=;7:DeXvdzATjvmo7dASrVdiHal13kQh5p2I3m/MHVT4TBe+HsLGb/QXRMFJz9aAX3N0WrOOB+/jSYxXK5gBvIkjYn6qNW4ystTFmttKXK8iGnPpqlIZV4CjrrAACzaFbwkq2/sUIeT2Y4FunEHrubvK+F0ByapqU8CvsTaCLXaoKeMmatlvpnxlhjgqffIwrSw1xjUq9Ze0M6spB63Qa5NhO22Se63ZTMPVQjb94MEpz7QNPw1ezUbSkxypNQzdwjit SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: sony.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Jan 2018 08:42:17.3048 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 1184d6ba-49cf-4151-a3fa-08d55e4f61b6 X-MS-Exchange-CrossTenant-Id: 66c65d8a-9158-4521-a2d8-664963db48e4 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=66c65d8a-9158-4521-a2d8-664963db48e4;Ip=[117.103.190.43];Helo=[jp.sony.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN1PR13MB0463 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Yasunari Takiguchi This is the SPI adapter part of the driver for the Sony CXD2880 DVB-T2/T tuner + demodulator. Signed-off-by: Yasunari Takiguchi Signed-off-by: Masayuki Yamamoto Signed-off-by: Hideki Nozawa Signed-off-by: Kota Yonezawa Signed-off-by: Toshihiko Matsumoto Signed-off-by: Satoshi Watanabe --- [Change list] Changes in V5 Using SPDX-License-Identifier drivers/media/spi/cxd2880-spi.c -modified typo about "ivnalid" -> "invalid" -modified typo about "drvier" -> "driver" -removed unnecessary if() -modified return error code -reduction of valiable names -removed unnecessary parentheses -changed members of struct cxd2880_ts_buf_info Changes in V4 drivers/media/spi/cxd2880-spi.c -removed Camel case -removed unnecessary initialization at variable declaration -removed unnecessary brace {} Changes in V3 drivers/media/spi/cxd2880-spi.c -adjusted of indent spaces -removed unnecessary cast -changed debugging code -changed timeout method -modified coding style of if() -changed hexadecimal code to lower case. Changes in V2 drivers/media/spi/cxd2880-spi.c -Modified PID filter setting. drivers/media/spi/cxd2880-spi.c | 670 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 drivers/media/spi/cxd2880-spi.c diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c new file mode 100644 index 000000000000..857e4c0d7a92 --- /dev/null +++ b/drivers/media/spi/cxd2880-spi.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cxd2880-spi.c + * Sony CXD2880 DVB-T2/T tuner + demodulator driver + * SPI adapter + * + * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include + +#include "dvb_demux.h" +#include "dmxdev.h" +#include "dvb_frontend.h" +#include "cxd2880.h" + +#define CXD2880_MAX_FILTER_SIZE 32 +#define BURST_WRITE_MAX 128 +#define MAX_TRANS_PKT 300 + +struct cxd2880_ts_buf_info { + u8 read_ready:1; + u8 almost_full:1; + u8 almost_empty:1; + u8 overflow:1; + u8 underflow:1; + u16 pkt_num; +}; + +struct cxd2880_pid_config { + u8 is_enable; + u16 pid; +}; + +struct cxd2880_pid_filter_config { + u8 is_negative; + struct cxd2880_pid_config pid_config[CXD2880_MAX_FILTER_SIZE]; +}; + +struct cxd2880_dvb_spi { + struct dvb_frontend dvb_fe; + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend dmx_fe; + struct task_struct *cxd2880_ts_read_thread; + struct spi_device *spi; + struct mutex spi_mutex; /* For SPI access exclusive control */ + int feed_count; + int all_pid_feed_count; + u8 *ts_buf; + struct cxd2880_pid_filter_config filter_config; +}; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size) +{ + struct spi_message msg; + struct spi_transfer tx; + + if (!spi || !data) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + memset(&tx, 0, sizeof(tx)); + tx.tx_buf = data; + tx.len = size; + + spi_message_init(&msg); + spi_message_add_tail(&tx, &msg); + + return spi_sync(spi, &msg); +} + +static int cxd2880_write_reg(struct spi_device *spi, + u8 sub_address, const u8 *data, u32 size) +{ + u8 send_data[BURST_WRITE_MAX + 4]; + const u8 *write_data_top = NULL; + int ret = 0; + + if (!spi || !data) { + pr_err("invalid arg\n"); + return -EINVAL; + } + if (size > BURST_WRITE_MAX) { + pr_err("data size > WRITE_MAX\n"); + return -EINVAL; + } + + if (sub_address + size > 0x100) { + pr_err("out of range\n"); + return -EINVAL; + } + + send_data[0] = 0x0e; + write_data_top = data; + + while (size > 0) { + send_data[1] = sub_address; + if (size > 255) + send_data[2] = 255; + else + send_data[2] = (u8)size; + + memcpy(&send_data[3], write_data_top, send_data[2]); + + ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3); + if (ret) { + pr_err("write spi failed %d\n", ret); + break; + } + sub_address += send_data[2]; + write_data_top += send_data[2]; + size -= send_data[2]; + } + + return ret; +} + +static int cxd2880_spi_read_ts(struct spi_device *spi, + u8 *read_data, + u32 packet_num) +{ + int ret; + u8 data[3]; + struct spi_message message; + struct spi_transfer transfer[2]; + + if (!spi || !read_data || !packet_num) { + pr_err("invalid arg\n"); + return -EINVAL; + } + if (packet_num > 0xffff) { + pr_err("packet num > 0xffff\n"); + return -EINVAL; + } + + data[0] = 0x10; + data[1] = packet_num >> 8; + data[2] = packet_num; + + spi_message_init(&message); + memset(transfer, 0, sizeof(transfer)); + + transfer[0].len = 3; + transfer[0].tx_buf = data; + spi_message_add_tail(&transfer[0], &message); + transfer[1].len = packet_num * 188; + transfer[1].rx_buf = read_data; + spi_message_add_tail(&transfer[1], &message); + + ret = spi_sync(spi, &message); + if (ret) + pr_err("spi_write_then_read failed\n"); + + return ret; +} + +static int cxd2880_spi_read_ts_buffer_info(struct spi_device *spi, + struct cxd2880_ts_buf_info *info) +{ + u8 send_data = 0x20; + u8 recv_data[2]; + int ret; + + if (!spi || !info) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + ret = spi_write_then_read(spi, &send_data, 1, + recv_data, sizeof(recv_data)); + if (ret) + pr_err("spi_write_then_read failed\n"); + + info->read_ready = (recv_data[0] & 0x80) ? 1 : 0; + info->almost_full = (recv_data[0] & 0x40) ? 1 : 0; + info->almost_empty = (recv_data[0] & 0x20) ? 1 : 0; + info->overflow = (recv_data[0] & 0x10) ? 1 : 0; + info->underflow = (recv_data[0] & 0x08) ? 1 : 0; + info->pkt_num = ((recv_data[0] & 0x07) << 8) | recv_data[1]; + + return ret; +} + +static int cxd2880_spi_clear_ts_buffer(struct spi_device *spi) +{ + u8 data = 0x03; + int ret; + + ret = cxd2880_write_spi(spi, &data, 1); + + if (ret) + pr_err("write spi failed\n"); + + return ret; +} + +static int cxd2880_set_pid_filter(struct spi_device *spi, + struct cxd2880_pid_filter_config *cfg) +{ + u8 data[65]; + int i; + u16 pid = 0; + int ret; + + if (!spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + data[0] = 0x00; + ret = cxd2880_write_reg(spi, 0x00, &data[0], 1); + if (ret) + return ret; + if (!cfg) { + data[0] = 0x02; + ret = cxd2880_write_reg(spi, 0x50, &data[0], 1); + } else { + data[0] = cfg->is_negative ? 0x01 : 0x00; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + pid = cfg->pid_config[i].pid; + if (cfg->pid_config[i].is_enable) { + data[1 + (i * 2)] = (pid >> 8) | 0x20; + data[2 + (i * 2)] = pid & 0xff; + } else { + data[1 + (i * 2)] = 0x00; + data[2 + (i * 2)] = 0x00; + } + } + ret = cxd2880_write_reg(spi, 0x50, data, 65); + } + + return ret; +} + +static int cxd2880_update_pid_filter(struct cxd2880_dvb_spi *dvb_spi, + struct cxd2880_pid_filter_config *cfg, + bool is_all_pid_filter) +{ + int ret; + + if (!dvb_spi || !cfg) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + mutex_lock(&dvb_spi->spi_mutex); + if (is_all_pid_filter) { + struct cxd2880_pid_filter_config tmpcfg; + + memset(&tmpcfg, 0, sizeof(tmpcfg)); + tmpcfg.is_negative = 1; + tmpcfg.pid_config[0].is_enable = 1; + tmpcfg.pid_config[0].pid = 0x1fff; + + ret = cxd2880_set_pid_filter(dvb_spi->spi, &tmpcfg); + } else { + ret = cxd2880_set_pid_filter(dvb_spi->spi, cfg); + } + mutex_unlock(&dvb_spi->spi_mutex); + + if (ret) + pr_err("set_pid_filter failed\n"); + + return ret; +} + +static int cxd2880_ts_read(void *arg) +{ + struct cxd2880_dvb_spi *dvb_spi = NULL; + struct cxd2880_ts_buf_info info; + ktime_t start; + u32 i; + int ret; + + dvb_spi = arg; + if (!dvb_spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi); + if (ret) { + pr_err("set_clear_ts_buffer failed\n"); + return ret; + } + + start = ktime_get(); + while (!kthread_should_stop()) { + ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi, + &info); + if (ret) { + pr_err("spi_read_ts_buffer_info error\n"); + return ret; + } + + if (info.pkt_num > MAX_TRANS_PKT) { + for (i = 0; i < info.pkt_num / MAX_TRANS_PKT; i++) { + cxd2880_spi_read_ts(dvb_spi->spi, + dvb_spi->ts_buf, + MAX_TRANS_PKT); + dvb_dmx_swfilter(&dvb_spi->demux, + dvb_spi->ts_buf, + MAX_TRANS_PKT * 188); + } + start = ktime_get(); + } else if ((info.pkt_num > 0) && + (ktime_to_ms(ktime_sub(ktime_get(), start)) >= 500)) { + cxd2880_spi_read_ts(dvb_spi->spi, + dvb_spi->ts_buf, + info.pkt_num); + dvb_dmx_swfilter(&dvb_spi->demux, + dvb_spi->ts_buf, + info.pkt_num * 188); + start = ktime_get(); + } else { + usleep_range(10000, 11000); + } + } + + return 0; +} + +static int cxd2880_start_feed(struct dvb_demux_feed *feed) +{ + int ret = 0; + int i = 0; + struct dvb_demux *demux = NULL; + struct cxd2880_dvb_spi *dvb_spi = NULL; + + if (!feed) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + demux = feed->demux; + if (!demux) { + pr_err("feed->demux is NULL\n"); + return -EINVAL; + } + dvb_spi = demux->priv; + + if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) { + pr_err("Exceeded maximum PID count (32)."); + pr_err("Selected PID cannot be enabled.\n"); + return -EINVAL; + } + + if (feed->pid == 0x2000) { + if (dvb_spi->all_pid_feed_count == 0) { + ret = cxd2880_update_pid_filter(dvb_spi, + &dvb_spi->filter_config, + true); + if (ret) { + pr_err("update pid filter failed\n"); + return ret; + } + } + dvb_spi->all_pid_feed_count++; + + pr_debug("all PID feed (count = %d)\n", + dvb_spi->all_pid_feed_count); + } else { + struct cxd2880_pid_filter_config cfgtmp; + + cfgtmp = dvb_spi->filter_config; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + if (cfgtmp.pid_config[i].is_enable == 0) { + cfgtmp.pid_config[i].is_enable = 1; + cfgtmp.pid_config[i].pid = feed->pid; + pr_debug("store PID %d to #%d\n", + feed->pid, i); + break; + } + } + if (i == CXD2880_MAX_FILTER_SIZE) { + pr_err("PID filter is full. Assumed bug.\n"); + return -EINVAL; + } + if (!dvb_spi->all_pid_feed_count) + ret = cxd2880_update_pid_filter(dvb_spi, + &cfgtmp, + false); + if (ret) + return ret; + + dvb_spi->filter_config = cfgtmp; + } + + if (dvb_spi->feed_count == 0) { + dvb_spi->ts_buf = + kmalloc(MAX_TRANS_PKT * 188, + GFP_KERNEL | GFP_DMA); + if (!dvb_spi->ts_buf) { + pr_err("ts buffer allocate failed\n"); + memset(&dvb_spi->filter_config, 0, + sizeof(dvb_spi->filter_config)); + dvb_spi->all_pid_feed_count = 0; + return -ENOMEM; + } + dvb_spi->cxd2880_ts_read_thread = kthread_run(cxd2880_ts_read, + dvb_spi, + "cxd2880_ts_read"); + if (IS_ERR(dvb_spi->cxd2880_ts_read_thread)) { + pr_err("kthread_run failed/\n"); + kfree(dvb_spi->ts_buf); + dvb_spi->ts_buf = NULL; + memset(&dvb_spi->filter_config, 0, + sizeof(dvb_spi->filter_config)); + dvb_spi->all_pid_feed_count = 0; + return PTR_ERR(dvb_spi->cxd2880_ts_read_thread); + } + } + + dvb_spi->feed_count++; + + pr_debug("start feed (count %d)\n", dvb_spi->feed_count); + return 0; +} + +static int cxd2880_stop_feed(struct dvb_demux_feed *feed) +{ + int i = 0; + int ret; + struct dvb_demux *demux = NULL; + struct cxd2880_dvb_spi *dvb_spi = NULL; + + if (!feed) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + demux = feed->demux; + if (!demux) { + pr_err("feed->demux is NULL\n"); + return -EINVAL; + } + dvb_spi = demux->priv; + + if (!dvb_spi->feed_count) { + pr_err("no feed is started\n"); + return -EINVAL; + } + + if (feed->pid == 0x2000) { + /* + * Special PID case. + * Number of 0x2000 feed request was stored + * in dvb_spi->all_pid_feed_count. + */ + if (dvb_spi->all_pid_feed_count <= 0) { + pr_err("PID %d not found.\n", feed->pid); + return -EINVAL; + } + dvb_spi->all_pid_feed_count--; + } else { + struct cxd2880_pid_filter_config cfgtmp; + + cfgtmp = dvb_spi->filter_config; + + for (i = 0; i < CXD2880_MAX_FILTER_SIZE; i++) { + if (feed->pid == cfgtmp.pid_config[i].pid && + cfgtmp.pid_config[i].is_enable != 0) { + cfgtmp.pid_config[i].is_enable = 0; + cfgtmp.pid_config[i].pid = 0; + pr_debug("removed PID %d from #%d\n", + feed->pid, i); + break; + } + } + dvb_spi->filter_config = cfgtmp; + + if (i == CXD2880_MAX_FILTER_SIZE) { + pr_err("PID %d not found\n", feed->pid); + return -EINVAL; + } + } + + ret = cxd2880_update_pid_filter(dvb_spi, + &dvb_spi->filter_config, + dvb_spi->all_pid_feed_count > 0); + dvb_spi->feed_count--; + + if (dvb_spi->feed_count == 0) { + int ret_stop = 0; + + ret_stop = kthread_stop(dvb_spi->cxd2880_ts_read_thread); + if (ret_stop) { + pr_err("'kthread_stop failed. (%d)\n", ret_stop); + ret = ret_stop; + } + kfree(dvb_spi->ts_buf); + dvb_spi->ts_buf = NULL; + } + + pr_debug("stop feed ok.(count %d)\n", dvb_spi->feed_count); + + return ret; +} + +static const struct of_device_id cxd2880_spi_of_match[] = { + { .compatible = "sony,cxd2880" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, cxd2880_spi_of_match); + +static int +cxd2880_spi_probe(struct spi_device *spi) +{ + int ret; + struct cxd2880_dvb_spi *dvb_spi = NULL; + struct cxd2880_config config; + + if (!spi) { + pr_err("invalid arg.\n"); + return -EINVAL; + } + + dvb_spi = kzalloc(sizeof(struct cxd2880_dvb_spi), GFP_KERNEL); + if (!dvb_spi) + return -ENOMEM; + + dvb_spi->spi = spi; + mutex_init(&dvb_spi->spi_mutex); + dev_set_drvdata(&spi->dev, dvb_spi); + config.spi = spi; + config.spi_mutex = &dvb_spi->spi_mutex; + + ret = dvb_register_adapter(&dvb_spi->adapter, + "CXD2880", + THIS_MODULE, + &spi->dev, + adapter_nr); + if (ret < 0) { + pr_err("dvb_register_adapter() failed\n"); + goto fail_adapter; + } + + if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) { + pr_err("cxd2880_attach failed\n"); + goto fail_attach; + } + + ret = dvb_register_frontend(&dvb_spi->adapter, + &dvb_spi->dvb_fe); + if (ret < 0) { + pr_err("dvb_register_frontend() failed\n"); + goto fail_frontend; + } + + dvb_spi->demux.dmx.capabilities = DMX_TS_FILTERING; + dvb_spi->demux.priv = dvb_spi; + dvb_spi->demux.filternum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->demux.feednum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->demux.start_feed = cxd2880_start_feed; + dvb_spi->demux.stop_feed = cxd2880_stop_feed; + + ret = dvb_dmx_init(&dvb_spi->demux); + if (ret < 0) { + pr_err("dvb_dmx_init() failed\n"); + goto fail_dmx; + } + + dvb_spi->dmxdev.filternum = CXD2880_MAX_FILTER_SIZE; + dvb_spi->dmxdev.demux = &dvb_spi->demux.dmx; + dvb_spi->dmxdev.capabilities = 0; + ret = dvb_dmxdev_init(&dvb_spi->dmxdev, + &dvb_spi->adapter); + if (ret < 0) { + pr_err("dvb_dmxdev_init() failed\n"); + goto fail_dmxdev; + } + + dvb_spi->dmx_fe.source = DMX_FRONTEND_0; + ret = dvb_spi->demux.dmx.add_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + if (ret < 0) { + pr_err("add_frontend() failed\n"); + goto fail_dmx_fe; + } + + ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + if (ret < 0) { + pr_err("dvb_register_frontend() failed\n"); + goto fail_fe_conn; + } + + pr_info("Sony CXD2880 has successfully attached.\n"); + + return 0; + +fail_fe_conn: + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); +fail_dmx_fe: + dvb_dmxdev_release(&dvb_spi->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb_spi->demux); +fail_dmx: + dvb_unregister_frontend(&dvb_spi->dvb_fe); +fail_frontend: + dvb_frontend_detach(&dvb_spi->dvb_fe); +fail_attach: + dvb_unregister_adapter(&dvb_spi->adapter); +fail_adapter: + kfree(dvb_spi); + return ret; +} + +static int +cxd2880_spi_remove(struct spi_device *spi) +{ + struct cxd2880_dvb_spi *dvb_spi; + + if (!spi) { + pr_err("invalid arg\n"); + return -EINVAL; + } + + dvb_spi = dev_get_drvdata(&spi->dev); + + if (!dvb_spi) { + pr_err("failed\n"); + return -EINVAL; + } + dvb_spi->demux.dmx.remove_frontend(&dvb_spi->demux.dmx, + &dvb_spi->dmx_fe); + dvb_dmxdev_release(&dvb_spi->dmxdev); + dvb_dmx_release(&dvb_spi->demux); + dvb_unregister_frontend(&dvb_spi->dvb_fe); + dvb_frontend_detach(&dvb_spi->dvb_fe); + dvb_unregister_adapter(&dvb_spi->adapter); + + kfree(dvb_spi); + pr_info("cxd2880_spi remove ok.\n"); + + return 0; +} + +static const struct spi_device_id cxd2880_spi_id[] = { + { "cxd2880", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, cxd2880_spi_id); + +static struct spi_driver cxd2880_spi_driver = { + .driver = { + .name = "cxd2880", + .of_match_table = cxd2880_spi_of_match, + }, + .id_table = cxd2880_spi_id, + .probe = cxd2880_spi_probe, + .remove = cxd2880_spi_remove, +}; +module_spi_driver(cxd2880_spi_driver); + +MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver SPI adapter"); +MODULE_AUTHOR("Sony Semiconductor Solutions Corporation"); +MODULE_LICENSE("GPL v2"); -- 2.15.1