* Re: [PATCH V10 06/12] of: device: Fix overflow of coherent_dma_mask
From: Robin Murphy @ 2017-04-06 10:24 UTC (permalink / raw)
To: Frank Rowand, Sricharan R, will.deacon-5wv7dgnIgG8,
joro-zLv9SwRftAIdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
bhelgaas-hpIqsD4AKlfQT0dZR+AlfA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
linux-acpi-u79uwXL29TY76Z2rM5mHXA, tn-nYOzD4b6Jr9Wk0Htik3J/w,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A, okaya-sgV2jX0FEOL9JmXXK+q4OQ,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, sudeep.holla-5wv7dgnIgG8,
rjw-LthD3rsA81gm4RdzfppkhA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
catalin.marinas-5wv7dgnIgG8, arnd-r2nGTMty4D4,
linux-arch-u79uwXL29TY76Z2rM5mHXA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <58E5E7B7.1050400-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 06/04/17 08:01, Frank Rowand wrote:
> On 04/04/17 03:18, Sricharan R wrote:
>> Size of the dma-range is calculated as coherent_dma_mask + 1
>> and passed to arch_setup_dma_ops further. It overflows when
>> the coherent_dma_mask is set for full 64 bits 0xFFFFFFFFFFFFFFFF,
>> resulting in size getting passed as 0 wrongly. Fix this by
>> passsing in max(mask, mask + 1). Note that in this case
>> when the mask is set to full 64bits, we will be passing the mask
>> itself to arch_setup_dma_ops instead of the size. The real fix
>> for this should be to make arch_setup_dma_ops receive the
>> mask and handle it, to be done in the future.
>>
>> Signed-off-by: Sricharan R <sricharan-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> ---
>> drivers/of/device.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/of/device.c b/drivers/of/device.c
>> index c17c19d..c2ae6bb 100644
>> --- a/drivers/of/device.c
>> +++ b/drivers/of/device.c
>> @@ -107,7 +107,7 @@ void of_dma_configure(struct device *dev, struct device_node *np)
>> ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
>> if (ret < 0) {
>> dma_addr = offset = 0;
>> - size = dev->coherent_dma_mask + 1;
>> + size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1);
>> } else {
>> offset = PFN_DOWN(paddr - dma_addr);
>> dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
>>
>
> NACK.
>
> Passing an invalid size to arch_setup_dma_ops() is only part of the problem.
> size is also used in of_dma_configure() before calling arch_setup_dma_ops():
>
> dev->coherent_dma_mask = min(dev->coherent_dma_mask,
> DMA_BIT_MASK(ilog2(dma_addr + size)));
> *dev->dma_mask = min((*dev->dma_mask),
> DMA_BIT_MASK(ilog2(dma_addr + size)));
>
> which would be incorrect for size == 0xffffffffffffffffULL when
> dma_addr != 0. So the proposed fix really is not papering over
> the base problem very well.
I'm not sure I agree there. Granted, there exist many more problematic
aspects than are dealt with here (I've got more patches cooking to sort
out some of the other issues we have with dma-ranges), but considering
size specifically:
- It is not possible to explicitly specify a range with a size of 2^64
in DT. If someone does specify a size of 0, they've done a silly thing
and should not be surprised that it ends badly.
- It *is* perfectly legitimate for bus code (or a previous device
driver, once we start coming here at probe time) to have set a device's
DMA mask to 0xffffffffffffffffULL. If this code then blindly overflows
and infers an invalid size of 0 from that, breaking things in the
process, that is this code's fault alone. It just so happens that
nothing managed to trigger the latent problem until patch #7 here shakes
up the callsites.
Yes, wacky impossible base + size combinations in DT were a theoretical
problem before, and remain a theoretical problem, but also fall into the
"how did you ever expect this to work?" category. There's certainly
plenty more we can do to improve the DT parsing/validation, but that
still doesn't apply to this path where the information is *not* coming
from the DT at all.
> I agree that the proper solution involves passing a mask instead
> of a size to arch_setup_dma_ops().
Having started writing that patch too, I can tell you it's a big bugger
touching multiple architectures and fixing up various drivers doing
stupid things, hence why I'm happy with this point fix being the lesser
of two evils in terms of not holding up this mostly-orthogonal series.
Robin.
>
> -Frank
>
^ permalink raw reply
* Re: [PATCH V8 0/4] phy: USB and PCIe phy drivers for Qcom chipsets
From: Kishon Vijay Abraham I @ 2017-04-06 10:11 UTC (permalink / raw)
To: Vivek Gautam, robh+dt
Cc: mark.rutland, devicetree, linux-arm-msm, linux-usb, sboyd,
linux-kernel, bjorn.andersson, srinivas.kandagatla,
linux-arm-kernel
In-Reply-To: <1491457885-5254-1-git-send-email-vivek.gautam@codeaurora.org>
On Thursday 06 April 2017 11:21 AM, Vivek Gautam wrote:
> Hi Kishon,
> Here's the series with fixed checkpatch warnings/checks.
> Please pick it for phy/next.
>
> This patch series adds couple of PHY drivers for Qualcomm chipsets.
> a) qcom-qusb2 phy driver: that provides High Speed USB functionality.
> b) qcom-qmp phy driver: that is a combo phy providing support for
> USB3, PCIe, UFS and few other controllers.
>
> The patches are based on next branch of linux-phy tree.
>
> These patches have been tested on Dragon board db820c hardware with
> required set of dt patches.
> The tested branch[3] is based on torvald's master with greg's usb/usb-next
> merged. Additionally the patches to get rpm up on msm8996 are also pulled
> in.
merged, thanks!
-Kishon
>
> Changes since v7:
> - Fixed 'checkpatch --strict' alignment warnings/checks, and
> added Stephen's Reviewed-by tag.
>
> Changes since v6:
> - Rebased on phy/next and *not* including phy grouping series[4].
> - qusb2-phy: addressed Stephen's comment.
> - Dropped pm8994_s2 corner regulator from QUSB2 phy bindings.
> - qmp-phy: none on functionality side.
>
> Changes since v5:
> - Addressed review comments from Bjorn:
> - Removed instances of readl/wirtel_relaxed calls from the drivers.
> Instead, using simple readl/writel. Inserting a readl after a writel
> to ensure the write is through to the device.
> - Replaced regulator handling with regulator_bulk_** apis. This helps
> in cutting down a lot of regulator handling code.
> - Fixed minor return statements.
>
> Changes since v4:
> - Addressed comment to add child nodes for qmp phy driver. Each phy lane
> now has a separate child node under the main qmp node.
> - Modified the clock and reset initialization and enable methods.
> Different phys - pcie, usb and later ufs, have varying number of clocks
> and resets that are mandatory. So adding provision for clocks and reset
> lists helps in requesting all mandatory resources for individual phys
> and handle their failure cases accordingly.
>
> Changes since v3:
> - Addressed review comments given by Rob and Stephen for qusb2 phy
> and qmp phy bindings respectively.
> - Addressed review comments given by Stephen and Bjorn for qmp phy driver.
>
> Changes since v2:
> - Addressed review comments given by Rob and Stephen for bindings.
> - Addressed the review comments given by Stephen for the qusb2 and qmp
> phy drivers.
>
> Changes since v1:
> - Moved device tree binding documentation to separate patches, as suggested
> by Rob.
> - Addressed review comment regarding qfprom accesses by qusb2 phy driver,
> given by Rob.
> - Addressed review comments from Kishon.
> - Addressed review comments from Srinivas for QMP phy driver.
> - Addressed kbuild warning.
>
> Please see individual patches for detailed changelogs.
>
> [1] https://patchwork.kernel.org/patch/9567767/
> [2] https://patchwork.kernel.org/patch/9567779/
> [3] https://github.com/vivekgautam1/linux/tree/linux-v4.11-rc5-qmp-phy-db820c
> [4] https://lkml.org/lkml/2017/3/20/407
>
> Vivek Gautam (4):
> dt-bindings: phy: Add support for QUSB2 phy
> phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips
> dt-bindings: phy: Add support for QMP phy
> phy: qcom-qmp: new qmp phy driver for qcom-chipsets
>
> .../devicetree/bindings/phy/qcom-qmp-phy.txt | 106 ++
> .../devicetree/bindings/phy/qcom-qusb2-phy.txt | 43 +
> drivers/phy/Kconfig | 18 +
> drivers/phy/Makefile | 2 +
> drivers/phy/phy-qcom-qmp.c | 1153 ++++++++++++++++++++
> drivers/phy/phy-qcom-qusb2.c | 493 +++++++++
> 6 files changed, 1815 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> create mode 100644 Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
> create mode 100644 drivers/phy/phy-qcom-qmp.c
> create mode 100644 drivers/phy/phy-qcom-qusb2.c
>
^ permalink raw reply
* Re: [PATCH V10 03/12] of: dma: Move range size workaround to of_dma_get_range()
From: Robin Murphy @ 2017-04-06 10:03 UTC (permalink / raw)
To: Frank Rowand, Sricharan R, will.deacon-5wv7dgnIgG8,
joro-zLv9SwRftAIdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
bhelgaas-hpIqsD4AKlfQT0dZR+AlfA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
linux-acpi-u79uwXL29TY76Z2rM5mHXA, tn-nYOzD4b6Jr9Wk0Htik3J/w,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A, okaya-sgV2jX0FEOL9JmXXK+q4OQ,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, sudeep.holla-5wv7dgnIgG8,
rjw-LthD3rsA81gm4RdzfppkhA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
catalin.marinas-5wv7dgnIgG8, arnd-r2nGTMty4D4,
linux-arch-u79uwXL29TY76Z2rM5mHXA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <58E5DF13.2020700-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Hi Frank,
On 06/04/17 07:24, Frank Rowand wrote:
> On 04/04/17 03:18, Sricharan R wrote:
>> From: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
>>
>> Invalid dma-ranges values should be worked around when retrieving the
>> DMA range in of_dma_get_range(), not by all callers of the function.
>> This isn't much of a problem now that we have a single caller, but that
>> situation will change when moving DMA configuration to device probe
>> time.
>
> Please drop this patch from the series. I just now sent Rob an
> alternative that reflects other changes that have occurred since
> this patch series was first created many, many moons ago
> (https://lkml.org/lkml/2017/4/6/53).
Fine by me too - if we can get rid of that dubious fixup altogether, all
the better. Feel free to transfer my Reviewed-by to your patch if you like.
Thanks,
Robin.
> Thanks,
>
> Frank
>
>>
>> Tested-by: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
>> ---
>> drivers/of/address.c | 20 ++++++++++++++++++--
>> drivers/of/device.c | 15 ---------------
>> 2 files changed, 18 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/of/address.c b/drivers/of/address.c
>> index 02b2903..6aeb816 100644
>> --- a/drivers/of/address.c
>> +++ b/drivers/of/address.c
>> @@ -819,8 +819,8 @@ void __iomem *of_io_request_and_map(struct device_node *np, int index,
>> * CPU addr (phys_addr_t) : pna cells
>> * size : nsize cells
>> *
>> - * It returns -ENODEV if "dma-ranges" property was not found
>> - * for this device in DT.
>> + * Return 0 on success, -ENODEV if the "dma-ranges" property was not found for
>> + * this device in DT, or -EINVAL if the CPU address or size is invalid.
>> */
>> int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size)
>> {
>> @@ -880,6 +880,22 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz
>> *dma_addr = dmaaddr;
>>
>> *size = of_read_number(ranges + naddr + pna, nsize);
>> + /*
>> + * DT nodes sometimes incorrectly set the size as a mask. Work around
>> + * those incorrect DT by computing the size as mask + 1.
>> + */
>> + if (*size & 1) {
>> + pr_warn("%s: size 0x%llx for dma-range in node(%s) set as mask\n",
>> + __func__, *size, np->full_name);
>> + *size = *size + 1;
>> + }
>> +
>> + if (!*size) {
>> + pr_err("%s: invalid size zero for dma-range in node(%s)\n",
>> + __func__, np->full_name);
>> + ret = -EINVAL;
>> + goto out;
>> + }
>>
>> pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
>> *dma_addr, *paddr, *size);
>> diff --git a/drivers/of/device.c b/drivers/of/device.c
>> index b1e6beb..09dedd0 100644
>> --- a/drivers/of/device.c
>> +++ b/drivers/of/device.c
>> @@ -110,21 +110,6 @@ void of_dma_configure(struct device *dev, struct device_node *np)
>> size = dev->coherent_dma_mask + 1;
>> } else {
>> offset = PFN_DOWN(paddr - dma_addr);
>> -
>> - /*
>> - * Add a work around to treat the size as mask + 1 in case
>> - * it is defined in DT as a mask.
>> - */
>> - if (size & 1) {
>> - dev_warn(dev, "Invalid size 0x%llx for dma-range\n",
>> - size);
>> - size = size + 1;
>> - }
>> -
>> - if (!size) {
>> - dev_err(dev, "Adjusted size 0x%llx invalid\n", size);
>> - return;
>> - }
>> dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
>> }
>>
>>
^ permalink raw reply
* Re: [PATCH V5 2/4] dt-bindings: arm: Add bindings for SP9860G
From: Chunyan Zhang @ 2017-04-06 10:00 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org, Mark Rutland,
Catalin Marinas, Will Deacon, Arnd Bergmann, Mathieu Poirier,
Orson Zhai (翟京),
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Chunyan Zhang
In-Reply-To: <1490594802-27073-3-git-send-email-chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
Hello Greg,
I guess there will be no further comments on this patchset :), could
you please take the patches 2/3/4 through tty git?
Thanks,
Chunyan
On 27 March 2017 at 14:06, Chunyan Zhang <chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org> wrote:
> Added bindings for Spreadtrum SP9860G board and SC9860 SoC.
> This patch also revised bindings of SC9836 to make the format
> more clear.
>
> Signed-off-by: Chunyan Zhang <chunyan.zhang-lxIno14LUO0EEoCn2XhGlw@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> Documentation/devicetree/bindings/arm/sprd.txt | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/sprd.txt b/Documentation/devicetree/bindings/arm/sprd.txt
> index 31a629d..3df034b 100644
> --- a/Documentation/devicetree/bindings/arm/sprd.txt
> +++ b/Documentation/devicetree/bindings/arm/sprd.txt
> @@ -1,11 +1,14 @@
> Spreadtrum SoC Platforms Device Tree Bindings
> ----------------------------------------------------
>
> -Sharkl64 is a Spreadtrum's SoC Platform which is based
> -on ARM 64-bit processor.
> +SC9836 openphone Board
> +Required root node properties:
> + - compatible = "sprd,sc9836-openphone", "sprd,sc9836";
>
> -SC9836 openphone board with SC9836 SoC based on the
> -Sharkl64 Platform shall have the following properties.
> +SC9860 SoC
> +Required root node properties:
> + - compatible = "sprd,sc9860"
>
> +SP9860G 3GFHD Board
> Required root node properties:
> - - compatible = "sprd,sc9836-openphone", "sprd,sc9836";
> + - compatible = "sprd,sp9860g-1h10", "sprd,sc9860";
> --
> 2.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC] [media] imx: assume MEDIA_ENT_F_ATV_DECODER entities output video on pad 1
From: Philipp Zabel @ 2017-04-06 9:57 UTC (permalink / raw)
To: Mauro Carvalho Chehab
Cc: Russell King - ARM Linux, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
Steve Longerbeam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, fabio.estevam-3arQi8VN3Tc,
hverkuil-qWit8jRvyhVmR6Xm/wNWPw, nick-gcszYUEDH4VrovVCs/uTlw,
markus.heiser-O6JHGLzbNUwb1SvskN2V4Q,
laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw,
bparrot-l0cyMroinI0, geert-Td1EMuHUCqxL1ZNQvxDV9g,
arnd-r2nGTMty4D4, sudipm.mukherjee-Re5JQEeQqe8AvxtiuMwx3w,
minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w,
tiffany.lin-NuS5LvNUpcJWk0Htik3J/w,
jean-christophe.trotin-qxv4g6HH51o,
horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ,
niklas.soderlund+renesas-1zkq55x86MTxsAP9Fp7wbw,
robert.jarzmik-GANU6spQydw, songjun.wu-UWL1GkI3JZL3oGB3hsPCZA,
andrew-ct.chen-NuS5LvNUpcJWk0Htik3J/w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
shuah-DgEjT+Ai2ygdnm+yROfE0A, sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
pavel-+ZI9xUNit7I, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170405115336.7135e542-ch4gOOMV7nf/PtFMR13I2A@public.gmane.org>
On Wed, 2017-04-05 at 11:53 -0300, Mauro Carvalho Chehab wrote:
[...]
> There are a number of drivers that can work with different
> types of TV demodulators. Typical examples of such hardware can be
> found at em28xx, saa7134, cx88 drivers (among lots of other drivers).
> Those drivers don't use the subdev API. Instead, they use a generic
> helper function with sets the pipelines, based on the pad number.
>
> The problem here is that, currently, both MC API and MC core
> lacks a way to identify PAD ports per type, as the only information
> that a bridge driver has is a pad number. So, in order for a
> generic helper function to work, we had to hardcode pad numbers,
> in a way that it would work for all possible types of demods.
>
> It shouldn't be hard to add a "pad_type" information at media_pad
> struct, but passing such info to userspace requires a new API
> (we're calling it as "properties API"). Sakari was meant to send
> us an updated RFC for it[1] with a patchset, back in 2015, but
> this never happened.
>
> [1] https://linuxtv.org/news.php?entry=2015-08-17.mchehab
[...]
That would be most useful.
> So, in short, the tvp5150 demod doesn't decode audio, but there
> are other demods that do it.
>
> In the case of VBI, tvp5150 has actually two ways of reporting
> it:
>
> 1) via YOUT[7:0] pins. VBI information is transmitted as a
> set of raw samples, via an ancillary data block, during
> vertical/horizontal blanking intervals. So, yes, it shares
> the same hardware output, although the VBI contents are
> actually multiplexed there. Please notice that not all
> video out PADS encapsulate raw VBI the same way as tvp5150
> (and some devices even don't support raw VBI, like saa7110 and
> some models supported by saa7115 driver).
This is the physical interface that corresponds to the output port
(should be port@1) in the device tree. It should correspond to the video
output media entity pad.
What is unclear to is me whether the VBI media entity pad also should
correspond to the same physical interface / DT port.
> 2) via an interrupt that indicates that it decoded VBI data. The
> VBI information itself is there on FIFO, accessible via a set of
> registers (see "VBI Data processor" chapter at the datasheet).
>
> Currently, the driver doesn't support (2), because, at the time
> I wrote the driver, I didn't find a way to read the interrupts generated
> by tvp5150 at em28xx[1], due to the lack of em28xx documentation,
> but adding support for it shoudn't be hard. I may eventually do it
> when I have some time to play with my ISEE hardware.
>
> So, in the case of tvp5150 hardware, have those PADS:
>
> - Input baseband;
> - Video + raw VBI output;
> - sliced VBI output.
This DEMOD_PAD_VBI_OUT, does it correspond to 1) or 2) above?
> Yet, we need an always unconnected audio output, in order to support
> different demods out there.
Are you saying we have to keep pad[DEMOD_PAD_AUDIO_OUT] in tvp5150 even
though it doesn't exist because the framework can't cope with an
audio-less ATV_DECODER that only has three pads?
> [1] tvp5150 was written to support some em28xx-based devices
>
> >
> > > So, it has one input and three outputs. How does marking the direction
> > > in the port node (which would indicate that there was a data flow out of
> > > TVP5150 into the iMX6 capture) help identify which of those pads should
> > > be used?
> > >
> > > It would eliminate the input pad, but you still have three output pads
> > > to choose from.
> > >
> > > So no, your idea can't work.
> >
> > In this case, removal of the VBI and audio pads might make this work,
> > but in general this is true. In my opinion, to make this truly generic,
> > we need an interface to ask the driver which media entity pad a given
> > device tree port corresponds to, as there might not even be a single
> > media entity corresponding to all ports for more complex devices.
>
> Yes. We also need something like that at the userspace API.
thanks
Philipp
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/4] arm64: renesas: enable M3ULCB board peripherals
From: Geert Uytterhoeven @ 2017-04-06 9:53 UTC (permalink / raw)
To: Simon Horman
Cc: Vladimir Barinov, Magnus Damm, Rob Herring, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux-Renesas,
Sjoerd Simons
In-Reply-To: <1489788135.8957.2.camel-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>
Hi Simon,
On Fri, Mar 17, 2017 at 11:02 PM, Sjoerd Simons
<sjoerd.simons-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org> wrote:
> On Thu, 2017-01-26 at 17:53 +0300, Vladimir Barinov wrote:
>> This adds the folowing:
>> - R8A7796 SoC based M3ULCB board peripherals
>>
>> Vladimir Barinov (4):
>> [1/4] arm64: dts: m3ulcb: enable I2C
>> [2/4] arm64: dts: m3ulcb: Update memory node to 2 GiB map
>> [3/4] arm64: dts: m3ulcb: enable EthernetAVB
>> [4/4] arm64: dts: m3ulcb: enable HS200 for eMMC
>
> Seems these didn't hit -next just yet, for this series (tested on a
> M3ULCB)
>
> Tested-By: Sjoerd Simons <sjoerd.simons-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>
It seems this series is still pending? Any reason (not) to apply it?
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v6 19/39] media: Add i.MX media core driver
From: Philipp Zabel @ 2017-04-06 9:43 UTC (permalink / raw)
To: Steve Longerbeam
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
fabio.estevam-3arQi8VN3Tc, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
nick-gcszYUEDH4VrovVCs/uTlw, markus.heiser-O6JHGLzbNUwb1SvskN2V4Q,
laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw,
bparrot-l0cyMroinI0, geert-Td1EMuHUCqxL1ZNQvxDV9g,
arnd-r2nGTMty4D4, sudipm.mukherjee-Re5JQEeQqe8AvxtiuMwx3w,
minghsiu.tsai-NuS5LvNUpcJWk0Htik3J/w,
tiffany.lin-NuS5LvNUpcJWk0Htik3J/w,
jean-christophe.trotin-qxv4g6HH51o,
horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ,
niklas.soderlund+renesas-1zkq55x86MTxsAP9Fp7wbw,
robert.jarzmik-GANU6spQydw, songjun.wu-UWL1GkI3JZL3oGB3hsPCZA,
andrew-ct.chen-NuS5LvNUpcJWk0Htik3J/w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
shuah-DgEjT+Ai2ygdnm+yROfE0A, sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
pavel-+ZI9xUNit7I, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490661656-10318-20-git-send-email-steve_longerbeam-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
On Mon, 2017-03-27 at 17:40 -0700, Steve Longerbeam wrote:
> Add the core media driver for i.MX SOC.
>
> Signed-off-by: Steve Longerbeam <steve_longerbeam-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
[...]
> diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
> new file mode 100644
> index 0000000..b383be4
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media-of.c
> @@ -0,0 +1,267 @@
[...]
> +/*
> + * find the remote device node and remote port id (remote pad #)
> + * given local endpoint node
> + */
> +static void of_get_remote_pad(struct device_node *epnode,
> + struct device_node **remote_node,
> + int *remote_pad)
> +{
> + struct device_node *rp, *rpp;
> + struct device_node *remote;
> +
> + rp = of_graph_get_remote_port(epnode);
> + rpp = of_graph_get_remote_port_parent(epnode);
> +
> + if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) {
> + /* the remote is one of the CSI ports */
> + remote = rp;
> + *remote_pad = 0;
> + of_node_put(rpp);
> + } else {
> + remote = rpp;
> + of_property_read_u32(rp, "reg", remote_pad);
If this fails because there is no reg property, *remote_pad will keep
the previous value. It should be set to 0 in this case.
----------8<----------
--- a/drivers/staging/media/imx/imx-media-of.c
+++ b/drivers/staging/media/imx/imx-media-of.c
@@ -85,7 +85,9 @@ static void of_get_remote_pad(struct device_node *epnode,
of_node_put(rpp);
} else {
remote = rpp;
- of_property_read_u32(rp, "reg", remote_pad);
+ /* FIXME port number and pad index are not the same */
+ if (of_property_read_u32(rp, "reg", remote_pad))
+ *remote_pad = 0;
of_node_put(rp);
}
---------->8----------
regards
Philipp
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] clk: stm32h7: Add stm32h743 clock driver
From: Gabriel Fernandez @ 2017-04-06 9:35 UTC (permalink / raw)
To: Stephen Boyd
Cc: Mark Rutland, devicetree, daniel.thompson, radoslaw.pietrzyk,
Alexandre Torgue, Arnd Bergmann, Nicolas Pitre, andrea.merello,
Michael Turquette, olivier.bideau, Russell King, linux-kernel,
Rob Herring, ludovic.barre, Maxime Coquelin, amelie.delaunay,
Lee Jones, linux-clk, linux-arm-kernel
In-Reply-To: <20170405223233.GJ7065@codeaurora.org>
Hi Stephen,
On 04/06/2017 12:32 AM, Stephen Boyd wrote:
> On 03/15, gabriel.fernandez@st.com wrote:
>> From: Gabriel Fernandez <gabriel.fernandez@st.com>
>>
>> This patch enables clocks for STM32H743 boards.
> Like what clocks exactly? All of them?
>
Yes all of them, it's new IP
>> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt
>> new file mode 100644
>> index 0000000..9d4b587
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt
>> @@ -0,0 +1,152 @@
>> +STMicroelectronics STM32H7 Reset and Clock Controller
>> +=====================================================
>> +
>> +The RCC IP is both a reset and a clock controller.
>> +
>> +Please refer to clock-bindings.txt for common clock controller binding usage.
>> +Please also refer to reset.txt for common reset controller binding usage.
>> +
>> +Required properties:
>> +- compatible: Should be:
>> + "st,stm32h743-rcc"
>> +
>> +- reg: should be register base and length as documented in the
>> + datasheet
>> +
>> +- #reset-cells: 1, see below
>> +
>> +- #clock-cells : from common clock binding; shall be set to 1
>> +
>> +- clocks: External oscillator clock phandle
>> + - high speed external clock signal (HSE)
>> + - low speed external clock signal (LSE)
>> + - external I2S clock (I2S_CKIN)
>> +
>> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
>> + write protection (RTC clock).
>> +
>> +- pll x node: Allow to register a pll with specific parameters.
>> + Please see PLL section below.
>> +
>> +Example:
>> +
>> + rcc: rcc@58024400 {
>> + #reset-cells = <1>;
>> + #clock-cells = <2>
>> + compatible = "st,stm32h743-rcc", "st,stm32-rcc";
>> + reg = <0x58024400 0x400>;
>> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>;
>> +
>> + st,syscfg = <&pwrcfg>;
>> +
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + vco1@58024430 {
>> + #clock-cells = <0>;
>> + compatible = "stm32,pll";
>> + reg = <0>;
> reg is super confusing and doesn't match unit address.
ok i fixed it in the v2
>
>> + };
> Why? Shouldn't we know this from the compatible string how many
> PLLs there are and where they're located? Export the PLLs through
> rcc node's clock-cells?
>
Because i need to offer the possibility to change the PLL VCO
frequencies at the start-up of this driver clock.
The VCO algorithm needs a division factor, a multiplication factor and a
fractional factor.
Lot's of solution are possible for one frequency and it's nightmare to
satisfy the 3 output dividers of the PLL.
>> +
>> + vco2@58024438 {
>> + #clock-cells = <0>;
>> + compatible = "stm32,pll";
>> + reg = <1>;
> reg is super confusing and doesn't match unit address.
>
>> + st,clock-div = <2>;
>> + st,clock-mult = <40>;
>> + st,frac-status = <0>;
>> + st,frac = <0>;
>> + st,vcosel = <1>;
>> + st,pllrge = <2>;
> Does this stuff change on a per-board basis? I hope none of these
> properties need to be in DT.
These properties are optionals.
I absolute need it to custumize VCO frequencies of a pll without the
boot loader..
i suppressed "st,frac-status" and "st,pllrge" in the v2
>
>> + };
>> + };
>> +
>> +
>> +STM32H7 PLL
>> +-----------
>> +
> [...]
>> +
>> +Specifying softreset control of devices
>> +=======================================
>> +
>> +Device nodes should specify the reset channel required in their "resets"
>> +property, containing a phandle to the reset device node and an index specifying
>> +which channel to use.
>> +The index is the bit number within the RCC registers bank, starting from RCC
>> +base address.
>> +It is calculated as: index = register_offset / 4 * 32 + bit_offset.
>> +Where bit_offset is the bit offset within the register.
>> +
>> +For example, for CRC reset:
>> + crc = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107
>> +
>> +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h
> One too many slashes?
ok i will fix it
>
>> +header and can be used in device tree sources.
>> +
>> +example:
>> +
>> + timer2 {
>> + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>;
>> + };
>> diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c
>> new file mode 100644
>> index 0000000..c8eb729
>> --- /dev/null
>> +++ b/drivers/clk/clk-stm32h7.c
>> @@ -0,0 +1,1586 @@
>> +/*
>> + * Copyright (C) Gabriel Fernandez 2017
>> + * Author: Gabriel Fernandez <gabriel.fernandez@st.com>
>> + *
>> + * License terms: GPL V2.0.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
> Is this used?
No i will suppress it
>
>> +#include <linux/clk-provider.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/regmap.h>
>> +
>> +#include <dt-bindings/clock/stm32h7-clks.h>
>> +
>> +/* Reset Clock Control Registers */
>> +#define RCC_CR 0x00
>> +#define RCC_CFGR 0x10
>> +#define RCC_D1CFGR 0x18
>> +#define RCC_D2CFGR 0x1C
>> +#define RCC_D3CFGR 0x20
>> +#define RCC_PLLCKSELR 0x28
>> +#define RCC_PLLCFGR 0x2C
>> +#define RCC_PLL1DIVR 0x30
>> +#define RCC_PLL1FRACR 0x34
>> +#define RCC_PLL2DIVR 0x38
>> +#define RCC_PLL2FRACR 0x3C
>> +#define RCC_PLL3DIVR 0x40
>> +#define RCC_PLL3FRACR 0x44
>> +#define RCC_D1CCIPR 0x4C
>> +#define RCC_D2CCIP1R 0x50
>> +#define RCC_D2CCIP2R 0x54
>> +#define RCC_D3CCIPR 0x58
>> +#define RCC_BDCR 0x70
>> +#define RCC_CSR 0x74
>> +#define RCC_AHB3ENR 0xD4
>> +#define RCC_AHB1ENR 0xD8
>> +#define RCC_AHB2ENR 0xDC
>> +#define RCC_AHB4ENR 0xE0
>> +#define RCC_APB3ENR 0xE4
>> +#define RCC_APB1LENR 0xE8
>> +#define RCC_APB1HENR 0xEC
>> +#define RCC_APB2ENR 0xF0
>> +#define RCC_APB4ENR 0xF4
>> +
>> +static DEFINE_SPINLOCK(rlock);
> This is super generic and will make lockdep debugging sad.
> Perhaps stm32rcc_lock?
ok
>
>> +
>> +static void __iomem *base;
>> +static struct regmap *pdrm;
>> +static struct clk_hw **hws;
>> +
>> +/* System clock parent */
>> +static const char * const sys_src[] = {
>> + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" };
>> +
>> +static const char * const tracein_src[] = {
>> + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" };
> [...]
>> +
>> +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + struct stm32_pll_obj *clk_elem = to_pll(hw);
>> + struct stm32_fractional_divider *fd = &clk_elem->div;
>> + unsigned long m, n;
>> + u32 val, mask;
>> + u64 rate, rate1 = 0;
>> +
>> + val = clk_readl(fd->mreg);
> Please don't use clk_readl() unless you need it for some reason.
ok
>
>> + mask = (GENMASK(fd->mwidth - 1, 0) << fd->mshift);
>> + m = (val & mask) >> fd->mshift;
>> +
>> + val = clk_readl(fd->nreg);
>> + mask = (GENMASK(fd->nwidth - 1, 0) << fd->nshift);
> Useless parentheses. And isn't GENMASK supposed to take the
> actual bit positions? Then we avoid overflow issues?
ok il will fix it.
>> + n = ((val & mask) >> fd->nshift) + 1;
>> +
>> + if (!n || !m)
>> + return parent_rate;
>> +
>> + rate = (u64)parent_rate * n;
>> + do_div(rate, m);
>> +
>> + if (pll_frac_is_enabled(hw)) {
>> + val = pll_read_frac(hw);
>> + rate1 = (u64) parent_rate * (u64) val;
>> + do_div(rate1, (m * 8191));
>> + }
>> +
>> + return rate + rate1;
>> +}
>> +
> [...]
>> +
>> + /* Micro-controller clocks */
>> + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) {
>> + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg,
>> + &rlock);
>> +
>> + hws[MCO_BANK + n] = clk_hw_register_composite(NULL,
>> + mco_clk[n].name,
>> + mco_clk[n].parent_name,
>> + mco_clk[n].num_parents,
>> + c_cfg.mux_hw, c_cfg.mux_ops,
>> + c_cfg.div_hw, c_cfg.div_ops,
>> + c_cfg.gate_hw, c_cfg.gate_ops,
>> + mco_clk[n].flags);
>> + }
>> +
>> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
>> +
>> + return;
>> +
>> +err_free_clks:
>> + kfree(clk_data);
>> +}
>> +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init);
> Is there another driver that uses the same register space?
> Nothing showing up in -next right now. Perhaps a comment should
> be added to indicate the other driver.
Yes the reset controler.
ok il will add a comment.
BR
Gabriel
^ permalink raw reply
* Re: [PATCH V10 03/12] of: dma: Move range size workaround to of_dma_get_range()
From: Sricharan R @ 2017-04-06 9:35 UTC (permalink / raw)
To: Frank Rowand, robin.murphy-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
joro-zLv9SwRftAIdnm+yROfE0A, lorenzo.pieralisi-5wv7dgnIgG8,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
bhelgaas-hpIqsD4AKlfQT0dZR+AlfA, linux-pci-u79uwXL29TY76Z2rM5mHXA,
linux-acpi-u79uwXL29TY76Z2rM5mHXA, tn-nYOzD4b6Jr9Wk0Htik3J/w,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A, okaya-sgV2jX0FEOL9JmXXK+q4OQ,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, sudeep.holla-5wv7dgnIgG8,
rjw-LthD3rsA81gm4RdzfppkhA, lenb-DgEjT+Ai2ygdnm+yROfE0A,
catalin.marinas-5wv7dgnIgG8, arnd-r2nGTMty4D4,
linux-arch-u79uwXL29TY76Z2rM5mHXA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <58E5DF13.2020700-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Hi Frank,
On 4/6/2017 11:54 AM, Frank Rowand wrote:
> On 04/04/17 03:18, Sricharan R wrote:
>> From: Laurent Pinchart <laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
>>
>> Invalid dma-ranges values should be worked around when retrieving the
>> DMA range in of_dma_get_range(), not by all callers of the function.
>> This isn't much of a problem now that we have a single caller, but that
>> situation will change when moving DMA configuration to device probe
>> time.
>
> Please drop this patch from the series. I just now sent Rob an
> alternative that reflects other changes that have occurred since
> this patch series was first created many, many moons ago
> (https://lkml.org/lkml/2017/4/6/53).
ok, would drop this.
Regards,
Sricharan
--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* [PATCH v3 3/3] clk: vc5: Add support for IDT VersaClock 5P49V5935
From: Alexey Firago @ 2017-04-06 9:15 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, marek.vasut, geert, linux-clk,
devicetree
Cc: Alexey Firago
In-Reply-To: <1491470130-6655-1-git-send-email-alexey_firago@mentor.com>
Update IDT VersaClock 5 driver to support 5P49V5935. This chip has
two clock inputs (internal XTAL or external CLKIN), four fractional
dividers (FODs) and five clock outputs (four universal clock outputs
and one reference clock output at OUT0_SELB_I2C).
Current driver supports up to 2 FODs and up to 3 clock outputs. This
patch sets max number of supported FODs to 4 and max number of supported
clock outputs to 5.
Signed-off-by: Alexey Firago <alexey_firago@mentor.com>
---
drivers/clk/clk-versaclock5.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
index 4e81fb1..34c4e5a 100644
--- a/drivers/clk/clk-versaclock5.c
+++ b/drivers/clk/clk-versaclock5.c
@@ -114,10 +114,10 @@
#define VC5_MUX_IN_CLKIN BIT(1)
/* Maximum number of clk_out supported by this driver */
-#define VC5_MAX_CLK_OUT_NUM 3
+#define VC5_MAX_CLK_OUT_NUM 5
/* Maximum number of FODs supported by this driver */
-#define VC5_MAX_FOD_NUM 2
+#define VC5_MAX_FOD_NUM 4
/* flags to describe chip features */
/* chip has built-in oscilator */
@@ -127,6 +127,7 @@
enum vc5_model {
IDT_VC5_5P49V5923,
IDT_VC5_5P49V5933,
+ IDT_VC5_5P49V5935,
};
/* Structure to describe features of a particular VC5 model */
@@ -594,6 +595,7 @@ static int vc5_map_index_to_output(const enum vc5_model model,
case IDT_VC5_5P49V5933:
return (n == 0) ? 0 : 3;
case IDT_VC5_5P49V5923:
+ case IDT_VC5_5P49V5935:
default:
return n;
}
@@ -790,9 +792,17 @@ static const struct vc5_chip_info idt_5p49v5933_info = {
.flags = VC5_HAS_INTERNAL_XTAL,
};
+static const struct vc5_chip_info idt_5p49v5935_info = {
+ .model = IDT_VC5_5P49V5935,
+ .clk_fod_cnt = 4,
+ .clk_out_cnt = 5,
+ .flags = VC5_HAS_INTERNAL_XTAL,
+};
+
static const struct i2c_device_id vc5_id[] = {
{ "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
{ "5p49v5933", .driver_data = IDT_VC5_5P49V5933 },
+ { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vc5_id);
@@ -800,6 +810,7 @@ MODULE_DEVICE_TABLE(i2c, vc5_id);
static const struct of_device_id clk_vc5_of_match[] = {
{ .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info },
{ .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info },
+ { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info },
{ },
};
MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
--
2.7.4
^ permalink raw reply related
* [PATCH v3 2/3] clk: vc5: Add bindings for IDT VersaClock 5P49V5935
From: Alexey Firago @ 2017-04-06 9:15 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, marek.vasut, geert, linux-clk,
devicetree
Cc: Alexey Firago
In-Reply-To: <1491470130-6655-1-git-send-email-alexey_firago@mentor.com>
IDT VersaClock 5 5P49V5935 has 4 clock outputs, 4 fractional dividers.
Input clock source can be taken from either integrated crystal or from
external reference clock.
Signed-off-by: Alexey Firago <alexey_firago@mentor.com>
---
.../devicetree/bindings/clock/idt,versaclock5.txt | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt
index 87e9c47..53d7e50 100644
--- a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt
+++ b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt
@@ -6,18 +6,21 @@ from 3 to 12 output clocks.
==I2C device node==
Required properties:
-- compatible: shall be one of "idt,5p49v5923" , "idt,5p49v5933".
+- compatible: shall be one of "idt,5p49v5923" , "idt,5p49v5933" ,
+ "idt,5p49v5935".
- reg: i2c device address, shall be 0x68 or 0x6a.
- #clock-cells: from common clock binding; shall be set to 1.
- clocks: from common clock binding; list of parent clock handles,
- 5p49v5923: (required) either or both of XTAL or CLKIN
reference clock.
- - 5p49v5933: (optional) property not present (internal
+ - 5p49v5933 and
+ - 5p49v5935: (optional) property not present (internal
Xtal used) or CLKIN reference
clock.
- clock-names: from common clock binding; clock input names, can be
- 5p49v5923: (required) either or both of "xin", "clkin".
- - 5p49v5933: (optional) property not present or "clkin".
+ - 5p49v5933 and
+ - 5p49v5935: (optional) property not present or "clkin".
==Mapping between clock specifier and physical pins==
@@ -34,6 +37,13 @@ clock specifier, the following mapping applies:
1 -- OUT1
2 -- OUT4
+5P49V5935:
+ 0 -- OUT0_SEL_I2CB
+ 1 -- OUT1
+ 2 -- OUT2
+ 3 -- OUT3
+ 4 -- OUT4
+
==Example==
/* 25MHz reference crystal */
--
2.7.4
^ permalink raw reply related
* [PATCH v3 1/3] clk: vc5: Add structure to describe particular chip features
From: Alexey Firago @ 2017-04-06 9:15 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, marek.vasut, geert, linux-clk,
devicetree
Cc: Alexey Firago
In-Reply-To: <1491470130-6655-1-git-send-email-alexey_firago@mentor.com>
Introduce vc5_chip_info structure to describe features of a particular
VC5 chip (id, number of FODs, number of outputs, flags).
For now flags are only used to indicate if chip has internal XTAL.
vc5_chip_info is set on probe from the matched of_device_id->data.
Also add defines to specify maximum number of FODs and clock outputs
supported by the driver.
With these changes it should be easier to extend driver to support
more VC5 models.
Signed-off-by: Alexey Firago <alexey_firago@mentor.com>
---
drivers/clk/clk-versaclock5.c | 65 +++++++++++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 18 deletions(-)
diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c
index 56741f3..4e81fb1 100644
--- a/drivers/clk/clk-versaclock5.c
+++ b/drivers/clk/clk-versaclock5.c
@@ -113,12 +113,30 @@
#define VC5_MUX_IN_XIN BIT(0)
#define VC5_MUX_IN_CLKIN BIT(1)
+/* Maximum number of clk_out supported by this driver */
+#define VC5_MAX_CLK_OUT_NUM 3
+
+/* Maximum number of FODs supported by this driver */
+#define VC5_MAX_FOD_NUM 2
+
+/* flags to describe chip features */
+/* chip has built-in oscilator */
+#define VC5_HAS_INTERNAL_XTAL BIT(0)
+
/* Supported IDT VC5 models. */
enum vc5_model {
IDT_VC5_5P49V5923,
IDT_VC5_5P49V5933,
};
+/* Structure to describe features of a particular VC5 model */
+struct vc5_chip_info {
+ const enum vc5_model model;
+ const unsigned int clk_fod_cnt;
+ const unsigned int clk_out_cnt;
+ u32 flags;
+};
+
struct vc5_driver_data;
struct vc5_hw_data {
@@ -132,15 +150,15 @@ struct vc5_hw_data {
struct vc5_driver_data {
struct i2c_client *client;
struct regmap *regmap;
- enum vc5_model model;
+ const struct vc5_chip_info *chip_info;
struct clk *pin_xin;
struct clk *pin_clkin;
unsigned char clk_mux_ins;
struct clk_hw clk_mux;
struct vc5_hw_data clk_pll;
- struct vc5_hw_data clk_fod[2];
- struct vc5_hw_data clk_out[3];
+ struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM];
+ struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM];
};
static const char * const vc5_mux_names[] = {
@@ -563,7 +581,7 @@ static struct clk_hw *vc5_of_clk_get(struct of_phandle_args *clkspec,
struct vc5_driver_data *vc5 = data;
unsigned int idx = clkspec->args[0];
- if (idx > 2)
+ if (idx >= vc5->chip_info->clk_out_cnt)
return ERR_PTR(-EINVAL);
return &vc5->clk_out[idx].hw;
@@ -586,12 +604,10 @@ static const struct of_device_id clk_vc5_of_match[];
static int vc5_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct of_device_id *of_id =
- of_match_device(clk_vc5_of_match, &client->dev);
struct vc5_driver_data *vc5;
struct clk_init_data init;
const char *parent_names[2];
- unsigned int n, idx;
+ unsigned int n, idx = 0;
int ret;
vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL);
@@ -600,7 +616,7 @@ static int vc5_probe(struct i2c_client *client,
i2c_set_clientdata(client, vc5);
vc5->client = client;
- vc5->model = (enum vc5_model)of_id->data;
+ vc5->chip_info = of_device_get_match_data(&client->dev);
vc5->pin_xin = devm_clk_get(&client->dev, "xin");
if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER)
@@ -622,8 +638,7 @@ static int vc5_probe(struct i2c_client *client,
if (!IS_ERR(vc5->pin_xin)) {
vc5->clk_mux_ins |= VC5_MUX_IN_XIN;
parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin);
- } else if (vc5->model == IDT_VC5_5P49V5933) {
- /* IDT VC5 5P49V5933 has built-in oscilator. */
+ } else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) {
vc5->pin_xin = clk_register_fixed_rate(&client->dev,
"internal-xtal", NULL,
0, 25000000);
@@ -672,8 +687,8 @@ static int vc5_probe(struct i2c_client *client,
}
/* Register FODs */
- for (n = 0; n < 2; n++) {
- idx = vc5_map_index_to_output(vc5->model, n);
+ for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) {
+ idx = vc5_map_index_to_output(vc5->chip_info->model, n);
memset(&init, 0, sizeof(init));
init.name = vc5_fod_names[idx];
init.ops = &vc5_fod_ops;
@@ -709,8 +724,8 @@ static int vc5_probe(struct i2c_client *client,
}
/* Register FOD-connected OUTx outputs */
- for (n = 1; n < 3; n++) {
- idx = vc5_map_index_to_output(vc5->model, n - 1);
+ for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) {
+ idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1);
parent_names[0] = vc5_fod_names[idx];
if (n == 1)
parent_names[1] = vc5_mux_names[0];
@@ -744,7 +759,7 @@ static int vc5_probe(struct i2c_client *client,
return 0;
err_clk:
- if (vc5->model == IDT_VC5_5P49V5933)
+ if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
clk_unregister_fixed_rate(vc5->pin_xin);
return ret;
}
@@ -755,12 +770,26 @@ static int vc5_remove(struct i2c_client *client)
of_clk_del_provider(client->dev.of_node);
- if (vc5->model == IDT_VC5_5P49V5933)
+ if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)
clk_unregister_fixed_rate(vc5->pin_xin);
return 0;
}
+static const struct vc5_chip_info idt_5p49v5923_info = {
+ .model = IDT_VC5_5P49V5923,
+ .clk_fod_cnt = 2,
+ .clk_out_cnt = 3,
+ .flags = 0,
+};
+
+static const struct vc5_chip_info idt_5p49v5933_info = {
+ .model = IDT_VC5_5P49V5933,
+ .clk_fod_cnt = 2,
+ .clk_out_cnt = 3,
+ .flags = VC5_HAS_INTERNAL_XTAL,
+};
+
static const struct i2c_device_id vc5_id[] = {
{ "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
{ "5p49v5933", .driver_data = IDT_VC5_5P49V5933 },
@@ -769,8 +798,8 @@ static const struct i2c_device_id vc5_id[] = {
MODULE_DEVICE_TABLE(i2c, vc5_id);
static const struct of_device_id clk_vc5_of_match[] = {
- { .compatible = "idt,5p49v5923", .data = (void *)IDT_VC5_5P49V5923 },
- { .compatible = "idt,5p49v5933", .data = (void *)IDT_VC5_5P49V5933 },
+ { .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info },
+ { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info },
{ },
};
MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
--
2.7.4
^ permalink raw reply related
* [PATCH v3 0/3] clk: Add support for IDT 5P49V5935
From: Alexey Firago @ 2017-04-06 9:15 UTC (permalink / raw)
To: mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, geert-Td1EMuHUCqxL1ZNQvxDV9g,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Alexey Firago
This series adds support for IDT VersaClock 5P49V5935 programmable clock
generator to the existing clk-versaclock5 driver.
Patches were verified on Avnet UltraZed-EG board with IO Carrier Card.
Changes in V2:
- Introduce vc5_chip_info structure describing chip features
- Set vc5_chip_info for the supported chips using clk_vc5_of_match[].data
- Add 5P49V5935 support using vc5_chip_info approach
- Fix idx comparison in vc5_of_clk_get ('>' to '>=')
Changes in V3:
- Change type of clk_fod_cnt and clk_out_cnt to unsigned int
- Add missed 'const' to vc5_chip_info instance declaration
- Use of_device_get_match_data() to initialize vc5_chip_info on probe
Alexey Firago (3):
clk: vc5: Add structure to describe particular chip features
clk: vc5: Add bindings for IDT VersaClock 5P49V5935
clk: vc5: Add support for IDT VersaClock 5P49V5935
.../devicetree/bindings/clock/idt,versaclock5.txt | 16 ++++-
drivers/clk/clk-versaclock5.c | 76 +++++++++++++++++-----
2 files changed, 71 insertions(+), 21 deletions(-)
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/4] pwm: tegra: Add DT binding details to configure pin in suspends/resume
From: Jon Hunter @ 2017-04-06 8:57 UTC (permalink / raw)
To: Laxman Dewangan, thierry.reding, robh+dt, mark.rutland
Cc: linux-pwm, devicetree, linux-tegra, linux-kernel
In-Reply-To: <1491401626-31303-4-git-send-email-ldewangan@nvidia.com>
On 05/04/17 15:13, Laxman Dewangan wrote:
> In some of NVIDIA Tegra's platform, PWM controller is used to
> control the PWM controlled regulators. PWM signal is connected to
> the VID pin of the regulator where duty cycle of PWM signal decide
> the voltage level of the regulator output.
>
> The tristate (high impedance of PWM pin form Tegra) also define
> one of the state of PWM regulator which needs to be configure in
> suspend state of system.
>
> Add DT binding details to provide the pin configuration state
> from PWM and pinctrl DT node in suspend and active state of
> the system.
>
> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
> ---
> .../devicetree/bindings/pwm/nvidia,tegra20-pwm.txt | 43 ++++++++++++++++++++++
> 1 file changed, 43 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
> index b4e7377..145c323 100644
> --- a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
> +++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt
> @@ -19,6 +19,19 @@ Required properties:
> - reset-names: Must include the following entries:
> - pwm
>
> +Optional properties:
> +============================
> +In some of the interface like PWM based regualator device, it is required
> +to configure the pins diffrently in different states, specially in suspend
s/diffrently/differently
s/specially/especially
> +state of the system. The configuration of pin is provided via the pinctrl
> +DT node as detailed in the pinctrl DT binding document
> + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
> +
> +The PWM node will have following optional properties.
> +pinctrl-names: Pin state names. Must be "suspend" and "resume".
Why not just use the pre-defined names here? There is a pre-defined name
for "default", "idle" and "sleep" and then you can use the following
APIs and avoid the lookup of the state ...
pinctrl_pm_select_default_state()
pinctrl_pm_select_idle_state()
pinctrl_pm_select_sleep_state()
Note for i2c [0][1], I used "default" as the active/on state (which I
know is not that descriptive) and then used 'idle' as the suspended
state. This way we don't need any custom names.
Cheers
Jon
[0]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/i2c/busses/i2c-tegra.c?id=718917b9875fcfa6450e7274d2727c4680c3591a
[1]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/arch/arm64/boot/dts/nvidia/tegra210.dtsi?id=66b2d6e9c93bc5bed55afda80c84bf0325c14ad4
--
nvpublic
^ permalink raw reply
* Re: [PATCH 1/3] ARM: dts: rockchip: Add support for phyCORE-RK3288 SoM
From: Heiko Stübner @ 2017-04-06 8:47 UTC (permalink / raw)
To: Wadim Egorov
Cc: robh+dt, mark.rutland, linux, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <1872284.4EEa8KbvLF@phil>
Am Donnerstag, 6. April 2017, 10:30:46 CEST schrieb Heiko Stuebner:
> Am Montag, 3. April 2017, 12:23:03 CEST schrieb Wadim Egorov:
> > The phyCORE-RK3288 is a SoM (System on Module) containing a RK3288 SoC.
> > The module can be connected to different carrier boards.
> > It can be also equipped with different RAM, SPI flash and eMMC variants.
> >
> > The Rapid Development Kit option is using the following setup:
> > - 1 GB DDR3 RAM (2 Banks)
> > - 1x 4 KB EEPROM
> > - DP83867 Gigabit Ethernet PHY
> > - 16 MB SPI Flash
> > - 4 GB eMMC Flash
> >
> > Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
>
> applied for 4.12 with the following minor changes:
had to unapply again ... can you please check the license of the files
They're currently GPL2-only, while we use a combined GPL+X11 license
everywhere else. See every other Rockchip dts (and even most other
socs) for reference.
Thanks
Heiko
^ permalink raw reply
* Re: [PATCH v4 2/2] i2c: mux: ltc4306: LTC4306 and LTC4305 I2C multiplexer/switch
From: Peter Rosin @ 2017-04-06 8:39 UTC (permalink / raw)
To: michael.hennerich-OyLXuOCK7orQT0dZR+AlfA,
wsa-z923LK4zBo2bacvFa/9K2g, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, linus.walleij-QSEj5FYQhm4dnm+yROfE0A
Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1491397671-14675-2-git-send-email-michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
Hi Michael,
I would still like to hear from someone with more gpio experience.
Anyway, from my point of view, there's just a few minor things left,
with comments inline as usual.
Thanks for you patience!
Cheers,
peda
On 2017-04-05 15:07, michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org wrote:
> From: Michael Hennerich <michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
>
> This patch adds support for the Analog Devices / Linear Technology
> LTC4306 and LTC4305 4/2 Channel I2C Bus Multiplexer/Switches.
> The LTC4306 optionally provides two general purpose input/output pins
> (GPIOs) that can be configured as logic inputs, opendrain outputs or
> push-pull outputs via the generic GPIOLIB framework.
>
> Signed-off-by: Michael Hennerich <michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
>
> ---
>
> Changes since v1:
>
> - Sort makefile entries
> - Sort driver includes
> - Use proper defines
> - Miscellaneous coding style fixups
> - Rename mux select callback
> - Revise i2c-mux-idle-disconnect handling
> - Add ENABLE GPIO handling on error and device removal.
> - Remove surplus of_match_device call.
>
> Changes since v2:
>
> - Stop double error reporting (i2c_mux_add_adapter)
> - Change subject
> - Split dt bindings to separate patch
>
> Changes since v3:
>
> - Change subject and add spaces
> - Convert to I2C_MUX_LOCKED
> - Convert to regmap
> - Remove local register cache
> - Restore previous ENABLE GPIO handling
> - Initially pulse ENABLE low
> - Eliminate i2c client struct in driver state structure
> - Simplify error return path
> - Misc minor cleanups
> ---
> MAINTAINERS | 8 +
> drivers/i2c/muxes/Kconfig | 11 ++
> drivers/i2c/muxes/Makefile | 1 +
> drivers/i2c/muxes/i2c-mux-ltc4306.c | 310 ++++++++++++++++++++++++++++++++++++
> 4 files changed, 330 insertions(+)
> create mode 100644 drivers/i2c/muxes/i2c-mux-ltc4306.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c776906..9a27a19 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7698,6 +7698,14 @@ S: Maintained
> F: Documentation/hwmon/ltc4261
> F: drivers/hwmon/ltc4261.c
>
> +LTC4306 I2C MULTIPLEXER DRIVER
> +M: Michael Hennerich <michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>
> +W: http://ez.analog.com/community/linux-device-drivers
> +L: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S: Supported
> +F: drivers/i2c/muxes/i2c-mux-ltc4306.c
> +F: Documentation/devicetree/bindings/i2c/i2c-mux-ltc4306.txt
> +
> LTP (Linux Test Project)
> M: Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
> M: Cyril Hrubis <chrubis-AlSwsSmVLrQ@public.gmane.org>
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index 10b3d17..41153b4 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -30,6 +30,17 @@ config I2C_MUX_GPIO
> This driver can also be built as a module. If so, the module
> will be called i2c-mux-gpio.
>
> +config I2C_MUX_LTC4306
> + tristate "LTC LTC4306/5 I2C multiplexer"
> + select GPIOLIB
> + select REGMAP_I2C
> + help
> + If you say yes here you get support for the LTC LTC4306 or LTC4305
This reads a bit funny, and I think you should just spell out the
first LTC? But perhaps not in the tristate above though, depending
on how long it gets?
> + I2C mux/switch devices.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-mux-ltc4306.
> +
> config I2C_MUX_PCA9541
> tristate "NXP PCA9541 I2C Master Selector"
> help
> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
> index 9948fa4..ff7618c 100644
> --- a/drivers/i2c/muxes/Makefile
> +++ b/drivers/i2c/muxes/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
> obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
>
> obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
> +obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o
> obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
> obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
> obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
> diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c
> new file mode 100644
> index 0000000..7d34434
> --- /dev/null
> +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c
> @@ -0,0 +1,310 @@
> +/*
> + * Linear Technology LTC4306 and LTC4305 I2C multiplexer/switch
> + *
> + * Copyright (C) 2017 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + *
> + * Based on: i2c-mux-pca954x.c
> + *
> + * Datasheet: http://cds.linear.com/docs/en/datasheet/4306.pdf
> + */
> +
> +#include <linux/device.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#define LTC4305_MAX_NCHANS 2
> +#define LTC4306_MAX_NCHANS 4
> +
> +#define LTC_REG_STATUS 0x0
> +#define LTC_REG_CONFIG 0x1
> +#define LTC_REG_MODE 0x2
> +#define LTC_REG_SWITCH 0x3
> +
> +#define LTC_DOWNSTREAM_ACCL_EN BIT(6)
> +#define LTC_UPSTREAM_ACCL_EN BIT(7)
> +
> +#define LTC_GPIO_ALL_INPUT 0xC0
> +#define LTC_SWITCH_MASK 0xF0
> +
> +enum ltc_type {
> + ltc_4305,
> + ltc_4306,
> +};
> +
> +struct chip_desc {
> + u8 nchans;
> + u8 num_gpios;
> +};
> +
> +struct ltc4306 {
> + struct regmap *regmap;
> + struct gpio_chip gpiochip;
> + const struct chip_desc *chip;
> +};
> +
> +static const struct chip_desc chips[] = {
> + [ltc_4305] = {
> + .nchans = LTC4305_MAX_NCHANS,
> + },
> + [ltc_4306] = {
> + .nchans = LTC4306_MAX_NCHANS,
> + .num_gpios = 2,
> + },
> +};
> +
> +static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg)
> +{
> + return (reg == LTC_REG_CONFIG) ? true : false;
> +}
> +
> +static const struct regmap_config ltc4306_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = LTC_REG_SWITCH,
> + .volatile_reg = ltc4306_is_volatile_reg,
> + .cache_type = REGCACHE_RBTREE,
Did you consider REGCACHE_FLAT? There are very few registers and no hole
in the map, and maintaining a tree seems like total overkill.
> +};
> +
> +static int ltc4306_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct ltc4306 *data = gpiochip_get_data(chip);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(data->regmap, LTC_REG_CONFIG, &val);
> + if (ret < 0)
> + return ret;
> +
> + return (val & BIT(1 - offset));
The outer parentheses do not add anything, and I think they might remain
from when you just removed a double negation at some point. But is it
good practice to indicate "high" with anything other than one? Sure, the
gpiolib function that wraps the ->get() op does the !! dance for you,
but even so, every single one of the half dozen random gpio providers I
looked at had code to coerce the value to 0/1 (or error). Which makes me
think you should also have it. And the gpio_chip documentation on ->get()
agrees with me...
> +}
> +
> +static void ltc4306_gpio_set(struct gpio_chip *chip, unsigned int offset,
> + int value)
> +{
> + struct ltc4306 *data = gpiochip_get_data(chip);
> +
> + regmap_update_bits(data->regmap, LTC_REG_CONFIG, BIT(5 - offset),
> + value ? BIT(5 - offset) : 0);
> +}
> +
> +static int ltc4306_gpio_direction_input(struct gpio_chip *chip,
> + unsigned int offset)
> +{
> + struct ltc4306 *data = gpiochip_get_data(chip);
> +
> + return regmap_update_bits(data->regmap, LTC_REG_MODE,
> + BIT(7 - offset), BIT(7 - offset));
> +}
> +
> +static int ltc4306_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct ltc4306 *data = gpiochip_get_data(chip);
> +
> + ltc4306_gpio_set(chip, offset, value);
> + return regmap_update_bits(data->regmap, LTC_REG_MODE,
> + BIT(7 - offset), 0);
> +}
> +
> +static int ltc4306_gpio_set_config(struct gpio_chip *chip,
> + unsigned int offset, unsigned long config)
> +{
> + struct ltc4306 *data = gpiochip_get_data(chip);
> + unsigned int val;
> +
> + switch (pinconf_to_config_param(config)) {
> + case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> + val = 0;
> + break;
> + case PIN_CONFIG_DRIVE_PUSH_PULL:
> + val = BIT(4 - offset);
> + break;
> + default:
> + return -ENOTSUPP;
> + }
> +
> + return regmap_update_bits(data->regmap, LTC_REG_MODE,
> + BIT(4 - offset), val);
> +}
> +
> +static int ltc4306_gpio_init(struct ltc4306 *data)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> +
> + if (!data->chip->num_gpios)
> + return 0;
> +
> + data->gpiochip.label = dev_name(dev);
> + data->gpiochip.base = -1;
> + data->gpiochip.ngpio = data->chip->num_gpios;
> + data->gpiochip.parent = dev;
> + data->gpiochip.can_sleep = true;
> + data->gpiochip.direction_input = ltc4306_gpio_direction_input;
> + data->gpiochip.direction_output = ltc4306_gpio_direction_output;
I'm missing a get_direction op?
> + data->gpiochip.get = ltc4306_gpio_get;
> + data->gpiochip.set = ltc4306_gpio_set;
> + data->gpiochip.set_config = ltc4306_gpio_set_config;
> + data->gpiochip.owner = THIS_MODULE;
> +
> + /* gpiolib assumes all GPIOs default input */
> + regmap_write(data->regmap, LTC_REG_MODE, LTC_GPIO_ALL_INPUT);
> +
> + return devm_gpiochip_add_data(dev, &data->gpiochip, data);
> +}
> +
> +static int ltc4306_select_mux(struct i2c_mux_core *muxc, u32 chan)
> +{
> + struct ltc4306 *data = i2c_mux_priv(muxc);
> +
> + return regmap_update_bits(data->regmap, LTC_REG_SWITCH,
> + LTC_SWITCH_MASK, BIT(7 - chan));
Since the bits outside the mask are ignored for writes, I'd go with
regmap_write. Especially since those bits are volatile, which admittedly
will not have much impact until there is a need to read those volatile
bits outside the mask. But still.
> +}
> +
> +static int ltc4306_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
> +{
> + struct ltc4306 *data = i2c_mux_priv(muxc);
> +
> + return regmap_update_bits(data->regmap, LTC_REG_SWITCH,
> + LTC_SWITCH_MASK, 0);
Dito.
> +}
> +
> +static const struct i2c_device_id ltc4306_id[] = {
> + { "ltc4305", ltc_4305 },
> + { "ltc4306", ltc_4306 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ltc4306_id);
> +
> +static const struct of_device_id ltc4306_of_match[] = {
> + { .compatible = "lltc,ltc4305", .data = &chips[ltc_4305] },
> + { .compatible = "lltc,ltc4306", .data = &chips[ltc_4306] },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, ltc4306_of_match);
> +
> +static int ltc4306_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
> + struct device_node *of_node = client->dev.of_node;
> + struct i2c_mux_core *muxc;
> + struct ltc4306 *data;
> + struct gpio_desc *gpio;
> + bool idle_disc = false;
> + int num, ret;
> +
> + if (of_node)
> + idle_disc = of_property_read_bool(of_node,
> + "i2c-mux-idle-disconnect");
> +
> + muxc = i2c_mux_alloc(adap, &client->dev,
> + LTC4306_MAX_NCHANS, sizeof(*data),
Hmmm, I didn't see this before, but if you do some more rearranging, it
should be possible to replace LTC4306_MAX_NCHANS with data->chip->nchans
and reduce resource waste for ltc4305. But it's just storage for two
pointers which is really really minor... Feel free to ignore.
But you want to set a good example, right :-)
> + I2C_MUX_LOCKED, ltc4306_select_mux,
> + idle_disc ? ltc4306_deselect_mux : NULL);
> + if (!muxc)
> + return -ENOMEM;
> + data = i2c_mux_priv(muxc);
> +
> + i2c_set_clientdata(client, muxc);
> +
> + data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config);
> + if (IS_ERR(data->regmap)) {
> + ret = PTR_ERR(data->regmap);
> + dev_err(&client->dev, "Failed to allocate register map: %d\n",
> + ret);
> + return ret;
> + }
> +
> + /* Reset and enable the mux if an enable GPIO is specified. */
> + gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW);
> + if (IS_ERR(gpio))
> + return PTR_ERR(gpio);
> +
> + if (gpio) {
> + udelay(1);
> + gpiod_set_value(gpio, 1);
> + }
> +
> + /*
> + * Write the mux register at addr to verify
> + * that the mux is in fact present. This also
> + * initializes the mux to disconnected state.
> + */
> + if (regmap_write(data->regmap, LTC_REG_SWITCH, 0) < 0) {
> + dev_warn(&client->dev, "probe failed\n");
> + return -ENODEV;
> + }
> +
> + if (of_node) {
> + unsigned int val = 0;
> +
> + data->chip = of_device_get_match_data(&client->dev);
> +
> + if (of_property_read_bool(of_node,
> + "ltc,downstream-accelerators-enable"))
> + val |= LTC_DOWNSTREAM_ACCL_EN;
> +
> + if (of_property_read_bool(of_node,
> + "ltc,upstream-accelerators-enable"))
> + val |= LTC_UPSTREAM_ACCL_EN;
> +
> + if (regmap_write(data->regmap, LTC_REG_CONFIG, val) < 0)
> + return -ENODEV;
> + } else {
> + data->chip = &chips[id->driver_data];
> + }
> +
> + ret = ltc4306_gpio_init(data);
> + if (ret < 0)
> + return ret;
> +
> + /* Now create an adapter for each channel */
> + for (num = 0; num < data->chip->nchans; num++) {
> + ret = i2c_mux_add_adapter(muxc, 0, num, 0);
> + if (ret) {
> + i2c_mux_del_adapters(muxc);
> + return ret;
> + }
> + }
> +
> + dev_info(&client->dev,
> + "registered %d multiplexed busses for I2C switch %s\n",
> + num, client->name);
> +
> + return 0;
> +}
> +
> +static int ltc4306_remove(struct i2c_client *client)
> +{
> + struct i2c_mux_core *muxc = i2c_get_clientdata(client);
> +
> + i2c_mux_del_adapters(muxc);
> +
> + return 0;
> +}
> +
> +static struct i2c_driver ltc4306_driver = {
> + .driver = {
> + .name = "ltc4306",
> + .of_match_table = of_match_ptr(ltc4306_of_match),
> + },
> + .probe = ltc4306_probe,
> + .remove = ltc4306_remove,
> + .id_table = ltc4306_id,
> +};
> +
> +module_i2c_driver(ltc4306_driver);
> +
> +MODULE_AUTHOR("Michael Hennerich <michael.hennerich-OyLXuOCK7orQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("Linear Technology LTC4306, LTC4305 I2C mux/switch driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/3] ARM: dts: rockchip: Add support for phyCORE-RK3288 SoM
From: Heiko Stuebner @ 2017-04-06 8:30 UTC (permalink / raw)
To: Wadim Egorov
Cc: robh+dt, mark.rutland, linux, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <1491214985-18331-1-git-send-email-w.egorov@phytec.de>
Am Montag, 3. April 2017, 12:23:03 CEST schrieb Wadim Egorov:
> The phyCORE-RK3288 is a SoM (System on Module) containing a RK3288 SoC.
> The module can be connected to different carrier boards.
> It can be also equipped with different RAM, SPI flash and eMMC variants.
> The Rapid Development Kit option is using the following setup:
>
> - 1 GB DDR3 RAM (2 Banks)
> - 1x 4 KB EEPROM
> - DP83867 Gigabit Ethernet PHY
> - 16 MB SPI Flash
> - 4 GB eMMC Flash
>
> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
applied for 4.12 with the following minor changes:
- moved phy-handle property up a bit
- switches compatible and #address+#size-cells in mdio0
- dropped rockchip,grf from &io_domains (grf is a simple-mfd and can
get the grf syscon on its own via its parent)
- vdd_cpu: regulator@60 (from fan53555@60)
- serial_flash: flash@0 (from m25p80@0)
Nodes should be named after their "category" not the actual device
Thanks
Heiko
^ permalink raw reply
* [PATCH] of: Add vendor prefix for Nordic Semiconductor
From: Kumar Gala @ 2017-04-06 8:29 UTC (permalink / raw)
To: Rob Herring; +Cc: Kumar Gala, Device Tree Mailing List, Mark Rutland
Nordic Semiconductor is a semiconductor company specializing in ARM
Cortex-M based SoCs for low-power wireless communication, especially
Bluetooth.
Signed-off-by: Kumar Gala <kumar.gala-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index ec0bfb9..dd9e7b2 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -212,6 +212,7 @@ newhaven Newhaven Display International
ni National Instruments
nintendo Nintendo
nokia Nokia
+nordic Nordic Semiconductor
nuvoton Nuvoton Technology Corporation
nvd New Vision Display
nvidia NVIDIA
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RESEND PATCH V7 1/5] Documentation: devicetree: watchdog: da9062/61 watchdog timer binding
From: Steve Twiss @ 2017-04-06 8:28 UTC (permalink / raw)
To: DEVICETREE, Guenter Roeck, LINUX-KERNEL, LINUX-WATCHDOG,
Mark Rutland, Rob Herring, Wim Van Sebroeck
Cc: Dmitry Torokhov, Eduardo Valentin, LINUX-INPUT, LINUX-PM,
Lee Jones, Liam Girdwood, Mark Brown, Support Opensource,
Zhang Rui
In-Reply-To: <cover.1491467308.git.stwiss.opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
From: Steve Twiss <stwiss.opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
Add binding information for DA9062 and DA9061 watchdog.
Example bindings for both DA9062 and DA9061 devices are added. For
the DA9061 device, a fallback compatible line is added as a valid
combination of compatible strings.
The original binding for DA9062 (only) used to reside inside the
Documentation/devicetree/bindings/mfd/da9062.txt MFD document.
The da9062-watchdog section was deleted in that file and replaced
with a link to the new DA9061/62 binding information stored in this
patch.
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Steve Twiss <stwiss.opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
---
Hi,
I have just noticed.
The driver code changes for the da9062/61 watchdog have been applied to
the linux kernel. The da9062/61 alterations for
drivers/watchdog/da9062_wdt.c appear in linux-stable/v4.10-rc1. Those
changes are linked to this binding patch, but this patch seems to have
been missed out.
The source code dependency for this patch is given in the commit:
72106c1 v4.10-rc1 watchdog: da9062/61: watchdog driver
I don't see anything blocking for merge of this patch now.
Regards,
Steve
This patch applies against linux-next and v4.11-rc3
v6 -> v7
- NO CODE CHANGE
v5 -> v6
- NO CODE CHANGE
- Rebased from v4.9 to v4.11-rc3
v4 -> v5
- NO CODE CHANGE
- Rebased from v4.8 to v4.9
v3 -> v4
- NO CODE CHANGE
- Patch renamed from [PATCH V3 2/9] to [PATCH V4 1/8]
- Added Acked-by Rob Herring
v2 -> v3
- Patch renamed from [PATCH V1 02/10] to [PATCH V3 2/9]
- Each compatible line should be a valid combination of compatible
strings, alter DA9061 line to include the fall back compatible string
- Update the commit message to describe this change
- Add information about associated patches from this set without
describing them as being explicitly dependent on this binding
v1 -> v2
- Patch renamed from [PATCH V1 07/10] to [PATCH V2 02/10] -- these
changes were made to fix checkpatch warnings caused by the patch
set dependency order
- Updated the patch description to be explicit about where parts of
this binding had originally been stored
- A second example for DA9061 is provided to highlight the use of a
fall-back compatible option for the DA9062 watchdog driver
As previously:
For the watchdog case: the DA9062 device driver is compatible with the
DA9061 and for this reason there is minimal change required to the DA9062
watchdog device driver. The example for the DA9061 watchdog shows the
use of a fall-back compatible string.
Other information:
The device driver from this patch set (associated with this binding) is
[PATCH V5 6/8] watchdog: da9061: watchdog driver
Regards,
Steve Twiss, Dialog Semiconductor
.../devicetree/bindings/watchdog/da9062-wdt.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/da9062-wdt.txt
diff --git a/Documentation/devicetree/bindings/watchdog/da9062-wdt.txt b/Documentation/devicetree/bindings/watchdog/da9062-wdt.txt
new file mode 100644
index 0000000..b935b52
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/da9062-wdt.txt
@@ -0,0 +1,23 @@
+* Dialog Semiconductor DA9062/61 Watchdog Timer
+
+Required properties:
+
+- compatible: should be one of the following valid compatible string lines:
+ "dlg,da9061-watchdog", "dlg,da9062-watchdog"
+ "dlg,da9062-watchdog"
+
+Example: DA9062
+
+ pmic0: da9062@58 {
+ watchdog {
+ compatible = "dlg,da9062-watchdog";
+ };
+ };
+
+Example: DA9061 using a fall-back compatible for the DA9062 watchdog driver
+
+ pmic0: da9061@58 {
+ watchdog {
+ compatible = "dlg,da9061-watchdog", "dlg,da9062-watchdog";
+ };
+ };
--
end-of-patch for RESEND PATCH V7
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 5/5] media: Update MAINTAINERS file for CXD2880
From: Yasunari.Takiguchi @ 2017-04-06 8:06 UTC (permalink / raw)
To: akpm, linux-kernel, linux-media, devicetree
Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
This is MAINTAINERS file update about the driver for
Sony CXD2880 DVB-T2/T tuner + demodulator.
Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6640601..c34335d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8034,6 +8034,15 @@ T: git git://linuxtv.org/media_tree.git
S: Supported
F: drivers/media/dvb-frontends/cxd2841er*
+MEDIA DRIVERS FOR CXD2880
+M: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+T: git git://linuxtv.org/media_tree.git
+S: Supported
+F: drivers/media/dvb-frontends/cxd2880/*
+F: drivers/media/spi/cxd2880*
+
MEDIA DRIVERS FOR HORUS3A
M: Sergey Kozlov <serjk@netup.ru>
M: Abylay Ospan <aospan@netup.ru>
--
1.7.9.5
^ permalink raw reply related
* RE: [v3, 0/7] Add SD UHS-I and eMMC HS200 support for eSDHC
From: Y.B. Lu @ 2017-04-06 8:02 UTC (permalink / raw)
To: Y.B. Lu, linux-mmc@vger.kernel.org, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, ulf.hansson@linaro.org,
Adrian Hunter, Rob Herring, Mark Rutland, Catalin Marinas,
Will Deacon
Cc: Xiaobo Xie
In-Reply-To: <1490600982-5410-1-git-send-email-yangbo.lu@nxp.com>
Hi all,
Any comments? Thanks.
Best regards,
Yangbo Lu
> -----Original Message-----
> From: Yangbo Lu [mailto:yangbo.lu@nxp.com]
> Sent: Monday, March 27, 2017 3:50 PM
> To: linux-mmc@vger.kernel.org; devicetree@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; ulf.hansson@linaro.org; Adrian Hunter; Rob
> Herring; Mark Rutland; Catalin Marinas; Will Deacon
> Cc: Xiaobo Xie; Y.B. Lu
> Subject: [v3, 0/7] Add SD UHS-I and eMMC HS200 support for eSDHC
>
> It's complicated to support SD UHS-I and eMMC HS200 for eSDHC because
> there're many differences between eSDHC and SD/eMMC spec. Several
> differences as below must be considered:
> 1. Peripheral clock must be used instead of platform clock.
> - eSDHC could select peripheral clock or platform clock as its clock
> source. According to RM, UHS-I/HS200 must use peripheral clock
> since
> it supports higher frequency than platform clock.
> - Patch 1 and patch 6 is to support this.
> 2. Signal voltage switching requires a control circuit out of eSDHC.
> - eSDHC supports signal voltage switch from 3.3v to 1.8v by
> eSDHC_PROCTL[VOLT_SEL] bit. This bit changes the value of output
> signal SDHC_VS, and there must be a control circuit out of eSDHC
> to change the signal voltage according to SDHC_VS output signal.
> - Patch 2 is to support this.
> 3. eSDHC uses tuning block for tuning procedure.
> - Tuning clock control register must be configured before tuning.
> - Patch 3 is to support this.
> 4. Delay is needed between tuning cycles for HS200 tuning.
> - Once a patch removed mdelay between tuning cycles.
> But eSDHC needs it.
> - Patch 4 and patch 5 is to support this.
> 5. UHS-I/HS200 modes could be enabled in dts node.
> - Patch 7 is to support this.
>
> Please review and merge these patches on mmc git tree if no changes are
> required.
>
> Adrian Hunter (1):
> mmc: sdhci: Control the delay between tuning commands
>
> Yangbo Lu (6):
> mmc: sdhci-of-esdhc: add peripheral clock support
> mmc: sdhci-of-esdhc: add support for signal voltage switch
> mmc: sdhci-of-esdhc: add tuning support
> mmc: sdhci-of-esdhc: add delay between tuning cycles
> arm64: dts: ls1046a: add clocks property and compatible for eSDHC node
> arm64: dts: ls1046ardb: add MMC HS200/UHS-1 modes support
>
> arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts | 8 ++
> arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 3 +-
> drivers/mmc/host/sdhci-esdhc.h | 7 +
> drivers/mmc/host/sdhci-of-esdhc.c | 165
> +++++++++++++++++++++-
> drivers/mmc/host/sdhci.c | 11 +-
> drivers/mmc/host/sdhci.h | 2 +
> 6 files changed, 190 insertions(+), 6 deletions(-)
>
> --
> 2.1.0.27.g96db324
^ permalink raw reply
* [PATCH 4/5] media: Add suppurt for CXD2880 DVB-T2/T functions
From: Yasunari.Takiguchi @ 2017-04-06 8:01 UTC (permalink / raw)
To: linux-kernel, linux-media, devicetree
Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
This is the driver for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the DVB-T2 and DVB-T part of the driver.
Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h | 91 +
.../media/dvb-frontends/cxd2880/cxd2880_dvbt2.h | 402 ++++
.../dvb-frontends/cxd2880/cxd2880_integ_dvbt.c | 197 ++
.../dvb-frontends/cxd2880/cxd2880_integ_dvbt.h | 58 +
.../dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c | 311 +++
.../dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h | 64 +
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c | 1072 +++++++++
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h | 62 +
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c | 1309 ++++++++++
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h | 82 +
.../cxd2880/cxd2880_tnrdmd_dvbt2_mon.c | 2523 ++++++++++++++++++++
.../cxd2880/cxd2880_tnrdmd_dvbt2_mon.h | 170 ++
.../cxd2880/cxd2880_tnrdmd_dvbt_mon.c | 1190 +++++++++
.../cxd2880/cxd2880_tnrdmd_dvbt_mon.h | 106 +
14 files changed, 7637 insertions(+)
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
new file mode 100644
index 0000000..345c094
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt.h
@@ -0,0 +1,91 @@
+/*
+ * cxd2880_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DVBT_H
+#define CXD2880_DVBT_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt_constellation {
+ CXD2880_DVBT_CONSTELLATION_QPSK,
+ CXD2880_DVBT_CONSTELLATION_16QAM,
+ CXD2880_DVBT_CONSTELLATION_64QAM,
+ CXD2880_DVBT_CONSTELLATION_RESERVED_3
+};
+
+enum cxd2880_dvbt_hierarchy {
+ CXD2880_DVBT_HIERARCHY_NON,
+ CXD2880_DVBT_HIERARCHY_1,
+ CXD2880_DVBT_HIERARCHY_2,
+ CXD2880_DVBT_HIERARCHY_4
+};
+
+enum cxd2880_dvbt_coderate {
+ CXD2880_DVBT_CODERATE_1_2,
+ CXD2880_DVBT_CODERATE_2_3,
+ CXD2880_DVBT_CODERATE_3_4,
+ CXD2880_DVBT_CODERATE_5_6,
+ CXD2880_DVBT_CODERATE_7_8,
+ CXD2880_DVBT_CODERATE_RESERVED_5,
+ CXD2880_DVBT_CODERATE_RESERVED_6,
+ CXD2880_DVBT_CODERATE_RESERVED_7
+};
+
+enum cxd2880_dvbt_guard {
+ CXD2880_DVBT_GUARD_1_32,
+ CXD2880_DVBT_GUARD_1_16,
+ CXD2880_DVBT_GUARD_1_8,
+ CXD2880_DVBT_GUARD_1_4
+};
+
+enum cxd2880_dvbt_mode {
+ CXD2880_DVBT_MODE_2K,
+ CXD2880_DVBT_MODE_8K,
+ CXD2880_DVBT_MODE_RESERVED_2,
+ CXD2880_DVBT_MODE_RESERVED_3
+};
+
+enum cxd2880_dvbt_profile {
+ CXD2880_DVBT_PROFILE_HP = 0,
+ CXD2880_DVBT_PROFILE_LP
+};
+
+struct cxd2880_dvbt_tpsinfo {
+ enum cxd2880_dvbt_constellation constellation;
+ enum cxd2880_dvbt_hierarchy hierarchy;
+ enum cxd2880_dvbt_coderate rate_hp;
+ enum cxd2880_dvbt_coderate rate_lp;
+ enum cxd2880_dvbt_guard guard;
+ enum cxd2880_dvbt_mode mode;
+ u8 fnum;
+ u8 length_indicator;
+ u16 cell_id;
+ u8 cell_id_ok;
+ u8 reserved_even;
+ u8 reserved_odd;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
new file mode 100644
index 0000000..1870398
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dvbt2.h
@@ -0,0 +1,402 @@
+/*
+ * cxd2880_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DVBT2_H
+#define CXD2880_DVBT2_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_dvbt2_profile {
+ CXD2880_DVBT2_PROFILE_BASE,
+ CXD2880_DVBT2_PROFILE_LITE,
+ CXD2880_DVBT2_PROFILE_ANY
+};
+
+enum cxd2880_dvbt2_version {
+ CXD2880_DVBT2_V111,
+ CXD2880_DVBT2_V121,
+ CXD2880_DVBT2_V131
+};
+
+enum cxd2880_dvbt2_s1 {
+ CXD2880_DVBT2_S1_BASE_SISO = 0x00,
+ CXD2880_DVBT2_S1_BASE_MISO = 0x01,
+ CXD2880_DVBT2_S1_NON_DVBT2 = 0x02,
+ CXD2880_DVBT2_S1_LITE_SISO = 0x03,
+ CXD2880_DVBT2_S1_LITE_MISO = 0x04,
+ CXD2880_DVBT2_S1_RSVD3 = 0x05,
+ CXD2880_DVBT2_S1_RSVD4 = 0x06,
+ CXD2880_DVBT2_S1_RSVD5 = 0x07,
+ CXD2880_DVBT2_S1_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_base_s2 {
+ CXD2880_DVBT2_BASE_S2_M2K_G_ANY = 0x00,
+ CXD2880_DVBT2_BASE_S2_M8K_G_DVBT = 0x01,
+ CXD2880_DVBT2_BASE_S2_M4K_G_ANY = 0x02,
+ CXD2880_DVBT2_BASE_S2_M1K_G_ANY = 0x03,
+ CXD2880_DVBT2_BASE_S2_M16K_G_ANY = 0x04,
+ CXD2880_DVBT2_BASE_S2_M32K_G_DVBT = 0x05,
+ CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2 = 0x06,
+ CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2 = 0x07,
+ CXD2880_DVBT2_BASE_S2_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_lite_s2 {
+ CXD2880_DVBT2_LITE_S2_M2K_G_ANY = 0x00,
+ CXD2880_DVBT2_LITE_S2_M8K_G_DVBT = 0x01,
+ CXD2880_DVBT2_LITE_S2_M4K_G_ANY = 0x02,
+ CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2 = 0x03,
+ CXD2880_DVBT2_LITE_S2_M16K_G_DVBT = 0x04,
+ CXD2880_DVBT2_LITE_S2_RSVD1 = 0x05,
+ CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2 = 0x06,
+ CXD2880_DVBT2_LITE_S2_RSVD2 = 0x07,
+ CXD2880_DVBT2_LITE_S2_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_guard {
+ CXD2880_DVBT2_G1_32 = 0x00,
+ CXD2880_DVBT2_G1_16 = 0x01,
+ CXD2880_DVBT2_G1_8 = 0x02,
+ CXD2880_DVBT2_G1_4 = 0x03,
+ CXD2880_DVBT2_G1_128 = 0x04,
+ CXD2880_DVBT2_G19_128 = 0x05,
+ CXD2880_DVBT2_G19_256 = 0x06,
+ CXD2880_DVBT2_G_RSVD1 = 0x07,
+ CXD2880_DVBT2_G_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_mode {
+ CXD2880_DVBT2_M2K = 0x00,
+ CXD2880_DVBT2_M8K = 0x01,
+ CXD2880_DVBT2_M4K = 0x02,
+ CXD2880_DVBT2_M1K = 0x03,
+ CXD2880_DVBT2_M16K = 0x04,
+ CXD2880_DVBT2_M32K = 0x05,
+ CXD2880_DVBT2_M_RSVD1 = 0x06,
+ CXD2880_DVBT2_M_RSVD2 = 0x07
+};
+
+enum cxd2880_dvbt2_bw {
+ CXD2880_DVBT2_BW_8 = 0x00,
+ CXD2880_DVBT2_BW_7 = 0x01,
+ CXD2880_DVBT2_BW_6 = 0x02,
+ CXD2880_DVBT2_BW_5 = 0x03,
+ CXD2880_DVBT2_BW_10 = 0x04,
+ CXD2880_DVBT2_BW_1_7 = 0x05,
+ CXD2880_DVBT2_BW_RSVD1 = 0x06,
+ CXD2880_DVBT2_BW_RSVD2 = 0x07,
+ CXD2880_DVBT2_BW_RSVD3 = 0x08,
+ CXD2880_DVBT2_BW_RSVD4 = 0x09,
+ CXD2880_DVBT2_BW_RSVD5 = 0x0A,
+ CXD2880_DVBT2_BW_RSVD6 = 0x0B,
+ CXD2880_DVBT2_BW_RSVD7 = 0x0C,
+ CXD2880_DVBT2_BW_RSVD8 = 0x0D,
+ CXD2880_DVBT2_BW_RSVD9 = 0x0E,
+ CXD2880_DVBT2_BW_RSVD10 = 0x0F,
+ CXD2880_DVBT2_BW_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1pre_type {
+ CXD2880_DVBT2_L1PRE_TYPE_TS = 0x00,
+ CXD2880_DVBT2_L1PRE_TYPE_GS = 0x01,
+ CXD2880_DVBT2_L1PRE_TYPE_TS_GS = 0x02,
+ CXD2880_DVBT2_L1PRE_TYPE_RESERVED = 0x03,
+ CXD2880_DVBT2_L1PRE_TYPE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_papr {
+ CXD2880_DVBT2_PAPR_0 = 0x00,
+ CXD2880_DVBT2_PAPR_1 = 0x01,
+ CXD2880_DVBT2_PAPR_2 = 0x02,
+ CXD2880_DVBT2_PAPR_3 = 0x03,
+ CXD2880_DVBT2_PAPR_RSVD1 = 0x04,
+ CXD2880_DVBT2_PAPR_RSVD2 = 0x05,
+ CXD2880_DVBT2_PAPR_RSVD3 = 0x06,
+ CXD2880_DVBT2_PAPR_RSVD4 = 0x07,
+ CXD2880_DVBT2_PAPR_RSVD5 = 0x08,
+ CXD2880_DVBT2_PAPR_RSVD6 = 0x09,
+ CXD2880_DVBT2_PAPR_RSVD7 = 0x0A,
+ CXD2880_DVBT2_PAPR_RSVD8 = 0x0B,
+ CXD2880_DVBT2_PAPR_RSVD9 = 0x0C,
+ CXD2880_DVBT2_PAPR_RSVD10 = 0x0D,
+ CXD2880_DVBT2_PAPR_RSVD11 = 0x0E,
+ CXD2880_DVBT2_PAPR_RSVD12 = 0x0F,
+ CXD2880_DVBT2_PAPR_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_constell {
+ CXD2880_DVBT2_L1POST_BPSK = 0x00,
+ CXD2880_DVBT2_L1POST_QPSK = 0x01,
+ CXD2880_DVBT2_L1POST_QAM16 = 0x02,
+ CXD2880_DVBT2_L1POST_QAM64 = 0x03,
+ CXD2880_DVBT2_L1POST_C_RSVD1 = 0x04,
+ CXD2880_DVBT2_L1POST_C_RSVD2 = 0x05,
+ CXD2880_DVBT2_L1POST_C_RSVD3 = 0x06,
+ CXD2880_DVBT2_L1POST_C_RSVD4 = 0x07,
+ CXD2880_DVBT2_L1POST_C_RSVD5 = 0x08,
+ CXD2880_DVBT2_L1POST_C_RSVD6 = 0x09,
+ CXD2880_DVBT2_L1POST_C_RSVD7 = 0x0A,
+ CXD2880_DVBT2_L1POST_C_RSVD8 = 0x0B,
+ CXD2880_DVBT2_L1POST_C_RSVD9 = 0x0C,
+ CXD2880_DVBT2_L1POST_C_RSVD10 = 0x0D,
+ CXD2880_DVBT2_L1POST_C_RSVD11 = 0x0E,
+ CXD2880_DVBT2_L1POST_C_RSVD12 = 0x0F,
+ CXD2880_DVBT2_L1POST_CONSTELL_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_cr {
+ CXD2880_DVBT2_L1POST_R1_2 = 0x00,
+ CXD2880_DVBT2_L1POST_R_RSVD1 = 0x01,
+ CXD2880_DVBT2_L1POST_R_RSVD2 = 0x02,
+ CXD2880_DVBT2_L1POST_R_RSVD3 = 0x03,
+ CXD2880_DVBT2_L1POST_R_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_l1post_fec_type {
+ CXD2880_DVBT2_L1POST_FEC_LDPC16K = 0x00,
+ CXD2880_DVBT2_L1POST_FEC_RSVD1 = 0x01,
+ CXD2880_DVBT2_L1POST_FEC_RSVD2 = 0x02,
+ CXD2880_DVBT2_L1POST_FEC_RSVD3 = 0x03,
+ CXD2880_DVBT2_L1POST_FEC_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_pp {
+ CXD2880_DVBT2_PP1 = 0x00,
+ CXD2880_DVBT2_PP2 = 0x01,
+ CXD2880_DVBT2_PP3 = 0x02,
+ CXD2880_DVBT2_PP4 = 0x03,
+ CXD2880_DVBT2_PP5 = 0x04,
+ CXD2880_DVBT2_PP6 = 0x05,
+ CXD2880_DVBT2_PP7 = 0x06,
+ CXD2880_DVBT2_PP8 = 0x07,
+ CXD2880_DVBT2_PP_RSVD1 = 0x08,
+ CXD2880_DVBT2_PP_RSVD2 = 0x09,
+ CXD2880_DVBT2_PP_RSVD3 = 0x0A,
+ CXD2880_DVBT2_PP_RSVD4 = 0x0B,
+ CXD2880_DVBT2_PP_RSVD5 = 0x0C,
+ CXD2880_DVBT2_PP_RSVD6 = 0x0D,
+ CXD2880_DVBT2_PP_RSVD7 = 0x0E,
+ CXD2880_DVBT2_PP_RSVD8 = 0x0F,
+ CXD2880_DVBT2_PP_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_code_rate {
+ CXD2880_DVBT2_R1_2 = 0x00,
+ CXD2880_DVBT2_R3_5 = 0x01,
+ CXD2880_DVBT2_R2_3 = 0x02,
+ CXD2880_DVBT2_R3_4 = 0x03,
+ CXD2880_DVBT2_R4_5 = 0x04,
+ CXD2880_DVBT2_R5_6 = 0x05,
+ CXD2880_DVBT2_R1_3 = 0x06,
+ CXD2880_DVBT2_R2_5 = 0x07,
+ CXD2880_DVBT2_PLP_CR_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_constell {
+ CXD2880_DVBT2_QPSK = 0x00,
+ CXD2880_DVBT2_QAM16 = 0x01,
+ CXD2880_DVBT2_QAM64 = 0x02,
+ CXD2880_DVBT2_QAM256 = 0x03,
+ CXD2880_DVBT2_CON_RSVD1 = 0x04,
+ CXD2880_DVBT2_CON_RSVD2 = 0x05,
+ CXD2880_DVBT2_CON_RSVD3 = 0x06,
+ CXD2880_DVBT2_CON_RSVD4 = 0x07,
+ CXD2880_DVBT2_CONSTELL_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_type {
+ CXD2880_DVBT2_PLP_TYPE_COMMON = 0x00,
+ CXD2880_DVBT2_PLP_TYPE_DATA1 = 0x01,
+ CXD2880_DVBT2_PLP_TYPE_DATA2 = 0x02,
+ CXD2880_DVBT2_PLP_TYPE_RSVD1 = 0x03,
+ CXD2880_DVBT2_PLP_TYPE_RSVD2 = 0x04,
+ CXD2880_DVBT2_PLP_TYPE_RSVD3 = 0x05,
+ CXD2880_DVBT2_PLP_TYPE_RSVD4 = 0x06,
+ CXD2880_DVBT2_PLP_TYPE_RSVD5 = 0x07,
+ CXD2880_DVBT2_PLP_TYPE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_payload {
+ CXD2880_DVBT2_PLP_PAYLOAD_GFPS = 0x00,
+ CXD2880_DVBT2_PLP_PAYLOAD_GCS = 0x01,
+ CXD2880_DVBT2_PLP_PAYLOAD_GSE = 0x02,
+ CXD2880_DVBT2_PLP_PAYLOAD_TS = 0x03,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD1 = 0x04,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD2 = 0x05,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD3 = 0x06,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD4 = 0x07,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD5 = 0x08,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD6 = 0x09,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD7 = 0x0A,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD8 = 0x0B,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD9 = 0x0C,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD10 = 0x0D,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD11 = 0x0E,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD12 = 0x0F,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD13 = 0x10,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD14 = 0x11,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD15 = 0x12,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD16 = 0x13,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD17 = 0x14,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD18 = 0x15,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD19 = 0x16,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD20 = 0x17,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD21 = 0x18,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD22 = 0x19,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD23 = 0x1A,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD24 = 0x1B,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD25 = 0x1C,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD26 = 0x1D,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD27 = 0x1E,
+ CXD2880_DVBT2_PLP_PAYLOAD_RSVD28 = 0x1F,
+ CXD2880_DVBT2_PLP_PAYLOAD_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_fec {
+ CXD2880_DVBT2_FEC_LDPC_16K = 0x00,
+ CXD2880_DVBT2_FEC_LDPC_64K = 0x01,
+ CXD2880_DVBT2_FEC_RSVD1 = 0x02,
+ CXD2880_DVBT2_FEC_RSVD2 = 0x03,
+ CXD2880_DVBT2_FEC_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_mode {
+ CXD2880_DVBT2_PLP_MODE_NOTSPECIFIED = 0x00,
+ CXD2880_DVBT2_PLP_MODE_NM = 0x01,
+ CXD2880_DVBT2_PLP_MODE_HEM = 0x02,
+ CXD2880_DVBT2_PLP_MODE_RESERVED = 0x03,
+ CXD2880_DVBT2_PLP_MODE_UNKNOWN = 0xFF
+};
+
+enum cxd2880_dvbt2_plp_btype {
+ CXD2880_DVBT2_PLP_COMMON,
+ CXD2880_DVBT2_PLP_DATA
+};
+
+enum cxd2880_dvbt2_stream {
+ CXD2880_DVBT2_STREAM_GENERIC_PACKETIZED = 0x00,
+ CXD2880_DVBT2_STREAM_GENERIC_CONTINUOUS = 0x01,
+ CXD2880_DVBT2_STREAM_GENERIC_ENCAPSULATED = 0x02,
+ CXD2880_DVBT2_STREAM_TRANSPORT = 0x03,
+ CXD2880_DVBT2_STREAM_UNKNOWN = 0xFF
+};
+
+struct cxd2880_dvbt2_l1pre {
+ enum cxd2880_dvbt2_l1pre_type type;
+ u8 bw_ext;
+ enum cxd2880_dvbt2_s1 s1;
+ u8 s2;
+ u8 mixed;
+ enum cxd2880_dvbt2_mode fft_mode;
+ u8 l1_rep;
+ enum cxd2880_dvbt2_guard gi;
+ enum cxd2880_dvbt2_papr papr;
+ enum cxd2880_dvbt2_l1post_constell mod;
+ enum cxd2880_dvbt2_l1post_cr cr;
+ enum cxd2880_dvbt2_l1post_fec_type fec;
+ u32 l1_post_size;
+ u32 l1_post_info_size;
+ enum cxd2880_dvbt2_pp pp;
+ u8 tx_id_availability;
+ u16 cell_id;
+ u16 network_id;
+ u16 sys_id;
+ u8 num_frames;
+ u16 num_symbols;
+ u8 regen;
+ u8 post_ext;
+ u8 num_rf_freqs;
+ u8 rf_idx;
+ enum cxd2880_dvbt2_version t2_version;
+ u8 l1_post_scrambled;
+ u8 t2_base_lite;
+ u32 crc32;
+};
+
+struct cxd2880_dvbt2_plp {
+ u8 id;
+ enum cxd2880_dvbt2_plp_type type;
+ enum cxd2880_dvbt2_plp_payload payload;
+ u8 ff;
+ u8 first_rf_idx;
+ u8 first_frm_idx;
+ u8 group_id;
+ enum cxd2880_dvbt2_plp_constell constell;
+ enum cxd2880_dvbt2_plp_code_rate plp_cr;
+ u8 rot;
+ enum cxd2880_dvbt2_plp_fec fec;
+ u16 num_blocks_max;
+ u8 frm_int;
+ u8 til_len;
+ u8 til_type;
+ u8 in_band_a_flag;
+ u8 in_band_b_flag;
+ u16 rsvd;
+ enum cxd2880_dvbt2_plp_mode plp_mode;
+ u8 static_flag;
+ u8 static_padding_flag;
+};
+
+struct cxd2880_dvbt2_l1post {
+ u16 sub_slices_per_frame;
+ u8 num_plps;
+ u8 num_aux;
+ u8 aux_cfg_rfu;
+ u8 rf_idx;
+ u32 freq;
+ u8 fef_type;
+ u32 fef_length;
+ u8 fef_intvl;
+};
+
+struct cxd2880_dvbt2_ofdm {
+ u8 mixed;
+ u8 is_miso;
+ enum cxd2880_dvbt2_mode mode;
+ enum cxd2880_dvbt2_guard gi;
+ enum cxd2880_dvbt2_pp pp;
+ u8 bw_ext;
+ enum cxd2880_dvbt2_papr papr;
+ u16 num_symbols;
+};
+
+struct cxd2880_dvbt2_bbheader {
+ enum cxd2880_dvbt2_stream stream_input;
+ u8 is_single_input_stream;
+ u8 is_constant_coding_modulation;
+ u8 issy_indicator;
+ u8 null_packet_deletion;
+ u8 ext;
+ u8 input_stream_identifier;
+ u16 user_packet_length;
+ u16 data_field_length;
+ u8 sync_byte;
+ u32 issy;
+ enum cxd2880_dvbt2_plp_mode plp_mode;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
new file mode 100644
index 0000000..43b7da6
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.c
@@ -0,0 +1,197 @@
+/*
+ * cxd2880_integ_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_integ_dvbt.h"
+
+static enum cxd2880_ret dvbt_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+ if ((tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt_tune1(tnr_dmd, tune_param);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ CXD2880_SLEEP(CXD2880_TNRDMD_WAIT_AGC_STABLE);
+
+ ret = cxd2880_tnrdmd_dvbt_tune2(tnr_dmd, tune_param);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt_wait_demod_lock(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_integ_dvbt_wait_ts_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ enum cxd2880_tnrdmd_lock_result lock =
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ struct cxd2880_stopwatch timer;
+ u8 continue_wait = 1;
+ u32 elapsed = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ for (;;) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (elapsed >= CXD2880_DVBT_WAIT_TS_LOCK)
+ continue_wait = 0;
+
+ ret = cxd2880_tnrdmd_dvbt_check_ts_lock(tnr_dmd, &lock);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ switch (lock) {
+ case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+ return CXD2880_RESULT_OK;
+
+ case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+ return CXD2880_RESULT_ERROR_UNLOCK;
+
+ default:
+ break;
+ }
+
+ ret = cxd2880_integ_check_cancellation(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (continue_wait) {
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_DVBT_WAIT_LOCK_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret = CXD2880_RESULT_ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ enum cxd2880_tnrdmd_lock_result lock =
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ struct cxd2880_stopwatch timer;
+ u8 continue_wait = 1;
+ u32 elapsed = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ for (;;) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (elapsed >= CXD2880_DVBT_WAIT_DMD_LOCK)
+ continue_wait = 0;
+
+ ret = cxd2880_tnrdmd_dvbt_check_demod_lock(tnr_dmd, &lock);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ switch (lock) {
+ case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+ return CXD2880_RESULT_OK;
+
+ case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+ return CXD2880_RESULT_ERROR_UNLOCK;
+
+ default:
+ break;
+ }
+
+ ret = cxd2880_integ_check_cancellation(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (continue_wait) {
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_DVBT_WAIT_LOCK_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret = CXD2880_RESULT_ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
new file mode 100644
index 0000000..41f35c0
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt.h
@@ -0,0 +1,58 @@
+/*
+ * cxd2880_integ_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_DVBT_H
+#define CXD2880_INTEG_DVBT_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_integ.h"
+
+#define CXD2880_DVBT_WAIT_DMD_LOCK 1000
+#define CXD2880_DVBT_WAIT_TS_LOCK 1000
+#define CXD2880_DVBT_WAIT_LOCK_INTVL 10
+
+struct cxd2880_integ_dvbt_scan_param {
+ u32 start_frequency_khz;
+ u32 end_frequency_khz;
+ u32 step_frequency_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+};
+
+struct cxd2880_integ_dvbt_scan_result {
+ u32 center_freq_khz;
+ enum cxd2880_ret tune_result;
+ struct cxd2880_dvbt_tune_param dvbt_tune_param;
+};
+
+enum cxd2880_ret cxd2880_integ_dvbt_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_integ_dvbt_wait_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
new file mode 100644
index 0000000..1d60b9c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.c
@@ -0,0 +1,311 @@
+/*
+ * cxd2880_integ_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_integ_dvbt2.h"
+
+static enum cxd2880_ret dvbt2_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ profile);
+
+static enum cxd2880_ret dvbt2_wait_l1_post_lock(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+ if ((tune_param->bandwidth != CXD2880_DTV_BW_1_7_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_5_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_6_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_7_MHZ) &&
+ (tune_param->bandwidth != CXD2880_DTV_BW_8_MHZ)) {
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+ }
+
+ if ((tune_param->profile != CXD2880_DVBT2_PROFILE_BASE) &&
+ (tune_param->profile != CXD2880_DVBT2_PROFILE_LITE))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_dvbt2_tune1(tnr_dmd, tune_param);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ CXD2880_SLEEP(CXD2880_TNRDMD_WAIT_AGC_STABLE);
+
+ ret = cxd2880_tnrdmd_dvbt2_tune2(tnr_dmd, tune_param);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt2_wait_demod_lock(tnr_dmd, tune_param->profile);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = cxd2880_tnrdmd_dvbt2_diver_fef_setting(tnr_dmd);
+ if (ret == CXD2880_RESULT_ERROR_HW_STATE)
+ return CXD2880_RESULT_ERROR_UNLOCK;
+ else if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt2_wait_l1_post_lock(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ {
+ u8 plp_not_found;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_data_plp_error(tnr_dmd,
+ &plp_not_found);
+ if (ret == CXD2880_RESULT_ERROR_HW_STATE)
+ return CXD2880_RESULT_ERROR_UNLOCK;
+ else if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (plp_not_found) {
+ ret = CXD2880_RESULT_OK_CONFIRM;
+ tune_param->tune_info =
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID;
+ } else {
+ tune_param->tune_info =
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK;
+ }
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_integ_dvbt2_wait_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ profile)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ enum cxd2880_tnrdmd_lock_result lock =
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ u16 timeout = 0;
+ struct cxd2880_stopwatch timer;
+ u8 continue_wait = 1;
+ u32 elapsed = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (profile == CXD2880_DVBT2_PROFILE_BASE)
+ timeout = CXD2880_DVBT2_BASE_WAIT_TS_LOCK;
+ else if (profile == CXD2880_DVBT2_PROFILE_LITE)
+ timeout = CXD2880_DVBT2_LITE_WAIT_TS_LOCK;
+ else
+ return CXD2880_RESULT_ERROR_ARG;
+
+ for (;;) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (elapsed >= timeout)
+ continue_wait = 0;
+
+ ret = cxd2880_tnrdmd_dvbt2_check_ts_lock(tnr_dmd, &lock);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ switch (lock) {
+ case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+ return CXD2880_RESULT_OK;
+
+ case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+ return CXD2880_RESULT_ERROR_UNLOCK;
+
+ default:
+ break;
+ }
+
+ ret = cxd2880_integ_check_cancellation(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (continue_wait) {
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_DVBT2_WAIT_LOCK_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret = CXD2880_RESULT_ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt2_wait_demod_lock(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ profile)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ enum cxd2880_tnrdmd_lock_result lock =
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ u16 timeout = 0;
+ struct cxd2880_stopwatch timer;
+ u8 continue_wait = 1;
+ u32 elapsed = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (profile == CXD2880_DVBT2_PROFILE_BASE)
+ timeout = CXD2880_DVBT2_BASE_WAIT_DMD_LOCK;
+ else if ((profile == CXD2880_DVBT2_PROFILE_LITE) ||
+ (profile == CXD2880_DVBT2_PROFILE_ANY))
+ timeout = CXD2880_DVBT2_LITE_WAIT_DMD_LOCK;
+ else
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ for (;;) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (elapsed >= timeout)
+ continue_wait = 0;
+
+ ret = cxd2880_tnrdmd_dvbt2_check_demod_lock(tnr_dmd, &lock);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ switch (lock) {
+ case CXD2880_TNRDMD_LOCK_RESULT_LOCKED:
+ return CXD2880_RESULT_OK;
+
+ case CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED:
+ return CXD2880_RESULT_ERROR_UNLOCK;
+
+ default:
+ break;
+ }
+
+ ret = cxd2880_integ_check_cancellation(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (continue_wait) {
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_DVBT2_WAIT_LOCK_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret = CXD2880_RESULT_ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt2_wait_l1_post_lock(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_stopwatch timer;
+ u8 continue_wait = 1;
+ u32 elapsed = 0;
+ u8 l1_post_valid;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ for (;;) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (elapsed >= CXD2880_DVBT2_L1POST_TIMEOUT)
+ continue_wait = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_check_l1post_valid(tnr_dmd,
+ &l1_post_valid);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (l1_post_valid)
+ return CXD2880_RESULT_OK;
+
+ ret = cxd2880_integ_check_cancellation(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (continue_wait) {
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_DVBT2_WAIT_LOCK_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret = CXD2880_RESULT_ERROR_TIMEOUT;
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
new file mode 100644
index 0000000..bc72eb8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ_dvbt2.h
@@ -0,0 +1,64 @@
+/*
+ * cxd2880_integ_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_DVBT2_H
+#define CXD2880_INTEG_DVBT2_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_integ.h"
+
+#define CXD2880_DVBT2_BASE_WAIT_DMD_LOCK 3500
+#define CXD2880_DVBT2_BASE_WAIT_TS_LOCK 1500
+#define CXD2880_DVBT2_LITE_WAIT_DMD_LOCK 5000
+#define CXD2880_DVBT2_LITE_WAIT_TS_LOCK 2300
+#define CXD2880_DVBT2_WAIT_LOCK_INTVL 10
+#define CXD2880_DVBT2_L1POST_TIMEOUT 500
+
+struct cxd2880_integ_dvbt2_scan_param {
+ u32 start_frequency_khz;
+ u32 end_frequency_khz;
+ u32 step_frequency_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ enum cxd2880_dvbt2_profile t2_profile;
+};
+
+struct cxd2880_integ_dvbt2_scan_result {
+ u32 center_freq_khz;
+ enum cxd2880_ret tune_result;
+ struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+};
+
+enum cxd2880_ret cxd2880_integ_dvbt2_tune(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_integ_dvbt2_wait_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ profile);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
new file mode 100644
index 0000000..f36cf53
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.c
@@ -0,0 +1,1072 @@
+/*
+ * cxd2880_tnrdmd_dvbt.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+
+static enum cxd2880_ret x_tune_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x31,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x52, 0x49 };
+ u8 data_b[2] = { 0x5D, 0x55 };
+ u8 data_c[2] = { 0x60, 0x00 };
+ u8 *data = NULL;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x65, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5D,
+ 0x07) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ u8 data[2] = { 0x01, 0x01 };
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xCE, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5C,
+ 0xFB) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xA4,
+ 0x03) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x14) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB0,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x25) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x01, 0xF0 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xF0, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) ||
+ (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x12) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x44,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x87,
+ 0xD2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ u8 data_a[3] = { 0x73, 0xCA, 0x49 };
+ u8 data_b[3] = { 0xC8, 0x13, 0xAA };
+ u8 data_c[3] = { 0xDC, 0x6C, 0x00 };
+ u8 *data = NULL;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x68, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+
+ {
+ u8 data_ac[5] = { 0x15, 0x00, 0x00, 0x00,
+ 0x00
+ };
+ u8 data_b[5] = { 0x14, 0x6A, 0xAA, 0xAA,
+ 0xAA
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60,
+ data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x01, 0x28 };
+ u8 data_b[2] = { 0x11, 0x44 };
+ u8 data_c[2] = { 0x15, 0x28 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x7D,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data = 0;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = 0x35;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = 0x34;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x71,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[5] = { 0x30, 0x00, 0x00, 0x90,
+ 0x00
+ };
+ u8 data_b[5] = { 0x36, 0x71, 0x00, 0xA3,
+ 0x55
+ };
+ u8 data_c[5] = { 0x38, 0x00, 0x00, 0xA8,
+ 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x51,
+ &data[2],
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[4] = { 0xB3, 0x00, 0x01, 0x02 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6B,
+ &data[2],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+
+ {
+ u8 data_ac[5] = { 0x18, 0x00, 0x00, 0x00,
+ 0x00
+ };
+ u8 data_b[5] = { 0x17, 0x55, 0x55, 0x55,
+ 0x55
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60,
+ data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x02) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x12, 0x4C };
+ u8 data_b[2] = { 0x1F, 0x15 };
+ u8 data_c[2] = { 0x1F, 0xF8 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x7D,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data = 0;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = 0x2F;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = 0x2E;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x71,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[5] = { 0x36, 0xDB, 0x00, 0xA4,
+ 0x92
+ };
+ u8 data_b[5] = { 0x3E, 0x38, 0x00, 0xBA,
+ 0xAA
+ };
+ u8 data_c[5] = { 0x40, 0x00, 0x00, 0xC0,
+ 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x51,
+ &data[2],
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[4] = { 0xB8, 0x00, 0x00, 0x03 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6B,
+ &data[2],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+
+ {
+ u8 data_ac[5] = { 0x1C, 0x00, 0x00, 0x00,
+ 0x00
+ };
+ u8 data_b[5] = { 0x1B, 0x38, 0xE3, 0x8E,
+ 0x38
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60,
+ data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x1F, 0xF8 };
+ u8 data_b[2] = { 0x24, 0x43 };
+ u8 data_c[2] = { 0x25, 0x4C };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x7D,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data = 0;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = 0x29;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = 0x2A;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x71,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[5] = { 0x40, 0x00, 0x00, 0xC0,
+ 0x00
+ };
+ u8 data_b[5] = { 0x48, 0x97, 0x00, 0xD9,
+ 0xC7
+ };
+ u8 data_c[5] = { 0x4A, 0xAA, 0x00, 0xDF,
+ 0xFF
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x51,
+ &data[2],
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[4] = { 0xBE, 0xAB, 0x00, 0x03 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6B,
+ &data[2],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+
+ {
+ u8 data_ac[5] = { 0x21, 0x99, 0x99, 0x99,
+ 0x99
+ };
+ u8 data_b[5] = { 0x20, 0xAA, 0xAA, 0xAA,
+ 0xAA
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60,
+ data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x06) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x26, 0x5D };
+ u8 data_b[2] = { 0x2B, 0x84 };
+ u8 data_c[2] = { 0x2C, 0xC2 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x7D,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data = 0;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = 0x24;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = 0x23;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x71,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[5] = { 0x4C, 0xCC, 0x00, 0xE6,
+ 0x66
+ };
+ u8 data_b[5] = { 0x57, 0x1C, 0x01, 0x05,
+ 0x55
+ };
+ u8 data_c[5] = { 0x59, 0x99, 0x01, 0x0C,
+ 0xCC
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x51,
+ &data[2],
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[4] = { 0xC8, 0x01, 0x00, 0x03 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72,
+ &data[0],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6B,
+ &data[2],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xFD,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep_dvbt_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5C,
+ 0xD8) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xA4,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x87,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt_profile profile)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x67,
+ (profile ==
+ CXD2880_DVBT_PROFILE_HP) ? 0x00 : 0x01) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = dvbt_set_profile(tnr_dmd, tune_param->profile);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd, CXD2880_DTV_SYS_DVBT,
+ 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep_dvbt_demod_setting(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if ((!tnr_dmd) || (!lock))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if ((!tnr_dmd) || (!lock))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (ts_lock)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
new file mode 100644
index 0000000..de394d8
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt.h
@@ -0,0 +1,62 @@
+/*
+ * cxd2880_tnrdmd_dvbt.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_H
+#define CXD2880_TNRDMD_DVBT_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+struct cxd2880_dvbt_tune_param {
+ u32 center_freq_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ enum cxd2880_dvbt_profile profile;
+};
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
new file mode 100644
index 0000000..bdad65b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.c
@@ -0,0 +1,1309 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control functions for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+
+static enum cxd2880_ret x_tune_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_bandwidth
+ bandwidth,
+ enum cxd2880_tnrdmd_clockmode
+ clk_mode)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x31,
+ 0x02) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5D,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ u8 data[2] = { 0x01, 0x01 };
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xCE, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[14] = { 0x07, 0x06, 0x01, 0xF0,
+ 0x00, 0x00, 0x04, 0xB0, 0x00, 0x00, 0x09, 0x9C, 0x0E,
+ 0x4C
+ };
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x8A,
+ data[0]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x90,
+ data[1]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x25) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xF0, &data[2],
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x2A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xDC,
+ data[4]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xDE,
+ data[5]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x2D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x73, &data[6],
+ 4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x8F, &data[10],
+ 4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a_1[9] = { 0x52, 0x49, 0x2C, 0x51,
+ 0x51, 0x3D, 0x15, 0x29, 0x0C
+ };
+ u8 data_b_1[9] = { 0x5D, 0x55, 0x32, 0x5C,
+ 0x5C, 0x45, 0x17, 0x2E, 0x0D
+ };
+ u8 data_c_1[9] = { 0x60, 0x00, 0x34, 0x5E,
+ 0x5E, 0x47, 0x18, 0x2F, 0x0E
+ };
+
+ u8 data_a_2[13] = { 0x04, 0xE7, 0x94, 0x92,
+ 0x09, 0xCF, 0x7E, 0xD0, 0x49, 0xCD, 0xCD, 0x1F, 0x5B
+ };
+ u8 data_b_2[13] = { 0x05, 0x90, 0x27, 0x55,
+ 0x0B, 0x20, 0x8F, 0xD6, 0xEA, 0xC8, 0xC8, 0x23, 0x91
+ };
+ u8 data_c_2[13] = { 0x05, 0xB8, 0xD8, 0x00,
+ 0x0B, 0x72, 0x93, 0xF3, 0x00, 0xCD, 0xCD, 0x24, 0x95
+ };
+
+ u8 data_a_3[5] = { 0x0B, 0x6A, 0xC9, 0x03,
+ 0x33
+ };
+ u8 data_b_3[5] = { 0x01, 0x02, 0xE4, 0x03,
+ 0x39
+ };
+ u8 data_c_3[5] = { 0x01, 0x02, 0xEB, 0x03,
+ 0x3B
+ };
+
+ u8 *data_1 = NULL;
+ u8 *data_2 = NULL;
+ u8 *data_3 = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data_1 = data_a_1;
+ data_2 = data_a_2;
+ data_3 = data_a_3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data_1 = data_b_1;
+ data_2 = data_b_2;
+ data_3 = data_b_3;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data_1 = data_c_1;
+ data_2 = data_c_2;
+ data_3 = data_c_3;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1D,
+ &data_1[0], 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x22,
+ data_1[3]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x24,
+ data_1[4]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x26,
+ data_1[5]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x29,
+ &data_1[6], 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x2D,
+ data_1[8]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_SUB) {
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x2E,
+ &data_2[0],
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x35,
+ &data_2[6],
+ 7) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x3C,
+ &data_3[0], 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x56,
+ &data_3[2], 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_8_MHZ:
+
+ {
+ u8 data_ac[6] = { 0x15, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ u8 data_b[6] = { 0x14, 0x6A, 0xAA, 0xAA,
+ 0xAB, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data_a[2] = { 0x19, 0xD2 };
+ u8 data_bc[2] = { 0x3F, 0xFF };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_bc;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x19,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a[2] = { 0x06, 0x2A };
+ u8 data_b[2] = { 0x06, 0x29 };
+ u8 data_c[2] = { 0x06, 0x28 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[9] = { 0x28, 0x00, 0x50, 0x00,
+ 0x60, 0x00, 0x00, 0x90, 0x00
+ };
+ u8 data_b[9] = { 0x2D, 0x5E, 0x5A, 0xBD,
+ 0x6C, 0xE3, 0x00, 0xA3, 0x55
+ };
+ u8 data_c[9] = { 0x2E, 0xAA, 0x5D, 0x55,
+ 0x70, 0x00, 0x00, 0xA8, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ data,
+ 9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_7_MHZ:
+
+ {
+ u8 data_ac[6] = { 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ u8 data_b[6] = { 0x17, 0x55, 0x55, 0x55,
+ 0x55, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x02) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x3F, 0xFF };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x19,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a[2] = { 0x06, 0x23 };
+ u8 data_b[2] = { 0x06, 0x22 };
+ u8 data_c[2] = { 0x06, 0x21 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[9] = { 0x2D, 0xB6, 0x5B, 0x6D,
+ 0x6D, 0xB6, 0x00, 0xA4, 0x92
+ };
+ u8 data_b[9] = { 0x33, 0xDA, 0x67, 0xB4,
+ 0x7C, 0x71, 0x00, 0xBA, 0xAA
+ };
+ u8 data_c[9] = { 0x35, 0x55, 0x6A, 0xAA,
+ 0x80, 0x00, 0x00, 0xC0, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ data,
+ 9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_6_MHZ:
+
+ {
+ u8 data_ac[6] = { 0x1C, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ u8 data_b[6] = { 0x1B, 0x38, 0xE3, 0x8E,
+ 0x39, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x3F, 0xFF };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x19,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a[2] = { 0x06, 0x1C };
+ u8 data_b[2] = { 0x06, 0x1B };
+ u8 data_c[2] = { 0x06, 0x1A };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[9] = { 0x35, 0x55, 0x6A, 0xAA,
+ 0x80, 0x00, 0x00, 0xC0, 0x00
+ };
+ u8 data_b[9] = { 0x3C, 0x7E, 0x78, 0xFC,
+ 0x91, 0x2F, 0x00, 0xD9, 0xC7
+ };
+ u8 data_c[9] = { 0x3E, 0x38, 0x7C, 0x71,
+ 0x95, 0x55, 0x00, 0xDF, 0xFF
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ data,
+ 9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_5_MHZ:
+
+ {
+ u8 data_ac[6] = { 0x21, 0x99, 0x99, 0x99,
+ 0x9A, 0x00
+ };
+ u8 data_b[6] = { 0x20, 0xAA, 0xAA, 0xAA,
+ 0xAB, 0x00
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_ac;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x06) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x3F, 0xFF };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x19,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a[2] = { 0x06, 0x15 };
+ u8 data_b[2] = { 0x06, 0x15 };
+ u8 data_c[2] = { 0x06, 0x14 };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[9] = { 0x40, 0x00, 0x6A, 0xAA,
+ 0x80, 0x00, 0x00, 0xE6, 0x66
+ };
+ u8 data_b[9] = { 0x48, 0x97, 0x78, 0xFC,
+ 0x91, 0x2F, 0x01, 0x05, 0x55
+ };
+ u8 data_c[9] = { 0x4A, 0xAA, 0x7C, 0x71,
+ 0x95, 0x55, 0x01, 0x0C, 0xCC
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ data,
+ 9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_DTV_BW_1_7_MHZ:
+
+ {
+ u8 data_a[6] = { 0x68, 0x0F, 0xA2, 0x32,
+ 0xCF, 0x03
+ };
+ u8 data_c[6] = { 0x68, 0x0F, 0xA2, 0x32,
+ 0xCF, 0x03
+ };
+ u8 data_b[6] = { 0x65, 0x2B, 0xA4, 0xCD,
+ 0xD8, 0x03
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4A,
+ 0x03) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x3F, 0xFF };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x19,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data_a[2] = { 0x06, 0x0C };
+ u8 data_b[2] = { 0x06, 0x0C };
+ u8 data_c[2] = { 0x06, 0x0B };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data_a[9] = { 0x40, 0x00, 0x6A, 0xAA,
+ 0x80, 0x00, 0x02, 0xC9, 0x8F
+ };
+ u8 data_b[9] = { 0x48, 0x97, 0x78, 0xFC,
+ 0x91, 0x2F, 0x03, 0x29, 0x5D
+ };
+ u8 data_c[9] = { 0x4A, 0xAA, 0x7C, 0x71,
+ 0x95, 0x55, 0x03, 0x40, 0x7D
+ };
+ u8 *data = NULL;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data = data_a;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data = data_b;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data = data_c;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ data,
+ 9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xFD,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep_dvbt2_demod_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ u8 data[] = { 0, 1, 0, 2,
+ 0, 4, 0, 8, 0, 16, 0, 32
+ };
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x1D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x47, data,
+ 12) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_set_profile(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_profile profile)
+{
+ u8 t2_mode_tune_mode = 0;
+ u8 seq_not2_dtime = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ {
+ u8 dtime1 = 0;
+ u8 dtime2 = 0;
+
+ switch (tnr_dmd->clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ dtime1 = 0x27;
+ dtime2 = 0x0C;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ dtime1 = 0x2C;
+ dtime2 = 0x0D;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ dtime1 = 0x2E;
+ dtime2 = 0x0E;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ switch (profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ t2_mode_tune_mode = 0x01;
+ seq_not2_dtime = dtime2;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_LITE:
+ t2_mode_tune_mode = 0x05;
+ seq_not2_dtime = dtime1;
+ break;
+
+ case CXD2880_DVBT2_PROFILE_ANY:
+ t2_mode_tune_mode = 0x00;
+ seq_not2_dtime = dtime1;
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x2E) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ t2_mode_tune_mode) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x2C,
+ seq_not2_dtime) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) &&
+ (tune_param->profile == CXD2880_DVBT2_PROFILE_ANY))
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting1(tnr_dmd, CXD2880_DTV_SYS_DVBT2,
+ tune_param->center_freq_khz,
+ tune_param->bandwidth, 0, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd, tune_param->bandwidth,
+ tnr_dmd->clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune_dvbt2_demod_setting(tnr_dmd->diver_sub,
+ tune_param->bandwidth,
+ tnr_dmd->diver_sub->clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = dvbt2_set_profile(tnr_dmd, tune_param->profile);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ dvbt2_set_profile(tnr_dmd->diver_sub, tune_param->profile);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ if (tune_param->data_plp_id == CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO) {
+ ret = cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 1, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ ret =
+ cxd2880_tnrdmd_dvbt2_set_plp_cfg(tnr_dmd, 0,
+ (u8)(tune_param->data_plp_id));
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!tune_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 en_fef_intmtnt_ctrl = 1;
+
+ switch (tune_param->profile) {
+ case CXD2880_DVBT2_PROFILE_BASE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_base;
+ break;
+ case CXD2880_DVBT2_PROFILE_LITE:
+ en_fef_intmtnt_ctrl = tnr_dmd->en_fef_intmtnt_lite;
+ break;
+ case CXD2880_DVBT2_PROFILE_ANY:
+ if (tnr_dmd->en_fef_intmtnt_base &&
+ tnr_dmd->en_fef_intmtnt_lite)
+ en_fef_intmtnt_ctrl = 1;
+ else
+ en_fef_intmtnt_ctrl = 0;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ ret =
+ cxd2880_tnrdmd_common_tune_setting2(tnr_dmd,
+ CXD2880_DTV_SYS_DVBT2,
+ en_fef_intmtnt_ctrl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->bandwidth = tune_param->bandwidth;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_ACTIVE;
+ tnr_dmd->diver_sub->frequency_khz = tune_param->center_freq_khz;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_DVBT2;
+ tnr_dmd->diver_sub->bandwidth = tune_param->bandwidth;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep_dvbt2_demod_setting(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if ((!tnr_dmd) || (!lock))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (sync_stat == 6) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (sync_stat == 6)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 sync_stat = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 unlock_detected_sub = 0;
+
+ if ((!tnr_dmd) || (!lock))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_stat, &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (ts_lock)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ else if (unlock_detected)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+ }
+
+ if (ts_lock) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_LOCKED;
+ return ret;
+ } else if (!unlock_detected) {
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+ return ret;
+ }
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(tnr_dmd, &sync_stat,
+ &unlock_detected_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (unlock_detected && unlock_detected_sub)
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED;
+ else
+ *lock = CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 auto_plp,
+ u8 plp_id)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x23) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!auto_plp) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xAF,
+ plp_id) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xAD,
+ auto_plp ? 0x00 : 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE)
+ return CXD2880_RESULT_OK;
+
+ {
+ struct cxd2880_dvbt2_ofdm ofdm;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_ofdm(tnr_dmd, &ofdm);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (!ofdm.mixed)
+ return CXD2880_RESULT_OK;
+ }
+
+ {
+ u8 data[] = { 0, 8, 0, 16,
+ 0, 32, 0, 64, 0, 128, 1, 0
+ };
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x1D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x47, data,
+ 12) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *l1_post_valid)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 data;
+
+ if ((!tnr_dmd) || (!l1_post_valid))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *l1_post_valid = data & 0x01;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
new file mode 100644
index 0000000..8735280
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2.h
@@ -0,0 +1,82 @@
+/*
+ * cxd2880_tnrdmd_dvbt2.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * control interface for DVB-T2
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_H
+#define CXD2880_TNRDMD_DVBT2_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_tnrdmd_dvbt2_tune_info {
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_OK,
+ CXD2880_TNRDMD_DVBT2_TUNE_INFO_INVALID_PLP_ID
+};
+
+struct cxd2880_dvbt2_tune_param {
+ u32 center_freq_khz;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ u16 data_plp_id;
+ enum cxd2880_dvbt2_profile profile;
+ enum cxd2880_tnrdmd_dvbt2_tune_info tune_info;
+};
+
+#define CXD2880_DVBT2_TUNE_PARAM_PLPID_AUTO 0xFFFF
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_tune_param
+ *tune_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_sleep_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_demod_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_ts_lock(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_lock_result
+ *lock);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_set_plp_cfg(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 auto_plp,
+ u8 plp_id);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_diver_fef_setting(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_check_l1post_valid(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *l1_post_valid);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
new file mode 100644
index 0000000..235db16
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.c
@@ -0,0 +1,2523 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_math.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected)
+{
+ if ((!tnr_dmd) || (!sync_stat) || (!ts_lock_stat) || (!unlock_detected))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10, &data,
+ sizeof(data)) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *sync_stat = data & 0x07;
+ *ts_lock_stat = ((data & 0x20) ? 1 : 0);
+ *unlock_detected = ((data & 0x10) ? 1 : 0);
+ }
+
+ if (*sync_stat == 0x07)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *sync_stat,
+ u8 *unlock_detected)
+{
+ u8 ts_lock_stat = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+ &ts_lock_stat, unlock_detected);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!offset))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[4];
+ u32 ctl_val = 0;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x30, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ctl_val =
+ ((data[0] & 0x0F) << 24) | (data[1] << 16) | (data[2] << 8)
+ | (data[3]);
+ *offset = cxd2880_convert2s_complement(ctl_val, 28);
+
+ switch (tnr_dmd->bandwidth) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ *offset = -1 * ((*offset) / 582);
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ case CXD2880_DTV_BW_6_MHZ:
+ case CXD2880_DTV_BW_7_MHZ:
+ case CXD2880_DTV_BW_8_MHZ:
+ *offset =
+ -1 * ((*offset) * (u8)tnr_dmd->bandwidth / 940);
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!offset))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_l1pre
+ *l1_pre)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!l1_pre))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[37];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 version = 0;
+ enum cxd2880_dvbt2_profile profile;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_profile(tnr_dmd, &profile);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x61, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ slvt_unfreeze_reg(tnr_dmd);
+
+ l1_pre->type = (enum cxd2880_dvbt2_l1pre_type)data[0];
+ l1_pre->bw_ext = data[1] & 0x01;
+ l1_pre->s1 = (enum cxd2880_dvbt2_s1)(data[2] & 0x07);
+ l1_pre->s2 = data[3] & 0x0F;
+ l1_pre->l1_rep = data[4] & 0x01;
+ l1_pre->gi = (enum cxd2880_dvbt2_guard)(data[5] & 0x07);
+ l1_pre->papr = (enum cxd2880_dvbt2_papr)(data[6] & 0x0F);
+ l1_pre->mod =
+ (enum cxd2880_dvbt2_l1post_constell)(data[7] & 0x0F);
+ l1_pre->cr = (enum cxd2880_dvbt2_l1post_cr)(data[8] & 0x03);
+ l1_pre->fec =
+ (enum cxd2880_dvbt2_l1post_fec_type)(data[9] & 0x03);
+ l1_pre->l1_post_size = (data[10] & 0x03) << 16;
+ l1_pre->l1_post_size |= (data[11]) << 8;
+ l1_pre->l1_post_size |= (data[12]);
+ l1_pre->l1_post_info_size = (data[13] & 0x03) << 16;
+ l1_pre->l1_post_info_size |= (data[14]) << 8;
+ l1_pre->l1_post_info_size |= (data[15]);
+ l1_pre->pp = (enum cxd2880_dvbt2_pp)(data[16] & 0x0F);
+ l1_pre->tx_id_availability = data[17];
+ l1_pre->cell_id = (data[18] << 8);
+ l1_pre->cell_id |= (data[19]);
+ l1_pre->network_id = (data[20] << 8);
+ l1_pre->network_id |= (data[21]);
+ l1_pre->sys_id = (data[22] << 8);
+ l1_pre->sys_id |= (data[23]);
+ l1_pre->num_frames = data[24];
+ l1_pre->num_symbols = (data[25] & 0x0F) << 8;
+ l1_pre->num_symbols |= data[26];
+ l1_pre->regen = data[27] & 0x07;
+ l1_pre->post_ext = data[28] & 0x01;
+ l1_pre->num_rf_freqs = data[29] & 0x07;
+ l1_pre->rf_idx = data[30] & 0x07;
+ version = (data[31] & 0x03) << 2;
+ version |= (data[32] & 0xC0) >> 6;
+ l1_pre->t2_version = (enum cxd2880_dvbt2_version)version;
+ l1_pre->l1_post_scrambled = (data[32] & 0x20) >> 5;
+ l1_pre->t2_base_lite = (data[32] & 0x10) >> 4;
+ l1_pre->crc32 = (data[33] << 24);
+ l1_pre->crc32 |= (data[34] << 16);
+ l1_pre->crc32 |= (data[35] << 8);
+ l1_pre->crc32 |= data[36];
+
+ if (profile == CXD2880_DVBT2_PROFILE_BASE) {
+ switch ((l1_pre->s2 >> 1)) {
+ case CXD2880_DVBT2_BASE_S2_M1K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M1K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M2K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M4K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT:
+ case CXD2880_DVBT2_BASE_S2_M8K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M16K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+ break;
+ case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT:
+ case CXD2880_DVBT2_BASE_S2_M32K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M32K;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ } else if (profile == CXD2880_DVBT2_PROFILE_LITE) {
+ switch ((l1_pre->s2 >> 1)) {
+ case CXD2880_DVBT2_LITE_S2_M2K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M2K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M4K_G_ANY:
+ l1_pre->fft_mode = CXD2880_DVBT2_M4K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT:
+ case CXD2880_DVBT2_LITE_S2_M8K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M8K;
+ break;
+ case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT:
+ case CXD2880_DVBT2_LITE_S2_M16K_G_DVBT2:
+ l1_pre->fft_mode = CXD2880_DVBT2_M16K;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ } else {
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ l1_pre->mixed = l1_pre->s2 & 0x01;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_version
+ *ver)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 version = 0;
+
+ if ((!tnr_dmd) || (!ver))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[2];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x80, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ version = ((data[0] & 0x03) << 2);
+ version |= ((data[1] & 0xC0) >> 6);
+ *ver = (enum cxd2880_dvbt2_version)version;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_ofdm *ofdm)
+{
+ if ((!tnr_dmd) || (!ofdm))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[5];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_ofdm(
+ tnr_dmd->diver_sub, ofdm);
+
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1D, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ofdm->mixed = ((data[0] & 0x20) ? 1 : 0);
+ ofdm->is_miso = ((data[0] & 0x10) >> 4);
+ ofdm->mode = (enum cxd2880_dvbt2_mode)(data[0] & 0x07);
+ ofdm->gi = (enum cxd2880_dvbt2_guard)((data[1] & 0x70) >> 4);
+ ofdm->pp = (enum cxd2880_dvbt2_pp)(data[1] & 0x07);
+ ofdm->bw_ext = (data[2] & 0x10) >> 4;
+ ofdm->papr = (enum cxd2880_dvbt2_papr)(data[2] & 0x0F);
+ ofdm->num_symbols = (data[3] << 8) | data[4];
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *plp_ids,
+ u8 *num_plps)
+{
+ if ((!tnr_dmd) || (!num_plps))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 l1_post_ok = 0;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86,
+ &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xC1, num_plps,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (*num_plps == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_OTHER;
+ }
+
+ if (!plp_ids) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_OK;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xC2, plp_ids,
+ ((*num_plps >
+ 62) ? 62 : *num_plps)) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (*num_plps > 62) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0C) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ plp_ids + 62,
+ *num_plps - 62) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+ }
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_plp
+ *plp_info)
+{
+ if ((!tnr_dmd) || (!plp_info))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[20];
+ u8 addr = 0;
+ u8 index = 0;
+ u8 l1_post_ok = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86,
+ &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!l1_post_ok) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xA9;
+ else
+ addr = 0x96;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, addr, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if ((type == CXD2880_DVBT2_PLP_COMMON) && (data[13] == 0))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ plp_info->id = data[index++];
+ plp_info->type =
+ (enum cxd2880_dvbt2_plp_type)(data[index++] & 0x07);
+ plp_info->payload =
+ (enum cxd2880_dvbt2_plp_payload)(data[index++] & 0x1F);
+ plp_info->ff = data[index++] & 0x01;
+ plp_info->first_rf_idx = data[index++] & 0x07;
+ plp_info->first_frm_idx = data[index++];
+ plp_info->group_id = data[index++];
+ plp_info->plp_cr =
+ (enum cxd2880_dvbt2_plp_code_rate)(data[index++] & 0x07);
+ plp_info->constell =
+ (enum cxd2880_dvbt2_plp_constell)(data[index++] & 0x07);
+ plp_info->rot = data[index++] & 0x01;
+ plp_info->fec =
+ (enum cxd2880_dvbt2_plp_fec)(data[index++] & 0x03);
+ plp_info->num_blocks_max = (u16)((data[index++] & 0x03)) << 8;
+ plp_info->num_blocks_max |= data[index++];
+ plp_info->frm_int = data[index++];
+ plp_info->til_len = data[index++];
+ plp_info->til_type = data[index++] & 0x01;
+
+ plp_info->in_band_a_flag = data[index++] & 0x01;
+ plp_info->rsvd = data[index++] << 8;
+ plp_info->rsvd |= data[index++];
+
+ plp_info->in_band_b_flag =
+ (u8)((plp_info->rsvd & 0x8000) >> 15);
+ plp_info->plp_mode =
+ (enum cxd2880_dvbt2_plp_mode)((plp_info->rsvd & 0x000C) >>
+ 2);
+ plp_info->static_flag = (u8)((plp_info->rsvd & 0x0002) >> 1);
+ plp_info->static_padding_flag = (u8)(plp_info->rsvd & 0x0001);
+ plp_info->rsvd = (u16)((plp_info->rsvd & 0x7FF0) >> 4);
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *plp_error)
+{
+ if ((!tnr_dmd) || (!plp_error))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if ((data & 0x01) == 0x00) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xC0, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *plp_error = data & 0x01;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *l1_change)
+{
+ if ((!tnr_dmd) || (!l1_change))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state < 5) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub
+ (tnr_dmd, &sync_state, &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state < 5) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ } else {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5F, &data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ *l1_change = data & 0x01;
+ if (*l1_change) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x22) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x16,
+ 0x01) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ }
+ slvt_unfreeze_reg(tnr_dmd);
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt2_l1post
+ *l1_post)
+{
+ if ((!tnr_dmd) || (!l1_post))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[16];
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86, data,
+ sizeof(data)) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!(data[0] & 0x01))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ l1_post->sub_slices_per_frame = (data[1] & 0x7F) << 8;
+ l1_post->sub_slices_per_frame |= data[2];
+ l1_post->num_plps = data[3];
+ l1_post->num_aux = data[4] & 0x0F;
+ l1_post->aux_cfg_rfu = data[5];
+ l1_post->rf_idx = data[6] & 0x07;
+ l1_post->freq = data[7] << 24;
+ l1_post->freq |= data[8] << 16;
+ l1_post->freq |= data[9] << 8;
+ l1_post->freq |= data[10];
+ l1_post->fef_type = data[11] & 0x0F;
+ l1_post->fef_length = data[12] << 16;
+ l1_post->fef_length |= data[13] << 8;
+ l1_post->fef_length |= data[14];
+ l1_post->fef_intvl = data[15];
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_bbheader
+ *bbheader)
+{
+ if ((!tnr_dmd) || (!bbheader))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!ts_lock) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ u8 l1_post_ok;
+ u8 data;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86,
+ &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB6, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ {
+ u8 data[14];
+ u8 addr = 0;
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0x51;
+ else
+ addr = 0x42;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, addr, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ bbheader->stream_input =
+ (enum cxd2880_dvbt2_stream)((data[0] >> 6) & 0x03);
+ bbheader->is_single_input_stream = (u8)((data[0] >> 5) & 0x01);
+ bbheader->is_constant_coding_modulation =
+ (u8)((data[0] >> 4) & 0x01);
+ bbheader->issy_indicator = (u8)((data[0] >> 3) & 0x01);
+ bbheader->null_packet_deletion = (u8)((data[0] >> 2) & 0x01);
+ bbheader->ext = (u8)(data[0] & 0x03);
+
+ bbheader->input_stream_identifier = data[1];
+ bbheader->plp_mode =
+ (data[3] & 0x01) ? CXD2880_DVBT2_PLP_MODE_HEM :
+ CXD2880_DVBT2_PLP_MODE_NM;
+ bbheader->data_field_length = (u16)((data[4] << 8) | data[5]);
+
+ if (bbheader->plp_mode == CXD2880_DVBT2_PLP_MODE_NM) {
+ bbheader->user_packet_length =
+ (u16)((data[6] << 8) | data[7]);
+ bbheader->sync_byte = data[8];
+ bbheader->issy = 0;
+ } else {
+ bbheader->user_packet_length = 0;
+ bbheader->sync_byte = 0;
+ bbheader->issy =
+ (u32)((data[11] << 16) | (data[12] << 8) |
+ data[13]);
+ }
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ u32 *ts_rate_bps)
+{
+ if ((!tnr_dmd) || (!ts_rate_bps))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (!ts_lock) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 l1_post_ok = 0;
+ u8 addr = 0;
+ u8 data = 0;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86,
+ &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xBA;
+ else
+ addr = 0xA7;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, addr, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if ((data & 0x80) == 0x00) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x25) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[4];
+ u8 addr = 0;
+
+ if (type == CXD2880_DVBT2_PLP_COMMON)
+ addr = 0xA6;
+ else
+ addr = 0xAA;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, addr, &data[0],
+ 4) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ *ts_rate_bps =
+ (u32)(((data[0] & 0x07) << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3]);
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 early_unlock = 0;
+
+ if ((!tnr_dmd) || (!sense))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state, &ts_lock,
+ &early_unlock);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(
+ tnr_dmd->diver_sub, sense);
+
+ return ret;
+ }
+
+ {
+ u8 data = 0;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x2F, &data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *sense =
+ (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+ CXD2880_TNRDMD_SPECTRUM_NORMAL;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *reg_value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!reg_value))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ {
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ u8 data[2];
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x13, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *reg_value = (data[0] << 8) | data[1];
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt2_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 reg_value, int *snr)
+{
+ if ((!tnr_dmd) || (!snr))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (reg_value == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (reg_value > 10876)
+ reg_value = 10876;
+
+ *snr =
+ 10 * 10 * ((int)cxd2880_math_log10(reg_value) -
+ (int)cxd2880_math_log10(12600 - reg_value));
+ *snr += 32000;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr)
+{
+ u16 reg_value = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!snr))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ *snr = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ ret = dvbt2_read_snr_reg(tnr_dmd, ®_value);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ int snr_main = 0;
+ int snr_sub = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_snr_diver(tnr_dmd, snr, &snr_main,
+ &snr_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub)
+{
+ u16 reg_value = 0;
+ u32 reg_value_sum = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!snr) || (!snr_main) || (!snr_sub))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ *snr = -1000 * 1000;
+ *snr_main = -1000 * 1000;
+ *snr_sub = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = dvbt2_read_snr_reg(tnr_dmd, ®_value);
+ if (ret == CXD2880_RESULT_OK) {
+ ret = dvbt2_calc_snr(tnr_dmd, reg_value, snr_main);
+ if (ret != CXD2880_RESULT_OK)
+ reg_value = 0;
+ } else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt2_read_snr_reg(tnr_dmd->diver_sub, ®_value);
+ if (ret == CXD2880_RESULT_OK) {
+ ret = dvbt2_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+ if (ret != CXD2880_RESULT_OK)
+ reg_value = 0;
+ } else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt2_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_ldpcber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u32 bit_error = 0;
+ u32 period_exp = 0;
+ u32 n_ldpc = 0;
+
+ if ((!tnr_dmd) || (!ber))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ u8 data[5];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x3C, data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(data[0] & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ bit_error =
+ ((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8)
+ | data[4];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xA0, data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+ CXD2880_DVBT2_FEC_LDPC_16K)
+ n_ldpc = 16200;
+ else
+ n_ldpc = 64800;
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6F, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = data[0] & 0x0F;
+ }
+
+ if (bit_error > ((1U << period_exp) * n_ldpc))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ if (period_exp >= 4) {
+ div = (1U << (period_exp - 4)) * (n_ldpc / 200);
+
+ Q = (bit_error * 5) / div;
+ R = (bit_error * 5) % div;
+
+ R *= 625;
+ Q = Q * 625 + R / div;
+ R = R % div;
+ } else {
+ div = (1U << period_exp) * (n_ldpc / 200);
+
+ Q = (bit_error * 10) / div;
+ R = (bit_error * 10) % div;
+
+ R *= 5000;
+ Q = Q * 5000 + R / div;
+ R = R % div;
+ }
+
+ if (div / 2 <= R)
+ *ber = Q + 1;
+ else
+ *ber = Q;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *fer)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u32 fec_error = 0;
+ u32 period = 0;
+
+ if ((!tnr_dmd) || (!fer))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!(data[0] & 0x80))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ fec_error = ((data[0] & 0x7F) << 8) | (data[1]);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period = (1 << (data[0] & 0x0F));
+ }
+
+ if ((period == 0) || (fec_error > period))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ div = period;
+
+ Q = (fec_error * 1000) / div;
+ R = (fec_error * 1000) % div;
+
+ R *= 1000;
+ Q = Q * 1000 + R / div;
+ R = R % div;
+
+ if ((div != 1) && (div / 2 <= R))
+ *fer = Q + 1;
+ else
+ *fer = Q;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u32 bit_error = 0;
+ u32 period_exp = 0;
+ u32 n_bch = 0;
+
+ if ((!tnr_dmd) || (!ber))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[3];
+ enum cxd2880_dvbt2_plp_fec plp_fec_type =
+ CXD2880_DVBT2_FEC_LDPC_16K;
+ enum cxd2880_dvbt2_plp_code_rate plp_cr = CXD2880_DVBT2_R1_2;
+
+ static const u16 n_bch_bits_lookup[2][8] = {
+ {7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+ {32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+ };
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x15, data,
+ 3) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(data[0] & 0x40)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ bit_error = ((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x9D, data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ plp_cr = (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xA0, data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x72, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = data[0] & 0x0F;
+
+ if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
+ (plp_cr > CXD2880_DVBT2_R2_5))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ n_bch = n_bch_bits_lookup[plp_fec_type][plp_cr];
+ }
+
+ if (bit_error > ((1U << period_exp) * n_bch))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ if (period_exp >= 6) {
+ div = (1U << (period_exp - 6)) * (n_bch / 40);
+
+ Q = (bit_error * 625) / div;
+ R = (bit_error * 625) % div;
+
+ R *= 625;
+ Q = Q * 625 + R / div;
+ R = R % div;
+ } else {
+ div = (1U << period_exp) * (n_bch / 40);
+
+ Q = (bit_error * 1000) / div;
+ R = (bit_error * 1000) % div;
+
+ R *= 25000;
+ Q = Q * 25000 + R / div;
+ R = R % div;
+ }
+
+ if (div / 2 <= R)
+ *ber = Q + 1;
+ else
+ *ber = Q;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ u8 data[3];
+
+ if ((!tnr_dmd) || (!pen))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x39, data,
+ sizeof(data)) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!(data[0] & 0x01))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *pen = ((data[1] << 8) | data[2]);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ if ((!tnr_dmd) || (!ppm))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 ctl_val_reg[5];
+ u8 nominal_rate_reg[5];
+ u32 trl_ctl_val = 0;
+ u32 trcg_nominal_rate = 0;
+ int num;
+ int den;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+ s8 diff_upper = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (sync_state != 6) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x34,
+ ctl_val_reg,
+ sizeof(ctl_val_reg)) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ nominal_rate_reg,
+ sizeof(nominal_rate_reg)) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ diff_upper =
+ (ctl_val_reg[0] & 0x7F) - (nominal_rate_reg[0] & 0x7F);
+
+ if ((diff_upper < -1) || (diff_upper > 1))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ trl_ctl_val = ctl_val_reg[1] << 24;
+ trl_ctl_val |= ctl_val_reg[2] << 16;
+ trl_ctl_val |= ctl_val_reg[3] << 8;
+ trl_ctl_val |= ctl_val_reg[4];
+
+ trcg_nominal_rate = nominal_rate_reg[1] << 24;
+ trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+ trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+ trcg_nominal_rate |= nominal_rate_reg[4];
+
+ trl_ctl_val >>= 1;
+ trcg_nominal_rate >>= 1;
+
+ if (diff_upper == 1)
+ num =
+ (int)((trl_ctl_val + 0x80000000u) -
+ trcg_nominal_rate);
+ else if (diff_upper == -1)
+ num =
+ -(int)((trcg_nominal_rate + 0x80000000u) -
+ trl_ctl_val);
+ else
+ num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+ den = (nominal_rate_reg[0] & 0x7F) << 24;
+ den |= nominal_rate_reg[1] << 16;
+ den |= nominal_rate_reg[2] << 8;
+ den |= nominal_rate_reg[3];
+ den = (den + (390625 / 2)) / 390625;
+
+ den >>= 1;
+
+ if (num >= 0)
+ *ppm = (num + (den / 2)) / den;
+ else
+ *ppm = (num - (den / 2)) / den;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ppm))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *quality)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ int snr = 0;
+ int snr_rel = 0;
+ u32 ber = 0;
+ u32 ber_sqi = 0;
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_dvbt2_plp_code_rate code_rate;
+
+ static const int snr_nordig_p1_db_1000[4][8] = {
+ {3500, 4700, 5600, 6600, 7200, 7700, 1300, 2200},
+ {8700, 10100, 11400, 12500, 13300, 13800, 6000, 7200},
+ {13000, 14800, 16200, 17700, 18700, 19400, 9800, 11100},
+ {17000, 19400, 20800, 22900, 24300, 25100, 13200, 14800},
+ };
+
+ if ((!tnr_dmd) || (!quality))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(tnr_dmd, &ber);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = cxd2880_tnrdmd_dvbt2_mon_snr(tnr_dmd, &snr);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+ &code_rate);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if ((code_rate > CXD2880_DVBT2_R2_5) || (qam > CXD2880_DVBT2_QAM256))
+ return CXD2880_RESULT_ERROR_OTHER;
+
+ if (ber > 100000)
+ ber_sqi = 0;
+ else if (ber >= 100)
+ ber_sqi = 6667;
+ else
+ ber_sqi = 16667;
+
+ snr_rel = snr - snr_nordig_p1_db_1000[qam][code_rate];
+
+ if (snr_rel < -3000) {
+ *quality = 0;
+ } else if (snr_rel <= 3000) {
+ u32 temp_sqi =
+ (((snr_rel + 3000) * ber_sqi) + 500000) / 1000000;
+ *quality = (temp_sqi > 100) ? 100 : (u8)temp_sqi;
+ } else {
+ *quality = 100;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ts_rate_kbps)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u32 rd_smooth_dp = 0;
+ u32 ep_ck_nume = 0;
+ u32 ep_ck_deno = 0;
+ u8 issy_on_data = 0;
+
+ if ((!tnr_dmd) || (!ts_rate_kbps))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[12];
+ u8 sync_state = 0;
+ u8 ts_lock = 0;
+ u8 unlock_detected = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_sync_stat(tnr_dmd, &sync_state,
+ &ts_lock,
+ &unlock_detected);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!ts_lock) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x23, data,
+ 12) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ rd_smooth_dp = (u32)((data[0] & 0x1F) << 24);
+ rd_smooth_dp |= (u32)(data[1] << 16);
+ rd_smooth_dp |= (u32)(data[2] << 8);
+ rd_smooth_dp |= (u32)data[3];
+
+ if (rd_smooth_dp < 214958) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ ep_ck_nume = (u32)((data[4] & 0x3F) << 24);
+ ep_ck_nume |= (u32)(data[5] << 16);
+ ep_ck_nume |= (u32)(data[6] << 8);
+ ep_ck_nume |= (u32)data[7];
+
+ ep_ck_deno = (u32)((data[8] & 0x3F) << 24);
+ ep_ck_deno |= (u32)(data[9] << 16);
+ ep_ck_deno |= (u32)(data[10] << 8);
+ ep_ck_deno |= (u32)data[11];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x41, data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ issy_on_data = data[0] & 0x01;
+
+ slvt_unfreeze_reg(tnr_dmd);
+ }
+
+ if (issy_on_data) {
+ if ((ep_ck_deno == 0) || (ep_ck_nume == 0) ||
+ (ep_ck_deno >= ep_ck_nume))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ {
+ u32 ick_x100;
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ switch (tnr_dmd->clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ ick_x100 = 8228;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ ick_x100 = 9330;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ ick_x100 = 9600;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ div = rd_smooth_dp;
+
+ Q = ick_x100 * 262144U / div;
+ R = ick_x100 * 262144U % div;
+
+ R *= 5U;
+ Q = Q * 5 + R / div;
+ R = R % div;
+
+ R *= 2U;
+ Q = Q * 2 + R / div;
+ R = R % div;
+
+ if (div / 2 <= R)
+ *ts_rate_kbps = Q + 1;
+ else
+ *ts_rate_kbps = Q;
+ }
+
+ if (issy_on_data) {
+ u32 diff = ep_ck_nume - ep_ck_deno;
+
+ while (diff > 0x7FFF) {
+ diff >>= 1;
+ ep_ck_nume >>= 1;
+ }
+
+ *ts_rate_kbps -=
+ (*ts_rate_kbps * diff + ep_ck_nume / 2) / ep_ck_nume;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 *per)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u32 packet_error = 0;
+ u32 period = 0;
+
+ if (!tnr_dmd || !per)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 rdata[3];
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x18, rdata,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ packet_error = (rdata[1] << 8) | rdata[2];
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x24) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xDC, rdata,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period = 1U << (rdata[0] & 0x0F);
+ }
+
+ if ((period == 0) || (packet_error > period))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ div = period;
+
+ Q = (packet_error * 1000) / div;
+ R = (packet_error * 1000) % div;
+
+ R *= 1000;
+ Q = Q * 1000 + R / div;
+ R = R % div;
+
+ if ((div != 1) && (div / 2 <= R))
+ *per = Q + 1;
+ else
+ *per = Q;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype type,
+ enum cxd2880_dvbt2_plp_constell
+ *qam)
+{
+ u8 data;
+ u8 l1_post_ok = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!qam))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86, &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB6, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB1, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ } else {
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x9E, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *qam = (enum cxd2880_dvbt2_plp_constell)(data & 0x07);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ enum
+ cxd2880_dvbt2_plp_code_rate
+ *code_rate)
+{
+ u8 data;
+ u8 l1_post_ok = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!code_rate))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x86, &l1_post_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(l1_post_ok & 0x01)) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (type == CXD2880_DVBT2_PLP_COMMON) {
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB6, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (data == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB0, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ } else {
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x9D, &data,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *code_rate = (enum cxd2880_dvbt2_plp_code_rate)(data & 0x07);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ *profile)
+{
+ if ((!tnr_dmd) || (!profile))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x22, &data,
+ sizeof(data)) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (data & 0x02) {
+ if (data & 0x01)
+ *profile = CXD2880_DVBT2_PROFILE_LITE;
+ else
+ *profile = CXD2880_DVBT2_PROFILE_BASE;
+ } else {
+ enum cxd2880_ret ret = CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_profile(
+ tnr_dmd->diver_sub, profile);
+
+ return ret;
+ }
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret dvbt2_calc_sdi(struct cxd2880_tnrdmd *tnr_dmd,
+ int rf_lvl, u8 *ssi)
+{
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_dvbt2_plp_code_rate code_rate;
+ int prel;
+ int temp_ssi = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ static const int ref_dbm_1000[4][8] = {
+ {-96000, -95000, -94000, -93000, -92000, -92000, -98000,
+ -97000},
+ {-91000, -89000, -88000, -87000, -86000, -86000, -93000,
+ -92000},
+ {-86000, -85000, -83000, -82000, -81000, -80000, -89000,
+ -88000},
+ {-82000, -80000, -78000, -76000, -75000, -74000, -86000,
+ -84000},
+ };
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_qam(tnr_dmd, CXD2880_DVBT2_PLP_DATA, &qam);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_dvbt2_mon_code_rate(tnr_dmd, CXD2880_DVBT2_PLP_DATA,
+ &code_rate);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if ((code_rate > CXD2880_DVBT2_R2_5) || (qam > CXD2880_DVBT2_QAM256))
+ return CXD2880_RESULT_ERROR_OTHER;
+
+ prel = rf_lvl - ref_dbm_1000[qam][code_rate];
+
+ if (prel < -15000)
+ temp_ssi = 0;
+ else if (prel < 0)
+ temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+ else if (prel < 20000)
+ temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+ else if (prel < 35000)
+ temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+ else
+ temp_ssi = 100;
+
+ *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt2_calc_sdi(tnr_dmd, rf_lvl, ssi);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *ssi)
+{
+ int rf_lvl = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt2_calc_sdi(tnr_dmd, rf_lvl, ssi);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
new file mode 100644
index 0000000..784ad28
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt2_mon.h
@@ -0,0 +1,170 @@
+/*
+ * cxd2880_tnrdmd_dvbt2_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T2 monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT2_MON_H
+#define CXD2880_TNRDMD_DVBT2_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt2.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *sync_stat,
+ u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_pre(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_l1pre
+ *l1_pre);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_version(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_version
+ *ver);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ofdm(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_dvbt2_ofdm *ofdm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plps(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *plp_ids,
+ u8 *num_plps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_active_plp(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_plp
+ *plp_info);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_data_plp_error(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *plp_error);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_change(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *l1_change);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_l1_post(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt2_l1post
+ *l1_post);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_bbheader(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ struct cxd2880_dvbt2_bbheader
+ *bbheader);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_in_bandb_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_dvbt2_plp_btype
+ type,
+ u32 *ts_rate_bps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main,
+ int *snr_sub);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_ldpcber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_post_bchfer(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *fer);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_pre_bchber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ts_rate(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ts_rate_kbps);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_quality(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *quality);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 *per);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_qam(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype type,
+ enum cxd2880_dvbt2_plp_constell
+ *qam);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_code_rate(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_plp_btype
+ type,
+ enum
+ cxd2880_dvbt2_plp_code_rate
+ *code_rate);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_profile(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt2_profile
+ *profile);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt2_mon_ssi_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *ssi);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
new file mode 100644
index 0000000..d890081
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.c
@@ -0,0 +1,1190 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_math.h"
+
+static enum cxd2880_ret is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected)
+{
+ u8 rdata = 0x00;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!sync_stat) || (!ts_lock_stat) || (!unlock_detected))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10, &rdata,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *unlock_detected = (u8)((rdata & 0x10) ? 1 : 0);
+ *sync_stat = (u8)(rdata & 0x07);
+ *ts_lock_stat = (u8)((rdata & 0x20) ? 1 : 0);
+
+ if (*sync_stat == 0x07)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *unlock_detected)
+{
+ u8 ts_lock_stat = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!sync_stat) || (!unlock_detected))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd->diver_sub, sync_stat,
+ &ts_lock_stat, unlock_detected);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt_mode
+ *mode,
+ enum cxd2880_dvbt_guard
+ *guard)
+{
+ u8 rdata = 0x00;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!mode) || (!guard))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_mode_guard(
+ tnr_dmd->diver_sub, mode, guard);
+
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1B, &rdata,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *mode = (enum cxd2880_dvbt_mode)((rdata >> 2) & 0x03);
+ *guard = (enum cxd2880_dvbt_guard)(rdata & 0x03);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset)
+{
+ u8 rdata[4];
+ u32 ctl_val = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!offset))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1D, rdata,
+ 4) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ ctl_val =
+ ((rdata[0] & 0x1F) << 24) | (rdata[1] << 16) | (rdata[2] << 8) |
+ (rdata[3]);
+ *offset = cxd2880_convert2s_complement(ctl_val, 29);
+ *offset = -1 * ((*offset) * (u8)tnr_dmd->bandwidth / 235);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!offset))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_carrier_offset(tnr_dmd->diver_sub, offset);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_viterbiber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber)
+{
+ u8 rdata[2];
+ u32 bit_error = 0;
+ u32 period = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ber))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x39, rdata,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if ((rdata[0] & 0x01) == 0) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x22, rdata,
+ 2) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ bit_error = (rdata[0] << 8) | rdata[1];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6F, rdata,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ period = ((rdata[0] & 0x07) == 0) ? 256 : (0x1000 << (rdata[0] & 0x07));
+
+ if ((period == 0) || (bit_error > period))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ div = period / 128;
+
+ Q = (bit_error * 3125) / div;
+ R = (bit_error * 3125) % div;
+
+ R *= 25;
+ Q = Q * 25 + R / div;
+ R = R % div;
+
+ if (div / 2 <= R)
+ *ber = Q + 1;
+ else
+ *ber = Q;
+ }
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber)
+{
+ u8 rdata[3];
+ u32 bit_error = 0;
+ u32 period_exp = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ber))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x15, rdata,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x40) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ bit_error = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60, rdata,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = (rdata[0] & 0x1F);
+
+ if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ if (period_exp <= 8)
+ div = (1U << period_exp) * 51;
+ else
+ div = (1U << 8) * 51;
+
+ Q = (bit_error * 250) / div;
+ R = (bit_error * 250) % div;
+
+ R *= 1250;
+ Q = Q * 1250 + R / div;
+ R = R % div;
+
+ if (period_exp > 8) {
+ *ber =
+ (Q + (1 << (period_exp - 9))) >> (period_exp - 8);
+ } else {
+ if (div / 2 <= R)
+ *ber = Q + 1;
+ else
+ *ber = Q;
+ }
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt_tpsinfo
+ *info)
+{
+ u8 rdata[7];
+ u8 cell_id_ok = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!info))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd->diver_sub,
+ info);
+
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x29, rdata,
+ 7) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x11) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xD5, &cell_id_ok,
+ 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ info->constellation =
+ (enum cxd2880_dvbt_constellation)((rdata[0] >> 6) & 0x03);
+ info->hierarchy = (enum cxd2880_dvbt_hierarchy)((rdata[0] >> 3) & 0x07);
+ info->rate_hp = (enum cxd2880_dvbt_coderate)(rdata[0] & 0x07);
+ info->rate_lp = (enum cxd2880_dvbt_coderate)((rdata[1] >> 5) & 0x07);
+ info->guard = (enum cxd2880_dvbt_guard)((rdata[1] >> 3) & 0x03);
+ info->mode = (enum cxd2880_dvbt_mode)((rdata[1] >> 1) & 0x03);
+ info->fnum = (rdata[2] >> 6) & 0x03;
+ info->length_indicator = rdata[2] & 0x3F;
+ info->cell_id = (u16)((rdata[3] << 8) | rdata[4]);
+ info->reserved_even = rdata[5] & 0x3F;
+ info->reserved_odd = rdata[6] & 0x3F;
+
+ info->cell_id_ok = cell_id_ok & 0x01;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen)
+{
+ u8 rdata[3];
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!pen))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x26, rdata,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!(rdata[0] & 0x01))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *pen = (rdata[1] << 8) | rdata[2];
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense)
+{
+ u8 data = 0;
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!sense))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_spectrum_sense(
+ tnr_dmd->diver_sub, sense);
+
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x1C, &data,
+ sizeof(data)) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *sense =
+ (data & 0x01) ? CXD2880_TNRDMD_SPECTRUM_INV :
+ CXD2880_TNRDMD_SPECTRUM_NORMAL;
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt_read_snr_reg(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *reg_value)
+{
+ u8 rdata[2];
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!reg_value))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x13, rdata,
+ 2) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ *reg_value = (rdata[0] << 8) | rdata[1];
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt_calc_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 reg_value, int *snr)
+{
+ if ((!tnr_dmd) || (!snr))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (reg_value == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (reg_value > 4996)
+ reg_value = 4996;
+
+ *snr =
+ 10 * 10 * ((int)cxd2880_math_log10(reg_value) -
+ (int)cxd2880_math_log10(5350 - reg_value));
+ *snr += 28500;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr)
+{
+ u16 reg_value = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!snr))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ *snr = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ ret = dvbt_read_snr_reg(tnr_dmd, ®_value);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt_calc_snr(tnr_dmd, reg_value, snr);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ int snr_main = 0;
+ int snr_sub = 0;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_snr_diver(tnr_dmd, snr, &snr_main,
+ &snr_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub)
+{
+ u16 reg_value = 0;
+ u32 reg_value_sum = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!snr) || (!snr_main) || (!snr_sub))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ *snr = -1000 * 1000;
+ *snr_main = -1000 * 1000;
+ *snr_sub = -1000 * 1000;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = dvbt_read_snr_reg(tnr_dmd, ®_value);
+ if (ret == CXD2880_RESULT_OK) {
+ ret = dvbt_calc_snr(tnr_dmd, reg_value, snr_main);
+ if (ret != CXD2880_RESULT_OK)
+ reg_value = 0;
+ } else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt_read_snr_reg(tnr_dmd->diver_sub, ®_value);
+ if (ret == CXD2880_RESULT_OK) {
+ ret = dvbt_calc_snr(tnr_dmd->diver_sub, reg_value, snr_sub);
+ if (ret != CXD2880_RESULT_OK)
+ reg_value = 0;
+ } else if (ret == CXD2880_RESULT_ERROR_HW_STATE) {
+ reg_value = 0;
+ } else {
+ return ret;
+ }
+
+ reg_value_sum += reg_value;
+
+ ret = dvbt_calc_snr(tnr_dmd, reg_value_sum, snr);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ppm))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 ctl_val_reg[5];
+ u8 nominal_rate_reg[5];
+ u32 trl_ctl_val = 0;
+ u32 trcg_nominal_rate = 0;
+ int num;
+ int den;
+ s8 diff_upper = 0;
+
+ if (slvt_freeze_reg(tnr_dmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = is_tps_locked(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return ret;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ ctl_val_reg,
+ sizeof(ctl_val_reg)) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x04) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60,
+ nominal_rate_reg,
+ sizeof(nominal_rate_reg)) !=
+ CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnr_dmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnr_dmd);
+
+ diff_upper =
+ (ctl_val_reg[0] & 0x7F) - (nominal_rate_reg[0] & 0x7F);
+
+ if ((diff_upper < -1) || (diff_upper > 1))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ trl_ctl_val = ctl_val_reg[1] << 24;
+ trl_ctl_val |= ctl_val_reg[2] << 16;
+ trl_ctl_val |= ctl_val_reg[3] << 8;
+ trl_ctl_val |= ctl_val_reg[4];
+
+ trcg_nominal_rate = nominal_rate_reg[1] << 24;
+ trcg_nominal_rate |= nominal_rate_reg[2] << 16;
+ trcg_nominal_rate |= nominal_rate_reg[3] << 8;
+ trcg_nominal_rate |= nominal_rate_reg[4];
+
+ trl_ctl_val >>= 1;
+ trcg_nominal_rate >>= 1;
+
+ if (diff_upper == 1)
+ num =
+ (int)((trl_ctl_val + 0x80000000u) -
+ trcg_nominal_rate);
+ else if (diff_upper == -1)
+ num =
+ -(int)((trcg_nominal_rate + 0x80000000u) -
+ trl_ctl_val);
+ else
+ num = (int)(trl_ctl_val - trcg_nominal_rate);
+
+ den = (nominal_rate_reg[0] & 0x7F) << 24;
+ den |= nominal_rate_reg[1] << 16;
+ den |= nominal_rate_reg[2] << 8;
+ den |= nominal_rate_reg[3];
+ den = (den + (390625 / 2)) / 390625;
+
+ den >>= 1;
+
+ if (num >= 0)
+ *ppm = (num + (den / 2)) / den;
+ else
+ *ppm = (num - (den / 2)) / den;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd, int *ppm)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ppm))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_sampling_offset(tnr_dmd->diver_sub, ppm);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_quality(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *quality)
+{
+ struct cxd2880_dvbt_tpsinfo tps;
+ enum cxd2880_dvbt_profile profile = CXD2880_DVBT_PROFILE_HP;
+ u32 ber = 0;
+ int sn = 0;
+ int sn_rel = 0;
+ int ber_sqi = 0;
+
+ static const int nordig_non_hdvbt_db_1000[3][5] = {
+ {5100, 6900, 7900, 8900, 9700},
+ {10800, 13100, 14600, 15600, 16000},
+ {16500, 18700, 20200, 21600, 22500}
+ };
+
+ static const int nordig_hier_hp_dvbt_db_1000[3][2][5] = {
+ {
+ {9100, 12000, 13600, 15000, 16600},
+ {10900, 14100, 15700, 19400, 20600}
+ },
+ {
+ {6800, 9100, 10400, 11900, 12700},
+ {8500, 11000, 12800, 15000, 16000}
+ },
+ {
+ {5800, 7900, 9100, 10300, 12100},
+ {8000, 9300, 11600, 13000, 12900}
+ }
+ };
+
+ static const int nordig_hier_lp_dvbt_db_1000[3][2][5] = {
+ {
+ {12500, 14300, 15300, 16300, 16900},
+ {16700, 19100, 20900, 22500, 23700}
+ },
+ {
+ {15000, 17200, 18400, 19100, 20100},
+ {18500, 21200, 23600, 24700, 25900}
+ },
+ {
+ {19500, 21400, 22500, 23700, 24700},
+ {21900, 24200, 25600, 26900, 27800}
+ }
+ };
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!quality))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) {
+ u8 data = 0;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x67, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ profile =
+ ((data & 0x01) ==
+ 0x01) ? CXD2880_DVBT_PROFILE_LP : CXD2880_DVBT_PROFILE_HP;
+ }
+
+ ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(tnr_dmd, &ber);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_snr(tnr_dmd, &sn);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if ((tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3) ||
+ (tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+ (tps.rate_lp >= CXD2880_DVBT_CODERATE_RESERVED_5) ||
+ (tps.hierarchy > CXD2880_DVBT_HIERARCHY_4)) {
+ return CXD2880_RESULT_ERROR_OTHER;
+ }
+
+ if ((tps.hierarchy != CXD2880_DVBT_HIERARCHY_NON) &&
+ (tps.constellation == CXD2880_DVBT_CONSTELLATION_QPSK))
+ return CXD2880_RESULT_ERROR_OTHER;
+
+ if (tps.hierarchy == CXD2880_DVBT_HIERARCHY_NON)
+ sn_rel =
+ sn -
+ nordig_non_hdvbt_db_1000[tps.constellation][tps.rate_hp];
+ else if (profile == CXD2880_DVBT_PROFILE_LP)
+ sn_rel =
+ sn - nordig_hier_lp_dvbt_db_1000[(int)tps.hierarchy -
+ 1][(int)tps.constellation -
+ 1][tps.rate_lp];
+ else
+ sn_rel =
+ sn - nordig_hier_hp_dvbt_db_1000[(int)tps.hierarchy -
+ 1][(int)tps.constellation -
+ 1][tps.rate_hp];
+
+ if (ber > 10000) {
+ ber_sqi = 0;
+ } else if (ber > 1) {
+ ber_sqi = (int)(10 * cxd2880_math_log10(ber));
+ ber_sqi = 20 * (7 * 1000 - (ber_sqi)) - 40 * 1000;
+ } else {
+ ber_sqi = 100 * 1000;
+ }
+
+ if (sn_rel < -7 * 1000) {
+ *quality = 0;
+ } else if (sn_rel < 3 * 1000) {
+ int tmp_sqi = (((sn_rel - (3 * 1000)) / 10) + 1000);
+ *quality =
+ (u8)(((tmp_sqi * ber_sqi) +
+ (1000000 / 2)) / (1000000)) & 0xFF;
+ } else {
+ *quality = (u8)((ber_sqi + 500) / 1000);
+ }
+
+ if (*quality > 100)
+ *quality = 100;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 *per)
+{
+ u32 packet_error = 0;
+ u32 period = 0;
+ u8 rdata[3];
+
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!per))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x18, rdata,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ packet_error = (rdata[1] << 8) | rdata[2];
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x5C, rdata,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period = 1U << (rdata[0] & 0x0F);
+
+ if ((period == 0) || (packet_error > period))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ {
+ u32 div = 0;
+ u32 Q = 0;
+ u32 R = 0;
+
+ div = period;
+
+ Q = (packet_error * 1000) / div;
+ R = (packet_error * 1000) % div;
+
+ R *= 1000;
+ Q = Q * 1000 + R / div;
+ R = R % div;
+
+ if ((div != 1) && (div / 2 <= R))
+ *per = Q + 1;
+ else
+ *per = Q;
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret dvbt_calc_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ int rf_lvl, u8 *ssi)
+{
+ struct cxd2880_dvbt_tpsinfo tps;
+ int prel;
+ int temp_ssi = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ static const int ref_dbm_1000[3][5] = {
+ {-93000, -91000, -90000, -89000, -88000},
+ {-87000, -85000, -84000, -83000, -82000},
+ {-82000, -80000, -78000, -77000, -76000},
+ };
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(tnr_dmd, &tps);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if ((tps.constellation >= CXD2880_DVBT_CONSTELLATION_RESERVED_3) ||
+ (tps.rate_hp >= CXD2880_DVBT_CODERATE_RESERVED_5))
+ return CXD2880_RESULT_ERROR_OTHER;
+
+ prel = rf_lvl - ref_dbm_1000[tps.constellation][tps.rate_hp];
+
+ if (prel < -15000)
+ temp_ssi = 0;
+ else if (prel < 0)
+ temp_ssi = ((2 * (prel + 15000)) + 1500) / 3000;
+ else if (prel < 20000)
+ temp_ssi = (((4 * prel) + 500) / 1000) + 10;
+ else if (prel < 35000)
+ temp_ssi = (((2 * (prel - 20000)) + 1500) / 3000) + 90;
+ else
+ temp_ssi = 100;
+
+ *ssi = (temp_ssi > 100) ? 100 : (u8)temp_ssi;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd, &rf_lvl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi)
+{
+ int rf_lvl = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!ssi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, &rf_lvl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = dvbt_calc_ssi(tnr_dmd, rf_lvl, ssi);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+static enum cxd2880_ret is_tps_locked(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 sync = 0;
+ u8 tslock = 0;
+ u8 early_unlock = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_dvbt_mon_sync_stat(tnr_dmd, &sync, &tslock,
+ &early_unlock);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (sync != 6)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
new file mode 100644
index 0000000..486fc46
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_dvbt_mon.h
@@ -0,0 +1,106 @@
+/*
+ * cxd2880_tnrdmd_dvbt_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DVB-T monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_DVBT_MON_H
+#define CXD2880_TNRDMD_DVBT_MON_H
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_dvbt.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *ts_lock_stat,
+ u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sync_stat_sub(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 *sync_stat,
+ u8 *unlock_detected);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_mode_guard(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dvbt_mode
+ *mode,
+ enum cxd2880_dvbt_guard
+ *guard);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_carrier_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *offset);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_viterbiber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_pre_rsber(struct cxd2880_tnrdmd
+ *tnr_dmd, u32 *ber);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_tps_info(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ struct cxd2880_dvbt_tpsinfo
+ *info);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_packet_error_number(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u32 *pen);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_spectrum_sense(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_tnrdmd_spectrum_sense
+ *sense);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr(struct cxd2880_tnrdmd *tnr_dmd,
+ int *snr);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_snr_diver(struct cxd2880_tnrdmd
+ *tnr_dmd, int *snr,
+ int *snr_main, int *snr_sub);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset(struct cxd2880_tnrdmd
+ *tnr_dmd, int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_sampling_offset_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ int *ppm);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_quality(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *quality);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_per(struct cxd2880_tnrdmd *tnr_dmd,
+ u32 *per);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+enum cxd2880_ret cxd2880_tnrdmd_dvbt_mon_ssi_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 *ssi);
+
+#endif
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/5] media: Add suppurt for CXD2880
From: Yasunari.Takiguchi @ 2017-04-06 7:59 UTC (permalink / raw)
To: linux-kernel, linux-media, devicetree
Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
This is the driver for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the top function of the driver.
Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
drivers/media/dvb-frontends/Kconfig | 2 +
drivers/media/dvb-frontends/Makefile | 1 +
drivers/media/dvb-frontends/cxd2880/Kconfig | 6 +
drivers/media/dvb-frontends/cxd2880/Makefile | 22 +
drivers/media/dvb-frontends/cxd2880/cxd2880.h | 46 +
.../media/dvb-frontends/cxd2880/cxd2880_common.c | 84 +
.../media/dvb-frontends/cxd2880/cxd2880_common.h | 86 +
.../dvb-frontends/cxd2880/cxd2880_devio_spi.c | 147 +
.../dvb-frontends/cxd2880/cxd2880_devio_spi.h | 40 +
drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h | 50 +
.../media/dvb-frontends/cxd2880/cxd2880_integ.c | 99 +
.../media/dvb-frontends/cxd2880/cxd2880_integ.h | 44 +
drivers/media/dvb-frontends/cxd2880/cxd2880_io.c | 68 +
drivers/media/dvb-frontends/cxd2880/cxd2880_io.h | 62 +
drivers/media/dvb-frontends/cxd2880/cxd2880_math.c | 89 +
drivers/media/dvb-frontends/cxd2880/cxd2880_math.h | 40 +
drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h | 51 +
.../dvb-frontends/cxd2880/cxd2880_spi_device.c | 130 +
.../dvb-frontends/cxd2880/cxd2880_spi_device.h | 45 +
.../media/dvb-frontends/cxd2880/cxd2880_stdlib.h | 35 +
.../dvb-frontends/cxd2880/cxd2880_stopwatch_port.c | 71 +
.../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c | 3925 ++++++++++++++++++++
.../media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h | 395 ++
.../cxd2880/cxd2880_tnrdmd_driver_version.h | 29 +
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c | 207 ++
.../dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h | 52 +
drivers/media/dvb-frontends/cxd2880/cxd2880_top.c | 1550 ++++++++
27 files changed, 7376 insertions(+)
create mode 100644 drivers/media/dvb-frontends/cxd2880/Kconfig
create mode 100644 drivers/media/dvb-frontends/cxd2880/Makefile
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
create mode 100644 drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index e8c6554..3a3a712 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -518,6 +518,8 @@ config DVB_GP8PSK_FE
depends on DVB_CORE
default DVB_USB_GP8PSK
+source "drivers/media/dvb-frontends/cxd2880/Kconfig"
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index 3fccaf3..d298c79 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -126,3 +126,4 @@ obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
obj-$(CONFIG_DVB_HELENE) += helene.o
obj-$(CONFIG_DVB_ZD1301_DEMOD) += zd1301_demod.o
+obj-$(CONFIG_DVB_CXD2880) += cxd2880/
diff --git a/drivers/media/dvb-frontends/cxd2880/Kconfig b/drivers/media/dvb-frontends/cxd2880/Kconfig
new file mode 100644
index 0000000..36b8b6f
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Kconfig
@@ -0,0 +1,6 @@
+config DVB_CXD2880
+ tristate "Sony CXD2880 DVB-T2/T tuner + demodulator"
+ depends on DVB_CORE && SPI
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
\ No newline at end of file
diff --git a/drivers/media/dvb-frontends/cxd2880/Makefile b/drivers/media/dvb-frontends/cxd2880/Makefile
new file mode 100644
index 0000000..551f20c
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/Makefile
@@ -0,0 +1,22 @@
+cxd2880-objs := cxd2880_common.o \
+ cxd2880_devio_spi.o \
+ cxd2880_integ.o \
+ cxd2880_integ_dvbt2.o \
+ cxd2880_integ_dvbt.o \
+ cxd2880_io.o \
+ cxd2880_spi_device.o \
+ cxd2880_stopwatch_port.o \
+ cxd2880_tnrdmd.o \
+ cxd2880_tnrdmd_dvbt2.o \
+ cxd2880_tnrdmd_dvbt2_mon.o \
+ cxd2880_tnrdmd_dvbt.o \
+ cxd2880_tnrdmd_dvbt_mon.o\
+ cxd2880_tnrdmd_mon.o\
+ cxd2880_math.o \
+ cxd2880_top.o
+
+obj-$(CONFIG_DVB_CXD2880) += cxd2880.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880.h b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
new file mode 100644
index 0000000..281f9a7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880.h
@@ -0,0 +1,46 @@
+/*
+ * cxd2880.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver public definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_H
+#define CXD2880_H
+
+struct cxd2880_config {
+ struct spi_device *spi;
+ struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+#if IS_REACHABLE(CONFIG_DVB_CXD2880)
+extern struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg);
+#else
+static inline struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_CXD2880 */
+
+#endif /* CXD2880_H */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
new file mode 100644
index 0000000..850f3a7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.c
@@ -0,0 +1,84 @@
+/*
+ * cxd2880_common.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+
+#define MASKUPPER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU << (32 - (n))))
+#define MASKLOWER(n) (((n) == 0) ? 0 : (0xFFFFFFFFU >> (32 - (n))))
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen)
+{
+ if ((bitlen == 0) || (bitlen >= 32))
+ return (int)value;
+
+ if (value & (u32)(1 << (bitlen - 1)))
+ return (int)(MASKUPPER(32 - bitlen) | value);
+ else
+ return (int)(MASKLOWER(bitlen) & value);
+}
+
+u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num)
+{
+ u32 value = 0;
+ u8 *array_read;
+ u8 bit_read;
+ u32 len_remain;
+
+ if (!array)
+ return 0;
+ if ((bit_num == 0) || (bit_num > 32))
+ return 0;
+
+ array_read = array + (start_bit / 8);
+ bit_read = (u8)(start_bit % 8);
+ len_remain = bit_num;
+
+ if (bit_read != 0) {
+ if (((int)len_remain) <= 8 - bit_read) {
+ value = (*array_read) >> ((8 - bit_read) - len_remain);
+ len_remain = 0;
+ } else {
+ value = *array_read++;
+ len_remain -= 8 - bit_read;
+ }
+ }
+
+ while (len_remain > 0) {
+ if (len_remain < 8) {
+ value <<= len_remain;
+ value |= (*array_read++ >> (8 - len_remain));
+ len_remain = 0;
+ } else {
+ value <<= 8;
+ value |= (u32)(*array_read++);
+ len_remain -= 8;
+ }
+ }
+
+ value &= MASKLOWER(bit_num);
+
+ return value;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
new file mode 100644
index 0000000..b1ecb44b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_common.h
@@ -0,0 +1,86 @@
+/*
+ * cxd2880_common.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver common definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_COMMON_H
+#define CXD2880_COMMON_H
+
+#include <linux/types.h>
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+#include <linux/delay.h>
+#define CXD2880_SLEEP(n) msleep(n)
+#ifndef CXD2880_SLEEP_IN_MON
+#define CXD2880_SLEEP_IN_MON(n, obj) CXD2880_SLEEP(n)
+#endif
+
+#define CXD2880_ARG_UNUSED(arg) ((void)(arg))
+
+enum cxd2880_ret {
+ CXD2880_RESULT_OK,
+ CXD2880_RESULT_ERROR_ARG,
+ CXD2880_RESULT_ERROR_IO,
+ CXD2880_RESULT_ERROR_SW_STATE,
+ CXD2880_RESULT_ERROR_HW_STATE,
+ CXD2880_RESULT_ERROR_TIMEOUT,
+ CXD2880_RESULT_ERROR_UNLOCK,
+ CXD2880_RESULT_ERROR_RANGE,
+ CXD2880_RESULT_ERROR_NOSUPPORT,
+ CXD2880_RESULT_ERROR_CANCEL,
+ CXD2880_RESULT_ERROR_OTHER,
+ CXD2880_RESULT_ERROR_OVERFLOW,
+ CXD2880_RESULT_OK_CONFIRM
+};
+
+int cxd2880_convert2s_complement(u32 value, u32 bitlen);
+
+u32 cxd2880_bit_split_from_byte_array(u8 *array, u32 start_bit, u32 bit_num);
+
+struct cxd2880_atomic {
+ int counter;
+};
+
+#define cxd2880_atomic_set(a, i) ((a)->counter = i)
+#define cxd2880_atomic_read(a) ((a)->counter)
+
+struct cxd2880_stopwatch {
+ u32 start_time;
+};
+
+enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch);
+
+enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+ u32 ms);
+
+enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+ u32 *elapsed);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
new file mode 100644
index 0000000..516efad
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.c
@@ -0,0 +1,147 @@
+/*
+ * cxd2880_devio_spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_stdlib.h"
+
+#define BURST_WRITE_MAX 128
+
+static enum cxd2880_ret cxd2880_io_spi_read_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 *data,
+ u32 size)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[6];
+ u8 *read_data_top = data;
+
+ if ((!io) || (!io->if_object) || (!data))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (sub_address + size > 0x100)
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ spi = (struct cxd2880_spi *)(io->if_object);
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0B;
+ else
+ send_data[0] = 0x0A;
+
+ send_data[3] = 0;
+ send_data[4] = 0;
+ send_data[5] = 0;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = (u8)size;
+
+ ret =
+ spi->write_read(spi, send_data, sizeof(send_data),
+ read_data_top, send_data[2]);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ sub_address += send_data[2];
+ read_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret cxd2880_io_spi_write_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address,
+ const u8 *data, u32 size)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_spi *spi = NULL;
+ u8 send_data[BURST_WRITE_MAX + 4];
+ const u8 *write_data_top = data;
+
+ if ((!io) || (!io->if_object) || (!data))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (size > BURST_WRITE_MAX)
+ return CXD2880_RESULT_ERROR_OVERFLOW;
+
+ if (sub_address + size > 0x100)
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ spi = (struct cxd2880_spi *)(io->if_object);
+
+ if (tgt == CXD2880_IO_TGT_SYS)
+ send_data[0] = 0x0F;
+ else
+ send_data[0] = 0x0E;
+
+ while (size > 0) {
+ send_data[1] = sub_address;
+ if (size > 255)
+ send_data[2] = 255;
+ else
+ send_data[2] = (u8)size;
+
+ cxd2880_memcpy(&send_data[3], write_data_top, send_data[2]);
+
+ if (tgt == CXD2880_IO_TGT_SYS) {
+ send_data[3 + send_data[2]] = 0x00;
+ ret = spi->write(spi, send_data, send_data[2] + 4);
+ } else {
+ ret = spi->write(spi, send_data, send_data[2] + 3);
+ }
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ sub_address += send_data[2];
+ write_data_top += send_data[2];
+ size -= send_data[2];
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
+ struct cxd2880_spi *spi, u8 slave_select)
+{
+ if ((!io) || (!spi))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ io->read_regs = cxd2880_io_spi_read_reg;
+ io->write_regs = cxd2880_io_spi_write_reg;
+ io->write_reg = cxd2880_io_common_write_one_reg;
+ io->if_object = spi;
+ io->i2c_address_sys = 0;
+ io->i2c_address_demod = 0;
+ io->slave_select = slave_select;
+
+ return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
new file mode 100644
index 0000000..15934bf
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_devio_spi.h
@@ -0,0 +1,40 @@
+/*
+ * cxd2880_devio_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * I/O interface via SPI
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DEVIO_SPI_H
+#define CXD2880_DEVIO_SPI_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_spi.h"
+
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_ret cxd2880_io_spi_create(struct cxd2880_io *io,
+ struct cxd2880_spi *spi,
+ u8 slave_select);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
new file mode 100644
index 0000000..7de098d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_dtv.h
@@ -0,0 +1,50 @@
+/*
+ * cxd2880_dtv.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * DTV related definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_DTV_H
+#define CXD2880_DTV_H
+
+enum cxd2880_dtv_sys {
+ CXD2880_DTV_SYS_UNKNOWN,
+ CXD2880_DTV_SYS_DVBT,
+ CXD2880_DTV_SYS_DVBT2,
+ CXD2880_DTV_SYS_ISDBT,
+ CXD2880_DTV_SYS_ISDBTSB,
+ CXD2880_DTV_SYS_ISDBTMM_A,
+ CXD2880_DTV_SYS_ISDBTMM_B,
+ CXD2880_DTV_SYS_ANY
+};
+
+enum cxd2880_dtv_bandwidth {
+ CXD2880_DTV_BW_UNKNOWN = 0,
+ CXD2880_DTV_BW_1_7_MHZ = 1,
+ CXD2880_DTV_BW_5_MHZ = 5,
+ CXD2880_DTV_BW_6_MHZ = 6,
+ CXD2880_DTV_BW_7_MHZ = 7,
+ CXD2880_DTV_BW_8_MHZ = 8
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
new file mode 100644
index 0000000..5ad6685
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.c
@@ -0,0 +1,99 @@
+/*
+ * cxd2880_integ.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_integ.h"
+
+enum cxd2880_ret cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_stopwatch timer;
+ u32 elapsed_time = 0;
+ u8 cpu_task_completed = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_init1(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = cxd2880_stopwatch_start(&timer);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ while (1) {
+ ret = cxd2880_stopwatch_elapsed(&timer, &elapsed_time);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (cpu_task_completed)
+ break;
+
+ if (elapsed_time > CXD2880_TNRDMD_WAIT_INIT_TIMEOUT)
+ return CXD2880_RESULT_ERROR_TIMEOUT;
+ ret =
+ cxd2880_stopwatch_sleep(&timer,
+ CXD2880_TNRDMD_WAIT_INIT_INTVL);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = cxd2880_tnrdmd_init2(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ cxd2880_atomic_set(&tnr_dmd->cancel, 1);
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+ *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (cxd2880_atomic_read(&tnr_dmd->cancel) != 0)
+ return CXD2880_RESULT_ERROR_CANCEL;
+
+ return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
new file mode 100644
index 0000000..9cfc52d
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_integ.h
@@ -0,0 +1,44 @@
+/*
+ * cxd2880_integ.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * integration layer common interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_INTEG_H
+#define CXD2880_INTEG_H
+
+#include "cxd2880_tnrdmd.h"
+
+#define CXD2880_TNRDMD_WAIT_INIT_TIMEOUT 500
+#define CXD2880_TNRDMD_WAIT_INIT_INTVL 10
+
+#define CXD2880_TNRDMD_WAIT_AGC_STABLE 100
+
+enum cxd2880_ret cxd2880_integ_init(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_cancel(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_integ_check_cancellation(struct cxd2880_tnrdmd
+ *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
new file mode 100644
index 0000000..f0f8205
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.c
@@ -0,0 +1,68 @@
+/*
+ * cxd2880_io.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_io.h"
+
+enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!io)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = io->write_regs(io, tgt, sub_address, &data, 1);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data, u8 mask)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!io)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (mask == 0x00)
+ return CXD2880_RESULT_OK;
+
+ if (mask != 0xFF) {
+ u8 rdata = 0x00;
+
+ ret = io->read_regs(io, tgt, sub_address, &rdata, 1);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ data = (u8)((data & mask) | (rdata & (mask ^ 0xFF)));
+ }
+
+ ret = io->write_reg(io, tgt, sub_address, data);
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
new file mode 100644
index 0000000..4d6db13
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_io.h
@@ -0,0 +1,62 @@
+/*
+ * cxd2880_io.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * register I/O interface definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_IO_H
+#define CXD2880_IO_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_io_tgt {
+ CXD2880_IO_TGT_SYS,
+ CXD2880_IO_TGT_DMD
+};
+
+struct cxd2880_io {
+ enum cxd2880_ret (*read_regs)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ u8 *data, u32 size);
+ enum cxd2880_ret (*write_regs)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ const u8 *data, u32 size);
+ enum cxd2880_ret (*write_reg)(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt, u8 sub_address,
+ u8 data);
+ void *if_object;
+ u8 i2c_address_sys;
+ u8 i2c_address_demod;
+ u8 slave_select;
+ void *user;
+};
+
+enum cxd2880_ret cxd2880_io_common_write_one_reg(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data);
+
+enum cxd2880_ret cxd2880_io_set_reg_bits(struct cxd2880_io *io,
+ enum cxd2880_io_tgt tgt,
+ u8 sub_address, u8 data, u8 mask);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
new file mode 100644
index 0000000..434c827
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.c
@@ -0,0 +1,89 @@
+/*
+ * cxd2880_math.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * mathmatics functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_math.h"
+
+#define MAX_BIT_PRECISION 5
+#define FRAC_BITMASK 0x1F
+#define LOG2_10_100X 332
+#define LOG2_E_100X 144
+
+static const u8 log2_look_up[] = {
+ 0, 4,
+ 9, 13,
+ 17, 21,
+ 25, 29,
+ 32, 36,
+ 39, 43,
+ 46, 49,
+ 52, 55,
+ 58, 61,
+ 64, 67,
+ 70, 73,
+ 75, 78,
+ 81, 83,
+ 86, 88,
+ 91, 93,
+ 95, 98
+};
+
+u32 cxd2880_math_log2(u32 x)
+{
+ u8 count = 0;
+ u8 index = 0;
+ u32 xval = x;
+
+ for (x >>= 1; x > 0; x >>= 1)
+ count++;
+
+ x = count * 100;
+
+ if (count > 0) {
+ if (count <= MAX_BIT_PRECISION) {
+ index =
+ (u8)(xval << (MAX_BIT_PRECISION - count)) &
+ FRAC_BITMASK;
+ x += log2_look_up[index];
+ } else {
+ index =
+ (u8)(xval >> (count - MAX_BIT_PRECISION)) &
+ FRAC_BITMASK;
+ x += log2_look_up[index];
+ }
+ }
+
+ return x;
+}
+
+u32 cxd2880_math_log10(u32 x)
+{
+ return ((100 * cxd2880_math_log2(x) + LOG2_10_100X / 2) / LOG2_10_100X);
+}
+
+u32 cxd2880_math_log(u32 x)
+{
+ return ((100 * cxd2880_math_log2(x) + LOG2_E_100X / 2) / LOG2_E_100X);
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
new file mode 100644
index 0000000..9421183
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_math.h
@@ -0,0 +1,40 @@
+/*
+ * cxd2880_math.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * mathmatics definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_MATH_H_
+#define CXD2880_MATH_H_
+
+#include "cxd2880_common.h"
+
+u32 cxd2880_math_log2(u32 x);
+u32 cxd2880_math_log10(u32 x);
+u32 cxd2880_math_log(u32 x);
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
new file mode 100644
index 0000000..81e5be7
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi.h
@@ -0,0 +1,51 @@
+/*
+ * cxd2880_spi.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access definitions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_SPI_H
+#define CXD2880_SPI_H
+
+#include "cxd2880_common.h"
+
+enum cxd2880_spi_mode {
+ CXD2880_SPI_MODE_0,
+ CXD2880_SPI_MODE_1,
+ CXD2880_SPI_MODE_2,
+ CXD2880_SPI_MODE_3
+};
+
+struct cxd2880_spi {
+ enum cxd2880_ret (*read)(struct cxd2880_spi *spi, u8 *data,
+ u32 size);
+ enum cxd2880_ret (*write)(struct cxd2880_spi *spi, const u8 *data,
+ u32 size);
+ enum cxd2880_ret (*write_read)(struct cxd2880_spi *spi,
+ const u8 *tx_data, u32 tx_size,
+ u8 *rx_data, u32 rx_size);
+ u32 flags;
+ void *user;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
new file mode 100644
index 0000000..af9ed40
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.c
@@ -0,0 +1,130 @@
+/*
+ * cxd2880_spi_device.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#include "cxd2880_spi_device.h"
+
+static enum cxd2880_ret cxd2880_spi_device_write(struct cxd2880_spi *spi,
+ const u8 *data, u32 size)
+{
+ struct cxd2880_spi_device *spi_device = NULL;
+ struct spi_message msg;
+ struct spi_transfer tx;
+ int result = 0;
+
+ if ((!spi) || (!spi->user) || (!data) || (size == 0))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ spi_device = (struct cxd2880_spi_device *)(spi->user);
+
+ memset(&tx, 0, sizeof(tx));
+ tx.tx_buf = data;
+ tx.len = size;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&tx, &msg);
+ result = spi_sync(spi_device->spi, &msg);
+
+ if (result < 0)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_spi_device_write_read(struct cxd2880_spi *spi,
+ const u8 *tx_data,
+ u32 tx_size,
+ u8 *rx_data,
+ u32 rx_size)
+{
+ struct cxd2880_spi_device *spi_device = NULL;
+ int result = 0;
+
+ if ((!spi) || (!spi->user) || (!tx_data) ||
+ (tx_size == 0) || (!rx_data) || (rx_size == 0))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ spi_device = (struct cxd2880_spi_device *)(spi->user);
+
+ result = spi_write_then_read(spi_device->spi, tx_data,
+ tx_size, rx_data, rx_size);
+ if (result < 0)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+ enum cxd2880_spi_mode mode,
+ u32 speed_hz)
+{
+ int result = 0;
+ struct spi_device *spi = spi_device->spi;
+
+ switch (mode) {
+ case CXD2880_SPI_MODE_0:
+ spi->mode = SPI_MODE_0;
+ break;
+ case CXD2880_SPI_MODE_1:
+ spi->mode = SPI_MODE_1;
+ break;
+ case CXD2880_SPI_MODE_2:
+ spi->mode = SPI_MODE_2;
+ break;
+ case CXD2880_SPI_MODE_3:
+ spi->mode = SPI_MODE_3;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ spi->max_speed_hz = speed_hz;
+ spi->bits_per_word = 8;
+ result = spi_setup(spi);
+ if (result != 0) {
+ pr_err("spi_setup failed %d\n", result);
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+ struct cxd2880_spi_device *spi_device)
+{
+ if ((!spi) || (!spi_device))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ spi->read = NULL;
+ spi->write = cxd2880_spi_device_write;
+ spi->write_read = cxd2880_spi_device_write_read;
+ spi->flags = 0;
+ spi->user = spi_device;
+
+ return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
new file mode 100644
index 0000000..343d916
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_spi_device.h
@@ -0,0 +1,45 @@
+/*
+ * cxd2880_spi_device.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI access interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_SPI_DEVICE_H
+#define CXD2880_SPI_DEVICE_H
+
+#include "cxd2880_spi.h"
+
+struct cxd2880_spi_device {
+ struct spi_device *spi;
+};
+
+enum cxd2880_ret
+cxd2880_spi_device_initialize(struct cxd2880_spi_device *spi_device,
+ enum cxd2880_spi_mode mode,
+ u32 speedHz);
+
+enum cxd2880_ret
+cxd2880_spi_device_create_spi(struct cxd2880_spi *spi,
+ struct cxd2880_spi_device *spi_device);
+
+#endif /* CXD2880_SPI_DEVICE_H */
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
new file mode 100644
index 0000000..b9ca1b9
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stdlib.h
@@ -0,0 +1,35 @@
+/*
+ * cxd2880_stdlib.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * standard lib function aliases
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_STDLIB_H
+#define CXD2880_STDLIB_H
+
+#include <linux/string.h>
+
+#define cxd2880_memcpy memcpy
+#define cxd2880_memset memset
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
new file mode 100644
index 0000000..14ad6aa
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_stopwatch_port.c
@@ -0,0 +1,71 @@
+/*
+ * cxd2880_stopwatch_port.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * time measurement functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+
+#include <linux/ktime.h>
+#include <linux/time.h>
+#include <linux/timekeeping.h>
+
+static u32 get_time_count(void)
+{
+ struct timespec tp;
+
+ getnstimeofday(&tp);
+
+ return (u32)((tp.tv_sec * 1000) + (tp.tv_nsec / 1000000));
+}
+
+enum cxd2880_ret cxd2880_stopwatch_start(struct cxd2880_stopwatch *stopwatch)
+{
+ if (!stopwatch)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ stopwatch->start_time = get_time_count();
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_stopwatch_sleep(struct cxd2880_stopwatch *stopwatch,
+ u32 ms)
+{
+ if (!stopwatch)
+ return CXD2880_RESULT_ERROR_ARG;
+ CXD2880_ARG_UNUSED(*stopwatch);
+ CXD2880_SLEEP(ms);
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_stopwatch_elapsed(struct cxd2880_stopwatch *stopwatch,
+ u32 *elapsed)
+{
+ if (!stopwatch || !elapsed)
+ return CXD2880_RESULT_ERROR_ARG;
+ *elapsed = get_time_count() - stopwatch->start_time;
+
+ return CXD2880_RESULT_OK;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
new file mode 100644
index 0000000..286384a
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.c
@@ -0,0 +1,3925 @@
+/*
+ * cxd2880_tnrdmd.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_stdlib.h"
+#include "cxd2880_tnrdmd.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt.h"
+#include "cxd2880_tnrdmd_dvbt2.h"
+
+static enum cxd2880_ret p_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) ||
+ (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ data = 0x00;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ data = 0x02;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x11,
+ 0x16) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x1A;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x16;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->create_param.en_internal_ldo)
+ data = 0x01;
+ else
+ data = 0x00;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x11,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x13,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x12,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (tnr_dmd->chip_id) {
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X:
+ data = 0x01;
+ break;
+ case CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11:
+ data = 0x00;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x69,
+ data) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret p_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[6] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = tnr_dmd->create_param.xosc_cap;
+ data[1] = tnr_dmd->create_param.xosc_i;
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ data[2] = 0x01;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ data[2] = 0x00;
+ data[3] = 0x00;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ data[2] = 0x01;
+ data[3] = 0x01;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ data[2] = 0x00;
+ data[3] = 0x01;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ data[4] = 0x06;
+ data[5] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x13, data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret p_init3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (tnr_dmd->diver_mode) {
+ case CXD2880_TNRDMD_DIVERMODE_SINGLE:
+ data[0] = 0x00;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_MAIN:
+ data[0] = 0x03;
+ break;
+ case CXD2880_TNRDMD_DIVERMODE_SUB:
+ data[0] = 0x02;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ data[1] = 0x01;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x1F, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret rf_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[80] = { 0 };
+ u8 addr = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x01;
+ data[1] = 0x00;
+ data[2] = 0x01;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x21, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x01;
+ data[1] = 0x01;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x17, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x1A,
+ 0x06) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x4F,
+ 0x18) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x61,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x71,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x9D,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x7D,
+ 0x02) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x8F,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x8B,
+ 0xC6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x9A,
+ 0x03) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x1C,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ if ((tnr_dmd->create_param.is_cxd2881gg) &&
+ (tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE))
+ data[1] = 0x00;
+ else
+ data[1] = 0x1F;
+ data[2] = 0x0A;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xB5, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xB9,
+ 0x07) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x33,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xC1,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xC4,
+ 0x1E) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) {
+ data[0] = 0x34;
+ data[1] = 0x2C;
+ } else {
+ data[0] = 0x2F;
+ data[1] = 0x25;
+ }
+ data[2] = 0x15;
+ data[3] = 0x19;
+ data[4] = 0x1B;
+ data[5] = 0x15;
+ data[6] = 0x19;
+ data[7] = 0x1B;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xD9, data,
+ 8) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x6C;
+ data[1] = 0x10;
+ data[2] = 0xA6;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x44, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x16;
+ data[1] = 0xA8;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x50, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x22;
+ data[2] = 0x00;
+ data[3] = 0x88;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x62, data,
+ 4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x74,
+ 0x75) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x05;
+ data[1] = 0x05;
+ data[2] = 0x05;
+ data[3] = 0x05;
+ data[4] = 0x05;
+ data[5] = 0x05;
+ data[6] = 0x05;
+ data[7] = 0x05;
+ data[8] = 0x05;
+ data[9] = 0x04;
+ data[10] = 0x04;
+ data[11] = 0x04;
+ data[12] = 0x03;
+ data[13] = 0x03;
+ data[14] = 0x03;
+ data[15] = 0x04;
+ data[16] = 0x04;
+ data[17] = 0x05;
+ data[18] = 0x05;
+ data[19] = 0x05;
+ data[20] = 0x02;
+ data[21] = 0x02;
+ data[22] = 0x02;
+ data[23] = 0x02;
+ data[24] = 0x02;
+ data[25] = 0x02;
+ data[26] = 0x02;
+ data[27] = 0x02;
+ data[28] = 0x02;
+ data[29] = 0x03;
+ data[30] = 0x02;
+ data[31] = 0x01;
+ data[32] = 0x01;
+ data[33] = 0x01;
+ data[34] = 0x02;
+ data[35] = 0x02;
+ data[36] = 0x03;
+ data[37] = 0x04;
+ data[38] = 0x04;
+ data[39] = 0x04;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x7F, data,
+ 40) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x16) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x71;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x23,
+ 0x89) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0xFF;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27, data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = 0x00;
+ data[5] = 0x01;
+ data[6] = 0x00;
+ data[7] = 0x01;
+ data[8] = 0x00;
+ data[9] = 0x02;
+ data[10] = 0x00;
+ data[11] = 0x63;
+ data[12] = 0x00;
+ data[13] = 0x00;
+ data[14] = 0x00;
+ data[15] = 0x03;
+ data[16] = 0x00;
+ data[17] = 0x04;
+ data[18] = 0x00;
+ data[19] = 0x04;
+ data[20] = 0x00;
+ data[21] = 0x06;
+ data[22] = 0x00;
+ data[23] = 0x06;
+ data[24] = 0x00;
+ data[25] = 0x08;
+ data[26] = 0x00;
+ data[27] = 0x09;
+ data[28] = 0x00;
+ data[29] = 0x0B;
+ data[30] = 0x00;
+ data[31] = 0x0B;
+ data[32] = 0x00;
+ data[33] = 0x0D;
+ data[34] = 0x00;
+ data[35] = 0x0D;
+ data[36] = 0x00;
+ data[37] = 0x0F;
+ data[38] = 0x00;
+ data[39] = 0x0F;
+ data[40] = 0x00;
+ data[41] = 0x0F;
+ data[42] = 0x00;
+ data[43] = 0x10;
+ data[44] = 0x00;
+ data[45] = 0x79;
+ data[46] = 0x00;
+ data[47] = 0x00;
+ data[48] = 0x00;
+ data[49] = 0x02;
+ data[50] = 0x00;
+ data[51] = 0x00;
+ data[52] = 0x00;
+ data[53] = 0x03;
+ data[54] = 0x00;
+ data[55] = 0x01;
+ data[56] = 0x00;
+ data[57] = 0x03;
+ data[58] = 0x00;
+ data[59] = 0x03;
+ data[60] = 0x00;
+ data[61] = 0x03;
+ data[62] = 0x00;
+ data[63] = 0x04;
+ data[64] = 0x00;
+ data[65] = 0x04;
+ data[66] = 0x00;
+ data[67] = 0x06;
+ data[68] = 0x00;
+ data[69] = 0x05;
+ data[70] = 0x00;
+ data[71] = 0x07;
+ data[72] = 0x00;
+ data[73] = 0x07;
+ data[74] = 0x00;
+ data[75] = 0x08;
+ data[76] = 0x00;
+ data[77] = 0x0A;
+ data[78] = 0x03;
+ data[79] = 0xE0;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x3A, data,
+ 80) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = 0x03;
+ data[1] = 0xE0;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xBC, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x51,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xC5,
+ 0x07) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x70,
+ 0xE9) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x76,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x78,
+ 0x32) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x7A,
+ 0x46) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x7C,
+ 0x86) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x7E,
+ 0xA4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xE1,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ data[0] = 0x06;
+ data[1] = 0x07;
+ data[2] = 0x1A;
+ } else {
+ data[0] = 0x00;
+ data[1] = 0x08;
+ data[2] = 0x19;
+ }
+ data[3] = 0x0E;
+ data[4] = 0x09;
+ data[5] = 0x0E;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x12) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ for (addr = 0x10; addr < 0x9F; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_air) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_air->thrs[idx].on_off;
+ }
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, addr,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ data[0] = 0x00;
+ data[1] = 0x08;
+ if (tnr_dmd->create_param.stationary_use)
+ data[2] = 0x1A;
+ else
+ data[2] = 0x19;
+ data[3] = 0x0E;
+ data[4] = 0x09;
+ data[5] = 0x0E;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x13) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ for (addr = 0x10; addr < 0xCF; addr += 6) {
+ if (tnr_dmd->lna_thrs_tbl_cable) {
+ u8 idx = 0;
+
+ idx = (addr - 0x10) / 6;
+ data[0] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].off_on;
+ data[1] =
+ tnr_dmd->lna_thrs_tbl_cable->thrs[idx].on_off;
+ }
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, addr,
+ data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xBD, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xC4, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x20;
+ data[1] = 0x20;
+ data[2] = 0x30;
+ data[3] = 0x41;
+ data[4] = 0x50;
+ data[5] = 0x5F;
+ data[6] = 0x6F;
+ data[7] = 0x80;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xC9, data,
+ 8) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x14) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x15;
+ data[1] = 0x18;
+ data[2] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x15,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x16) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x09;
+ data[2] = 0x00;
+ data[3] = 0x08;
+ data[4] = 0x00;
+ data[5] = 0x07;
+ data[6] = 0x00;
+ data[7] = 0x06;
+ data[8] = 0x00;
+ data[9] = 0x05;
+ data[10] = 0x00;
+ data[11] = 0x03;
+ data[12] = 0x00;
+ data[13] = 0x02;
+ data[14] = 0x00;
+ data[15] = 0x00;
+ data[16] = 0x00;
+ data[17] = 0x78;
+ data[18] = 0x00;
+ data[19] = 0x00;
+ data[20] = 0x00;
+ data[21] = 0x06;
+ data[22] = 0x00;
+ data[23] = 0x08;
+ data[24] = 0x00;
+ data[25] = 0x08;
+ data[26] = 0x00;
+ data[27] = 0x0C;
+ data[28] = 0x00;
+ data[29] = 0x0C;
+ data[30] = 0x00;
+ data[31] = 0x0D;
+ data[32] = 0x00;
+ data[33] = 0x0F;
+ data[34] = 0x00;
+ data[35] = 0x0E;
+ data[36] = 0x00;
+ data[37] = 0x0E;
+ data[38] = 0x00;
+ data[39] = 0x10;
+ data[40] = 0x00;
+ data[41] = 0x0F;
+ data[42] = 0x00;
+ data[43] = 0x0E;
+ data[44] = 0x00;
+ data[45] = 0x10;
+ data[46] = 0x00;
+ data[47] = 0x0F;
+ data[48] = 0x00;
+ data[49] = 0x0E;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x12, data,
+ 50) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0x00)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x25,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x11, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0x00)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x02,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x8F,
+ 0x16) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x67,
+ 0x60) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6A,
+ 0x0F) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6C,
+ 0x17) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0xFE;
+ data[2] = 0xEE;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x6E, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0xA1;
+ data[1] = 0x8B;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x8D, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x08;
+ data[1] = 0x09;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x77, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->create_param.stationary_use) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x80,
+ 0xAA) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x41,
+ 0xA0) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x4B,
+ 0x68) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x25,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x1A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0x00)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x14,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x26,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret rf_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[5] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x40;
+ data[1] = 0x40;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xEA, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ data[0] = 0x00;
+ if (tnr_dmd->chip_id == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X)
+ data[1] = 0x00;
+ else
+ data[1] = 0x01;
+ data[2] = 0x01;
+ data[3] = 0x03;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x30, data,
+ 4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x14) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x1B,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xD3,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune1(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys, u32 freq_khz,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ u8 is_cable, int shift_frequency_khz)
+{
+ u8 data[11] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x0E;
+ data[3] = 0x00;
+ data[4] = 0x03;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xE7, data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = 0x1F;
+ data[1] = 0x80;
+ data[2] = 0x18;
+ data[3] = 0x00;
+ data[4] = 0x07;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xE7, data,
+ 5) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ data[0] = 0x72;
+ data[1] = 0x81;
+ data[3] = 0x1D;
+ data[4] = 0x6F;
+ data[5] = 0x7E;
+ data[7] = 0x1C;
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ case CXD2880_DTV_SYS_ISDBT:
+ case CXD2880_DTV_SYS_ISDBTSB:
+ case CXD2880_DTV_SYS_ISDBTMM_A:
+ case CXD2880_DTV_SYS_ISDBTMM_B:
+ data[2] = 0x94;
+ data[6] = 0x91;
+ break;
+ case CXD2880_DTV_SYS_DVBT2:
+ data[2] = 0x96;
+ data[6] = 0x93;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x44, data,
+ 8) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x62,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x15) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x03;
+ data[1] = 0xE2;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x1E, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = (u8)(is_cable ? 0x01 : 0x00);
+ data[1] = 0x00;
+ data[2] = 0x6B;
+ data[3] = 0x4D;
+
+ switch (bandwidth) {
+ case CXD2880_DTV_BW_1_7_MHZ:
+ data[4] = 0x03;
+ break;
+ case CXD2880_DTV_BW_5_MHZ:
+ case CXD2880_DTV_BW_6_MHZ:
+ data[4] = 0x00;
+ break;
+ case CXD2880_DTV_BW_7_MHZ:
+ data[4] = 0x01;
+ break;
+ case CXD2880_DTV_BW_8_MHZ:
+ data[4] = 0x02;
+ break;
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ data[5] = 0x00;
+
+ freq_khz += shift_frequency_khz;
+
+ data[6] = (u8)((freq_khz >> 16) & 0x0F);
+ data[7] = (u8)((freq_khz >> 8) & 0xFF);
+ data[8] = (u8)(freq_khz & 0xFF);
+ data[9] = 0xFF;
+ data[10] = 0xFE;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x52, data,
+ 11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune2(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_bandwidth bandwidth,
+ enum cxd2880_tnrdmd_clockmode clk_mode,
+ int shift_frequency_khz)
+{
+ u8 data[3] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = 0x01;
+ data[1] = 0x0E;
+ data[2] = 0x01;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2D, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x1A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x29,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2C, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x60,
+ data[0]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x62,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x11) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2D,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2F,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (shift_frequency_khz != 0) {
+ int shift_freq = 0;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ shift_freq = shift_frequency_khz * 1000;
+
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 183 / 2) / 183;
+ else
+ shift_freq = (shift_freq - 183 / 2) / 183;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq = (shift_freq + 178 / 2) / 178;
+ else
+ shift_freq = (shift_freq - 178 / 2) / 178;
+ break;
+ }
+
+ shift_freq +=
+ cxd2880_convert2s_complement((data[0] << 8) | data[1], 16);
+
+ if (shift_freq > 32767)
+ shift_freq = 32767;
+ else if (shift_freq < -32768)
+ shift_freq = -32768;
+
+ data[0] = (u8)(((u32)shift_freq >> 8) & 0xFF);
+ data[1] = (u8)((u32)shift_freq & 0xFF);
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x60, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x69, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ shift_freq = -shift_frequency_khz;
+
+ if (bandwidth == CXD2880_DTV_BW_1_7_MHZ) {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17578 / 2) / 17578;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17578 / 2) / 17578;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 17090 / 2) / 17090;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 17090 / 2) / 17090;
+ break;
+ }
+ } else {
+ switch (clk_mode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ default:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 35156 / 2) / 35156;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 35156 / 2) / 35156;
+ break;
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ if (shift_freq >= 0)
+ shift_freq =
+ (shift_freq * 1000 +
+ 34180 / 2) / 34180;
+ else
+ shift_freq =
+ (shift_freq * 1000 -
+ 34180 / 2) / 34180;
+ break;
+ }
+ }
+
+ shift_freq += cxd2880_convert2s_complement(data[0], 8);
+
+ if (shift_freq > 127)
+ shift_freq = 127;
+ else if (shift_freq < -128)
+ shift_freq = -128;
+
+ data[0] = (u8)((u32)shift_freq & 0xFF);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x69,
+ data[0]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->create_param.stationary_use) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x8A,
+ 0x87) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune3(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ u8 data[6] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x41,
+ 0xA0) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xFE,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl) {
+ data[0] = 0x01;
+ data[1] = 0x01;
+ data[2] = 0x01;
+ data[3] = 0x01;
+ data[4] = 0x01;
+ data[5] = 0x01;
+ } else {
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ data[4] = 0x00;
+ data[5] = 0x00;
+ }
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xEF, data,
+ 6) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x2D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((sys == CXD2880_DTV_SYS_DVBT2) && en_fef_intmtnt_ctrl)
+ data[0] = 0x00;
+ else
+ data[0] = 0x01;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB1,
+ data[0]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_tune4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ {
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x55,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x0B;
+ data[1] = 0xFF;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x53, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x57,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x0B;
+ data[1] = 0xFF;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x55, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x14;
+ data[1] = 0x00;
+ if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x53,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x57,
+ 0x02) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xFE,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_DMD, 0xFE,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data[3] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x57,
+ 0x03) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x53, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ if (tnr_dmd->diver_sub->io->write_reg(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x1F;
+ data[1] = 0xFF;
+ data[2] = 0x03;
+ if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x55,
+ data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ if (tnr_dmd->diver_sub->io->write_regs(tnr_dmd->diver_sub->io,
+ CXD2880_IO_TGT_SYS, 0x53,
+ data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x1F;
+ data[1] = 0xFF;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x55, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ u8 data = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x2D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB1,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xB2, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data & 0x01) == 0x00)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xF4,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xF3,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xF2,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xF1,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xF0,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xEF,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep3(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xFD,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret x_sleep4(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0xE2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x41,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x21,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret spll_reset(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_clockmode clockmode)
+{
+ u8 data[4] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x29,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x28,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x26,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x22,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ switch (clockmode) {
+ case CXD2880_TNRDMD_CLOCKMODE_A:
+ data[0] = 0x00;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_B:
+ data[0] = 0x01;
+ break;
+
+ case CXD2880_TNRDMD_CLOCKMODE_C:
+ data[0] = 0x02;
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x30,
+ data[0]) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x22,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(2);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x10, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0x00)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x26, data,
+ 4) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret t_power_x(struct cxd2880_tnrdmd *tnr_dmd, u8 on)
+{
+ u8 data[3] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x29,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x28,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x25,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (on) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2B,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x12, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2A,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ } else {
+ data[0] = 0x03;
+ data[1] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2A, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x13, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x25,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x11, data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if ((data[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x27, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+struct cxd2880_tnrdmd_ts_clk_cfg {
+ u8 srl_clk_mode;
+ u8 srl_duty_mode;
+ u8 ts_clk_period;
+};
+
+static enum cxd2880_ret set_ts_clk_mode_and_freq(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 backwards_compatible = 0;
+ struct cxd2880_tnrdmd_ts_clk_cfg ts_clk_cfg;
+
+ const struct cxd2880_tnrdmd_ts_clk_cfg srl_ts_clk_stgs[2][2] = {
+ {
+ {3, 1, 8,},
+ {0, 2, 16,}
+ },
+ {
+ {1, 1, 8,},
+ {2, 2, 16,}
+ }
+ };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 ts_rate_ctrl_off = 0;
+ u8 ts_in_off = 0;
+ u8 ts_clk_manaul_on = 0;
+
+ if ((sys == CXD2880_DTV_SYS_ISDBT) ||
+ (sys == CXD2880_DTV_SYS_ISDBTSB) ||
+ (sys == CXD2880_DTV_SYS_ISDBTMM_A) ||
+ (sys == CXD2880_DTV_SYS_ISDBTMM_B)) {
+ backwards_compatible = 0;
+ ts_rate_ctrl_off = 1;
+ ts_in_off = 0;
+ } else if (tnr_dmd->is_ts_backwards_compatible_mode) {
+ backwards_compatible = 1;
+ ts_rate_ctrl_off = 1;
+ ts_in_off = 1;
+ } else {
+ backwards_compatible = 0;
+ ts_rate_ctrl_off = 0;
+ ts_in_off = 0;
+ }
+
+ if (tnr_dmd->ts_byte_clk_manual_setting) {
+ ts_clk_manaul_on = 1;
+ ts_rate_ctrl_off = 0;
+ }
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+ 0xD3, ts_rate_ctrl_off, 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+ 0xDE, ts_in_off, 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD,
+ 0xDA, ts_clk_manaul_on, 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ts_clk_cfg =
+ srl_ts_clk_stgs[tnr_dmd->srl_ts_clk_mod_cnts]
+ [(u8)tnr_dmd->srl_ts_clk_frq];
+
+ if (tnr_dmd->ts_byte_clk_manual_setting)
+ ts_clk_cfg.ts_clk_period = tnr_dmd->ts_byte_clk_manual_setting;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xC4,
+ ts_clk_cfg.srl_clk_mode, 0x03);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io, CXD2880_IO_TGT_DMD, 0xD1,
+ ts_clk_cfg.srl_duty_mode, 0x03);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xD9,
+ ts_clk_cfg.ts_clk_period);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ {
+ u8 data = (u8)(backwards_compatible ? 0x00 : 0x01);
+
+ if (sys == CXD2880_DTV_SYS_DVBT) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret =
+ cxd2880_io_set_reg_bits(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x66,
+ data, 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret pid_ftr_setting(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (!pid_ftr_cfg) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x50,
+ 0x02) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ } else {
+ u8 data[65];
+
+ data[0] = (u8)(pid_ftr_cfg->is_negative ? 0x01 : 0x00);
+ {
+ int i = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (pid_ftr_cfg->pid_cfg[i].is_en) {
+ data[1 + (i * 2)] =
+ (u8)((u8)
+ (pid_ftr_cfg->pid_cfg[i].pid
+ >> 8) | 0x20);
+ data[2 + (i * 2)] =
+ (u8)(pid_ftr_cfg->pid_cfg[i].pid
+ & 0xFF);
+ } else {
+ data[1 + (i * 2)] = 0x00;
+ data[2 + (i * 2)] = 0x00;
+ }
+ }
+ }
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x50, data,
+ 65) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret load_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 i;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ 0x00, tnr_dmd->cfg_mem[i].bank);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret = cxd2880_io_set_reg_bits(tnr_dmd->io,
+ tnr_dmd->cfg_mem[i].tgt,
+ tnr_dmd->cfg_mem[i].address,
+ tnr_dmd->cfg_mem[i].value,
+ tnr_dmd->cfg_mem[i].bit_mask);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+static enum cxd2880_ret set_cfg_mem(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address, u8 value, u8 bit_mask)
+{
+ u8 i;
+ u8 value_stored = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ for (i = 0; i < tnr_dmd->cfg_mem_last_entry; i++) {
+ if ((value_stored == 0) &&
+ (tnr_dmd->cfg_mem[i].tgt == tgt) &&
+ (tnr_dmd->cfg_mem[i].bank == bank) &&
+ (tnr_dmd->cfg_mem[i].address == address)) {
+ tnr_dmd->cfg_mem[i].value &= ~bit_mask;
+ tnr_dmd->cfg_mem[i].value |= (value & bit_mask);
+
+ tnr_dmd->cfg_mem[i].bit_mask |= bit_mask;
+
+ value_stored = 1;
+ }
+ }
+
+ if (value_stored == 0) {
+ if (tnr_dmd->cfg_mem_last_entry <
+ CXD2880_TNRDMD_MAX_CFG_MEM_COUNT) {
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].tgt = tgt;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bank =
+ bank;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].address =
+ address;
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].value =
+ (value & bit_mask);
+ tnr_dmd->cfg_mem[tnr_dmd->cfg_mem_last_entry].bit_mask =
+ bit_mask;
+ tnr_dmd->cfg_mem_last_entry++;
+ } else {
+ return CXD2880_RESULT_ERROR_OVERFLOW;
+ }
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_io *io,
+ struct cxd2880_tnrdmd_create_param
+ *create_param)
+{
+ if ((!tnr_dmd) || (!io) || (!create_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ cxd2880_memset(tnr_dmd, 0, sizeof(struct cxd2880_tnrdmd));
+
+ tnr_dmd->io = io;
+ tnr_dmd->create_param = *create_param;
+
+ tnr_dmd->diver_mode = CXD2880_TNRDMD_DIVERMODE_SINGLE;
+ tnr_dmd->diver_sub = NULL;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd->en_fef_intmtnt_base = 1;
+ tnr_dmd->en_fef_intmtnt_lite = 1;
+ tnr_dmd->rf_lvl_cmpstn = NULL;
+ tnr_dmd->lna_thrs_tbl_air = NULL;
+ tnr_dmd->lna_thrs_tbl_cable = NULL;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+ *tnr_dmd_main,
+ struct cxd2880_io *io_main,
+ struct cxd2880_tnrdmd *tnr_dmd_sub,
+ struct cxd2880_io *io_sub,
+ struct
+ cxd2880_tnrdmd_diver_create_param
+ *create_param)
+{
+ if ((!tnr_dmd_main) || (!io_main) || (!tnr_dmd_sub) || (!io_sub) ||
+ (!create_param))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ cxd2880_memset(tnr_dmd_main, 0, sizeof(struct cxd2880_tnrdmd));
+ cxd2880_memset(tnr_dmd_sub, 0, sizeof(struct cxd2880_tnrdmd));
+
+ tnr_dmd_main->io = io_main;
+ tnr_dmd_main->diver_mode = CXD2880_TNRDMD_DIVERMODE_MAIN;
+ tnr_dmd_main->diver_sub = tnr_dmd_sub;
+ tnr_dmd_main->create_param.en_internal_ldo =
+ create_param->en_internal_ldo;
+ tnr_dmd_main->create_param.ts_output_if = create_param->ts_output_if;
+ tnr_dmd_main->create_param.xtal_share_type =
+ CXD2880_TNRDMD_XTAL_SHARE_MASTER;
+ tnr_dmd_main->create_param.xosc_cap = create_param->xosc_cap_main;
+ tnr_dmd_main->create_param.xosc_i = create_param->xosc_i_main;
+ tnr_dmd_main->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
+ tnr_dmd_main->create_param.stationary_use =
+ create_param->stationary_use;
+
+ tnr_dmd_sub->io = io_sub;
+ tnr_dmd_sub->diver_mode = CXD2880_TNRDMD_DIVERMODE_SUB;
+ tnr_dmd_sub->diver_sub = NULL;
+ tnr_dmd_sub->create_param.en_internal_ldo =
+ create_param->en_internal_ldo;
+ tnr_dmd_sub->create_param.ts_output_if = create_param->ts_output_if;
+ tnr_dmd_sub->create_param.xtal_share_type =
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE;
+ tnr_dmd_sub->create_param.xosc_cap = 0;
+ tnr_dmd_sub->create_param.xosc_i = create_param->xosc_i_sub;
+ tnr_dmd_sub->create_param.is_cxd2881gg = create_param->is_cxd2881gg;
+ tnr_dmd_sub->create_param.stationary_use = create_param->stationary_use;
+
+ tnr_dmd_main->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_main->en_fef_intmtnt_base = 1;
+ tnr_dmd_main->en_fef_intmtnt_lite = 1;
+ tnr_dmd_main->rf_lvl_cmpstn = NULL;
+ tnr_dmd_main->lna_thrs_tbl_air = NULL;
+ tnr_dmd_main->lna_thrs_tbl_cable = NULL;
+
+ tnr_dmd_sub->srl_ts_clk_mod_cnts = 1;
+ tnr_dmd_sub->en_fef_intmtnt_base = 1;
+ tnr_dmd_sub->en_fef_intmtnt_lite = 1;
+ tnr_dmd_sub->rf_lvl_cmpstn = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_air = NULL;
+ tnr_dmd_sub->lna_thrs_tbl_cable = NULL;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ tnr_dmd->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->scan_mode = 0;
+ cxd2880_atomic_set(&tnr_dmd->cancel, 0);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->chip_id = CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_UNKNOWN;
+ tnr_dmd->diver_sub->clk_mode = CXD2880_TNRDMD_CLOCKMODE_UNKNOWN;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ tnr_dmd->diver_sub->scan_mode = 0;
+ cxd2880_atomic_set(&tnr_dmd->diver_sub->cancel, 0);
+ }
+
+ ret = cxd2880_tnrdmd_chip_id(tnr_dmd, &tnr_dmd->chip_id);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->chip_id))
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ cxd2880_tnrdmd_chip_id(tnr_dmd->diver_sub,
+ &tnr_dmd->diver_sub->chip_id);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (!CXD2880_TNRDMD_CHIP_ID_VALID(tnr_dmd->diver_sub->chip_id))
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+ }
+
+ ret = p_init1(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init1(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ CXD2880_SLEEP(1);
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init2(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = p_init2(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ CXD2880_SLEEP(5);
+
+ ret = p_init3(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = p_init3(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = rf_init1(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = rf_init1(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ {
+ u8 cpu_task_completed = 0;
+
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (!cpu_task_completed)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ ret = rf_init2(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = rf_init2(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *task_completed)
+{
+ u16 cpu_status = 0;
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!task_completed))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd, &cpu_status);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SINGLE) {
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return ret;
+ }
+ if (cpu_status != 0) {
+ *task_completed = 0;
+ return ret;
+ }
+
+ ret = cxd2880_tnrdmd_mon_internal_cpu_status_sub(tnr_dmd, &cpu_status);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (cpu_status == 0)
+ *task_completed = 1;
+ else
+ *task_completed = 0;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u32 frequency_khz,
+ enum cxd2880_dtv_bandwidth
+ bandwidth, u8 one_seg_opt,
+ u8 one_seg_opt_shft_dir)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (frequency_khz < 4000)
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret = cxd2880_tnrdmd_sleep(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ {
+ u8 data = 0;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x2B, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ switch (sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ case CXD2880_DTV_SYS_ISDBT:
+ case CXD2880_DTV_SYS_ISDBTSB:
+ case CXD2880_DTV_SYS_ISDBTMM_A:
+ case CXD2880_DTV_SYS_ISDBTMM_B:
+ if (data == 0x00) {
+ ret = t_power_x(tnr_dmd, 1);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 1);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ }
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ if (data == 0x01) {
+ ret = t_power_x(tnr_dmd, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = t_power_x(tnr_dmd->diver_sub, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+ }
+
+ {
+ enum cxd2880_tnrdmd_clockmode new_clk_mode =
+ CXD2880_TNRDMD_CLOCKMODE_A;
+
+ ret = spll_reset(tnr_dmd, new_clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ tnr_dmd->clk_mode = new_clk_mode;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = spll_reset(tnr_dmd->diver_sub, new_clk_mode);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ tnr_dmd->diver_sub->clk_mode = new_clk_mode;
+ }
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = load_cfg_mem(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ }
+
+ {
+ int shift_frequency_khz = 0;
+
+ if (one_seg_opt) {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 350;
+ } else {
+ if (one_seg_opt_shft_dir)
+ shift_frequency_khz = 350;
+ else
+ shift_frequency_khz = -350;
+
+ if (tnr_dmd->create_param.xtal_share_type ==
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE)
+ shift_frequency_khz *= -1;
+ }
+ } else {
+ if (tnr_dmd->diver_mode ==
+ CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ shift_frequency_khz = 150;
+ } else {
+ switch (tnr_dmd->create_param.xtal_share_type) {
+ case CXD2880_TNRDMD_XTAL_SHARE_NONE:
+ case CXD2880_TNRDMD_XTAL_SHARE_EXTREF:
+ default:
+ shift_frequency_khz = 0;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_MASTER:
+ shift_frequency_khz = 150;
+ break;
+ case CXD2880_TNRDMD_XTAL_SHARE_SLAVE:
+ shift_frequency_khz = -150;
+ break;
+ }
+ }
+ }
+
+ ret =
+ x_tune1(tnr_dmd, sys, frequency_khz, bandwidth,
+ tnr_dmd->is_cable_input, shift_frequency_khz);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune1(tnr_dmd->diver_sub, sys, frequency_khz,
+ bandwidth, tnr_dmd->is_cable_input,
+ -shift_frequency_khz);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ CXD2880_SLEEP(10);
+
+ {
+ u8 cpu_task_completed = 0;
+
+ ret =
+ cxd2880_tnrdmd_check_internal_cpu_status(tnr_dmd,
+ &cpu_task_completed);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (!cpu_task_completed)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ ret =
+ x_tune2(tnr_dmd, bandwidth, tnr_dmd->clk_mode,
+ shift_frequency_khz);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret =
+ x_tune2(tnr_dmd->diver_sub, bandwidth,
+ tnr_dmd->diver_sub->clk_mode,
+ -shift_frequency_khz);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ }
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS) {
+ ret = set_ts_clk_mode_and_freq(tnr_dmd, sys);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ } else {
+ struct cxd2880_tnrdmd_pid_ftr_cfg *pid_ftr_cfg;
+
+ if (tnr_dmd->pid_ftr_cfg_en)
+ pid_ftr_cfg = &tnr_dmd->pid_ftr_cfg;
+ else
+ pid_ftr_cfg = NULL;
+
+ ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret = x_tune3(tnr_dmd, sys, en_fef_intmtnt_ctrl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_tune3(tnr_dmd->diver_sub, sys, en_fef_intmtnt_ctrl);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_tune4(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 1);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_SLEEP) {
+ } else if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
+ ret = cxd2880_tnrdmd_set_ts_output(tnr_dmd, 0);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep1(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = x_sleep2(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep2(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ switch (tnr_dmd->sys) {
+ case CXD2880_DTV_SYS_DVBT:
+ ret = cxd2880_tnrdmd_dvbt_sleep_setting(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_DTV_SYS_DVBT2:
+ ret = cxd2880_tnrdmd_dvbt2_sleep_setting(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ ret = x_sleep3(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep3(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ ret = x_sleep4(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ ret = x_sleep4(tnr_dmd->diver_sub);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ tnr_dmd->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->frequency_khz = 0;
+ tnr_dmd->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ tnr_dmd->diver_sub->state = CXD2880_TNRDMD_STATE_SLEEP;
+ tnr_dmd->diver_sub->frequency_khz = 0;
+ tnr_dmd->diver_sub->sys = CXD2880_DTV_SYS_UNKNOWN;
+ tnr_dmd->diver_sub->bandwidth = CXD2880_DTV_BW_UNKNOWN;
+ }
+ } else {
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_cfg_id id,
+ int value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 data[2] = { 0 };
+ u8 need_sub_setting = 0;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ switch (id) {
+ case CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC4,
+ (u8)(value ? 0x00 :
+ 0x10), 0x10);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC5,
+ (u8)(value ? 0x00 :
+ 0x02), 0x02);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC5,
+ (u8)(value ? 0x00 :
+ 0x04), 0x04);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xCB,
+ (u8)(value ? 0x00 :
+ 0x01), 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC5,
+ (u8)(value ? 0x01 :
+ 0x00), 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_CONT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ tnr_dmd->srl_ts_clk_mod_cnts = (u8)(value ? 0x01 : 0x00);
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 0x1F))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC6, (u8)value,
+ 0x1F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSVALID_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 0x1F))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC8, (u8)value,
+ 0x1F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_MASK:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 0x1F))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xC9, (u8)value,
+ 0x1F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSERR_VALID_DIS:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x91,
+ (u8)(value ? 0x01 :
+ 0x00), 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_CURRENT:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x51, (u8)value,
+ 0x3F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50,
+ (u8)(value ? 0x80 :
+ 0x00), 0x80);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSPIN_PULLUP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x50, (u8)value,
+ 0x3F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSCLK_FREQ:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 1))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ tnr_dmd->srl_ts_clk_frq =
+ (enum cxd2880_tnrdmd_serial_ts_clk)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 0xFF))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ tnr_dmd->ts_byte_clk_manual_setting = (u8)value;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_PACKET_GAP:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if ((value < 0) || (value > 7))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0xD6, (u8)value,
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE:
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ tnr_dmd->is_ts_backwards_compatible_mode = (u8)(value ? 1 : 0);
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_PWM_VALUE:
+ if ((value < 0) || (value > 0x1000))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x22,
+ (u8)(value ? 0x01 :
+ 0x00), 0x01);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ {
+ u8 data[2];
+
+ data[0] = (u8)(((u16)value >> 8) & 0x1F);
+ data[1] = (u8)((u16)value & 0xFF);
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x23,
+ data[0], 0x1F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x24,
+ data[1], 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT:
+ data[0] = (u8)((value >> 8) & 0xFF);
+ data[1] = (u8)(value & 0xFF);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x48, data[0],
+ 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x49, data[1],
+ 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL:
+ data[0] = (u8)(value & 0x07);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4A, data[0],
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL:
+ data[0] = (u8)((value & 0x07) << 3);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_SYS,
+ 0x00, 0x4A, data[0],
+ 0x38);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE:
+ if ((value < (int)CXD2880_TNRDMD_CLOCKMODE_UNKNOWN) ||
+ (value > (int)CXD2880_TNRDMD_CLOCKMODE_C))
+ return CXD2880_RESULT_ERROR_RANGE;
+ tnr_dmd->fixed_clk_mode = (enum cxd2880_tnrdmd_clockmode)value;
+ break;
+
+ case CXD2880_TNRDMD_CFG_CABLE_INPUT:
+ tnr_dmd->is_cable_input = (u8)(value ? 1 : 0);
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE:
+ tnr_dmd->en_fef_intmtnt_base = (u8)(value ? 1 : 0);
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE:
+ tnr_dmd->en_fef_intmtnt_lite = (u8)(value ? 1 : 0);
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS:
+ data[0] = (u8)((value >> 8) & 0x07);
+ data[1] = (u8)(value & 0xFF);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x99, data[0],
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9A, data[1],
+ 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS:
+ data[0] = (u8)((value >> 8) & 0x07);
+ data[1] = (u8)(value & 0xFF);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9B, data[0],
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9C, data[1],
+ 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS:
+ data[0] = (u8)((value >> 8) & 0x07);
+ data[1] = (u8)(value & 0xFF);
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9D, data[0],
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x00, 0x9E, data[1],
+ 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST:
+ tnr_dmd->blind_tune_dvbt2_first = (u8)(value ? 1 : 0);
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD:
+ if ((value < 0) || (value > 31))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x60,
+ (u8)(value & 0x1F),
+ 0x1F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD:
+ if ((value < 0) || (value > 7))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x6F,
+ (u8)(value & 0x07),
+ 0x07);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_BBER_MES:
+ if ((value < 0) || (value > 15))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x72,
+ (u8)(value & 0x0F),
+ 0x0F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_LBER_MES:
+ if ((value < 0) || (value > 15))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x20, 0x6F,
+ (u8)(value & 0x0F),
+ 0x0F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT_PER_MES:
+ if ((value < 0) || (value > 15))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x10, 0x5C,
+ (u8)(value & 0x0F),
+ 0x0F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_DVBT2_PER_MES:
+ if ((value < 0) || (value > 15))
+ return CXD2880_RESULT_ERROR_RANGE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x24, 0xDC,
+ (u8)(value & 0x0F),
+ 0x0F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ break;
+
+ case CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD:
+ {
+ u8 data[2];
+
+ data[0] = (u8)((value & 0x00007F00) >> 8);
+ data[1] = (u8)(value & 0x000000FF);
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x60, 0x5B,
+ data[0], 0x7F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd,
+ CXD2880_IO_TGT_DMD,
+ 0x60, 0x5C,
+ data[1], 0xFF);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_ARG;
+ }
+
+ if (need_sub_setting &&
+ (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN)) {
+ ret = cxd2880_tnrdmd_set_cfg(tnr_dmd->diver_sub, id, value);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode mode,
+ u8 open_drain, u8 invert)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (id > 2)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (mode > CXD2880_TNRDMD_GPIO_MODE_EEW)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x40 + id, (u8)mode,
+ 0x0F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x43,
+ (u8)(open_drain ? (1 << id) :
+ 0), (u8)(1 << id));
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x44,
+ (u8)(invert ? (1 << id) : 0),
+ (u8)(1 << id));
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x45,
+ (u8)(en ? 0 : (1 << id)),
+ (u8)(1 << id));
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode
+ mode, u8 open_drain, u8 invert)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_gpio_set_cfg(tnr_dmd->diver_sub, id, en, mode,
+ open_drain, invert);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ u8 data = 0;
+
+ if ((!tnr_dmd) || (!value))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (id > 2)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x20, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *value = (u8)((data >> id) & 0x01);
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_gpio_read(tnr_dmd->diver_sub, id, value);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (id > 2)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ ret =
+ cxd2880_tnrdmd_set_and_save_reg_bits(tnr_dmd, CXD2880_IO_TGT_SYS,
+ 0x00, 0x46,
+ (u8)(value ? (1 << id) : 0),
+ (u8)(1 << id));
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_gpio_write(tnr_dmd->diver_sub, id, value);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *value)
+{
+ u8 data[2] = { 0 };
+
+ if ((!tnr_dmd) || (!value))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x15, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *value = (u16)(((u16)data[0] << 8) | (data[1]));
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 value)
+{
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = (u8)((value >> 8) & 0xFF);
+ data[1] = (u8)(value & 0xFF);
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x3C, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 clear_overflow_flag,
+ u8 clear_underflow_flag,
+ u8 clear_buf)
+{
+ u8 data[2] = { 0 };
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ data[0] = (u8)(clear_overflow_flag ? 0x02 : 0x00);
+ data[0] |= (u8)(clear_underflow_flag ? 0x01 : 0x00);
+ data[1] = (u8)(clear_buf ? 0x01 : 0x00);
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x9F, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_chip_id *chip_id)
+{
+ u8 data = 0;
+
+ if ((!tnr_dmd) || (!chip_id))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0xFD, &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *chip_id = (enum cxd2880_tnrdmd_chip_id)data;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address,
+ u8 value, u8 bit_mask)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ tgt, 0x00, bank) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (cxd2880_io_set_reg_bits(tnr_dmd->io, tgt, address, value, bit_mask)
+ != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = set_cfg_mem(tnr_dmd, tgt, bank, address, value, bit_mask);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 scan_mode_end)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ CXD2880_ARG_UNUSED(sys);
+
+ tnr_dmd->scan_mode = scan_mode_end;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_MAIN) {
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ ret =
+ cxd2880_tnrdmd_set_scan_mode(tnr_dmd->diver_sub, sys,
+ scan_mode_end);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->create_param.ts_output_if == CXD2880_TNRDMD_TSOUT_IF_TS)
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+ if (pid_ftr_cfg) {
+ tnr_dmd->pid_ftr_cfg = *pid_ftr_cfg;
+ tnr_dmd->pid_ftr_cfg_en = 1;
+ } else {
+ tnr_dmd->pid_ftr_cfg_en = 0;
+ }
+
+ if (tnr_dmd->state == CXD2880_TNRDMD_STATE_ACTIVE) {
+ ret = pid_ftr_setting(tnr_dmd, pid_ftr_cfg);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_ret(*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *))
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ tnr_dmd->rf_lvl_cmpstn = rf_lvl_cmpstn;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_ret
+ (*rf_lvl_cmpstn)(struct
+ cxd2880_tnrdmd
+ *,
+ int *))
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_set_rf_lvl_cmpstn(tnr_dmd->diver_sub, rf_lvl_cmpstn);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ tnr_dmd->lna_thrs_tbl_air = tbl_air;
+ tnr_dmd->lna_thrs_tbl_cable = tbl_cable;
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_set_lna_thrs(tnr_dmd->diver_sub, tbl_air, tbl_cable);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 en, u8 value)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->create_param.ts_output_if != CXD2880_TNRDMD_TSOUT_IF_TS)
+ return CXD2880_RESULT_ERROR_NOSUPPORT;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (en) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x50,
+ ((value & 0x1F) | 0x80)) !=
+ CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x52,
+ (value & 0x1F)) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ } else {
+ ret = tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x50, 0x3F);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x52,
+ 0x1F) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ ret = load_cfg_mem(tnr_dmd);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 en)
+{
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ if (en) {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x52,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xC3,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ } else {
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0xC3,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x52,
+ 0x1F) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ break;
+
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ break;
+
+ default:
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ }
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if (!tnr_dmd)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ switch (tnr_dmd->create_param.ts_output_if) {
+ case CXD2880_TNRDMD_TSOUT_IF_SPI:
+ case CXD2880_TNRDMD_TSOUT_IF_SDIO:
+ {
+ u8 data = 0;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ &data,
+ 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+ break;
+ case CXD2880_TNRDMD_TSOUT_IF_TS:
+ default:
+ break;
+ }
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x01,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
new file mode 100644
index 0000000..26e29b3
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd.h
@@ -0,0 +1,395 @@
+/*
+ * cxd2880_tnrdmd.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common control interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_H
+#define CXD2880_TNRDMD_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_io.h"
+#include "cxd2880_dtv.h"
+#include "cxd2880_dvbt.h"
+#include "cxd2880_dvbt2.h"
+
+#define CXD2880_TNRDMD_MAX_CFG_MEM_COUNT 100
+
+#define slvt_unfreeze_reg(tnr_dmd) ((void)((tnr_dmd)->io->write_reg\
+((tnr_dmd)->io, CXD2880_IO_TGT_DMD, 0x01, 0x00)))
+
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_UNDERFLOW 0x0001
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_OVERFLOW 0x0002
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_EMPTY 0x0004
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_ALMOST_FULL 0x0008
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_BUF_RRDY 0x0010
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_COMMAND 0x0020
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_ILLEGAL_ACCESS 0x0040
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_CPU_ERROR 0x0100
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_LOCK 0x0200
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_INV_LOCK 0x0400
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_NOOFDM 0x0800
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EWS 0x1000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_EEW 0x2000
+#define CXD2880_TNRDMD_INTERRUPT_TYPE_FEC_FAIL 0x4000
+
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_L1POST_OK 0x01
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_DMD_LOCK 0x02
+#define CXD2880_TNRDMD_INTERRUPT_LOCK_SEL_TS_LOCK 0x04
+
+enum cxd2880_tnrdmd_chip_id {
+ CXD2880_TNRDMD_CHIP_ID_UNKNOWN = 0x00,
+ CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X = 0x62,
+ CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11 = 0x6A
+};
+
+#define CXD2880_TNRDMD_CHIP_ID_VALID(chip_id) (((chip_id) == \
+CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) || \
+((chip_id) == CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11))
+
+enum cxd2880_tnrdmd_state {
+ CXD2880_TNRDMD_STATE_UNKNOWN,
+ CXD2880_TNRDMD_STATE_SLEEP,
+ CXD2880_TNRDMD_STATE_ACTIVE,
+ CXD2880_TNRDMD_STATE_INVALID
+};
+
+enum cxd2880_tnrdmd_divermode {
+ CXD2880_TNRDMD_DIVERMODE_SINGLE,
+ CXD2880_TNRDMD_DIVERMODE_MAIN,
+ CXD2880_TNRDMD_DIVERMODE_SUB
+};
+
+enum cxd2880_tnrdmd_clockmode {
+ CXD2880_TNRDMD_CLOCKMODE_UNKNOWN,
+ CXD2880_TNRDMD_CLOCKMODE_A,
+ CXD2880_TNRDMD_CLOCKMODE_B,
+ CXD2880_TNRDMD_CLOCKMODE_C
+};
+
+enum cxd2880_tnrdmd_tsout_if {
+ CXD2880_TNRDMD_TSOUT_IF_TS,
+ CXD2880_TNRDMD_TSOUT_IF_SPI,
+ CXD2880_TNRDMD_TSOUT_IF_SDIO
+};
+
+enum cxd2880_tnrdmd_xtal_share {
+ CXD2880_TNRDMD_XTAL_SHARE_NONE,
+ CXD2880_TNRDMD_XTAL_SHARE_EXTREF,
+ CXD2880_TNRDMD_XTAL_SHARE_MASTER,
+ CXD2880_TNRDMD_XTAL_SHARE_SLAVE
+};
+
+enum cxd2880_tnrdmd_spectrum_sense {
+ CXD2880_TNRDMD_SPECTRUM_NORMAL,
+ CXD2880_TNRDMD_SPECTRUM_INV
+};
+
+enum cxd2880_tnrdmd_cfg_id {
+ CXD2880_TNRDMD_CFG_OUTPUT_SEL_MSB,
+ CXD2880_TNRDMD_CFG_TSVALID_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_TSSYNC_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_TSERR_ACTIVE_HI,
+ CXD2880_TNRDMD_CFG_LATCH_ON_POSEDGE,
+ CXD2880_TNRDMD_CFG_TSCLK_CONT,
+ CXD2880_TNRDMD_CFG_TSCLK_MASK,
+ CXD2880_TNRDMD_CFG_TSVALID_MASK,
+ CXD2880_TNRDMD_CFG_TSERR_MASK,
+ CXD2880_TNRDMD_CFG_TSERR_VALID_DIS,
+ CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+ CXD2880_TNRDMD_CFG_TSPIN_PULLUP_MANUAL,
+ CXD2880_TNRDMD_CFG_TSPIN_PULLUP,
+ CXD2880_TNRDMD_CFG_TSCLK_FREQ,
+ CXD2880_TNRDMD_CFG_TSBYTECLK_MANUAL,
+ CXD2880_TNRDMD_CFG_TS_PACKET_GAP,
+ CXD2880_TNRDMD_CFG_TS_BACKWARDS_COMPATIBLE,
+ CXD2880_TNRDMD_CFG_PWM_VALUE,
+ CXD2880_TNRDMD_CFG_INTERRUPT,
+ CXD2880_TNRDMD_CFG_INTERRUPT_LOCK_SEL,
+ CXD2880_TNRDMD_CFG_INTERRUPT_INV_LOCK_SEL,
+ CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_EMPTY_THRS,
+ CXD2880_TNRDMD_CFG_TS_BUF_ALMOST_FULL_THRS,
+ CXD2880_TNRDMD_CFG_TS_BUF_RRDY_THRS,
+ CXD2880_TNRDMD_CFG_FIXED_CLOCKMODE,
+ CXD2880_TNRDMD_CFG_CABLE_INPUT,
+ CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_BASE,
+ CXD2880_TNRDMD_CFG_DVBT2_FEF_INTERMITTENT_LITE,
+ CXD2880_TNRDMD_CFG_BLINDTUNE_DVBT2_FIRST,
+ CXD2880_TNRDMD_CFG_DVBT_BERN_PERIOD,
+ CXD2880_TNRDMD_CFG_DVBT_VBER_PERIOD,
+ CXD2880_TNRDMD_CFG_DVBT_PER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_BBER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_LBER_MES,
+ CXD2880_TNRDMD_CFG_DVBT2_PER_MES,
+ CXD2880_TNRDMD_CFG_ISDBT_BERPER_PERIOD
+};
+
+enum cxd2880_tnrdmd_lock_result {
+ CXD2880_TNRDMD_LOCK_RESULT_NOTDETECT,
+ CXD2880_TNRDMD_LOCK_RESULT_LOCKED,
+ CXD2880_TNRDMD_LOCK_RESULT_UNLOCKED
+};
+
+enum cxd2880_tnrdmd_gpio_mode {
+ CXD2880_TNRDMD_GPIO_MODE_OUTPUT = 0x00,
+ CXD2880_TNRDMD_GPIO_MODE_INPUT = 0x01,
+ CXD2880_TNRDMD_GPIO_MODE_INT = 0x02,
+ CXD2880_TNRDMD_GPIO_MODE_FEC_FAIL = 0x03,
+ CXD2880_TNRDMD_GPIO_MODE_PWM = 0x04,
+ CXD2880_TNRDMD_GPIO_MODE_EWS = 0x05,
+ CXD2880_TNRDMD_GPIO_MODE_EEW = 0x06
+};
+
+enum cxd2880_tnrdmd_serial_ts_clk {
+ CXD2880_TNRDMD_SERIAL_TS_CLK_FULL,
+ CXD2880_TNRDMD_SERIAL_TS_CLK_HALF
+};
+
+struct cxd2880_tnrdmd_cfg_mem {
+ enum cxd2880_io_tgt tgt;
+ u8 bank;
+ u8 address;
+ u8 value;
+ u8 bit_mask;
+};
+
+struct cxd2880_tnrdmd_pid_cfg {
+ u8 is_en;
+ u16 pid;
+};
+
+struct cxd2880_tnrdmd_pid_ftr_cfg {
+ u8 is_negative;
+ struct cxd2880_tnrdmd_pid_cfg pid_cfg[32];
+};
+
+struct cxd2880_tnrdmd_ts_buf_info {
+ u8 read_ready;
+ u8 almost_full;
+ u8 almost_empty;
+ u8 overflow;
+ u8 underflow;
+ u16 packet_num;
+};
+
+struct cxd2880_tnrdmd_lna_thrs {
+ u8 off_on;
+ u8 on_off;
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_air {
+ struct cxd2880_tnrdmd_lna_thrs thrs[24];
+};
+
+struct cxd2880_tnrdmd_lna_thrs_tbl_cable {
+ struct cxd2880_tnrdmd_lna_thrs thrs[32];
+};
+
+struct cxd2880_tnrdmd_create_param {
+ enum cxd2880_tnrdmd_tsout_if ts_output_if;
+ u8 en_internal_ldo;
+ enum cxd2880_tnrdmd_xtal_share xtal_share_type;
+ u8 xosc_cap;
+ u8 xosc_i;
+ u8 is_cxd2881gg;
+ u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd_diver_create_param {
+ enum cxd2880_tnrdmd_tsout_if ts_output_if;
+ u8 en_internal_ldo;
+ u8 xosc_cap_main;
+ u8 xosc_i_main;
+ u8 xosc_i_sub;
+ u8 is_cxd2881gg;
+ u8 stationary_use;
+};
+
+struct cxd2880_tnrdmd {
+ struct cxd2880_tnrdmd *diver_sub;
+ struct cxd2880_io *io;
+ struct cxd2880_tnrdmd_create_param create_param;
+ enum cxd2880_tnrdmd_divermode diver_mode;
+ enum cxd2880_tnrdmd_clockmode fixed_clk_mode;
+ u8 is_cable_input;
+ u8 en_fef_intmtnt_base;
+ u8 en_fef_intmtnt_lite;
+ u8 blind_tune_dvbt2_first;
+ enum cxd2880_ret (*rf_lvl_cmpstn)(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+ struct cxd2880_tnrdmd_lna_thrs_tbl_air *lna_thrs_tbl_air;
+ struct cxd2880_tnrdmd_lna_thrs_tbl_cable *lna_thrs_tbl_cable;
+ u8 srl_ts_clk_mod_cnts;
+ enum cxd2880_tnrdmd_serial_ts_clk srl_ts_clk_frq;
+ u8 ts_byte_clk_manual_setting;
+ u8 is_ts_backwards_compatible_mode;
+ struct cxd2880_tnrdmd_cfg_mem cfg_mem[CXD2880_TNRDMD_MAX_CFG_MEM_COUNT];
+ u8 cfg_mem_last_entry;
+ struct cxd2880_tnrdmd_pid_ftr_cfg pid_ftr_cfg;
+ u8 pid_ftr_cfg_en;
+ void *user;
+ enum cxd2880_tnrdmd_chip_id chip_id;
+ enum cxd2880_tnrdmd_state state;
+ enum cxd2880_tnrdmd_clockmode clk_mode;
+ u32 frequency_khz;
+ enum cxd2880_dtv_sys sys;
+ enum cxd2880_dtv_bandwidth bandwidth;
+ u8 scan_mode;
+ struct cxd2880_atomic cancel;
+};
+
+enum cxd2880_ret cxd2880_tnrdmd_create(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_io *io,
+ struct cxd2880_tnrdmd_create_param
+ *create_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_diver_create(struct cxd2880_tnrdmd
+ *tnr_dmd_main,
+ struct cxd2880_io *io_main,
+ struct cxd2880_tnrdmd *tnr_dmd_sub,
+ struct cxd2880_io *io_sub,
+ struct
+ cxd2880_tnrdmd_diver_create_param
+ *create_param);
+
+enum cxd2880_ret cxd2880_tnrdmd_init1(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_init2(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_check_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ u8 *task_completed);
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting1(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u32 frequency_khz,
+ enum cxd2880_dtv_bandwidth
+ bandwidth, u8 one_seg_opt,
+ u8 one_seg_opt_shft_dir);
+
+enum cxd2880_ret cxd2880_tnrdmd_common_tune_setting2(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 en_fef_intmtnt_ctrl);
+
+enum cxd2880_ret cxd2880_tnrdmd_sleep(struct cxd2880_tnrdmd *tnr_dmd);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_cfg_id id,
+ int value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode mode,
+ u8 open_drain, u8 invert);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_set_cfg_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id,
+ u8 en,
+ enum cxd2880_tnrdmd_gpio_mode
+ mode, u8 open_drain,
+ u8 invert);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_read_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_gpio_write_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 id, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_read(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 *value);
+
+enum cxd2880_ret cxd2880_tnrdmd_interrupt_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u16 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_ts_buf_clear(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 clear_overflow_flag,
+ u8 clear_underflow_flag,
+ u8 clear_buf);
+
+enum cxd2880_ret cxd2880_tnrdmd_chip_id(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_tnrdmd_chip_id *chip_id);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_and_save_reg_bits(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum cxd2880_io_tgt tgt,
+ u8 bank, u8 address,
+ u8 value, u8 bit_mask);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_scan_mode(struct cxd2880_tnrdmd *tnr_dmd,
+ enum cxd2880_dtv_sys sys,
+ u8 scan_mode_end);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_pid_ftr(struct cxd2880_tnrdmd *tnr_dmd,
+ struct cxd2880_tnrdmd_pid_ftr_cfg
+ *pid_ftr_cfg);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_ret(*rf_lvl_cmpstn)
+ (struct cxd2880_tnrdmd *,
+ int *));
+
+enum cxd2880_ret cxd2880_tnrdmd_set_rf_lvl_cmpstn_sub(struct cxd2880_tnrdmd
+ *tnr_dmd,
+ enum
+ cxd2880_ret
+ (*rf_lvl_cmpstn)(struct
+ cxd2880_tnrdmd
+ *,
+ int *));
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_lna_thrs_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_air
+ *tbl_air,
+ struct
+ cxd2880_tnrdmd_lna_thrs_tbl_cable
+ *tbl_cable);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_pin_high_low(struct cxd2880_tnrdmd
+ *tnr_dmd, u8 en, u8 value);
+
+enum cxd2880_ret cxd2880_tnrdmd_set_ts_output(struct cxd2880_tnrdmd *tnr_dmd,
+ u8 en);
+
+enum cxd2880_ret slvt_freeze_reg(struct cxd2880_tnrdmd *tnr_dmd);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
new file mode 100644
index 0000000..9de8585
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
@@ -0,0 +1,29 @@
+/*
+ * cxd2880_tnrdmd_driver_version.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * version information
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.0"
+
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2017-03-28"
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
new file mode 100644
index 0000000..0ac5b9b
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.c
@@ -0,0 +1,207 @@
+/*
+ * cxd2880_tnrdmd_mon.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor functions
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd_mon.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!rf_lvl_db))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x01) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2] = { 0x80, 0x00 };
+
+ if (tnr_dmd->io->write_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x5B, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ CXD2880_SLEEP_IN_MON(2, tnr_dmd);
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x1A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ {
+ u8 data[2];
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x15, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((data[0] != 0) || (data[1] != 0))
+ return CXD2880_RESULT_ERROR_OTHER;
+
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x11, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *rf_lvl_db =
+ cxd2880_convert2s_complement((data[0] << 3) |
+ ((data[1] & 0xE0) >> 5), 11);
+ }
+
+ *rf_lvl_db *= 125;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x10,
+ 0x00) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnr_dmd->rf_lvl_cmpstn) {
+ ret = tnr_dmd->rf_lvl_cmpstn(tnr_dmd, rf_lvl_db);
+ if (ret != CXD2880_RESULT_OK)
+ return ret;
+ }
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!rf_lvl_db))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret = cxd2880_tnrdmd_mon_rf_lvl(tnr_dmd->diver_sub, rf_lvl_db);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd, u16 *status)
+{
+ u8 data[2] = { 0 };
+
+ if ((!tnr_dmd) || (!status))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x00,
+ 0x1A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_SYS, 0x15, data,
+ 2) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *status = (u16)(((u16)data[0] << 8) | data[1]);
+
+ return CXD2880_RESULT_OK;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u16 *status)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!status))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode != CXD2880_TNRDMD_DIVERMODE_MAIN)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ ret =
+ cxd2880_tnrdmd_mon_internal_cpu_status(tnr_dmd->diver_sub, status);
+
+ return ret;
+}
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_ts_buf_info
+ *info)
+{
+ u8 data[3] = { 0 };
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+
+ if ((!tnr_dmd) || (!info))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnr_dmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if ((tnr_dmd->state != CXD2880_TNRDMD_STATE_SLEEP) &&
+ (tnr_dmd->state != CXD2880_TNRDMD_STATE_ACTIVE))
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnr_dmd->io->write_reg(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x00,
+ 0x0A) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+ if (tnr_dmd->io->read_regs(tnr_dmd->io,
+ CXD2880_IO_TGT_DMD, 0x50, data,
+ 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ info->read_ready = (u8)((data[0] & 0x10) ? 0x01 : 0x00);
+ info->almost_full = (u8)((data[0] & 0x08) ? 0x01 : 0x00);
+ info->almost_empty = (u8)((data[0] & 0x04) ? 0x01 : 0x00);
+ info->overflow = (u8)((data[0] & 0x02) ? 0x01 : 0x00);
+ info->underflow = (u8)((data[0] & 0x01) ? 0x01 : 0x00);
+
+ info->packet_num = (u16)(((u32)(data[1] & 0x07) << 8) | data[2]);
+
+ return ret;
+}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
new file mode 100644
index 0000000..506bd55
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_mon.h
@@ -0,0 +1,52 @@
+/*
+ * cxd2880_tnrdmd_mon.h
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * common monitor interface
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CXD2880_TNRDMD_MON_H
+#define CXD2880_TNRDMD_MON_H
+
+#include "cxd2880_common.h"
+#include "cxd2880_tnrdmd.h"
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_rf_lvl_sub(struct cxd2880_tnrdmd *tnr_dmd,
+ int *rf_lvl_db);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status(struct cxd2880_tnrdmd
+ *tnr_dmd, u16 *status);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_internal_cpu_status_sub(struct
+ cxd2880_tnrdmd
+ *tnr_dmd,
+ u16 *status);
+
+enum cxd2880_ret cxd2880_tnrdmd_mon_ts_buf_info(struct cxd2880_tnrdmd *tnr_dmd,
+ struct
+ cxd2880_tnrdmd_ts_buf_info
+ *info);
+
+#endif
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
new file mode 100644
index 0000000..66d78fb
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -0,0 +1,1550 @@
+/*
+ * cxd2880_top.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#include "dvb_frontend.h"
+
+#include "cxd2880.h"
+#include "cxd2880_tnrdmd_mon.h"
+#include "cxd2880_tnrdmd_dvbt2_mon.h"
+#include "cxd2880_tnrdmd_dvbt_mon.h"
+#include "cxd2880_integ_dvbt2.h"
+#include "cxd2880_integ_dvbt.h"
+#include "cxd2880_devio_spi.h"
+#include "cxd2880_spi_device.h"
+#include "cxd2880_tnrdmd_driver_version.h"
+
+struct cxd2880_priv {
+ struct cxd2880_tnrdmd tnrdmd;
+ struct spi_device *spi;
+ struct cxd2880_io regio;
+ struct cxd2880_spi_device spi_device;
+ struct cxd2880_spi cxd2880_spi;
+ struct cxd2880_dvbt_tune_param dvbt_tune_param;
+ struct cxd2880_dvbt2_tune_param dvbt2_tune_param;
+ struct mutex *spi_mutex; /* For SPI access exclusive control */
+};
+
+/*
+ * return value conversion table
+ */
+static int return_tbl[] = {
+ 0, /* CXD2880_RESULT_OK */
+ -EINVAL, /* CXD2880_RESULT_ERROR_ARG*/
+ -EIO, /* CXD2880_RESULT_ERROR_IO */
+ -EPERM, /* CXD2880_RESULT_ERROR_SW_STATE */
+ -EBUSY, /* CXD2880_RESULT_ERROR_HW_STATE */
+ -ETIME, /* CXD2880_RESULT_ERROR_TIMEOUT */
+ -EAGAIN, /* CXD2880_RESULT_ERROR_UNLOCK */
+ -ERANGE, /* CXD2880_RESULT_ERROR_RANGE */
+ -EOPNOTSUPP, /* CXD2880_RESULT_ERROR_NOSUPPORT */
+ -ECANCELED, /* CXD2880_RESULT_ERROR_CANCEL */
+ -EPERM, /* CXD2880_RESULT_ERROR_OTHER */
+ -EOVERFLOW, /* CXD2880_RESULT_ERROR_OVERFLOW */
+ 0, /* CXD2880_RESULT_OK_CONFIRM */
+};
+
+static enum cxd2880_ret cxd2880_pre_bit_err_t(
+ struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
+ u32 *pre_bit_count)
+{
+ u8 rdata[2];
+
+ if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x10) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x39, rdata, 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if ((rdata[0] & 0x01) == 0) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x22, rdata, 2) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ *pre_bit_err = (rdata[0] << 8) | rdata[1];
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x6F, rdata, 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ slvt_unfreeze_reg(tnrdmd);
+
+ *pre_bit_count = ((rdata[0] & 0x07) == 0) ?
+ 256 : (0x1000 << (rdata[0] & 0x07));
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_pre_bit_err_t2(
+ struct cxd2880_tnrdmd *tnrdmd, u32 *pre_bit_err,
+ u32 *pre_bit_count)
+{
+ u32 period_exp = 0;
+ u32 n_ldpc = 0;
+ u8 data[5];
+
+ if ((!tnrdmd) || (!pre_bit_err) || (!pre_bit_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x3C, data, sizeof(data))
+ != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(data[0] & 0x01)) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+ *pre_bit_err =
+ ((data[1] & 0x0F) << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0xA0, data, 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (((enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03)) ==
+ CXD2880_DVBT2_FEC_LDPC_16K)
+ n_ldpc = 16200;
+ else
+ n_ldpc = 64800;
+ slvt_unfreeze_reg(tnrdmd);
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x6F, data, 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = data[0] & 0x0F;
+
+ *pre_bit_count = (1U << period_exp) * n_ldpc;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_post_bit_err_t(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *post_bit_err,
+ u32 *post_bit_count)
+{
+ u8 rdata[3];
+ u32 bit_error = 0;
+ u32 period_exp = 0;
+
+ if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x15, rdata, 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x40) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *post_bit_err = ((rdata[0] & 0x3F) << 16) | (rdata[1] << 8) | rdata[2];
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x60, rdata, 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = (rdata[0] & 0x1F);
+
+ if ((period_exp <= 11) && (bit_error > (1U << period_exp) * 204 * 8))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ if (period_exp == 11)
+ *post_bit_count = 3342336;
+ else
+ *post_bit_count = (1U << period_exp) * 204 * 81;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_post_bit_err_t2(struct cxd2880_tnrdmd *tnrdmd,
+ u32 *post_bit_err,
+ u32 *post_bit_count)
+{
+ u32 period_exp = 0;
+ u32 n_bch = 0;
+
+ if ((!tnrdmd) || (!post_bit_err) || (!post_bit_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 data[3];
+ enum cxd2880_dvbt2_plp_fec plp_fec_type =
+ CXD2880_DVBT2_FEC_LDPC_16K;
+ enum cxd2880_dvbt2_plp_code_rate plp_code_rate =
+ CXD2880_DVBT2_R1_2;
+
+ static const u16 n_bch_bits_lookup[2][8] = {
+ {7200, 9720, 10800, 11880, 12600, 13320, 5400, 6480},
+ {32400, 38880, 43200, 48600, 51840, 54000, 21600, 25920}
+ };
+
+ if (slvt_freeze_reg(tnrdmd) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x0B) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x15, data, 3) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ if (!(data[0] & 0x40)) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_HW_STATE;
+ }
+
+ *post_bit_err =
+ ((data[0] & 0x3F) << 16) | (data[1] << 8) | data[2];
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x9D, data, 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ plp_code_rate =
+ (enum cxd2880_dvbt2_plp_code_rate)(data[0] & 0x07);
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0xA0, data, 1) != CXD2880_RESULT_OK) {
+ slvt_unfreeze_reg(tnrdmd);
+ return CXD2880_RESULT_ERROR_IO;
+ }
+
+ plp_fec_type = (enum cxd2880_dvbt2_plp_fec)(data[0] & 0x03);
+
+ slvt_unfreeze_reg(tnrdmd);
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x20) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x72, data, 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ period_exp = data[0] & 0x0F;
+
+ if ((plp_fec_type > CXD2880_DVBT2_FEC_LDPC_64K) ||
+ (plp_code_rate > CXD2880_DVBT2_R2_5))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ n_bch = n_bch_bits_lookup[plp_fec_type][plp_code_rate];
+ }
+
+ if (*post_bit_err > ((1U << period_exp) * n_bch))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *post_bit_count = (1U << period_exp) * n_bch;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_read_block_err_t(
+ struct cxd2880_tnrdmd *tnrdmd,
+ u32 *block_err,
+ u32 *block_count)
+{
+ u8 rdata[3];
+
+ if ((!tnrdmd) || (!block_err) || (!block_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x0D) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x18, rdata, 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *block_err = (rdata[1] << 8) | rdata[2];
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x10) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x5C, rdata, 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *block_count = 1U << (rdata[0] & 0x0F);
+
+ if ((*block_count == 0) || (*block_err > *block_count))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ return CXD2880_RESULT_OK;
+}
+
+static enum cxd2880_ret cxd2880_read_block_err_t2(
+ struct cxd2880_tnrdmd *tnrdmd,
+ u32 *block_err,
+ u32 *block_count)
+{
+ if ((!tnrdmd) || (!block_err) || (!block_count))
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->diver_mode == CXD2880_TNRDMD_DIVERMODE_SUB)
+ return CXD2880_RESULT_ERROR_ARG;
+
+ if (tnrdmd->state != CXD2880_TNRDMD_STATE_ACTIVE)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+ if (tnrdmd->sys != CXD2880_DTV_SYS_DVBT2)
+ return CXD2880_RESULT_ERROR_SW_STATE;
+
+ {
+ u8 rdata[3];
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x0B) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x18, rdata, 3) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if ((rdata[0] & 0x01) == 0)
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ *block_err = (rdata[1] << 8) | rdata[2];
+
+ if (tnrdmd->io->write_reg(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0x00, 0x24) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ if (tnrdmd->io->read_regs(tnrdmd->io, CXD2880_IO_TGT_DMD,
+ 0xDC, rdata, 1) != CXD2880_RESULT_OK)
+ return CXD2880_RESULT_ERROR_IO;
+
+ *block_count = 1U << (rdata[0] & 0x0F);
+ }
+
+ if ((*block_count == 0) || (*block_err > *block_count))
+ return CXD2880_RESULT_ERROR_HW_STATE;
+
+ return CXD2880_RESULT_OK;
+}
+
+static void cxd2880_release(struct dvb_frontend *fe)
+{
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("%s: invalid arg.\n", __func__);
+ return;
+ }
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ kfree(priv);
+}
+
+static int cxd2880_init(struct dvb_frontend *fe)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_priv *priv = NULL;
+ struct cxd2880_tnrdmd_create_param create_param;
+
+ if (!fe) {
+ pr_err("%s: invalid arg.\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+ create_param.ts_output_if = CXD2880_TNRDMD_TSOUT_IF_SPI;
+ create_param.xtal_share_type = CXD2880_TNRDMD_XTAL_SHARE_NONE;
+ create_param.en_internal_ldo = 1;
+ create_param.xosc_cap = 18;
+ create_param.xosc_i = 8;
+ create_param.stationary_use = 1;
+
+ mutex_lock(priv->spi_mutex);
+ if (priv->tnrdmd.io != &priv->regio) {
+ ret = cxd2880_tnrdmd_create(&priv->tnrdmd,
+ &priv->regio, &create_param);
+ if (ret != CXD2880_RESULT_OK) {
+ mutex_unlock(priv->spi_mutex);
+ dev_info(&priv->spi->dev,
+ "%s: cxd2880 tnrdmd create failed %d\n",
+ __func__, ret);
+ return return_tbl[ret];
+ }
+ }
+ ret = cxd2880_integ_init(&priv->tnrdmd);
+ if (ret != CXD2880_RESULT_OK) {
+ mutex_unlock(priv->spi_mutex);
+ dev_err(&priv->spi->dev, "%s: cxd2880 integ init failed %d\n",
+ __func__, ret);
+ return return_tbl[ret];
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ dev_dbg(&priv->spi->dev, "%s: OK.\n", __func__);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_sleep(struct dvb_frontend *fe)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_priv *priv = NULL;
+
+ if (!fe) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_sleep(&priv->tnrdmd);
+ mutex_unlock(priv->spi_mutex);
+
+ dev_dbg(&priv->spi->dev, "%s: tnrdmd_sleep ret %d\n",
+ __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+ int level = 0;
+
+ if ((!fe) || (!strength)) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if ((c->delivery_system == SYS_DVBT) ||
+ (c->delivery_system == SYS_DVBT2)) {
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &level);
+ } else {
+ dev_dbg(&priv->spi->dev, "%s: invalid system\n", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ level /= 125;
+ /* -105dBm - -30dBm (-105000/125 = -840, -30000/125 = -240 */
+ level = clamp(level, -840, -240);
+ /* scale value to 0x0000-0xFFFF */
+ *strength = (u16)(((level + 840) * 0xFFFF) / (-240 + 840));
+
+ if (ret != CXD2880_RESULT_OK)
+ dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ int snrvalue = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if ((!fe) || (!snr)) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_snr(&priv->tnrdmd,
+ &snrvalue);
+ } else {
+ dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (snrvalue < 0)
+ snrvalue = 0;
+ *snr = (u16)snrvalue;
+
+ if (ret != CXD2880_RESULT_OK)
+ dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if ((!fe) || (!ucblocks)) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_packet_error_number(
+ &priv->tnrdmd,
+ ucblocks);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_packet_error_number(
+ &priv->tnrdmd,
+ ucblocks);
+ } else {
+ dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (ret != CXD2880_RESULT_OK)
+ dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if ((!fe) || (!ber)) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_pre_rsber(&priv->tnrdmd,
+ ber);
+ /* x100 to change unit.(10^7 -> 10^9 */
+ *ber *= 100;
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_pre_bchber(&priv->tnrdmd,
+ ber);
+ } else {
+ dev_err(&priv->spi->dev, "%s: invlaid system\n", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+
+ if (ret != CXD2880_RESULT_OK)
+ dev_dbg(&priv->spi->dev, "%s: ret = %d\n", __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_set_frontend(struct dvb_frontend *fe)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ struct dtv_frontend_properties *c;
+ struct cxd2880_priv *priv;
+ enum cxd2880_dtv_bandwidth bw = CXD2880_DTV_BW_1_7_MHZ;
+
+ if (!fe) {
+ pr_err("%s: inavlid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+
+ switch (c->bandwidth_hz) {
+ case 1712000:
+ bw = CXD2880_DTV_BW_1_7_MHZ;
+ break;
+ case 5000000:
+ bw = CXD2880_DTV_BW_5_MHZ;
+ break;
+ case 6000000:
+ bw = CXD2880_DTV_BW_6_MHZ;
+ break;
+ case 7000000:
+ bw = CXD2880_DTV_BW_7_MHZ;
+ break;
+ case 8000000:
+ bw = CXD2880_DTV_BW_8_MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_info(&priv->spi->dev, "%s: sys:%d freq:%d bw:%d\n", __func__,
+ c->delivery_system, c->frequency, bw);
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT;
+ priv->dvbt_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt_tune_param.bandwidth = bw;
+ priv->dvbt_tune_param.profile = CXD2880_DVBT_PROFILE_HP;
+ ret = cxd2880_integ_dvbt_tune(&priv->tnrdmd,
+ &priv->dvbt_tune_param);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ priv->tnrdmd.sys = CXD2880_DTV_SYS_DVBT2;
+ priv->dvbt2_tune_param.center_freq_khz = c->frequency / 1000;
+ priv->dvbt2_tune_param.bandwidth = bw;
+ priv->dvbt2_tune_param.data_plp_id = (u16)c->stream_id;
+ ret = cxd2880_integ_dvbt2_tune(&priv->tnrdmd,
+ &priv->dvbt2_tune_param);
+ } else {
+ dev_err(&priv->spi->dev, "%s: invalid system\n", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+ mutex_unlock(priv->spi_mutex);
+ dev_info(&priv->spi->dev, "%s: tune result %d\n", __func__, ret);
+
+ return return_tbl[ret];
+}
+
+static int cxd2880_read_status(struct dvb_frontend *fe,
+ enum fe_status *status)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ u8 sync = 0;
+ u8 lock = 0;
+ u8 unlock = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct dtv_frontend_properties *c = NULL;
+
+ if ((!fe) || (!status)) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+ c = &fe->dtv_property_cache;
+ *status = 0;
+
+ if (priv->tnrdmd.state == CXD2880_TNRDMD_STATE_ACTIVE) {
+ mutex_lock(priv->spi_mutex);
+ if (c->delivery_system == SYS_DVBT) {
+ ret = cxd2880_tnrdmd_dvbt_mon_sync_stat(
+ &priv->tnrdmd,
+ &sync,
+ &lock,
+ &unlock);
+ } else if (c->delivery_system == SYS_DVBT2) {
+ ret = cxd2880_tnrdmd_dvbt2_mon_sync_stat(
+ &priv->tnrdmd,
+ &sync,
+ &lock,
+ &unlock);
+ } else {
+ dev_err(&priv->spi->dev,
+ "%s: invlaid system", __func__);
+ mutex_unlock(priv->spi_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(priv->spi_mutex);
+ if (ret != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev, "%s: failed. sys = %d\n",
+ __func__, priv->tnrdmd.sys);
+ return return_tbl[ret];
+ }
+
+ if (sync == 6) {
+ *status = FE_HAS_SIGNAL |
+ FE_HAS_CARRIER;
+ }
+ if (lock)
+ *status |= FE_HAS_VITERBI |
+ FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+
+ dev_dbg(&priv->spi->dev, "%s: status %d result %d\n", __func__,
+ *status, ret);
+
+ return return_tbl[CXD2880_RESULT_OK];
+}
+
+static int cxd2880_tune(struct dvb_frontend *fe,
+ bool retune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ enum fe_status *status)
+{
+ int ret = 0;
+
+ if ((!fe) || (!delay) || (!status)) {
+ pr_err("%s: invalid arg.", __func__);
+ return -EINVAL;
+ }
+
+ if (retune) {
+ ret = cxd2880_set_frontend(fe);
+ if (ret) {
+ pr_err("%s: cxd2880_set_frontend failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ *delay = HZ / 5;
+
+ return cxd2880_read_status(fe, status);
+}
+
+static int cxd2880_get_frontend_t(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ int result = 0;
+ struct cxd2880_priv *priv = NULL;
+ enum cxd2880_dvbt_mode mode = CXD2880_DVBT_MODE_2K;
+ enum cxd2880_dvbt_guard guard = CXD2880_DVBT_GUARD_1_32;
+ struct cxd2880_dvbt_tpsinfo tps;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+ u32 pre_bit_err = 0, pre_bit_count = 0;
+ u32 post_bit_err = 0, post_bit_count = 0;
+ u32 block_err = 0, block_count = 0;
+
+ if ((!fe) || (!c)) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_mode_guard(&priv->tnrdmd,
+ &mode, &guard);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (mode) {
+ case CXD2880_DVBT_MODE_2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT_MODE_8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ dev_err(&priv->spi->dev, "%s: get invalid mode %d\n",
+ __func__, mode);
+ break;
+ }
+ switch (guard) {
+ case CXD2880_DVBT_GUARD_1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT_GUARD_1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT_GUARD_1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT_GUARD_1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ dev_err(&priv->spi->dev, "%s: get invalid guard %d\n",
+ __func__, guard);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ dev_dbg(&priv->spi->dev,
+ "%s: ModeGuard err %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_tps_info(&priv->tnrdmd, &tps);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (tps.hierarchy) {
+ case CXD2880_DVBT_HIERARCHY_NON:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case CXD2880_DVBT_HIERARCHY_1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case CXD2880_DVBT_HIERARCHY_2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case CXD2880_DVBT_HIERARCHY_4:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ default:
+ c->hierarchy = HIERARCHY_NONE;
+ dev_err(&priv->spi->dev,
+ "%s: TPSInfo hierarchy invalid %d\n",
+ __func__, tps.hierarchy);
+ break;
+ }
+
+ switch (tps.rate_hp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_HP = FEC_NONE;
+ dev_err(&priv->spi->dev,
+ "%s: TPSInfo rateHP invalid %d\n",
+ __func__, tps.rate_hp);
+ break;
+ }
+ switch (tps.rate_lp) {
+ case CXD2880_DVBT_CODERATE_1_2:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case CXD2880_DVBT_CODERATE_2_3:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case CXD2880_DVBT_CODERATE_3_4:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case CXD2880_DVBT_CODERATE_5_6:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case CXD2880_DVBT_CODERATE_7_8:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ default:
+ c->code_rate_LP = FEC_NONE;
+ dev_err(&priv->spi->dev,
+ "%s: TPSInfo rateLP invalid %d\n",
+ __func__, tps.rate_lp);
+ break;
+ }
+ switch (tps.constellation) {
+ case CXD2880_DVBT_CONSTELLATION_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_16QAM:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT_CONSTELLATION_64QAM:
+ c->modulation = QAM_64;
+ break;
+ default:
+ c->modulation = QPSK;
+ dev_err(&priv->spi->dev,
+ "%s: TPSInfo constellation invalid %d\n",
+ __func__, tps.constellation);
+ break;
+ }
+ } else {
+ c->hierarchy = HIERARCHY_NONE;
+ c->code_rate_HP = FEC_NONE;
+ c->code_rate_LP = FEC_NONE;
+ c->modulation = QPSK;
+ dev_dbg(&priv->spi->dev,
+ "%s: TPS info err %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ dev_err(&priv->spi->dev,
+ "%s: spectrum sense invalid %d\n",
+ __func__, sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ dev_dbg(&priv->spi->dev,
+ "%s: spectrum_sense %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev, "%s: mon_rf_lvl %d\n",
+ __func__, result);
+ }
+
+ result = cxd2880_read_snr(fe, &snr);
+ if (!result) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_pre_bit_err_t(&priv->tnrdmd, &pre_bit_err,
+ &pre_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[0].uvalue = pre_bit_err;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[0].uvalue = pre_bit_count;
+ } else {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: pre_bit_error_t failed %d\n",
+ __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_post_bit_err_t(&priv->tnrdmd,
+ &post_bit_err, &post_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = post_bit_err;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = post_bit_count;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: post_bit_err_t %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_read_block_err_t(&priv->tnrdmd,
+ &block_err, &block_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = block_err;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue = block_count;
+ } else {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: read_block_err_t %d\n", __func__, ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend_t2(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ int result = 0;
+ struct cxd2880_priv *priv = NULL;
+ struct cxd2880_dvbt2_l1pre l1pre;
+ enum cxd2880_dvbt2_plp_code_rate coderate;
+ enum cxd2880_dvbt2_plp_constell qam;
+ enum cxd2880_tnrdmd_spectrum_sense sense;
+ u16 snr = 0;
+ int strength = 0;
+ u32 pre_bit_err = 0, pre_bit_count = 0;
+ u32 post_bit_err = 0, post_bit_count = 0;
+ u32 block_err = 0, block_count = 0;
+
+ if ((!fe) || (!c)) {
+ pr_err("%s: invalid arg.\n", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_l1_pre(&priv->tnrdmd, &l1pre);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (l1pre.fft_mode) {
+ case CXD2880_DVBT2_M2K:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case CXD2880_DVBT2_M8K:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ case CXD2880_DVBT2_M4K:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case CXD2880_DVBT2_M1K:
+ c->transmission_mode = TRANSMISSION_MODE_1K;
+ break;
+ case CXD2880_DVBT2_M16K:
+ c->transmission_mode = TRANSMISSION_MODE_16K;
+ break;
+ case CXD2880_DVBT2_M32K:
+ c->transmission_mode = TRANSMISSION_MODE_32K;
+ break;
+ default:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ dev_err(&priv->spi->dev,
+ "%s: L1Pre fft_mode invalid %d\n",
+ __func__, l1pre.fft_mode);
+ break;
+ }
+ switch (l1pre.gi) {
+ case CXD2880_DVBT2_G1_32:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case CXD2880_DVBT2_G1_16:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case CXD2880_DVBT2_G1_8:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case CXD2880_DVBT2_G1_4:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case CXD2880_DVBT2_G1_128:
+ c->guard_interval = GUARD_INTERVAL_1_128;
+ break;
+ case CXD2880_DVBT2_G19_128:
+ c->guard_interval = GUARD_INTERVAL_19_128;
+ break;
+ case CXD2880_DVBT2_G19_256:
+ c->guard_interval = GUARD_INTERVAL_19_256;
+ break;
+ default:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ dev_err(&priv->spi->dev,
+ "%s: L1Pre gi invalid %d\n",
+ __func__, l1pre.gi);
+ break;
+ }
+ } else {
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ dev_dbg(&priv->spi->dev,
+ "%s: L1Pre err %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_code_rate(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &coderate);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (coderate) {
+ case CXD2880_DVBT2_R1_2:
+ c->fec_inner = FEC_1_2;
+ break;
+ case CXD2880_DVBT2_R3_5:
+ c->fec_inner = FEC_3_5;
+ break;
+ case CXD2880_DVBT2_R2_3:
+ c->fec_inner = FEC_2_3;
+ break;
+ case CXD2880_DVBT2_R3_4:
+ c->fec_inner = FEC_3_4;
+ break;
+ case CXD2880_DVBT2_R4_5:
+ c->fec_inner = FEC_4_5;
+ break;
+ case CXD2880_DVBT2_R5_6:
+ c->fec_inner = FEC_5_6;
+ break;
+ default:
+ c->fec_inner = FEC_NONE;
+ dev_err(&priv->spi->dev,
+ "%s: CodeRate invalid %d\n",
+ __func__, coderate);
+ break;
+ }
+ } else {
+ c->fec_inner = FEC_NONE;
+ dev_dbg(&priv->spi->dev, "%s: CodeRate %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_qam(&priv->tnrdmd,
+ CXD2880_DVBT2_PLP_DATA,
+ &qam);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (qam) {
+ case CXD2880_DVBT2_QPSK:
+ c->modulation = QPSK;
+ break;
+ case CXD2880_DVBT2_QAM16:
+ c->modulation = QAM_16;
+ break;
+ case CXD2880_DVBT2_QAM64:
+ c->modulation = QAM_64;
+ break;
+ case CXD2880_DVBT2_QAM256:
+ c->modulation = QAM_256;
+ break;
+ default:
+ c->modulation = QPSK;
+ dev_err(&priv->spi->dev,
+ "%s: QAM invalid %d\n",
+ __func__, qam);
+ break;
+ }
+ } else {
+ c->modulation = QPSK;
+ dev_dbg(&priv->spi->dev, "%s: QAM %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_dvbt2_mon_spectrum_sense(&priv->tnrdmd, &sense);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ switch (sense) {
+ case CXD2880_TNRDMD_SPECTRUM_NORMAL:
+ c->inversion = INVERSION_OFF;
+ break;
+ case CXD2880_TNRDMD_SPECTRUM_INV:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ c->inversion = INVERSION_OFF;
+ dev_err(&priv->spi->dev,
+ "%s: spectrum sense invalid %d\n",
+ __func__, sense);
+ break;
+ }
+ } else {
+ c->inversion = INVERSION_OFF;
+ dev_dbg(&priv->spi->dev,
+ "%s: SpectrumSense %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_tnrdmd_mon_rf_lvl(&priv->tnrdmd, &strength);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = strength;
+ } else {
+ c->strength.len = 1;
+ c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: mon_rf_lvl %d\n", __func__, ret);
+ }
+
+ result = cxd2880_read_snr(fe, &snr);
+ if (!result) {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ c->cnr.stat[0].svalue = snr;
+ } else {
+ c->cnr.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev, "%s: read_snr %d\n", __func__, result);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_pre_bit_err_t2(&priv->tnrdmd,
+ &pre_bit_err,
+ &pre_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_error.stat[0].uvalue = pre_bit_err;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->pre_bit_count.stat[0].uvalue = pre_bit_count;
+ } else {
+ c->pre_bit_error.len = 1;
+ c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->pre_bit_count.len = 1;
+ c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: read_bit_err_t2 %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_post_bit_err_t2(&priv->tnrdmd,
+ &post_bit_err, &post_bit_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue = post_bit_err;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue = post_bit_count;
+ } else {
+ c->post_bit_error.len = 1;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.len = 1;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: post_bit_err_t2 %d\n", __func__, ret);
+ }
+
+ mutex_lock(priv->spi_mutex);
+ ret = cxd2880_read_block_err_t2(&priv->tnrdmd,
+ &block_err, &block_count);
+ mutex_unlock(priv->spi_mutex);
+ if (ret == CXD2880_RESULT_OK) {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = block_err;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue = block_count;
+ } else {
+ c->block_error.len = 1;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.len = 1;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ dev_dbg(&priv->spi->dev,
+ "%s: read_block_err_t2 %d\n", __func__, ret);
+ }
+
+ return 0;
+}
+
+static int cxd2880_get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *props)
+{
+ struct cxd2880_priv *priv = NULL;
+ int result = 0;
+
+ if ((!fe) || (!props)) {
+ pr_err("%s: invalid arg.", __func__);
+ return -EINVAL;
+ }
+
+ priv = (struct cxd2880_priv *)fe->demodulator_priv;
+
+ dev_dbg(&priv->spi->dev, "%s: system=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ result = cxd2880_get_frontend_t(fe, props);
+ break;
+ case SYS_DVBT2:
+ result = cxd2880_get_frontend_t2(fe, props);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+
+ return result;
+}
+
+static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops;
+
+struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe,
+ struct cxd2880_config *cfg)
+{
+ enum cxd2880_ret ret = CXD2880_RESULT_OK;
+ enum cxd2880_tnrdmd_chip_id chipid =
+ CXD2880_TNRDMD_CHIP_ID_UNKNOWN;
+ static struct cxd2880_priv *priv;
+ u8 data = 0;
+
+ if (!fe) {
+ pr_err("%s: invalid arg.\n", __func__);
+ return NULL;
+ }
+
+ priv = kzalloc(sizeof(struct cxd2880_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->spi = cfg->spi;
+ priv->spi_mutex = cfg->spi_mutex;
+ priv->spi_device.spi = cfg->spi;
+
+ memcpy(&fe->ops, &cxd2880_dvbt_t2_ops,
+ sizeof(struct dvb_frontend_ops));
+
+ ret = cxd2880_spi_device_initialize(&priv->spi_device,
+ CXD2880_SPI_MODE_0,
+ 55000000);
+ if (ret != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev,
+ "%s: spi_device_initialize failed. %d\n",
+ __func__, ret);
+ kfree(priv);
+ return NULL;
+ }
+
+ ret = cxd2880_spi_device_create_spi(&priv->cxd2880_spi,
+ &priv->spi_device);
+ if (ret != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev,
+ "%s: spi_device_create_spi failed. %d\n",
+ __func__, ret);
+ kfree(priv);
+ return NULL;
+ }
+
+ ret = cxd2880_io_spi_create(&priv->regio, &priv->cxd2880_spi, 0);
+ if (ret != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev,
+ "%s: io_spi_create failed. %d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+ }
+ if (priv->regio.write_reg(&priv->regio, CXD2880_IO_TGT_SYS, 0x00, 0x00)
+ != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev,
+ "%s: set bank to 0x00 failed.\n", __func__);
+ kfree(priv);
+ return NULL;
+ }
+ if (priv->regio.read_regs(&priv->regio,
+ CXD2880_IO_TGT_SYS, 0xFD, &data, 1)
+ != CXD2880_RESULT_OK) {
+ dev_err(&priv->spi->dev,
+ "%s: read chip id failed.\n", __func__);
+ kfree(priv);
+ return NULL;
+ }
+
+ chipid = (enum cxd2880_tnrdmd_chip_id)data;
+ if ((chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_0X) &&
+ (chipid != CXD2880_TNRDMD_CHIP_ID_CXD2880_ES1_11)) {
+ dev_err(&priv->spi->dev,
+ "%s: chip id invalid.\n", __func__);
+ kfree(priv);
+ return NULL;
+ }
+
+ fe->demodulator_priv = priv;
+ dev_info(&priv->spi->dev,
+ "CXD2880 driver version: Ver %s\n",
+ CXD2880_TNRDMD_DRIVER_VERSION);
+
+ return fe;
+}
+EXPORT_SYMBOL(cxd2880_attach);
+
+static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = {
+ .info = {
+ .name = "Sony CXD2880",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 1000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS,
+ },
+ .delsys = { SYS_DVBT, SYS_DVBT2 },
+
+ .release = cxd2880_release,
+ .init = cxd2880_init,
+ .sleep = cxd2880_sleep,
+ .tune = cxd2880_tune,
+ .set_frontend = cxd2880_set_frontend,
+ .get_frontend = cxd2880_get_frontend,
+ .read_status = cxd2880_read_status,
+ .read_ber = cxd2880_read_ber,
+ .read_signal_strength = cxd2880_read_signal_strength,
+ .read_snr = cxd2880_read_snr,
+ .read_ucblocks = cxd2880_read_ucblocks,
+ .get_frontend_algo = cxd2880_get_frontend_algo,
+};
+
+MODULE_DESCRIPTION(
+"Sony CXD2880 DVB-T2/T tuner + demodulator drvier");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/5] media: Add support for CXD2880 SPI I/F
From: Yasunari.Takiguchi @ 2017-04-06 7:55 UTC (permalink / raw)
To: linux-kernel, linux-media, devicetree
Cc: tbird20d, frowand.list, Yasunari Takiguchi, Masayuki Yamamoto,
Hideki Nozawa, Kota Yonezawa, Toshihiko Matsumoto,
Satoshi Watanabe
From: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
This is the driver for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the SPI adapter part of the driver.
Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto@sony.com>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa@sony.com>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa@sony.com>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto@sony.com>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe@sony.com>
---
drivers/media/spi/Kconfig | 14 +
drivers/media/spi/Makefile | 5 +
drivers/media/spi/cxd2880-spi.c | 727 +++++++++++++++++++++++++++++++++++++++
3 files changed, 746 insertions(+)
create mode 100644 drivers/media/spi/cxd2880-spi.c
diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
index a21f5a3..b07ac86 100644
--- a/drivers/media/spi/Kconfig
+++ b/drivers/media/spi/Kconfig
@@ -12,3 +12,17 @@ config VIDEO_GS1662
endmenu
endif
+
+if SPI
+menu "Media SPI Adapters"
+
+config CXD2880_SPI_DRV
+ tristate "Sony CXD2880 SPI support"
+ depends on DVB_CORE && SPI
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Choose if you would like to have SPI interface support for Sony CXD2880.
+
+endmenu
+
+endif
diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile
index ea64013..40e0f88 100644
--- a/drivers/media/spi/Makefile
+++ b/drivers/media/spi/Makefile
@@ -1 +1,6 @@
obj-$(CONFIG_VIDEO_GS1662) += gs1662.o
+obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/dvb-frontends/cxd2880
\ No newline at end of file
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
new file mode 100644
index 0000000..7ba0ed8
--- /dev/null
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -0,0 +1,727 @@
+/*
+ * cxd2880-spi.c
+ * Sony CXD2880 DVB-T2/T tuner + demodulator driver
+ * SPI adapter
+ *
+ * Copyright (C) 2016, 2017 Sony Semiconductor Solutions Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/spi/spi.h>
+
+#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_PACKET 300
+
+struct cxd2880_ts_buf_info {
+ u8 read_ready;
+ u8 almost_full;
+ u8 almost_empty;
+ u8 overflow;
+ u8 underflow;
+ u16 packet_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;
+ int ret = 0;
+
+ if ((!spi) || (!data)) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&tx, 0, sizeof(tx));
+ tx.tx_buf = data;
+ tx.len = size;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&tx, &msg);
+ ret = spi_sync(spi, &msg);
+
+ return ret;
+}
+
+static int cxd2880_write_reg(struct spi_device *spi,
+ u8 subAddress, 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("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+ if (size > BURST_WRITE_MAX) {
+ pr_err("%s: data size > WRITE_MAX\n", __func__);
+ return -EINVAL;
+ }
+
+ if (subAddress + size > 0x100) {
+ pr_err("%s: out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ send_data[0] = 0x0E;
+ write_data_top = data;
+
+ while (size > 0) {
+ send_data[1] = subAddress;
+ 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) {
+ dev_err(&spi->dev, "%s: write spi failed %d\n",
+ __func__, ret);
+ break;
+ }
+ subAddress += 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 = 0;
+ u8 data[3];
+ struct spi_message message;
+ struct spi_transfer transfer[2];
+
+ if ((!spi) || (!read_data) || (!packet_num)) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+ if (packet_num > 0xFFFF) {
+ dev_err(&spi->dev, "%s: packet num > 0xFFFF\n", __func__);
+ return -EINVAL;
+ }
+
+ data[0] = 0x10;
+ data[1] = (u8)((packet_num >> 8) & 0xFF);
+ data[2] = (u8)(packet_num & 0xFF);
+
+ 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)
+ dev_err(&spi->dev, "%s: spi_write_then_read failed\n",
+ __func__);
+
+ 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 = 0;
+
+ if ((!spi) || (!info)) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = spi_write_then_read(spi, &send_data, 1,
+ recv_data, sizeof(recv_data));
+ if (ret)
+ dev_err(&spi->dev,
+ "%s: spi_write_then_read failed\n", __func__);
+
+ info->read_ready = (u8)((recv_data[0] & 0x80) ? 1 : 0);
+ info->almost_full = (u8)((recv_data[0] & 0x40) ? 1 : 0);
+ info->almost_empty = (u8)((recv_data[0] & 0x20) ? 1 : 0);
+ info->overflow = (u8)((recv_data[0] & 0x10) ? 1 : 0);
+ info->underflow = (u8)((recv_data[0] & 0x08) ? 1 : 0);
+ info->packet_num = (u16)(((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 = 0;
+
+ ret = cxd2880_write_spi(spi, &data, 1);
+
+ if (ret)
+ pr_err("%s: write spi failed\n", __func__);
+
+ return ret;
+}
+
+static int cxd2880_set_pid_filter(struct spi_device *spi,
+ struct cxd2880_pid_filter_config *cfg)
+{
+ u8 data[65];
+
+ if (!spi) {
+ pr_err("%s: ivnalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ data[0] = 0x00;
+ if (cxd2880_write_reg(spi, 0x00, &data[0], 1) != 0)
+ return -EIO;
+ if (!cfg) {
+ data[0] = 0x02;
+ if (cxd2880_write_reg(spi, 0x50, &data[0], 1) != 0)
+ return -EIO;
+ } else {
+ data[0] = (u8)(cfg->is_negative ? 0x01 : 0x00);
+ {
+ int i = 0;
+ u16 pid = 0;
+
+ 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)] =
+ (u8)((u8)(pid >> 8) | 0x20);
+ data[2 + (i * 2)] =
+ (u8)(pid & 0xFF);
+ } else {
+ data[1 + (i * 2)] = 0x00;
+ data[2 + (i * 2)] = 0x00;
+ }
+ }
+ }
+ if (cxd2880_write_reg(spi, 0x50, data, 65) != 0)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+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 = 0;
+
+ if ((!dvb_spi) || (!cfg)) {
+ pr_err("%s: invalid arg.\n", __func__);
+ 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) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: set_pid_filter failed\n", __func__);
+ }
+
+ return ret;
+}
+
+static int cxd2880_ts_read(void *arg)
+{
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+ struct cxd2880_ts_buf_info info;
+ struct timespec ts;
+ long elapsed = 0;
+ long starttime = 0;
+ u32 i;
+ int ret;
+
+ dvb_spi = (struct cxd2880_dvb_spi *)arg;
+ if (!dvb_spi) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = cxd2880_spi_clear_ts_buffer(dvb_spi->spi);
+ if (ret) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: set_clear_ts_buffer failed\n", __func__);
+ return ret;
+ }
+
+ getnstimeofday(&ts);
+ starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+ while (!kthread_should_stop()) {
+ getnstimeofday(&ts);
+ elapsed =
+ ((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000))
+ - starttime;
+ ret = cxd2880_spi_read_ts_buffer_info(dvb_spi->spi,
+ &info);
+ if (ret) {
+ pr_err("%s: spi_read_ts_buffer_info error\n",
+ __func__);
+ return ret;
+ }
+
+ if (info.packet_num > MAX_TRANS_PACKET) {
+ for (i = 0; i < info.packet_num / MAX_TRANS_PACKET;
+ i++) {
+ cxd2880_spi_read_ts(dvb_spi->spi,
+ dvb_spi->ts_buf,
+ MAX_TRANS_PACKET);
+ dvb_dmx_swfilter(&dvb_spi->demux,
+ dvb_spi->ts_buf,
+ MAX_TRANS_PACKET * 188);
+ }
+ starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+ } else if ((info.packet_num > 0) && (elapsed >= 500)) {
+ cxd2880_spi_read_ts(dvb_spi->spi,
+ dvb_spi->ts_buf,
+ info.packet_num);
+ dvb_dmx_swfilter(&dvb_spi->demux,
+ dvb_spi->ts_buf,
+ info.packet_num * 188);
+ starttime = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+ } 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("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ demux = feed->demux;
+ if (!demux) {
+ pr_err("%s: feed->demux is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
+
+ if (dvb_spi->feed_count == CXD2880_MAX_FILTER_SIZE) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: Exceeded maximum PID count (32).", __func__);
+ dev_err(&dvb_spi->spi->dev,
+ "Selected PID cannot be enabled.\n");
+ return -EBUSY;
+ }
+
+ 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) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: update pid filter failed\n",
+ __func__);
+ return ret;
+ }
+ }
+ dvb_spi->all_pid_feed_count++;
+
+ dev_dbg(&dvb_spi->spi->dev,
+ "%s: all PID feed (count = %d)\n",
+ __func__, 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;
+ dev_dbg(&dvb_spi->spi->dev,
+ "%s: store PID %d to #%d\n",
+ __func__, feed->pid, i);
+ break;
+ }
+ }
+ if (i == CXD2880_MAX_FILTER_SIZE) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: PID filter is full. Assumed bug.\n",
+ __func__);
+ return -EBUSY;
+ }
+ 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(sizeof(u8) * MAX_TRANS_PACKET * 188,
+ GFP_KERNEL | GFP_DMA);
+ if (!dvb_spi->ts_buf) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: ts buffer allocate failed\n", __func__);
+ 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)) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: kthread_run failed/\n",
+ __func__);
+ 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++;
+
+ dev_dbg(&dvb_spi->spi->dev, "%s: start feed (count %d)\n",
+ __func__, dvb_spi->feed_count);
+ return 0;
+}
+
+static int cxd2880_stop_feed(struct dvb_demux_feed *feed)
+{
+ int i = 0;
+ int ret = 0;
+ struct dvb_demux *demux = NULL;
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+
+ if (!feed) {
+ pr_err("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ demux = feed->demux;
+ if (!demux) {
+ pr_err("%s: feed->demux is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dvb_spi = (struct cxd2880_dvb_spi *)demux->priv;
+
+ if (!dvb_spi->feed_count) {
+ dev_warn(&dvb_spi->spi->dev,
+ "%s: no feed is started\n", __func__);
+ 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) {
+ dev_warn(&dvb_spi->spi->dev,
+ "%s: PID %d not found.\n",
+ __func__, 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].pid = 0;
+ dev_dbg(&dvb_spi->spi->dev,
+ "%s: removed PID %d from #%d\n",
+ __func__, feed->pid, i);
+ break;
+ }
+ }
+ dvb_spi->filter_config = cfgtmp;
+
+ if (i == CXD2880_MAX_FILTER_SIZE) {
+ dev_warn(&dvb_spi->spi->dev, "%s: PID %d not found\n",
+ __func__, 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) {
+ dev_err(&dvb_spi->spi->dev,
+ "%s: cxd2880_ts_read thread didn't terminate normally\n",
+ __func__);
+ ret = ret_stop;
+ }
+ kfree(dvb_spi->ts_buf);
+ dvb_spi->ts_buf = NULL;
+ }
+
+ dev_dbg(&dvb_spi->spi->dev, "%s: stop feed ok.(count %d)\n",
+ __func__, 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 = 0;
+ struct cxd2880_dvb_spi *dvb_spi = NULL;
+ struct cxd2880_config config;
+
+ if (!spi) {
+ pr_err("%s: invalid arg.\n", __func__);
+ 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) {
+ dev_err(&spi->dev, "%s: dvb_register_adapter() failed\n",
+ __func__);
+ goto fail_adapter;
+ }
+
+ if (!dvb_attach(cxd2880_attach, &dvb_spi->dvb_fe, &config)) {
+ dev_err(&spi->dev, "%s: cxd2880_attach failed\n", __func__);
+ goto fail_attach;
+ }
+
+ ret = dvb_register_frontend(&dvb_spi->adapter,
+ &dvb_spi->dvb_fe);
+ if (ret < 0) {
+ dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
+ __func__);
+ 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) {
+ dev_err(&spi->dev, "%s: dvb_dmx_init() failed\n", __func__);
+ 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) {
+ dev_err(&spi->dev, "%s: dvb_dmxdev_init() failed\n", __func__);
+ 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) {
+ dev_err(&spi->dev, "%s: add_frontend() failed\n", __func__);
+ goto fail_dmx_fe;
+ }
+
+ ret = dvb_spi->demux.dmx.connect_frontend(&dvb_spi->demux.dmx,
+ &dvb_spi->dmx_fe);
+ if (ret < 0) {
+ dev_err(&spi->dev, "%s: dvb_register_frontend() failed\n",
+ __func__);
+ goto fail_fe_conn;
+ }
+
+ dev_info(&spi->dev, "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("%s: invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ dvb_spi = (struct cxd2880_dvb_spi *)dev_get_drvdata(&spi->dev);
+
+ if (!dvb_spi) {
+ pr_err("%s: failed\n", __func__);
+ 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);
+ dev_info(&spi->dev, "%s: cxd2880_spi remove ok.\n", __func__);
+
+ 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 + demodulator drvier SPI adapter");
+MODULE_AUTHOR("Sony Semiconductor Solutions Corporation");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/5] dt-bindings: media: Add document file for CXD2880 SPI I/F
From: Yasunari.Takiguchi-7U/KSKJipcs @ 2017-04-06 7:54 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: tbird20d-Re5JQEeQqe8AvxtiuMwx3w,
frowand.list-Re5JQEeQqe8AvxtiuMwx3w, Yasunari Takiguchi,
Masayuki Yamamoto, Hideki Nozawa, Kota Yonezawa,
Toshihiko Matsumoto, Satoshi Watanabe
From: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
This is the document file for Sony CXD2880 DVB-T2/T tuner + demodulator.
It contains the description of the SPI adapter binding.
Signed-off-by: Yasunari Takiguchi <Yasunari.Takiguchi-7U/KSKJipcs@public.gmane.org>
Signed-off-by: Masayuki Yamamoto <Masayuki.Yamamoto-7U/KSKJipcs@public.gmane.org>
Signed-off-by: Hideki Nozawa <Hideki.Nozawa-7U/KSKJipcs@public.gmane.org>
Signed-off-by: Kota Yonezawa <Kota.Yonezawa-7U/KSKJipcs@public.gmane.org>
Signed-off-by: Toshihiko Matsumoto <Toshihiko.Matsumoto-7U/KSKJipcs@public.gmane.org>
Signed-off-by: Satoshi Watanabe <Satoshi.C.Watanabe-7U/KSKJipcs@public.gmane.org>
---
.../devicetree/bindings/media/spi/sony-cxd2880.txt | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
diff --git a/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
new file mode 100644
index 0000000..fc5aa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/spi/sony-cxd2880.txt
@@ -0,0 +1,14 @@
+Sony CXD2880 DVB-T2/T tuner + demodulator driver SPI adapter
+
+Required properties:
+- compatible: Should be "sony,cxd2880".
+- reg: SPI chip select number for the device.
+- spi-max-frequency: Maximum bus speed, should be set to <55000000> (55MHz).
+
+Example:
+
+cxd2880@0 {
+ compatible = "sony,cxd2880";
+ reg = <0>; /* CE0 */
+ spi-max-frequency = <55000000>; /* 55MHz */
+};
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox